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

Commit c532bbe

Browse files
Initial import
0 parents  commit c532bbe

15 files changed

+4717
-0
lines changed

.eslintignore

Whitespace-only changes.

.eslintrc

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es6": true,
5+
"node": true
6+
},
7+
"globals": {
8+
"atom": "readonly"
9+
},
10+
"parserOptions": {
11+
"ecmaVersion": "latest",
12+
"sourceType": "module",
13+
"ecmaFeatures": {
14+
"jsx": true
15+
}
16+
},
17+
"plugins": [
18+
"eslint-plugin-import"
19+
],
20+
"extends": [
21+
"eslint:recommended"
22+
],
23+
"root": true,
24+
"rules": {
25+
"no-trailing-spaces": [
26+
"error",
27+
{ "ignoreComments": true }
28+
],
29+
"no-console": "off",
30+
"max-len": [
31+
1,
32+
{
33+
"code": 130
34+
}
35+
],
36+
"semi": [
37+
"error",
38+
"always"
39+
],
40+
"comma-dangle": "off",
41+
"global-require": "off",
42+
"import/no-import-module-exports": "off",
43+
"import/no-unresolved": [
44+
"error",
45+
{
46+
"ignore": [
47+
"atom"
48+
]
49+
}
50+
],
51+
"object-curly-newline": [
52+
"error",
53+
{
54+
"ObjectExpression": {
55+
"minProperties": 5,
56+
"multiline": true,
57+
"consistent": true
58+
},
59+
"ObjectPattern": {
60+
"minProperties": 5,
61+
"multiline": true,
62+
"consistent": true
63+
}
64+
}
65+
]
66+
},
67+
}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.DS_Store
2+
npm-debug.log
3+
node_modules
4+
.linter-eslint

CHANGELOG.md

Whitespace-only changes.

LICENSE.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2022 Andrew Dupont
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# linter-eslint-node package
2+
3+
A “bring-your-own-Node” linter for newer versions of ESLint: v7 and above.
4+
5+
## Installation
6+
7+
```ShellSession
8+
apm install linter-eslint-node
9+
```
10+
11+
The `linter` package will be installed for you if it’s not already present in your Atom installation. If you’re using an alternative `linter-*` consumer, the `linter` package can be disabled.
12+
13+
14+
## Why does this need to exist separate from linter-eslint?
15+
16+
Two reasons:
17+
18+
1. After it was deprecated in v7, the `CLIEngine` class that `linter-eslint` relied upon was removed from ESLint in v8. Its replacement removed a few methods that supported some of `linter-eslint`’s features, making it impossible to abstract away the difference between the two, or to deliver an experience that’s consistent across ESLint versions.
19+
20+
2. As the Node world slowly migrates away from CommonJS and toward [ECMAScript modules][], certain high-profile ESLint plugins now offer native ESM versions. This is not a problem for any recent version of Node, but it is a problem for `linter-eslint`’s practice of linting within a worker script using _Atom’s_ version of Node, which is too old to support ESM and is unlikely to be updated in the near future. The solution to this problem is to switch to a “bring-your-own-Node” model that runs the worker script inside the same version of Node that your project itself uses.
21+
22+
## Should I uninstall linter-eslint?
23+
24+
Depends. The `linter-eslint` package supports **ESLint up through and including v7**. This new package supports **ESLint v7 and greater**. The overlap in v7 is because that’s the one major version where both interfaces, `CLIEngine` and `ESLint`, are available.
25+
26+
If all your projects use ESLint `>=7.0.0`, you can keep this package and uninstall `linter-eslint`. If any of your projects use an older ESLint, you should keep `linter-eslint` installed alongside this package.
27+
28+
Since they can both lint when ESLint 7.x is present, they have to coordinate who does the linting when both packages are installed. **If `linter-eslint` is installed, this package will not perform linting in ESLint 7.x environments** — only 8.x or greater. If only this package is installed, it will lint with any version of ESLint it supports.
29+
30+
## How do I “bring my own Node”?
31+
32+
To run your version of Node, `linter-eslint-node` needs to know _where_ your version of Node is, and that question sometimes has a complex answer.
33+
34+
The command `Linter Eslint Node: Debug` will show a panel with the version of Node that this package will use for a particular project.
35+
36+
### First: just see if it works
37+
38+
If you use exactly one version of Node on your system, there’s a good chance this package will work out of the box without further configuration.
39+
40+
If you manage several versions of Node using a tool like [NVM][], you might still want to do nothing at first and rely on this package’s heuristics to figure out which version of Node to use for a given project. This is highly likely to work if
41+
42+
* you’re on Linux or macOS;
43+
* you only ever open projects from a terminal in which you’ve got NVM installed (and never via your OS’s file browser or **File → Open Recent…**); and
44+
* you are sure to run `nvm use` before running `atom .`, or else have auto-switching set up via `.nvmrc` files.
45+
46+
If you do all these things, the Atom windows you spawn will inherit the environment defined by your shell, including the current value of `$PATH`.
47+
48+
It’s also likely to work if you use a version manager like [Volta][] or [asdf][] in which there’s a single “shim” executable with a consistent location.
49+
50+
### Failing that: set it explicitly
51+
52+
If you use one version of Node on your system, and this package somehow hasn’t inferred it from your `$PATH` variable, then you can use the package settings page to set “Node Binary” manually. On macOS or Linux, `which node` will typically retrieve this path.
53+
54+
If you manage several versions of node with [NVM][] or a similar tool, and sometimes don’t launch a project via the terminal, you might notice this package using your the path to your NVM-default version of Node instead of the correct version for that project.
55+
56+
You can fix this by bypassing our heuristics and setting your Node binary path **on a per-project basis** using one of several methods.
57+
58+
If you’re already using a package like [project-config][] or [atomic-management][], you can specify this setting in a file that resides at `.atom/config.json` (or `config.cson` for atomic-management):
59+
60+
```json
61+
{
62+
"linter-eslint-node": {
63+
"nodeBin": "/Users/foo/.nvm/versions/node/v17.4.0/bin/node"
64+
}
65+
}
66+
```
67+
68+
Otherwise, you can specify your Node binary path (or any other project-specific `linter-eslint-node` settings) with a file called `.linter-eslint` that lives in your project root and contains only configuration settings for this package:
69+
70+
```json
71+
{
72+
"nodeBin": "/Users/foo/.nvm/versions/node/v17.4.0/bin/node"
73+
}
74+
```
75+
76+
To know which path to use:
77+
78+
* `cd` to your project root in a terminal;
79+
* be sure to run any version-manager-specific commands (like `nvm use`) if necessary; then
80+
* run `which node`.
81+
82+
Keep in mind you’ll have to update this setting whenever you update the version of Node that a given project uses.
83+
84+
## Which ESLint version will this package use?
85+
86+
`linter-eslint-node` will look for a version of `eslint` local to your project, as long as it’s at least v7.0.0. Ideally, this would be installed into a `node_modules` folder in the project root. If it’s in a different location for whatever reason, you can use the `advanced.localNodeModules` setting to specify a path (relative or absolute) in either your `config.cson` or your `.linter-eslint` file.
87+
88+
If it doesn’t find an ESLint in your project, `linter-eslint-node` will fall back to the version it ships with.
89+
90+
## Other configuration
91+
92+
Common JavaScript-derivative languages (TypeScript, Flow, etc.) will also trigger this linter by default. If you’d prefer that they don’t, or if you use a more obscure JS-derivative language that should nonetheless be linted, you can change the list of language scope names in this package’s “List of scopes” setting.
93+
94+
95+
## Using ESLint
96+
97+
Recent versions of ESLint don’t use any rules by default. For all but the most basic of usages, you must create an `.eslintrc` file in your project root:
98+
99+
```ShellSession
100+
npx eslint --init # or without "npx " if installed globally
101+
```
102+
103+
You can also create the `.eslintrc` file manually. It’s a good idea to consult the [ESLint documentation](http://eslint.org/docs/user-guide/configuring), including the [list of rules](http://eslint.org/docs/rules/).
104+
105+
### Plugins
106+
107+
It’s better practice to install ESLint plugins locally in your project, but plugins installed globally will also work just fine. But keep in mind that either way you’ll need to define an `.eslintrc` file in your project that includes those plugins.
108+
109+
110+
[ECMAScript modules]: https://nodejs.org/api/esm.html
111+
[NVM]: https://github.com/nvm-sh/nvm/blob/master/README.md
112+
[Volta]: https://volta.sh/
113+
[asdf]: https://asdf-vm.com/
114+
[project-config]: https://github.com/steelbrain/project-config/
115+
[atomic-management]: https://github.com/harmsk/atomic-management

lib/config.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use babel';
2+
import { CompositeDisposable, Disposable } from 'atom';
3+
4+
import console from './console';
5+
6+
// Returns config values, but prioritizes anything defined in `.linter-eslint`
7+
// as a per-project override for this linter's settings.
8+
const Config = {
9+
subscriptions: null,
10+
initialized: false,
11+
handlers: [],
12+
13+
initialize () {
14+
if (this.initialized) { return; }
15+
this.rescan();
16+
this.initialized = true;
17+
console.log('Config initialized. nodeBin:', this.get('nodeBin'));
18+
},
19+
20+
rescan () {
21+
if (this.subscriptions) {
22+
this.subscriptions.dispose();
23+
}
24+
this.subscriptions = new CompositeDisposable();
25+
this.overrides = {};
26+
this._currentConfig = null;
27+
28+
for (let dir of atom.project.getDirectories()) {
29+
let candidate = dir.getFile('.linter-eslint');
30+
if (candidate.existsSync()) {
31+
this.configFile = candidate;
32+
break;
33+
}
34+
}
35+
36+
if (this.configFile) {
37+
this.subscriptions.add(
38+
this.configFile.onDidChange(this.update.bind(this))
39+
);
40+
this.update();
41+
}
42+
this.subscriptions.add(
43+
atom.config.observe(
44+
'linter-eslint-node',
45+
this.triggerConfigChange.bind(this)
46+
)
47+
);
48+
},
49+
50+
update () {
51+
if (!this.configFile) {
52+
this.overrides = {};
53+
return;
54+
}
55+
try {
56+
this.overrides = JSON.parse(this.configFile.readSync());
57+
} catch (err) {
58+
console.error('Linter ESLint v8: Error parsing .linter-eslint file', err);
59+
this.overrides = {};
60+
}
61+
this.triggerConfigChange();
62+
},
63+
64+
get (keyName = null) {
65+
if (!keyName) {
66+
return Object.assign(
67+
{},
68+
atom.config.get('linter-eslint-node'),
69+
this.overrides
70+
);
71+
}
72+
if (keyName in this.overrides) {
73+
return this.overrides[keyName];
74+
}
75+
return atom.config.get(`linter-eslint-node.${keyName}`);
76+
},
77+
78+
triggerConfigChange () {
79+
let newConfig = this.get();
80+
for (let handler of this.handlers) {
81+
handler(newConfig, this._currentConfig || {});
82+
}
83+
this._currentConfig = newConfig;
84+
},
85+
86+
onConfigDidChange (handler) {
87+
this.handlers.push(handler);
88+
return new Disposable(() => {
89+
let indexToRemove = this.handlers.indexOf(handler);
90+
this.handlers.splice(indexToRemove, 1);
91+
});
92+
},
93+
94+
dispose () {
95+
this.subscriptions.dispose();
96+
}
97+
};
98+
99+
export default Config;

lib/console.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use babel';
2+
3+
const CONSOLE = {};
4+
5+
const isEnabled = atom.inDevMode();
6+
7+
function makeConsoleMethod (name) {
8+
return (...args) => {
9+
if (!isEnabled) { return; }
10+
return window.console[name](
11+
'[linter-eslint-node]',
12+
...args
13+
);
14+
};
15+
}
16+
17+
['log', 'warn', 'error', 'info', 'debug', 'group'].forEach(name => {
18+
CONSOLE[name] = makeConsoleMethod(name);
19+
});
20+
21+
export default CONSOLE;

lib/helpers.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use babel';
2+
import { Range } from 'atom';
3+
import { generateRange } from 'atom-linter';
4+
5+
6+
export function hasValidScope(editor, scopes) {
7+
return editor.getCursors()
8+
.some(
9+
cursor => (
10+
cursor.getScopeDescriptor()
11+
.getScopesArray()
12+
.some((scope) => scopes.includes(scope))
13+
)
14+
);
15+
}
16+
17+
export function generateUserMessage (textEditor, options) {
18+
const {
19+
severity = 'error',
20+
excerpt = '',
21+
description,
22+
} = options;
23+
return [{
24+
severity,
25+
excerpt,
26+
description,
27+
location: {
28+
file: textEditor.getPath(),
29+
position: generateRange(textEditor),
30+
},
31+
}];
32+
}
33+
34+
function configThatMayInvalidateWorkerCache (config) {
35+
let {
36+
advanced: { disableEslintIgnore },
37+
autofix: { rulesToDisableWhileFixing }
38+
} = config;
39+
40+
return {
41+
disableEslintIgnore,
42+
rulesToDisableWhileFixing
43+
};
44+
}
45+
46+
export function configShouldInvalidateWorkerCache (prev, current) {
47+
let prevDigest = configThatMayInvalidateWorkerCache(prev);
48+
let currentDigest = configThatMayInvalidateWorkerCache(current);
49+
50+
return JSON.stringify(prevDigest) !== JSON.stringify(currentDigest);
51+
}
52+
53+
export function solutionsForFix (fix, textBuffer) {
54+
let { range, text } = fix;
55+
let [start, end] = range.map(p => (
56+
textBuffer.positionForCharacterIndex(p)
57+
));
58+
59+
return [{
60+
position: new Range(start, end),
61+
replaceWith: text
62+
}];
63+
}

0 commit comments

Comments
 (0)