Skip to content

Commit

Permalink
Allow usage as JSCS preset.
Browse files Browse the repository at this point in the history
Also, add blueprint and blueprint test.

Now:

```
ember install ember-suave
```

Will auto-generate a `.jscsrc` with `{ "preset": "ember-suave" }`.
  • Loading branch information
rwjblue committed Oct 15, 2015
1 parent 834e574 commit 717db06
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
language: node_js
node_js:
- "0.12"
- "stable"

sudo: false

Expand Down
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,26 @@ Ember style guide rules.
* [JavaScript Style Guide](https://github.com/dockyard/styleguides/blob/master/javascript.md)
* [Ember Style Guide](https://github.com/dockyard/styleguides/blob/master/ember.md)

## Installation

```bash
ember install ember-suave
```

## Usage

* ember-cli >= 0.2.3 `ember install ember-suave`
* ember-cli < 0.2.3 `ember install:addon ember-suave`
`ember-suave` is used as a JSCS preset, and can be enabled by adding a `preset` to your `.jscsrc` file.

```
{
"preset": "ember-suave"
}
```

`ember-suave` integrates well with ember-cli, but can also be used as a standalone JSCS preset. This allows custom
editor integration, and non-ember-cli projects to utilize our curated set of rules.

When used from within ember-cli, your test suite will automatically fail if any of the rules are broken.

## JSCS Rules

Expand All @@ -25,19 +41,21 @@ so that all you need to do is install the addon and start writing stylish code.
### Customization

If `ember-suave` isn't suave enough for you and you'd like to override
certain rules, simply provide your own `.jscsrc` file at the root of
your Ember CLI project. Those rules will be merged with the ones in the
default config.
certain rules, simply add your own rules to `.jscsrc` at the root of
your Ember CLI project. Those rules will take precedence over the ones in the
default preset.

You can specify any of the [rules](http://jscs.info/rules.html) that are
built into JSCS, or even provide your own custom ones.
built into JSCS, provide your own custom ones, or even override the ones we
have enabled by default.

To disable a rule, set its value to `null`.

```json
// .jscsrc
{
"additionalRules": ["lib/rules/*.js"],
"preset": "ember-suave",
"additionalRules": ["./lib/rules/*.js"],
"myAwesomeCustomRule": true,
"disallowDanglingUnderscores": true,
"disallowEmptyBlocks": null
Expand Down
6 changes: 6 additions & 0 deletions blueprints/.jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"predef": [
"console"
],
"strict": false
}
3 changes: 3 additions & 0 deletions blueprints/ember-suave/files/.jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"preset": "ember-suave"
}
9 changes: 9 additions & 0 deletions blueprints/ember-suave/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
description: 'ember-suave install generator',

normalizeEntityName: function() {
// this prevents an error when the entityName is
// not specified (since that doesn't actually matter
// to us
}
};
71 changes: 64 additions & 7 deletions lib/jscsrc-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,43 @@ var temp = require('temp');

temp.track();

var messagesLogged = {};
function logMessage(ui, message) {
// only log each message once, this prevents
// duplicate messages for `app` and `tests` trees
if (!messagesLogged[message]) {
ui.writeLine(message);
messagesLogged[message] = true;
}
}

function getCustomConfig() {
if (fs.existsSync('.jscsrc')) {
var contents = fs.readFileSync('.jscsrc', { encoding: 'utf8' });

return JSON.parse(contents);
} else {
return {};
return null;
}
}

function getSuaveConfig() {
// avoiding `require` (which would have worked fine)
// to prevent mutating the cached result
var configPath = path.join(__dirname, 'jscsrc.json');
var contents = fs.readFileSync(configPath, { encoding: 'utf8' });

var suaveConfig = JSON.parse(contents);
// the rules specified there by default need to be
// deleted, because they are specified relative to
// ember-suave's root directory, and not the projects
// this is later replaced with the actual node_modules
// path
delete suaveConfig.additionalRules;

return suaveConfig;
}

function mergeConfigs(userConfig, suaveConfig) {
var config = {};
var keys, index, length, key, value;
Expand Down Expand Up @@ -54,28 +81,58 @@ function hasEmberSuaveCustomRules(additionalRules) {
return false;
}

/*
When using `"preset": "ember-suave"` all processing in this file is avoided, and we
let JSCS do its normal preset processing. All other scenarios are deprecated with instructions
are provided in the deprecation message.
This entire file can be removed when we no longer need to support non-preset usage.
TODO: Remove lib/jscsrc-builder.js for 2.0.0.
*/

module.exports = function(project) {
var ui = project && project.ui || { writeLine: function() {} };
var userConfig = getCustomConfig();
var suaveConfig = require('./jscsrc');
var suaveConfig = getSuaveConfig();
var jscsConfig, projectRoot, projectRules, customRulePath, info;

var jscsConfig = mergeConfigs(userConfig, suaveConfig);
if (userConfig) {
if (userConfig.preset === 'ember-suave') {
// this is the happy path and short circuits all other logic
return '.jscsrc';
} else {
// when there is a `.jscsrc` but it doesn't include ember-suave as a preset
logMessage(ui, 'Your project\'s `.jscsrc` file does not include `ember-suave` as its preset. Please add `"preset": "ember-suave"` to your `.jscsrc` file.');
}
} else {
// when there is no .jscsrc file
logMessage(ui, 'Your project does not include a `.jscsrc` file. Please generate one via `ember generate ember-suave`.');
}

// everything here and below is for the deprecated cases
jscsConfig = mergeConfigs(userConfig || {}, suaveConfig);

// Project custom rules
var projectRoot = project && project.root || process.cwd();
var projectRules = jscsConfig.additionalRules || [];
projectRoot = project && project.root || process.cwd();
projectRules = jscsConfig.additionalRules || [];
jscsConfig.additionalRules = projectRules.map(function(rulePath) {
return path.resolve(projectRoot, rulePath);
});

// Addon custom rules unless userConfig already included them
if (!hasEmberSuaveCustomRules(jscsConfig.additionalRules)) {
var customRulePath = path.join(__dirname, 'rules');
customRulePath = path.join(__dirname, 'rules');
jscsConfig.additionalRules.push(path.join(customRulePath, '*.js'));
}

var info = temp.openSync('ember-suave');
info = temp.openSync('ember-suave');
fs.writeSync(info.fd, JSON.stringify(jscsConfig));
fs.closeSync(info.fd);

return info.path;
};

module.exports.resetMessageLoggedStatus = function() {
messagesLogged = {};
};
4 changes: 4 additions & 0 deletions lib/jscsrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
"esnext": true,
"verbose": true,

"additionalRules": [
"./rules/*.js"
],

"disallowEmptyBlocks": true,
"disallowKeywordsOnNewLine": ["else"],
"disallowMultipleLineBreaks": true,
Expand Down
13 changes: 9 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,31 @@
"broccoli-asset-rev": "^2.1.1",
"chai": "^2.2.0",
"ember-cli": "1.13.7",
"ember-cli-blueprint-test-helpers": "^0.3.0",
"ember-cli-internal-test-helpers": "^0.4.0",
"ember-cli-release": "0.2.5",
"jscs": "^2.0.0",
"mocha": "^2.2.4",
"mocha-jshint": "^2.1.1",
"walk-sync": "0.1.3"
},
"keywords": [
"ember-addon"
"ember-addon",
"jscs-preset",
"preset"
],
"dependencies": {
"broccoli-jscs": "^1.0.0",
"ember-cli-babel": "^5.0.0",
"temp": "0.8.3"
},
"ember-addon": {
"configPath": "tests/dummy/config"
"configPath": "tests/dummy/config",
"main": "index.js"
},
"main": "index.js",
"main": "lib/jscsrc.json",
"bugs": {
"url": "https://github.com/dockyard/ember-suave/issues"
},
"homepage": "https://github.com/dockyard/ember-suave"
}
}
17 changes: 17 additions & 0 deletions tests/blueprint-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

var setupTestHooks = require('ember-cli-blueprint-test-helpers/lib/helpers/setup');
var BlueprintHelpers = require('ember-cli-blueprint-test-helpers/lib/helpers/blueprint-helper');
var generateAndDestroy = BlueprintHelpers.generateAndDestroy;

describe('Acceptance: ember generate and destroy ember-suave', function() {
setupTestHooks(this);

it('ember-suave', function() {
return generateAndDestroy(['ember-suave'], {
files: [
{ file: '.jscsrc', contents: ['"preset": "ember-suave"'] }
]
});
});
});
1 change: 1 addition & 0 deletions tests/fixtures/.jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "preset": "../../lib/jscsrc.json"}
37 changes: 34 additions & 3 deletions tests/jscsrc-builder-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@ var path = require('path');
var temp = require('temp');
var expect = require('chai').expect;
var jscsrcBuilder = require('../lib/jscsrc-builder');
var resetMessageLoggedStatus = jscsrcBuilder.resetMessageLoggedStatus;

var root = process.cwd();

describe('jscsrc-builder', function() {
function MockProject() {
this.root = 'fake/project/root';
var messages = this.messages = [];
this.ui = {
writeLine: function(message) {
messages.push(message);
}
};
}

beforeEach(function() {
var dir = temp.mkdirSync();

process.chdir(dir);
resetMessageLoggedStatus();
});

afterEach(function() {
process.chdir(root);
});
Expand All @@ -18,13 +36,11 @@ describe('jscsrc-builder', function() {
}

function testCustomConfig(config) {
var dir = temp.mkdirSync();
var dir = process.cwd();
var jscsrcPath = path.join(dir, '.jscsrc');
var jscsrcContents = JSON.stringify(config);

fs.writeFileSync(jscsrcPath, jscsrcContents, { encoding: 'utf8' });

process.chdir(dir);
}

it('returns a path to a valid file', function() {
Expand Down Expand Up @@ -116,4 +132,19 @@ describe('jscsrc-builder', function() {

expect('esnext' in config).to.equal(false);
});

it('prints a deprecation warning when ran without a `.jscsrc` in the project root', function() {
var project = new MockProject();
jscsrcBuilder(project);

expect(project.messages).to.include('Your project does not include a `.jscsrc` file. Please generate one via `ember generate ember-suave`.');
});

it('prints a deprecation warning when `.jscsrc` does not include `"preset": "ember-suave"`', function() {
var project = new MockProject();
testCustomConfig({});
jscsrcBuilder(project);

expect(project.messages).to.include('Your project\'s `.jscsrc` file does not include `ember-suave` as its preset. Please add `"preset": "ember-suave"` to your `.jscsrc` file.');
});
});
3 changes: 2 additions & 1 deletion tests/rules-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ var suaveLintTree = require('../index').lintTree;

describe('rules tests', function() {
var fixturePath = path.join(__dirname, 'fixtures');
var jscsrcPath = path.join(fixturePath, '.jscsrc');
var builder, originLog;

beforeEach(function() {
this.lintTree = suaveLintTree;
this.app = { options: {} };
this.app = { options: { jscsOptions: { configPath: jscsrcPath } } };

originLog = console.log;
console.log = function(contents) {
Expand Down

0 comments on commit 717db06

Please sign in to comment.