A simple and attractive code quality tool for CSS built on top of LESS

  • By Twitter Archive
  • Last update: Sep 21, 2022
  • Comments: 11

RECESS - NO LONGER MAINTAINED, DOES NOT WORK WITH NEWER LESS VERSIONS Build Status

Developed at Twitter to support our internal styleguide, RECESS is a simple, attractive code quality tool for CSS built on top of LESS.

Incorporate it into your development process as a linter, or integrate it directly into your build system as a compiler, RECESS will keep your source looking clean and super manageable.

GENERAL USE

$ recess [path] [options]

OPTIONS

  • --compile - compiles your code and outputs it to the terminal. Fixes white space and sort order. Can compile css or less.
  • --compress - compress your compiled code.
  • --config - accepts a path, which specifies a json config object
  • --format - control the output format of errors:
    • --format text - the default format, shows errors and context
    • --format compact - show errors one-error-per-line, useful for IDE integration
  • --noSummary - don't output the summary block for each file
  • --includePath - accepts an additional directory path to look for @import:ed LESS files in.
  • --stripColors - removes color from output (useful when logging)
  • --watch - watch filesystem for changes, useful when compiling Less projects
  • --noIDs - doesn't complain about using IDs in your stylesheets
  • --noJSPrefix - doesn't complain about styling .js- prefixed classnames
  • --noOverqualifying - doesn't complain about overqualified selectors (ie: div#foo.bar)
  • --noUnderscores - doesn't complain about using underscores in your class names
  • --noUniversalSelectors - doesn't complain about using the universal * selector
  • --prefixWhitespace - adds whitespace prefix to line up vender prefixed properties
  • --strictPropertyOrder - doesn't looking into your property ordering
  • --zeroUnits - doesn't complain if you add units to values of 0

EXAMPLES

Lint all css files

$ recess *.css

Lint file, ignore styling of IDs

$ recess ./bootstrap.css --noIds false

Lint file with compact output and no color

$ recess ./bootstrap.css --format compact --stripColors

Compile and compress .less file, then output it to a new file

$ recess ./bootstrap.less --compress > ./bootstrap-production.css

Watch a directory for changes and auto compile a css file from the changes. experimental

$ recess input.less:ouput.css --watch watch/this/dir/for/changes

Watch a single file for changes and auto compile a css file from the changes. experimental

$ recess input.less:ouput.css --watch

PROGRAMMATIC API

Recess provides a pretty simple programmatic api.

var recess = require('recess')

Once you've required recess, just pass it a path (or array of paths) and an optional options object and an optional callback:

recess(['../fat.css', '../twitter.css'], { compile: true }, callback)

The following options (and defaults) are available in the programatic api:

  • compile: false
  • compress: false
  • includePath: []
  • noIDs: true
  • noJSPrefix: true
  • noOverqualifying: true
  • noUnderscores: true
  • noUniversalSelectors: true
  • prefixWhitespace: true
  • strictPropertyOrder: true
  • stripColors: false
  • zeroUnits: true

The callback is fired when each instance has finished processessing an input. The callback is passed an array of of instances (one for each path). The instances have a bunch of useful things on them like the raw data and an array of output strings.

When compiling, access the compiled source through the output property:

var recess = require('recess')

recess('./js/fat.css', { compile: true }, function (err, obj) {
  if (err) throw err
  console.log(
  	obj // recess instance for fat.css
  , obj.output // array of loggable content
  , obj.errors // array of failed lint rules
  )
})

INSTALLATION

To install recess you need both node and npm installed.

$ npm install recess -g

AUTHORS

LICENSE

Copyright 2012-2013 Twitter, Inc.

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0

Github

https://github.com/twitter-archive/recess

Comments(11)

  • 1

    Added --inlineImages option to embed images.

    Have enabled it by default, which may not be suitable.

    I did have to make one change to core.js so .on would have access to the file path that was being processed, because I needed to get the image data relative to the .css file. I couldn't think of another way to do it.

    Added lint rule and tests for both compile and linting.

  • 2

    Add --format option with support for "text" and "compact" as per csslint.

    Adds the --format and --noSummary options:

    --format defaults to "text", aka the existing format for lint errors. It may also be set to "compact", which uses "file:line:error" one-line-per-error style which can be easily integrated into many IDEs to highlight errors while editing.

    --noSummary avoids outputting the summary block of text for each file, and is automatically set by --format=compact.

    This pull request includes the line-number changes in my other pull request, twitter/recess#39.

  • 3

    Add "includePath" option (à la lessc)

    Based on issue #57 – I didn't write any tests because the tests that are there only related to the linting of recess. This uses the same strategy as lessc's --include-path but instead allows you to use --includePath more than once to specify multiple paths (instead of using path separators).

    Also uses the name includePath instead of include-path because camelcase seems to be the recess-way.

    Usage:

      project/
        my_less.less ('@import "import.less";)
        imports/
          import.less ('b { color: gold; ')
    

    Then,

    recess --includePath=imports --compile my_less.less
    

    And the import diretive in my_less.less will find import.less automatically. Very useful when working with Bootstrap, since you can add Bootstrap's less/ directory to includePath and only override the parts you want to change (LESS still looks in the "input" directory first).

    More than one directory can be specified:

    recess --includePath=imports --includePath=exports my_less.less
    

    Or, in .recessrc:

      {
        'includePath': ['imports', 'exports']
      }
    
  • 4

    Ending process with exit code of 1 for fatal error or validation failure

    This addresses issue #23 by calling process.exit(code=1) when either an error is passed to the callback, or any of the instances have any failures. (added fails count to each instance that matches the corresponding local var)

  • 5

    Making settings easier to understand

    It took me a while to figure out that I had to use --noIDs false to disable ID warnings, I thought this might clarify a bit. Maybe should be fleshed out more.

  • 6

    Max Nesting Level Validation

    Adding support to validate against deeply nested selectors, for example:

    .one .two .three. .four {
      color: red;
    }
    
    ~/Projects/recess (master)λ ./bin/recess test.less --maxNestingLevel 3
    
    Analyzing the following files: test.less
    
    FILE: test.less
    STATUS: Busted
    FAILURES: 1 failure
    
    Selector should not be nested more than 3 levels deep.
           2. .one .two .three .four
    
    ~/Projects/recess (master)λ ./bin/recess test.less --maxNestingLevel 4
    
    Analyzing the following files: test.less
    
    FILE: test.less
    STATUS: Perfect!
    
  • 7

    Updated the list of properties in `strict-property-order.js`, fixes #22

    I've added 190 new CSS properties (Chrome 21 and Firefox 14) to the order list, alphabetically sorted (no problems with shorthands, ie margin comes before margin-top).

    To get this list, I've written the a simple tool (see below)and followed the following steps:

    1. Open the tool in Chrome 21 and copy-paste the existing order from strict-property-order.js into it.
    2. Open tool in Firefox 14, copy-paste the output from the previous step to Firefox.
    3. Copied output to strict-property-order.js.
    <!DOCTYPE html>
    <meta charset="utf8">
    <title>Generates the "order" array for strict-property-order.js</title>
    <style>
    html,body{margin:0;padding:0;height:100%;}
    textarea{box-sizing:border-box;-moz-box-sizing:border-box;padding:1em;width:100%;height:48%;border:1px solid #759;}
    #inp{margin-bottom:1%;}
    #out{margin-top:1%;}
    </style>
    <textarea id="inp" placeholder="Paste the previous result OR paste the inspector's output"></textarea>
    <textarea id="out" placeholder="After pasting the result in the previous box,
    hit Ctrl+Enter to parse the input and merge it with the current browser's properties"></textarea>
    <script>
    function add(out, inp) {
        'use strict';
        // `out` = array of unique CSS properties
        // `inp` = input which is merged with `out` (`out` is modified!)
        if (inp == null) inp = getComputedStyle(document.documentElement);
    
        [].forEach.call(inp, function(prop) {
            prop = prop.replace(/^-(webkit|epub|moz|ms|o)-/, '');
            if (out.indexOf(prop) === -1) out.push(prop);
        });
        out.sort(String.localeCompare);
        return '  , order = ' +
            JSON.stringify(out, null, 1)
            .replace(/"/g, "'")          // Use single quotes
            .replace("'", "     '")   // Indent first key
            .replace(/,\n/g, '\n    ,'); // Format comma
    }
    function process() {
        var inp = document.getElementById('inp').value.replace(/^\s*,?\s*[a-z]*\s*=\s*/i,'');
        try {
            if (/^\s*[a-zA-Z\-]/.test(inp)) {
                // Copy-pasted from inspector
                inp = inp.replace(/^\s+/g,'').match(/^[^: ]+/gm);
            } else {
                inp = inp ? JSON.parse(inp.replace(/'/g,"'")) : [];
            }
        } catch (e) {
            console.log(e, inp);
            alert("Failed to parse the input. Make sure that it's valid JSON (single quotes are replaced with double quotes).\nOr, paste the result from the inspector (key:value; pairs)");
            return;
        }
        document.getElementById('out').value = add(inp);
    }
    document.documentElement.addEventListener('keydown', function(e) {
        if (e.ctrlKey && !e.altKey && !e.metaKey && e.keyCode == 13) process();
    });
    document.getElementById('inp').focus();
    </script>
    
  • 8

    Support less' rootpath and relativeUrls options

    • The rootpath and relativeUrls options were added to less in version 1.3.2
    • rootpath exposed as urlRootPath to make more sense along with the other available recess options than just "rootpath" would. Also, camelCase.

    This allows properly compiling Less files that import from a subfolder with Less files referencing image or font files in that same subfolder, without having to flatten the file hierarchy, by updating urls to be relative to the main Less file's location. The same functionality was used to fix up browser-side loading when updating from Less 1.3.1 to 1.3.3 (The breaking change happened in 1.3.2).

  • 9

    added data-uri support

    As it states in http://lesscss.org/#reference

    Inlines a resource and falls back to url() if the ieCompat option is on and the resource is too large, or if you use the function in the browser. If the mime is not given then node uses the mime package to determine the correct mime type.

    Parameters:

    mimetype: A mime type string. Optional. url: The URL of the file to inline.

  • 10

    Adding repository section to package.json to suppress warning from NPM.

    NPM emits a warning when the repository field is not defined in the package.json file:

    npm WARN package.json [email protected] No repository field.
    

    This pull request adds the repository field. :-)

  • 11

    Added another try/catch for tree.toCSS

    Added another try/catch for tree.toCSS because parse errors were not captured by parent try/catch.

    I'm not sure when or even if less.Parser(options).parse throws errors.

    Also updated the way errors are logged; replaced custom error parser with less.formatError