curated-linter
v1.1.2
Published
Creates curated linters, like standard
Downloads
4
Maintainers
Readme
curated-linter (WIP)
Makes curated ESLint linters, like standard
Work in progress
Not all documented features are implemented.
Feel free to contribute.
Table of contents
- What is a curated linter?
- Example
- API
config
- End user API
- End user CLI
- End user configuration via
package.json
- End user overrides
- Curated extensions
What is a curated linter?
- a linter based on ESLint’s
CLIEngine
- probably has a name (e.g. "standard")
- probably has curated ESLint configuration and rules
- the extent to which the end-user is able to override and/or extend the curated configuration is configurable
- may or may not allow using an official, curated, set of extensions
- may have either or both of a (promise) API and a CLI
- CLI may report using a custom ESLint formatter
- and more features, that are not in ESLint
Example
In this example we create our curated linter as an npm package.
package.json
:
{
"name": "nofoobar",
"main": "index.js",
"bin": { "nofoobar": "index.js" }
}
Yes, the CLI is the same module as the API.
index.js
For instantiation, we do not provide an config
object—we provide a function that will return a new config
object with each call—a getter.
#!/usr/bin/env node
const CuratedLinter = require('curated-linter')
// `config` getter function
const getConfig = () => ({
name: 'nofoobar', // CLI name and `package.json` config key
packageJson: true, // read end user config from `package.json`
gitIgnore: true, // (also) ignore what git ignores
formatter: (results, config) => { // customize your CLI error output
return `${config.name}: Nope. Do not use \`foo\` or \`bar\``
// or… something more real than this
},
CLIEngineOptions: { // options for ESLint’s `CLIEngine`
ignore: false,
useEslintrc: false,
rules: { // http://eslint.org/docs/rules/
'id-blacklist': ['foo', 'bar'] // http://eslint.org/docs/rules/id-blacklist
// your usage may include all your favorite rules!
}
// for more `CLIEngine` options: http://eslint.org/docs/developer-guide/nodejs-api#cliengine
}
// not all curated-linter configuration options are used in this example
})
const noFoobar = new CuratedLinter(getConfig)
// The API is ready. Now just export it!
module.exports = noFoobar
This example hopefully provided you with a basic understanding. Read below for the API and some awesome features.
API
new CuratedLinter(getConfig)
Constructs a curated linter
This is the main export of this package.
getConfig
must be a function that returns a new config
object with each call.
config
Contains all of the configuration, policy and behavior of a desired curated linter
The config
object is provided to the curated linter instance firstly via the getConfig
constructor argument.
It can also be provided to the lintText
and lintFiles
methods, for the sake of possibly allowing the end users to override or extend the curated configuration.
Following are all of the possible properties of config
:
name
Machine name of the curated linter
Must be provided if the CLI feature is to be used.
bugs
URL where the end-user should report bugs
Must be provided if the CLI feature is to be used.
packageJson
Whether to allow end user configuration via
package.json
gitIgnore
Whether to (also) ignore according to
.gitignore
This determines whether, in addition to any other ignore configuration, to ignore files that are ignored by a possibly existing .gitignore
.
The .gitignore
that may be read is the one that may be at the same directory as the package.json
.
ignore
List of glob file patterns to ignore
cwd
Relative file paths will be resolved to this
Defaults to process.cwd()
.
curatedExtensions
List of official curated extensions
CLIEngineOptions
Will be passed to
CLIEngine
This is where you may define your rules, plugins, etc.
Tip: if you can’t find a certain property on this interface, take a look at the baseConfig
property.
Caveat: For the sake of the config.gitIgnore
feature, CuratedLinter does its own file deglobbing and serves ESLint absolute file paths. Therefore, instead of using config.CLIEngineOptions.ignore(*)
options, it is recommended that you set config.CLIEngineOptions.ignore
to false
and use config.ignore
, instead.
formatter
Formats the CLI error messages
If this is a string, it must represent a built-in ESLint formatter.
Otherwise, it must be a function. It will be called as formatter(results, config)
. Except for the config
argument, the signature is identical to ESLint formatters.
The config
provided will be post user overrides.
defaultFiles
Files to lint by default
Whether via the CLI or via #lintFiles
, if no files provided, these will be linted.
An array of globs.
End user API
#lintText(text, config)
Lints provided text
text
: text (source code) to lintconfig
- returns a promise of the ESLint results object
#lintFiles(files, config)
Lints files
files
: array of globs of files to lintconfig
- returns a promise of the ESLint results object
The ESLint results object
Resolved value of promises returned by
#lintFiles
and#lintText
Documentation of the ESLint results object can be found in ESLint’s #executeOnFiles
documentation.
End user CLI
To provide end users with a CLI, the config.name
property must be provided.
Also, there must be a package.json
bin
property, like so:
{
"name": "nofoobar",
"bin": {
"nofoobar": "index.js"
}
}
The value of the property under bin
(here "index.js"
) must be a module where CuratedLinter
is instantiated. No further method calling required. Thus your main export and your bin
can be the same module.
To allow this unified main
/bin
"magic" , the property under the bin
property must be identical to your config.name
.
The module must start with the Node.js shebang (#!/usr/bin/env node
).
End user configuration via package.json
If config.packageJson
is true
, then a config from a certain property of the end user’s package.json
will be read and applied as explained in user overrides. That certain property is config.name
.
For example if config.name === 'nofoobar'
:
{
"name": "the-users-package",
"nofoobar": {
"ignore": ["**/*.test.js"]
}
}
End user overrides
This is about implementing a policy regarding whether, what and how the end user of the curated linter is allowed to override or extend, the curated config
.
You may implement whatever policy you like, by having your getConfig
return a version of config
that is protected using proxies.
Whether from end user config
in package.json
and/or from the end user passing config
to lintText
or lintFiles
, those user config
s will be merged into the config
that your getConfig
returned, using deep assignment. This means that, if your getConfig
provides an unprotected config
, the user will be able to override any property in that tree.
Overrides occur on method invocation (the CLI also uses those methods). On each such invocation, your curated config
is retrieved by calling your getConfig
. Thus, overrides applied on one method call will not persist on the instance.
Example of protected config
using proxies
The only allowed override is that the id-blacklist
rule can be added more identifiers:
const CuratedLinter = require('curated-linter')
const frozen = {
set: () => false
}
const append = {
set: (target, property, value) => {
target.push(value) // convert setting into appending
return true
}
}
const getConfig = () => (new Proxy({
CLIEngineOptions: new Proxy({
name: 'nofoobar',
rules: new Proxy({
'id-blacklist': new Proxy([
'foo',
'bar'
], append)
}, frozen)
}, frozen)
}, frozen))
const noFooBar = new CuratedLinter(getConfig)
module.exports = noFooBar
As you can see, using proxies, it is possible to implement any override/extension policy.
Curated extensions
This feature allows official, curated extensions to be automatically used if the end user has any of them installed.
An official, curated extension is a separate ESLint sharable configuration package.
Each member of a provided config.curatedExtensions
array is expected to be a name of an ESLint shareable configuration. The eslint-config-
prefix may be omitted. Each such package, if the end user has it installed, will be pushed to the end of the config.CLIEngineOptions
.baseConfig.extends
array (will be created if undefined
and will be made into an array if false
).