├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── keymaps └── gulp-control.cson ├── lib ├── gulp-control-view.coffee └── gulp-control.coffee ├── menus └── gulp-control.cson ├── package.json ├── screenshots └── gulp-01.png ├── spec ├── gulp-control-spec.coffee └── gulp-control-view-spec.coffee └── styles └── gulp-control.less /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Development 2 | 3 | ## 0.4.2 4 | 5 | - Allow for running tasks that have ':' in their name, fixes [#5](https://github.com/jacogr/atom-gulp-control/issues/5) and [#14](https://github.com/jacogr/atom-gulp-control/issues/14) 6 | 7 | ... where there is smoke, there is fire. 8 | 9 | ## 0.4.1 10 | 11 | - Make test for gulpfile.[js|coffee] case insensitive, fixes [#13](https://github.com/jacogr/atom-gulp-control/issues/13) 12 | 13 | ... conform to gulp naming conventions 14 | 15 | ## 0.4.0 16 | 17 | - Use atom.project.getPaths() instead of atom.project.getPath() (1.0 deprecation) 18 | - View require now requires from atom-space-pen-views (1.0 deprecation) 19 | - Move styles from stylesheet/ into styles. [Bruno Sabot](https://github.com/brunosabot) contributed [#11](https://github.com/jacogr/atom-gulp-control/pull/11) 20 | - Add support for executing locally installed gulp. [Micah Zoltu](https://github.com/Zoltu) contributed [#9](https://github.com/jacogr/atom-gulp-control/pull/9) 21 | - Fix deprecation. [Amit Choukroun](https://github.com/amitmtrn) contributed [#4](https://github.com/jacogr/atom-gulp-control/pull/4) 22 | 23 | ... ready for the Atom 1.0 release 24 | 25 | ## 0.3.3 26 | 27 | - Updated description & README 28 | 29 | ... tidying up 30 | 31 | ## 0.3.2 32 | 33 | - Export the full current environment to the gulp sub-process 34 | - Explicitly adjust path on nix/OSX to include /usr/local/bin 35 | - Only display exit code in red when there has been an actual error 36 | - Use ui-variables colors everywhere to help with UI theme switching 37 | 38 | ... best practices for doing the UI, doesn't mean light works yet 39 | 40 | ## 0.3.1 41 | 42 | - Gulpfile name should be case-insensitive 43 | 44 | ... ouch. 45 | 46 | ## 0.3.0 47 | 48 | - Locate the Gulpfile.js/Gulpfile.coffee in your project automatically when not in the project root 49 | - Above fixes https://github.com/jacogr/atom-gulp-control/issues/1 50 | 51 | ... allowing for files not in the root 52 | 53 | ## 0.2.1 54 | 55 | - Output now in pre blocks, console outputs deserves console-look 56 | 57 | ... making things pretty 58 | 59 | ## 0.2.0 60 | 61 | - Tested on Windows, NOTE removed 62 | - Fixed Open not opening when selected again 63 | - ability to run multiple control tabs 64 | 65 | ... bring it close to decent and stable 66 | 67 | ## 0.1.1 68 | 69 | - Allow generic $PATH to be passed-in, should help Windows compatibility 70 | - Rename menu "Toggle" to "Open" to reflect actual action 71 | - Updated README to reflect where to get things 72 | 73 | ... going down the road of cleaning up 74 | 75 | ## 0.1.0 76 | 77 | - Initial version 78 | - Displays all gulp tasks as list, allowing for traversing 79 | - Allows execution of any task via click, re-starting on subsequent clicks 80 | - Captures gulp output, along with the original --color output 81 | 82 | ... the basics are there 83 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2015 Jaco Greeff 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Atom gulp-control 2 | 3 | ## What 4 | 5 | Displays a list of gulp tasks and allows execution within Atom. 6 | 7 | Tasks can be re-started, tracked and controlled from a central point. Open the control from the package menu and you are good to go. 8 | 9 | ![Gulp](https://raw.githubusercontent.com/jacogr/atom-gulp-control/master/screenshots/gulp-01.png) 10 | 11 | ## Why 12 | 13 | Reasons why this might work for you 14 | 15 | - Allows the ability to execute any gulp tasks directly within Atom 16 | - Allows a single overview of all tasks available to you 17 | - Gulpfile.[coffee|js] is automatically located, either withing the root project folder or a sub-folder 18 | 19 | ## Configuration 20 | 21 | Because Gulp requires the node executable, there is a potential for it to break if you use [nvm](https://github.com/creationix/nvm) or [nodebrew](https://github.com/hokaccha/nodebrew). If it doesn't initally work, you can specify node's bin folder in the package settings. 22 | 23 | *NOTE: if you type* `which node` *into the console you can get node's system path, but you need to remove "node" from the end of it for it to work.* 24 | 25 | ## Where 26 | 27 | The Atom package can be found on the Atom registry, [https://atom.io/packages/gulp-control](https://atom.io/packages/gulp-control). 28 | 29 | Pull requests, issues, feature requests are all welcome and encouraged via [https://github.com/jacogr/atom-gulp-control](https://github.com/jacogr/atom-gulp-control). 30 | -------------------------------------------------------------------------------- /keymaps/gulp-control.cson: -------------------------------------------------------------------------------- 1 | # Keybindings require three things to be fully defined: A selector that is 2 | # matched against the focused element, the keystroke and the command to 3 | # execute. 4 | # 5 | # Below is a basic keybinding which registers on all platforms by applying to 6 | # the root workspace element. 7 | 8 | # For more detailed documentation see 9 | # https://atom.io/docs/latest/advanced/keymaps 10 | 'atom-workspace': 11 | 'ctrl-alt-o': 'gulp-control:toggle' 12 | -------------------------------------------------------------------------------- /lib/gulp-control-view.coffee: -------------------------------------------------------------------------------- 1 | crypto = require 'crypto' 2 | fs = require 'fs' 3 | path = require 'path' 4 | 5 | {BufferedProcess} = require 'atom' 6 | {View, $} = require 'atom-space-pen-views' 7 | 8 | Convert = require 'ansi-to-html' 9 | convert = new Convert() 10 | 11 | module.exports = 12 | class GulpControlView extends View 13 | @content: -> 14 | @div class: 'gulp-control', => 15 | @ul class: 'tasks', outlet: 'taskList' 16 | @div class: 'output', outlet: 'outputPane' 17 | 18 | serialize: -> 19 | 20 | initialize: -> 21 | console.log 'GulpControlView: initialize' 22 | 23 | projpaths = atom.project.getPaths() 24 | if !projpaths or !projpaths.length or !projpaths[0] 25 | @writeOutput 'No project path found, aborting', 'error' 26 | return 27 | 28 | @click '.tasks li.task', (event) => 29 | target = $(event.target) 30 | task = target.text() 31 | if target.hasClass('running') && @process 32 | @process.kill() 33 | @process = null 34 | target.removeClass('active running') 35 | @writeOutput "Task '#{task}' stopped" 36 | else 37 | for t in @tasks when t is task 38 | return @runGulp(task) 39 | 40 | @getGulpTasks() 41 | return 42 | 43 | destroy: -> 44 | console.log 'GulpControlView: destroy' 45 | 46 | if @process 47 | @process.kill() 48 | @process = null 49 | @detach() 50 | return 51 | 52 | getTitle: -> 53 | return 'gulp.js:control' 54 | 55 | getGulpCwd: (cwd) -> 56 | dirs = [] 57 | 58 | gfregx = /^gulpfile(\.babel)?\.(js|coffee)/i 59 | for entry in fs.readdirSync(cwd) when entry.indexOf('.') isnt 0 60 | if gfregx.test(entry) 61 | @gulpFile = entry 62 | return cwd 63 | 64 | else if entry.indexOf('node_modules') is -1 65 | abs = path.join(cwd, entry) 66 | if fs.statSync(abs).isDirectory() 67 | dirs.push abs 68 | 69 | for dir in dirs 70 | if found = @getGulpCwd(dir) 71 | return found 72 | 73 | return 74 | 75 | getTaskId: (taskname) -> 76 | shasum = crypto.createHash('sha1') 77 | shasum.update(taskname) 78 | return "gulp-#{shasum.digest('hex')}" 79 | 80 | getGulpTasks: -> 81 | @tasks = [] 82 | 83 | projpath = atom.project.getPaths()[0] 84 | unless @gulpCwd = @getGulpCwd(projpath) 85 | @writeOutput "Unable to find #{projpath}/**/gulpfile.[js|coffee]", 'error' 86 | return 87 | 88 | @writeOutput "Using #{@gulpCwd}/#{@gulpFile}" 89 | @writeOutput 'Retrieving list of gulp tasks' 90 | 91 | onOutput = (output) => 92 | for task in output.split('\n') when task.length 93 | @tasks.push task 94 | 95 | onError = (output) => 96 | @gulpErr(output) 97 | 98 | onExit = (code) => 99 | if code is 0 100 | for task in @tasks.sort() 101 | tid = @getTaskId(task) 102 | @taskList.append "
  • #{task}
  • " 103 | @writeOutput "#{@tasks.length} tasks found" 104 | 105 | else 106 | @gulpExit(code) 107 | console.error 'GulpControl: getGulpTasks, exit', code 108 | 109 | @runGulp '--tasks-simple', onOutput, onError, onExit 110 | return 111 | 112 | runGulp: (task, stdout, stderr, exit) -> 113 | if @process 114 | @process.kill() 115 | @process = null 116 | 117 | command = 'gulp' 118 | # if gulp is installed localy, use that instead 119 | projpath = atom.project.getPaths()[0] 120 | localGulpPath = path.join(projpath, 'node_modules', '.bin', 'gulp') 121 | if fs.existsSync(localGulpPath) 122 | command = localGulpPath 123 | 124 | args = [task, '--color'] 125 | 126 | process.env.PATH = switch process.platform 127 | when 'win32' then process.env.PATH 128 | else "#{process.env.PATH}:" + atom.config.get('gulp-control.nodePath') 129 | 130 | options = 131 | cwd: @gulpCwd 132 | env: process.env 133 | 134 | stdout or= (output) => @gulpOut(output) 135 | stderr or= (code) => @gulpErr(code) 136 | exit or= (code) => @gulpExit(code) 137 | 138 | if task.indexOf('-') 139 | @writeOutput ' ' 140 | @writeOutput "Running gulp #{task}" 141 | 142 | tid = @getTaskId(task) 143 | 144 | @find('.tasks li.task.active').removeClass 'active' 145 | @find(".tasks li.task##{tid}").addClass 'active running' 146 | 147 | @process = new BufferedProcess({command, args, options, stdout, stderr, exit}) 148 | return 149 | 150 | writeOutput: (line, klass) -> 151 | parts = line.split('\r') 152 | line = parts[parts.length-1] 153 | if line and line.length 154 | @outputPane.append "
    #{line}
    " 155 | @outputPane.scrollToBottom() 156 | return 157 | 158 | gulpOut: (output) -> 159 | for line in output.split('\n') 160 | @writeOutput convert.toHtml(line) 161 | return 162 | 163 | gulpErr: (output) -> 164 | for line in output.split('\n') 165 | @writeOutput convert.toHtml(line), 'error' 166 | return 167 | 168 | gulpExit: (code) -> 169 | @find('.tasks li.task.active.running').removeClass 'running' 170 | @writeOutput "Exited with code #{code}", "#{if code then 'error' else ''}" 171 | @process = null 172 | return 173 | -------------------------------------------------------------------------------- /lib/gulp-control.coffee: -------------------------------------------------------------------------------- 1 | GulpControlView = require './gulp-control-view' 2 | {CompositeDisposable} = require 'atom' 3 | 4 | views = [] 5 | 6 | module.exports = GulpControl = 7 | activate: (state) -> 8 | console.log 'GulpControl: activate' 9 | 10 | atom.commands.add 'atom-workspace', "gulp-control:toggle": => @newView() 11 | return 12 | 13 | deactivate: -> 14 | console.log 'GulpControl: deactivate' 15 | return 16 | 17 | newView: -> 18 | console.log 'GulpControl: toggle' 19 | 20 | view = new GulpControlView() 21 | views.push view 22 | 23 | pane = atom.workspace.getActivePane() 24 | item = pane.addItem view, 0 25 | pane.activateItem item 26 | return 27 | 28 | serialize: -> 29 | 30 | config: 31 | nodePath: 32 | title: 'Bin Path', 33 | description: 'This should be set to the folder in which the node executable is located. This can be found by typing \'which node\' from the command line, but you need to remove \'node\' from the end.' 34 | type: 'string' 35 | default: '/usr/local/bin' 36 | -------------------------------------------------------------------------------- /menus/gulp-control.cson: -------------------------------------------------------------------------------- 1 | # See https://atom.io/docs/latest/creating-a-package#menus for more details 2 | 'context-menu': 3 | 'atom-text-editor': [ 4 | { 5 | 'label': 'Open gulp-control' 6 | 'command': 'gulp-control:toggle' 7 | } 8 | ] 9 | 'menu': [ 10 | { 11 | 'label': 'Packages' 12 | 'submenu': [ 13 | 'label': 'gulp-control' 14 | 'submenu': [ 15 | { 16 | 'label': 'Open' 17 | 'command': 'gulp-control:toggle' 18 | } 19 | ] 20 | ] 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gulp-control", 3 | "main": "./lib/gulp-control", 4 | "version": "0.5.0", 5 | "description": "Displays a list of gulp tasks and allows execution within Atom", 6 | "activationCommands": { 7 | "atom-workspace": "gulp-control:toggle" 8 | }, 9 | "repository": "https://github.com/jacogr/atom-gulp-control", 10 | "license": "MIT", 11 | "engines": { 12 | "atom": ">=0.174.0 <2.0.0" 13 | }, 14 | "dependencies": { 15 | "ansi-to-html": "^0.3.0", 16 | "atom-space-pen-views": "^2.0.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /screenshots/gulp-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacogr/atom-gulp-control/7bc4418a60c2153d5eb876e0cd6dc837630fd58c/screenshots/gulp-01.png -------------------------------------------------------------------------------- /spec/gulp-control-spec.coffee: -------------------------------------------------------------------------------- 1 | GulpControl = require '../lib/gulp-control' 2 | 3 | # Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs. 4 | # 5 | # To run a specific `it` or `describe` block add an `f` to the front (e.g. `fit` 6 | # or `fdescribe`). Remove the `f` to unfocus the block. 7 | 8 | describe "GulpControl", -> 9 | [workspaceElement, activationPromise] = [] 10 | 11 | beforeEach -> 12 | workspaceElement = atom.views.getView(atom.workspace) 13 | activationPromise = atom.packages.activatePackage('gulp-control') 14 | 15 | describe "when the gulp-control:toggle event is triggered", -> 16 | it "hides and shows the modal panel", -> 17 | # Before the activation event the view is not on the DOM, and no panel 18 | # has been created 19 | expect(workspaceElement.querySelector('.gulp-control')).not.toExist() 20 | 21 | # This is an activation event, triggering it will cause the package to be 22 | # activated. 23 | atom.commands.dispatch workspaceElement, 'gulp-control:toggle' 24 | 25 | waitsForPromise -> 26 | activationPromise 27 | 28 | runs -> 29 | expect(workspaceElement.querySelector('.gulp-control')).toExist() 30 | 31 | gulpControlElement = workspaceElement.querySelector('.gulp-control') 32 | expect(gulpControlElement).toExist() 33 | 34 | gulpControlPanel = atom.workspace.panelForItem(gulpControlElement) 35 | expect(gulpControlPanel.isVisible()).toBe true 36 | atom.commands.dispatch workspaceElement, 'gulp-control:toggle' 37 | expect(gulpControlPanel.isVisible()).toBe false 38 | 39 | it "hides and shows the view", -> 40 | # This test shows you an integration test testing at the view level. 41 | 42 | # Attaching the workspaceElement to the DOM is required to allow the 43 | # `toBeVisible()` matchers to work. Anything testing visibility or focus 44 | # requires that the workspaceElement is on the DOM. Tests that attach the 45 | # workspaceElement to the DOM are generally slower than those off DOM. 46 | jasmine.attachToDOM(workspaceElement) 47 | 48 | expect(workspaceElement.querySelector('.gulp-control')).not.toExist() 49 | 50 | # This is an activation event, triggering it causes the package to be 51 | # activated. 52 | atom.commands.dispatch workspaceElement, 'gulp-control:toggle' 53 | 54 | waitsForPromise -> 55 | activationPromise 56 | 57 | runs -> 58 | # Now we can test for view visibility 59 | gulpControlElement = workspaceElement.querySelector('.gulp-control') 60 | expect(gulpControlElement).toBeVisible() 61 | atom.commands.dispatch workspaceElement, 'gulp-control:toggle' 62 | expect(gulpControlElement).not.toBeVisible() 63 | -------------------------------------------------------------------------------- /spec/gulp-control-view-spec.coffee: -------------------------------------------------------------------------------- 1 | GulpControlView = require '../lib/gulp-control-view' 2 | 3 | describe "GulpControlView", -> 4 | it "has one valid test", -> 5 | expect("life").toBe "easy" 6 | -------------------------------------------------------------------------------- /styles/gulp-control.less: -------------------------------------------------------------------------------- 1 | // The ui-variables file is provided by base themes provided by Atom. 2 | // 3 | // See https://github.com/atom/atom-dark-ui/blob/master/stylesheets/ui-variables.less 4 | // for a full listing of what's available. 5 | @import "ui-variables"; 6 | 7 | .gulp-control { 8 | color: @text-color; 9 | 10 | .tasks { 11 | float: left; 12 | height: 100%; 13 | width: 25%; 14 | background: @tab-background-color; 15 | border-right: 1px solid @base-border-color; 16 | padding: 10px; 17 | overflow-y: scroll; 18 | 19 | .task { 20 | cursor: pointer; 21 | padding: 5px; 22 | border-radius: 4px; 23 | 24 | &.active { 25 | background: @button-background-color; 26 | color: @text-color-highlight; 27 | 28 | &.running { 29 | background: @button-background-color-selected; 30 | } 31 | } 32 | 33 | &:hover { 34 | color: @text-color-highlight; 35 | } 36 | } 37 | } 38 | 39 | .output { 40 | float: left; 41 | height: 100%; 42 | width: 75%; 43 | background: @tool-panel-background-color; 44 | padding: 10px; 45 | overflow-y: scroll; 46 | 47 | pre { 48 | padding: 0; 49 | font-size: 11px; 50 | background: @tool-panel-background-color; 51 | 52 | &.error { 53 | color: @text-color-error; 54 | } 55 | } 56 | } 57 | } 58 | --------------------------------------------------------------------------------