Add Executable class to abstract CLI beautifiers

This commit is contained in:
Glavin Wiechert 2017-05-28 18:21:10 -03:00
parent 800066963c
commit bc21afd424
10 changed files with 453 additions and 280 deletions

View File

@ -157,6 +157,8 @@ Handlebars.registerHelper('beautifiers-info', (beautifiers, options) ->
rows = _.map(beautifiers, (beautifier, k) ->
name = beautifier.name
isPreInstalled = beautifier.isPreInstalled
if typeof isPreInstalled is "function"
isPreInstalled = beautifier.isPreInstalled()
link = beautifier.link
installationInstructions = if isPreInstalled then "Nothing!" else "Go to #{link} and follow the instructions."
return "| #{name} | #{if isPreInstalled then ':white_check_mark:' else ':x:'} | #{installationInstructions} |"

View File

@ -179,6 +179,8 @@
"pug-beautify": "^0.1.1",
"remark": "^6.0.1",
"season": "^5.3.0",
"semver": "^5.3.0",
"shell-env": "^0.3.0",
"space-pen": "^5.1.1",
"strip-json-comments": "^2.0.1",
"temp": "^0.8.3",

View File

@ -1,4 +1,5 @@
Beautifiers = require "../src/beautifiers"
Executable = require "../src/beautifiers/executable"
beautifiers = new Beautifiers()
Beautifier = require "../src/beautifiers/beautifier"
Languages = require('../src/languages/')
@ -124,7 +125,7 @@ describe "Atom-Beautify", ->
pathOption: "Lang - Test Program Path"
}
# Force to be Windows
beautifier.isWindows = true
Executable.isWindows = () ->true
terminal = 'CMD prompt'
whichCmd = "where.exe"
# Process
@ -132,7 +133,7 @@ describe "Atom-Beautify", ->
expect(p).not.toBe(null)
expect(p instanceof beautifier.Promise).toBe(true)
cb = (v) ->
# console.log(v)
console.log("error", v, v.description)
expect(v).not.toBe(null)
expect(v instanceof Error).toBe(true)
expect(v.code).toBe("CommandNotFound")
@ -167,7 +168,7 @@ describe "Atom-Beautify", ->
pathOption: "Lang - Test Program Path"
}
# Force to be Mac/Linux (not Windows)
beautifier.isWindows = false
Executable.isWindows = () ->false
terminal = "Terminal"
whichCmd = "which"
# Process

View File

@ -1,5 +1,6 @@
PHPCSFixer = require "../src/beautifiers/php-cs-fixer"
Beautifier = require "../src/beautifiers/beautifier"
Executable = require "../src/beautifiers/executable"
path = require 'path'
# Use the command `window:run-package-specs` (cmd-alt-ctrl-p) to run specs.
@ -30,10 +31,15 @@ describe "PHP-CS-Fixer Beautifier", ->
describe "Beautifier::beautify", ->
beautifier = null
execSpawn = null
beforeEach ->
beautifier = new PHPCSFixer()
# console.log('new beautifier')
execSpawn = Executable.prototype.spawn
afterEach ->
Executable.prototype.spawn = execSpawn
OSSpecificSpecs = ->
text = "<?php echo \"test\"; ?>"
@ -49,13 +55,14 @@ describe "PHP-CS-Fixer Beautifier", ->
levels: ""
}
# Mock spawn
beautifier.spawn = (exe, args, options) ->
# beautifier.spawn
Executable.prototype.spawn = (exe, args, options) ->
# console.log('spawn', exe, args, options)
er = new Error('ENOENT')
er.code = 'ENOENT'
return beautifier.Promise.reject(er)
# Beautify
p = beautifier.beautify(text, language, options)
p = beautifier.loadExecutables().then(() -> beautifier.beautify(text, language, options))
expect(p).not.toBe(null)
expect(p instanceof beautifier.Promise).toBe(true)
cb = (v) ->
@ -74,7 +81,7 @@ describe "PHP-CS-Fixer Beautifier", ->
expect(beautifier).not.toBe(null)
expect(beautifier instanceof Beautifier).toBe(true)
if not beautifier.isWindows and failingProgram is "php"
if not Executable.isWindows and failingProgram is "php"
# Only applicable on Windows
return
@ -104,8 +111,9 @@ describe "PHP-CS-Fixer Beautifier", ->
# console.log('fake exe path', exe)
beautifier.Promise.resolve("/#{exe}")
oldSpawn = beautifier.spawn.bind(beautifier)
beautifier.spawn = (exe, args, options) ->
# oldSpawn = beautifier.spawn.bind(beautifier)
# beautifier.spawn
Executable.prototype.spawn = (exe, args, options) ->
# console.log('spawn', exe, args, options)
if exe is failingProgram
er = new Error('ENOENT')
@ -117,21 +125,21 @@ describe "PHP-CS-Fixer Beautifier", ->
stdout: 'stdout',
stderr: ''
})
p = beautifier.beautify(text, language, options)
p = beautifier.loadExecutables().then(() -> beautifier.beautify(text, language, options))
expect(p).not.toBe(null)
expect(p instanceof beautifier.Promise).toBe(true)
p.then(cb, cb)
return p
# failWhichProgram('php')
failWhichProgram('php-cs-fixer')
failWhichProgram('PHP')
# failWhichProgram('php-cs-fixer')
unless isWindows
describe "Mac/Linux", ->
beforeEach ->
# console.log('mac/linx')
beautifier.isWindows = false
Executable.isWindows = () -> false
do OSSpecificSpecs
@ -139,6 +147,6 @@ describe "PHP-CS-Fixer Beautifier", ->
beforeEach ->
# console.log('windows')
beautifier.isWindows = true
Executable.isWindows = () -> true
do OSSpecificSpecs

View File

@ -4,8 +4,9 @@ fs = require('fs')
temp = require('temp').track()
readFile = Promise.promisify(fs.readFile)
which = require('which')
spawn = require('child_process').spawn
path = require('path')
shellEnv = require('shell-env')
Executable = require('./executable')
module.exports = class Beautifier
@ -31,10 +32,42 @@ module.exports = class Beautifier
###
options: {}
executables: []
###
Is the beautifier a command-line interface beautifier?
###
isPreInstalled: true
isPreInstalled: () ->
@executables.length is 0
_exe: {}
loadExecutables: () ->
if Object.keys(@_exe).length is @executables.length
Promise.resolve(@_exe)
else
Promise.resolve(executables = @executables.map((e) -> new Executable(e)))
.then((executables) -> Promise.all(executables.map((e) -> e.init())))
.then((es) =>
exe = {}
missingInstalls = []
es.forEach((e) ->
exe[e.cmd] = e
if not e.isInstalled
missingInstalls.push(e)
)
@_exe = exe
@debug("exe", exe)
if missingInstalls.length is 0
return @_exe
else
throw new Error("Missing required executables: #{missingInstalls.map((e) -> e.cmd).join(' and ')}")
)
exe: (cmd) ->
console.log('exe', cmd, @_exe)
e = @_exe[cmd]
if !e?
throw new Error("Missing executable \"#{cmd}\". Please report this bug to https://github.com/Glavin001/atom-beautify/issues")
e
###
Supported languages by this Beautifier
@ -102,7 +135,7 @@ module.exports = class Beautifier
startDir.pop()
return null
# Retrieves the default line ending based upon the Atom configuration
# Retrieves the default line ending based upon the Atom configuration
# `line-ending-selector.defaultLineEnding`. If the Atom configuration
# indicates "OS Default", the `process.platform` is queried, returning
# CRLF for Windows systems and LF for all other systems.
@ -124,64 +157,6 @@ module.exports = class Beautifier
else
return lf
###
If platform is Windows
###
isWindows: do ->
return new RegExp('^win').test(process.platform)
###
Get Shell Environment variables
Special thank you to @ioquatix
See https://github.com/ioquatix/script-runner/blob/v1.5.0/lib/script-runner.coffee#L45-L63
###
_envCache: null
_envCacheDate: null
_envCacheExpiry: 10000 # 10 seconds
getShellEnvironment: ->
return new Promise((resolve, reject) =>
# Check Cache
if @_envCache? and @_envCacheDate?
# Check if Cache is old
if (new Date() - @_envCacheDate) < @_envCacheExpiry
# Still fresh
return resolve(@_envCache)
# Check if Windows
if @isWindows
# Windows
# Use default
resolve(process.env)
else
# Mac & Linux
# I tried using ChildProcess.execFile but there is no way to set detached and
# this causes the child shell to lock up.
# This command runs an interactive login shell and
# executes the export command to get a list of environment variables.
# We then use these to run the script:
child = spawn process.env.SHELL, ['-ilc', 'env'],
# This is essential for interactive shells, otherwise it never finishes:
detached: true,
# We don't care about stdin, stderr can go out the usual way:
stdio: ['ignore', 'pipe', process.stderr]
# We buffer stdout:
buffer = ''
child.stdout.on 'data', (data) -> buffer += data
# When the process finishes, extract the environment variables and pass them to the callback:
child.on 'close', (code, signal) =>
if code isnt 0
return reject(new Error("Could not get Shell Environment. Exit code: "+code+", Signal: "+signal))
environment = {}
for definition in buffer.split('\n')
[key, value] = definition.split('=', 2)
environment[key] = value if key != ''
# Cache Environment
@_envCache = environment
@_envCacheDate = new Date()
resolve(environment)
)
###
Like the unix which utility.
@ -191,182 +166,19 @@ module.exports = class Beautifier
See https://github.com/isaacs/node-which
###
which: (exe, options = {}) ->
# Get PATH and other environment variables
@getShellEnvironment()
.then((env) =>
new Promise((resolve, reject) =>
options.path ?= env.PATH
if @isWindows
# Environment variables are case-insensitive in windows
# Check env for a case-insensitive 'path' variable
if !options.path
for i of env
if i.toLowerCase() is "path"
options.path = env[i]
break
# Trick node-which into including files
# with no extension as executables.
# Put empty extension last to allow for other real extensions first
options.pathExt ?= "#{process.env.PATHEXT ? '.EXE'};"
which(exe, options, (err, path) ->
resolve(exe) if err
resolve(path)
)
)
)
###
Add help to error.description
Note: error.description is not officially used in JavaScript,
however it is used internally for Atom Beautify when displaying errors.
###
commandNotFoundError: (exe, help) ->
# Create new improved error
# notify user that it may not be
# installed or in path
message = "Could not find '#{exe}'. \
The program may not be installed."
er = new Error(message)
er.code = 'CommandNotFound'
er.errno = er.code
er.syscall = 'beautifier::run'
er.file = exe
if help?
if typeof help is "object"
# Basic notice
helpStr = "See #{help.link} for program \
installation instructions.\n"
# Help to configure Atom Beautify for program's path
helpStr += "You can configure Atom Beautify \
with the absolute path \
to '#{help.program or exe}' by setting \
'#{help.pathOption}' in \
the Atom Beautify package settings.\n" if help.pathOption
# Optional, additional help
helpStr += help.additional if help.additional
# Common Help
issueSearchLink =
"https://github.com/Glavin001/atom-beautify/\
search?q=#{exe}&type=Issues"
docsLink = "https://github.com/Glavin001/\
atom-beautify/tree/master/docs"
helpStr += "Your program is properly installed if running \
'#{if @isWindows then 'where.exe' \
else 'which'} #{exe}' \
in your #{if @isWindows then 'CMD prompt' \
else 'Terminal'} \
returns an absolute path to the executable. \
If this does not work then you have not \
installed the program correctly and so \
Atom Beautify will not find the program. \
Atom Beautify requires that the program be \
found in your PATH environment variable. \n\
Note that this is not an Atom Beautify issue \
if beautification does not work and the above \
command also does not work: this is expected \
behaviour, since you have not properly installed \
your program. Please properly setup the program \
and search through existing Atom Beautify issues \
before creating a new issue. \
See #{issueSearchLink} for related Issues and \
#{docsLink} for documentation. \
If you are still unable to resolve this issue on \
your own then please create a new issue and \
ask for help.\n"
er.description = helpStr
else #if typeof help is "string"
er.description = help
return er
Executable.which(exe, options)
###
Run command-line interface command
###
run: (executable, args, {cwd, ignoreReturnCode, help, onStdin} = {}) ->
# Flatten args first
args = _.flatten(args)
# Resolve executable and all args
Promise.all([executable, Promise.all(args)])
.then(([exeName, args]) =>
@debug('exeName, args:', exeName, args)
# Get PATH and other environment variables
Promise.all([exeName, args, @getShellEnvironment(), @which(exeName)])
)
.then(([exeName, args, env, exePath]) =>
@debug('exePath, env:', exePath, env)
@debug('args', args)
exe = exePath ? exeName
options = {
cwd: cwd
env: env
}
@spawn(exe, args, options, onStdin)
.then(({returnCode, stdout, stderr}) =>
@verbose('spawn result', returnCode, stdout, stderr)
# If return code is not 0 then error occured
if not ignoreReturnCode and returnCode isnt 0
# operable program or batch file
windowsProgramNotFoundMsg = "is not recognized as an internal or external command"
@verbose(stderr, windowsProgramNotFoundMsg)
if @isWindows and returnCode is 1 and stderr.indexOf(windowsProgramNotFoundMsg) isnt -1
throw @commandNotFoundError(exeName, help)
else
throw new Error(stderr)
else
stdout
)
.catch((err) =>
@debug('error', err)
# Check if error is ENOENT (command could not be found)
if err.code is 'ENOENT' or err.errno is 'ENOENT'
throw @commandNotFoundError(exeName, help)
else
# continue as normal error
throw err
)
)
###
Spawn
###
spawn: (exe, args, options, onStdin) ->
# Remove undefined/null values
args = _.without(args, undefined)
args = _.without(args, null)
return new Promise((resolve, reject) =>
@debug('spawn', exe, args)
cmd = spawn(exe, args, options)
stdout = ""
stderr = ""
cmd.stdout.on('data', (data) ->
stdout += data
)
cmd.stderr.on('data', (data) ->
stderr += data
)
cmd.on('close', (returnCode) =>
@debug('spawn done', returnCode, stderr, stdout)
resolve({returnCode, stdout, stderr})
)
cmd.on('error', (err) =>
@debug('error', err)
reject(err)
)
onStdin cmd.stdin if onStdin
)
exe = new Executable({
name: @name
homepage: @link
installation: @link
cmd: executable
})
exe.run(args, {cwd, ignoreReturnCode, help, onStdin})
###
Logger instance

View File

@ -0,0 +1,309 @@
Promise = require('bluebird')
_ = require('lodash')
which = require('which')
spawn = require('child_process').spawn
path = require('path')
semver = require('semver')
shellEnv = require('shell-env')
module.exports = class Executable
name = null
cmd = null
homepage = null
installation = null
versionArgs = ['--version']
versionParse = (text) -> semver.clean(text)
versionsSupported: '>= 0.0.0'
constructor: (options) ->
@name = options.name
@cmd = options.cmd
@homepage = options.homepage
@installation = options.installation
if options.version?
versionOptions = options.version
@versionArgs = versionOptions.args
@versionParse = versionOptions.parse
@versionsSupported = versionOptions.supported
@setupLogger()
init: () ->
Promise.all([
@loadVersion()
])
.then(() => @)
###
Logger instance
###
logger: null
###
Initialize and configure Logger
###
setupLogger: ->
@logger = require('../logger')("#{@name} Executable")
for key, method of @logger
@[key] = method
@verbose("#{@name} executable logger has been initialized.")
isInstalled = null
version = null
loadVersion: (force = false) ->
@verbose("loadVersion", @version, force)
if force or !@version?
@verbose("Loading version without cache")
@runVersion()
.then((text) => @versionParse(text))
.then((version) ->
valid = Boolean(semver.valid(version))
if not valid
throw new Error("Version is not valid: "+version)
version
)
.then((version) =>
@isInstalled = true
@version = version
)
.then((version) =>
@verbose("#{@cmd} version: #{version}")
version
)
.catch((error) =>
@isInstalled = false
@error(error)
Promise.reject(@commandNotFoundError())
)
else
@verbose("Loading cached version")
Promise.resolve(@version)
runVersion: () ->
@run(@versionArgs)
isSupported: () ->
@isVersion(@versionsSupported)
isVersion: (range) ->
semver.satisfies(@version, range)
###
Run command-line interface command
###
run: (args, options = {}) ->
@debug("Run: ", args, options)
exeName = @cmd
{ cwd, ignoreReturnCode, help, onStdin } = options
# Flatten args first
args = _.flatten(args)
# Resolve executable and all args
Promise.all([@shellEnv(), Promise.all(args)])
.then(([env, args]) =>
@debug('exeName, args:', exeName, args)
# Get PATH and other environment variables
Promise.all([exeName, args, env, @which(exeName)])
)
.then(([exeName, args, env, exePath]) =>
@debug('exePath:', exePath)
@debug('env:', env)
@debug('args', args)
exe = exePath ? exeName
spawnOptions = {
cwd: cwd
env: env
}
@spawn(exe, args, spawnOptions, onStdin)
.then(({returnCode, stdout, stderr}) =>
@verbose('spawn result', returnCode, stdout, stderr)
# If return code is not 0 then error occured
if not ignoreReturnCode and returnCode isnt 0
# operable program or batch file
windowsProgramNotFoundMsg = "is not recognized as an internal or external command"
@verbose(stderr, windowsProgramNotFoundMsg)
if @isWindows() and returnCode is 1 and stderr.indexOf(windowsProgramNotFoundMsg) isnt -1
throw @commandNotFoundError(exeName, help)
else
throw new Error(stderr)
else
stdout
)
.catch((err) =>
@debug('error', err)
# Check if error is ENOENT (command could not be found)
if err.code is 'ENOENT' or err.errno is 'ENOENT'
throw @commandNotFoundError(exeName, help)
else
# continue as normal error
throw err
)
)
###
Spawn
###
spawn: (exe, args, options, onStdin) ->
# Remove undefined/null values
args = _.without(args, undefined)
args = _.without(args, null)
return new Promise((resolve, reject) =>
@debug('spawn', exe, args)
cmd = spawn(exe, args, options)
stdout = ""
stderr = ""
cmd.stdout.on('data', (data) ->
stdout += data
)
cmd.stderr.on('data', (data) ->
stderr += data
)
cmd.on('close', (returnCode) =>
@debug('spawn done', returnCode, stderr, stdout)
resolve({returnCode, stdout, stderr})
)
cmd.on('error', (err) =>
@debug('error', err)
reject(err)
)
onStdin cmd.stdin if onStdin
)
###
Add help to error.description
Note: error.description is not officially used in JavaScript,
however it is used internally for Atom Beautify when displaying errors.
###
commandNotFoundError: (exe, help) ->
exe ?= @name or @cmd
# help ?= {
# program: @cmd
# link: @installation or @homepage
# }
# Create new improved error
# notify user that it may not be
# installed or in path
message = "Could not find '#{exe}'. \
The program may not be installed."
er = new Error(message)
er.code = 'CommandNotFound'
er.errno = er.code
er.syscall = 'beautifier::run'
er.file = exe
if help?
if typeof help is "object"
# Basic notice
helpStr = "See #{help.link} for program \
installation instructions.\n"
# Help to configure Atom Beautify for program's path
helpStr += "You can configure Atom Beautify \
with the absolute path \
to '#{help.program or exe}' by setting \
'#{help.pathOption}' in \
the Atom Beautify package settings.\n" if help.pathOption
# Optional, additional help
helpStr += help.additional if help.additional
# Common Help
issueSearchLink =
"https://github.com/Glavin001/atom-beautify/\
search?q=#{exe}&type=Issues"
docsLink = "https://github.com/Glavin001/\
atom-beautify/tree/master/docs"
helpStr += "Your program is properly installed if running \
'#{if @isWindows() then 'where.exe' \
else 'which'} #{exe}' \
in your #{if @isWindows() then 'CMD prompt' \
else 'Terminal'} \
returns an absolute path to the executable. \
If this does not work then you have not \
installed the program correctly and so \
Atom Beautify will not find the program. \
Atom Beautify requires that the program be \
found in your PATH environment variable. \n\
Note that this is not an Atom Beautify issue \
if beautification does not work and the above \
command also does not work: this is expected \
behaviour, since you have not properly installed \
your program. Please properly setup the program \
and search through existing Atom Beautify issues \
before creating a new issue. \
See #{issueSearchLink} for related Issues and \
#{docsLink} for documentation. \
If you are still unable to resolve this issue on \
your own then please create a new issue and \
ask for help.\n"
er.description = helpStr
else #if typeof help is "string"
er.description = help
return er
@_envCache = null
shellEnv: () ->
@constructor.shellEnv()
@shellEnv: () ->
if @_envCache
return Promise.resolve(@_envCache)
else
shellEnv()
.then((env) =>
@_envCache = env
)
###
Like the unix which utility.
Finds the first instance of a specified executable in the PATH environment variable.
Does not cache the results,
so hash -r is not needed when the PATH changes.
See https://github.com/isaacs/node-which
###
which: (exe, options) ->
@.constructor.which(exe, options)
@_whichCache = {}
@which: (exe, options = {}) ->
if @_whichCache[exe]
return Promise.resolve(@_whichCache[exe])
# Get PATH and other environment variables
@shellEnv()
.then((env) =>
new Promise((resolve, reject) =>
options.path ?= env.PATH
if @isWindows()
# Environment variables are case-insensitive in windows
# Check env for a case-insensitive 'path' variable
if !options.path
for i of env
if i.toLowerCase() is "path"
options.path = env[i]
break
# Trick node-which into including files
# with no extension as executables.
# Put empty extension last to allow for other real extensions first
options.pathExt ?= "#{process.env.PATHEXT ? '.EXE'};"
which(exe, options, (err, path) =>
return resolve(exe) if err
@_whichCache[exe] = path
resolve(path)
)
)
)
###
If platform is Windows
###
isWindows: () -> @constructor.isWindows()
@isWindows: () -> new RegExp('^win').test(process.platform)

View File

@ -3,8 +3,6 @@
"use strict"
Beautifier = require('./beautifier')
Lexer = require('gherkin').Lexer('en')
logger = require('../logger')(__filename)
module.exports = class Gherkin extends Beautifier
name: "Gherkin formatter"
@ -15,6 +13,8 @@ module.exports = class Gherkin extends Beautifier
}
beautify: (text, language, options) ->
Lexer = require('gherkin').Lexer('en')
logger = @logger
return new @Promise((resolve, reject) ->
recorder = {
lines: []

View File

@ -345,36 +345,40 @@ module.exports = class Beautifiers extends EventEmitter
filePath: filePath
startTime = new Date()
beautifier.beautify(text, language.name, options, context)
.then((result) =>
resolve(result)
# Track Timing
@trackTiming({
utc: "Beautify" # Category
utv: language?.name # Variable
utt: (new Date() - startTime) # Value
utl: version # Label
})
# Track Empty beautification results
if not result
beautifier.loadExecutables()
.then((executables) ->
logger.verbose('executables', executables)
beautifier.beautify(text, language.name, options, context)
)
.then((result) =>
resolve(result)
# Track Timing
@trackTiming({
utc: "Beautify" # Category
utv: language?.name # Variable
utt: (new Date() - startTime) # Value
utl: version # Label
})
# Track Empty beautification results
if not result
@trackEvent({
ec: version, # Category
ea: "Beautify:Empty" # Action
el: language?.name # Label
})
)
.catch((error) =>
reject(error)
# Track Errors
@trackEvent({
ec: version, # Category
ea: "Beautify:Empty" # Action
ea: "Beautify:Error" # Action
el: language?.name # Label
})
)
.catch((error) =>
reject(error)
# Track Errors
@trackEvent({
ec: version, # Category
ea: "Beautify:Error" # Action
el: language?.name # Label
})
)
.finally(=>
@emit "beautify::end"
)
)
.finally(=>
@emit "beautify::end"
)
# Check if Analytics is enabled
@trackEvent({

View File

@ -10,7 +10,30 @@ module.exports = class PHPCSFixer extends Beautifier
name: 'PHP-CS-Fixer'
link: "https://github.com/FriendsOfPHP/PHP-CS-Fixer"
isPreInstalled: false
executables: [
{
name: "PHP"
cmd: "php"
homepage: "http://php.net/"
installation: "http://php.net/manual/en/install.php"
version: {
args: ['--version']
parse: (text) -> text.match(/PHP (.*) \(cli\)/)[1]
supported: '>= 0.0.0'
}
}
{
name: "PHP-CS-Fixer"
cmd: "php-cs-fixer"
homepage: "https://github.com/FriendsOfPHP/PHP-CS-Fixer"
installation: "https://github.com/FriendsOfPHP/PHP-CS-Fixer#installation"
version: {
args: ['--version']
parse: (text) -> text.match(/version (.*) by/)[1] + ".0"
supported: '>= 0.0.0'
}
}
]
options:
PHP:
@ -24,7 +47,8 @@ module.exports = class PHPCSFixer extends Beautifier
beautify: (text, language, options, context) ->
@debug('php-cs-fixer', options)
version = options.cs_fixer_version
php = @exe('php')
phpCsFixer = @exe('php-cs-fixer')
configFiles = ['.php_cs', '.php_cs.dist']
# Find a config file in the working directory if a custom one was not provided
@ -42,7 +66,7 @@ module.exports = class PHPCSFixer extends Beautifier
"--allow-risky=#{options.allow_risky}" if options.allow_risky
"--using-cache=no"
]
if version is 1
if phpCsFixer.isVersion('1.x')
phpCsFixerOptions = [
"fix"
"--level=#{options.level}" if options.level
@ -60,7 +84,9 @@ module.exports = class PHPCSFixer extends Beautifier
@Promise.all([
@which(options.cs_fixer_path) if options.cs_fixer_path
@which('php-cs-fixer')
]).then((paths) =>
tempFile = @tempFile("temp", text, '.php')
]).then(([customPath, phpCsFixerPath]) =>
paths = [customPath, phpCsFixerPath]
@debug('php-cs-fixer paths', paths)
_ = require 'lodash'
# Get first valid, absolute path
@ -71,10 +97,8 @@ module.exports = class PHPCSFixer extends Beautifier
# Check if PHP-CS-Fixer path was found
if phpCSFixerPath?
# Found PHP-CS-Fixer path
tempFile = @tempFile("temp", text)
if @isWindows
@run("php", [phpCSFixerPath, phpCsFixerOptions, tempFile], runOptions)
php([phpCSFixerPath, phpCsFixerOptions, tempFile], runOptions)
.then(=>
@readFile(tempFile)
)

View File

@ -8,6 +8,17 @@ Beautifier = require('./beautifier')
module.exports = class PHPCBF extends Beautifier
name: "PHPCBF"
link: "http://php.net/manual/en/install.php"
executables: [
{
name: "PHPCBF"
cmd: "phpcbf"
homepage: "https://github.com/squizlabs/PHP_CodeSniffer"
installation: "https://github.com/squizlabs/PHP_CodeSniffer#installation"
version: {
args: ['--version']
}
}
]
isPreInstalled: false
options: {