Skip to content
This repository was archived by the owner on Aug 7, 2023. It is now read-only.

Move to using mainline CSSLint #170

Merged
merged 7 commits into from
Aug 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.DS_Store
.idea
npm-debug.log
node_modules
node_modules/*
.github_changelog_generator
9 changes: 4 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ matrix:
- os: linux
env: ATOM_CHANNEL=beta

- os: osx
env: ATOM_CHANNEL=stable

### Generic setup follows ###
script:
- curl -s -O https://raw.githubusercontent.com/atom/ci/master/build-package.sh
Expand All @@ -32,10 +29,12 @@ git:

sudo: false

dist: trusty

addons:
apt:
packages:
- build-essential
- git
- libgnome-keyring-dev
- fakeroot
- git
- libsecret-1-dev
3 changes: 1 addition & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ Please note that modifications should follow these coding guidelines:

- Indent is 2 spaces.

- Code should pass [CoffeeLint](http://www.coffeelint.org/) with the provided
`coffeelint.json`
- Code should pass [ESlint](https://eslint.org/).

- Vertical whitespace helps readability, don’t be afraid to use it.

Expand Down
209 changes: 147 additions & 62 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
'use babel';

/* eslint-disable import/extensions, import/no-extraneous-dependencies */
// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
import { CompositeDisposable } from 'atom';
/* eslint-enable import/extensions, import/no-extraneous-dependencies */

let helpers = null;
let path = null;
// Dependencies
let fs;
let path;
let helpers;

// Internal Variables
let bundledCsslintPath;

const loadDeps = () => {
if (!fs) {
fs = require('fs-plus');
}
if (!path) {
path = require('path');
}
if (!helpers) {
helpers = require('atom-linter');
}
};

export default {
activate() {
require('atom-package-deps').install('linter-csslint');
this.idleCallbacks = new Set();
let depsCallbackID;
const installLinterCsslintDeps = () => {
this.idleCallbacks.delete(depsCallbackID);
if (!atom.inSpecMode()) {
require('atom-package-deps').install('linter-csslint');
}
loadDeps();

// FIXME: Remove this after a few versions
if (atom.config.get('linter-csslint.disableTimeout')) {
atom.config.unset('linter-csslint.disableTimeout');
}
};
depsCallbackID = window.requestIdleCallback(installLinterCsslintDeps);
this.idleCallbacks.add(depsCallbackID);

this.subscriptions = new CompositeDisposable();
this.subscriptions.add(
atom.config.observe('linter-csslint.disableTimeout', (value) => {
this.disableTimeout = value;
atom.config.observe('linter-csslint.executablePath', (value) => {
this.executablePath = value;
}),
);
},

deactivate() {
this.idleCallbacks.forEach(callbackID => window.cancelIdleCallback(callbackID));
this.idleCallbacks.clear();
this.subscriptions.dispose();
},

Expand All @@ -28,77 +61,129 @@ export default {
name: 'CSSLint',
grammarScopes: ['source.css', 'source.html'],
scope: 'file',
lintOnFly: true,
lint(textEditor) {
if (!helpers) {
helpers = require('atom-linter');
}
if (!path) {
path = require('path');
}
lintsOnChange: false,
lint: async (textEditor) => {
loadDeps();
const filePath = textEditor.getPath();
const text = textEditor.getText();
if (text.length === 0) {
return Promise.resolve([]);
if (!filePath || text.length === 0) {
// Empty or unsaved file
return [];
}
const parameters = ['--format=json', '-'];
const exec = path.join(__dirname, '..', 'node_modules', 'atomlinter-csslint', 'cli.js');

const parameters = [
'--format=json',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, requires CSSLint v1.0.0 minimum for this functionality. We might need a check for this if users start filing many support requests about broken versions.

filePath,
];

const projectPath = atom.project.relativizePath(filePath)[0];
let cwd = projectPath;
if (!(cwd)) {
if (!cwd) {
cwd = path.dirname(filePath);
}
const options = { stdin: text, cwd };
if (this.disableTimeout) {
options.timeout = Infinity;

const execOptions = {
cwd,
uniqueKey: `linter-csslint::${filePath}`,
timeout: 1000 * 30, // 30 seconds
ignoreExitCode: true,
};

const execPath = this.determineExecPath(this.executablePath, projectPath);

const output = await helpers.exec(execPath, parameters, execOptions);

if (textEditor.getText() !== text) {
// The editor contents have changed, tell Linter not to update
return null;
}
return helpers.execNode(exec, parameters, options).then((output) => {
if (textEditor.getText() !== text) {
// The editor contents have changed, tell Linter not to update
return null;
}

const toReturn = [];
if (output.length < 1) {
// No output, no errors
return toReturn;
const toReturn = [];

if (output.length < 1) {
// No output, no errors
return toReturn;
}

let lintResult;
try {
lintResult = JSON.parse(output);
} catch (e) {
const excerpt = 'Invalid response received from CSSLint, check ' +
'your console for more details.';
return [{
severity: 'error',
excerpt,
location: {
file: filePath,
position: helpers.generateRange(textEditor, 0),
},
}];
}

if (lintResult.messages.length < 1) {
// Output, but no errors found
return toReturn;
}

lintResult.messages.forEach((data) => {
let line;
let col;
if (!(data.line && data.col)) {
// Use the file start if a location wasn't defined
[line, col] = [0, 0];
} else {
[line, col] = [data.line - 1, data.col - 1];
}

const lintResult = JSON.parse(output);
const severity = data.type === 'error' ? 'error' : 'warning';

if (lintResult.messages.length < 1) {
// Output, but no errors found
return toReturn;
const msg = {
severity,
excerpt: data.message,
location: {
file: filePath,
position: helpers.generateRange(textEditor, line, col),
},
};
if (data.rule.id && data.rule.desc) {
msg.details = `${data.rule.desc} (${data.rule.id})`;
}
if (data.rule.url) {
msg.url = data.rule.url;
}

lintResult.messages.forEach((data) => {
let line;
let col;
if (!(data.line && data.col)) {
// Use the file start if a location wasn't defined
[line, col] = [0, 0];
} else {
[line, col] = [data.line - 1, data.col - 1];
}

const msg = {
type: data.type.charAt(0).toUpperCase() + data.type.slice(1),
text: data.message,
filePath,
range: helpers.generateRange(textEditor, line, col),
};

if (data.rule.id && data.rule.desc) {
msg.trace = [{
type: 'Trace',
text: `[${data.rule.id}] ${data.rule.desc}`,
}];
}
toReturn.push(msg);
});
return toReturn;
toReturn.push(msg);
});

return toReturn;
},
};
},

determineExecPath(givenPath, projectPath) {
let execPath = givenPath;
if (execPath === '') {
// Use the bundled copy of CSSLint
let relativeBinPath = path.join('node_modules', '.bin', 'csslint');
if (process.platform === 'win32') {
relativeBinPath += '.cmd';
}
if (!bundledCsslintPath) {
const packagePath = atom.packages.resolvePackagePath('linter-csslint');
bundledCsslintPath = path.join(packagePath, relativeBinPath);
}
execPath = bundledCsslintPath;
if (projectPath) {
const localCssLintPath = path.join(projectPath, relativeBinPath);
if (fs.existsSync(localCssLintPath)) {
execPath = localCssLintPath;
}
}
} else {
// Normalize any usage of ~
fs.normalize(execPath);
}
return execPath;
},
};
27 changes: 15 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,29 @@
"license": "MIT",
"private": true,
"configSchema": {
"disableTimeout": {
"type": "boolean",
"description": "Disable the 10 second execution timeout",
"default": false
"executablePath": {
"type": "string",
"default": "",
"description": "If unset a project local install of CSSLint is attempted to be used first, falling back to the bundled version. Requires a full path to `csslint` (e.g.: `/usr/bin/csslint` or `C:\\foo\\bar\\csslint.cmd`)."
}
},
"engines": {
"atom": ">=1.4.0 <2.0.0"
},
"dependencies": {
"atom-linter": "^10.0.0",
"atom-package-deps": "^4.0.1",
"atomlinter-csslint": "0.10.1"
"atom-package-deps": "^4.6.0",
"csslint": "^1.0.5",
"fs-plus": "^3.0.1"
},
"devDependencies": {
"eslint": "^3.15.0",
"eslint-config-airbnb-base": "^11.1.0",
"eslint-plugin-import": "^2.2.0"
"eslint": "^4.3.0",
"eslint-config-airbnb-base": "^11.3.1",
"eslint-plugin-import": "^2.7.0",
"jasmine-fix": "^1.3.0"
},
"package-deps": [
"linter"
"linter:2.0.0"
],
"scripts": {
"lint": "eslint .",
Expand All @@ -56,13 +58,14 @@
"atom": true
},
"env": {
"node": true
"node": true,
"browser": true
}
},
"providedServices": {
"linter": {
"versions": {
"1.0.0": "provideLinter"
"2.0.0": "provideLinter"
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion spec/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
module.exports = {
env: {
jasmine: true,
atomtest: true
atomtest: true,
},
rules: {
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": true
}
]
}
};
Empty file.
Empty file.
Loading