diff --git a/Cakefile b/Cakefile index fb6f7ae2e5..305cd66456 100644 --- a/Cakefile +++ b/Cakefile @@ -7,10 +7,13 @@ _ = require 'underscore' { spawn, exec, execSync } = require 'child_process' CoffeeScript = require './lib/coffeescript' helpers = require './lib/coffeescript/helpers' +{ setupConsole } = require './build-support/console' +{ PatternSet } = require './build-support/patterns' # ANSI Terminal Colors. bold = red = green = yellow = reset = '' -unless process.env.NODE_DISABLE_COLORS +USE_COLORS = process.stdout.hasColors?() and not process.env.NODE_DISABLE_COLORS +if USE_COLORS bold = '\x1B[0;1m' red = '\x1B[0;31m' green = '\x1B[0;32m' @@ -31,6 +34,12 @@ header = """ # Used in folder names like `docs/v1`. majorVersion = parseInt CoffeeScript.VERSION.split('.')[0], 10 +option '-l', '--level [LEVEL]', 'log level [debug < info < log(default) < warn < error]' + +task = (name, description, action) -> + global.task name, description, ({level = 'log', ...opts} = {}) -> + setupConsole {level, useColors: USE_COLORS} + action {...opts} class CakeConsole extends oldConsole.Console @LEVELS: ['debug', 'info', 'log', 'warn', 'error', 'trace'] @@ -134,7 +143,7 @@ buildParser = -> buildExceptParser = (callback) -> files = fs.readdirSync 'src' files = ('src/' + file for file in files when file.match(/\.(lit)?coffee$/)) - console.info {files} + console.dir.debug {files} run ['-c', '-o', 'lib/coffeescript'].concat(files), callback build = (callback) -> @@ -504,7 +513,7 @@ runTests = (CoffeeScript, {filePatterns, negFilePatterns, descPatterns, negDescP negFilePatterns ?= PatternSet.empty {negated: yes} descPatterns ?= PatternSet.empty() negDescPatterns ?= PatternSet.empty {negated: yes} - console.dir {filePatterns, negFilePatterns, descPatterns, negDescPatterns} + console.dir.debug {filePatterns, negFilePatterns, descPatterns, negDescPatterns} # These are attached to `global` so that they’re accessible from within # `test/async.coffee`, which has an async-capable version of @@ -535,13 +544,13 @@ runTests = (CoffeeScript, {filePatterns, negFilePatterns, descPatterns, negDescP description: description source: fn.toString() if fn.toString? onFilteredOut = (description, fn) -> - console.info "test '#{description}' was filtered out by patterns" + console.warn "test '#{description}' was filtered out by patterns" filteredOut.tests.push filename: global.currentFile description: description fn: fn onFilteredFile = (file) -> - console.info "file '#{file}' was filtered out by patterns" + console.warn "file '#{file}' was filtered out by patterns" filteredOut.files.push filename: file @@ -617,12 +626,12 @@ runTests = (CoffeeScript, {filePatterns, negFilePatterns, descPatterns, negDescP Promise.reject() if failures.length isnt 0 -option '-f', '--file [REGEXP*]', 'test file patterns to positively match' -option null, '--negFile [REGEXP*]', 'test file patterns to negatively match' -option '-d', '--desc [REGEXP*]', 'test description patterns to positively match' -option null, '--negDesc [REGEXP*]', 'test description patterns to negatively match' +option '-f', '--file [REGEXP*]', 'regexp patterns to positively match against test file paths' +option null, '--negFile [REGEXP*]', 'regexp patterns to negatively match against test file paths' +option '-d', '--desc [REGEXP*]', 'regexp patterns to positively match against test descriptions' +option null, '--negDesc [REGEXP*]', 'regexp patterns to negatively match against test descriptions' -consoleTask 'test', 'run the CoffeeScript language test suite', ({ +task 'test', 'run the CoffeeScript language test suite', ({ file = [], negFile = ['sourcemap'], desc = [], diff --git a/build-support/console.coffee b/build-support/console.coffee new file mode 100644 index 0000000000..c6d1055ea5 --- /dev/null +++ b/build-support/console.coffee @@ -0,0 +1,95 @@ +{ Console } = require 'console' +process = require 'process' + +exports.CakeConsole = class CakeConsole extends Console + @LEVELS: ['trace', 'debug', 'info', 'log', 'warn', 'error'] + @validLevels: => "[#{(@LEVELS.map (l) -> "'#{l}'").join ', '}]" + @checkLevel: (level) => + unless level in @LEVELS + throw new TypeError "argument '#{level}' was not a valid log level (should be: #{@validLevels()})" + level + + constructor: ({level, ...opts} = {}) -> + super opts + @level = @constructor.checkLevel level ? 'log' + + @getLevelNum: (l) => @LEVELS.indexOf @checkLevel l + curLevelNum: -> @constructor.getLevelNum @level + doesThisLevelApply: (l) -> @curLevelNum() <= @constructor.getLevelNum(l) + + # Always log, regardless of level. This is for terminal output not intended to be configured by + # logging level. + unconditionalLog: (...args) -> + super.log ...args + + # Define the named logging methods (.log(), .warn(), ...) by extracting them from the superclass. + trace: (...args) -> + if @doesThisLevelApply 'trace' + super ...args + + debug: (...args) -> + if @doesThisLevelApply 'debug' + super ...args + + info: (...args) -> + if @doesThisLevelApply 'info' + super ...args + + log: (...args) -> + if @doesThisLevelApply 'log' + super ...args + + warn: (...args) -> + if @doesThisLevelApply 'warn' + super ...args + + error: (...args) -> + if @doesThisLevelApply 'error' + super ...args + + # Call .dir(), but filtering by configured level. + dirLevel: (level, ...args) -> + if @doesThisLevelApply level + super.dir ...args + + # We want to be able to call .dir() as normal, but we also want to be able to call .dir.log() to + # explicitly set the logging level for .dir(). + Object.defineProperty @::, 'dir', + configurable: yes + get: -> + # By default, .dir() uses the 'log' level. + dir = (...args) -> @dirLevel 'log', ...args + Object.defineProperties dir, Object.fromEntries do => for k in @constructor.LEVELS + f = do (k) => (...args) => @dirLevel k, ...args + [k, + enumerable: yes + writable: yes + configurable: yes + value: Object.defineProperty f, 'name', + configurable: yes + value: k] + # We wouldn't normally have to set this, but Console does some wonky prototype munging: + # https://github.com/nodejs/node/blob/17fae65c72321659390c4cbcd9ddaf248accb953/lib/internal/console/constructor.js#L145-L147 + set: (dir) -> # ignore + + @stdio: ({ + stdout = process.stdout, + stderr = process.stderr, + ...opts, + } = {}) => new @ { + stdout, + stderr, + ...opts + } + + +exports.setupConsole = ({level, useColors}) -> + if global.cakeConsole? + return global.cakeConsole + + opts = {level} + unless useColors + opts.colorMode = no + global.console = global.cakeConsole = cakeConsole = CakeConsole.stdio opts + console.debug "log level = #{level}" + cakeConsole diff --git a/build-support/patterns.coffee b/build-support/patterns.coffee new file mode 100644 index 0000000000..e164324a3e --- /dev/null +++ b/build-support/patterns.coffee @@ -0,0 +1,18 @@ +exports.PatternSet = class PatternSet + constructor: (patternStrings = [], {@negated = no} = {}) -> + @matchers = (new RegExp p for p in patternStrings when p isnt '') + + isEmpty: -> @matchers.length is 0 + + iterMatchers: -> @matchers[Symbol.iterator]() + + test_: (arg) -> @iterMatchers().some (m) -> m.exec arg + + allows: (arg) -> + return yes if @isEmpty() + if @negated + not @test_ arg + else + @test_ arg + + @empty: ({negated = no} = {}) => new @ [], {negated}