curated-linter

1.1.2 • Public • Published

Build Status Standard - JavaScript Style Guide codecov

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

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

#lintFiles(files, config)

Lints files

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 configs 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).

Dependents (0)

Package Sidebar

Install

npm i curated-linter

Weekly Downloads

1

Version

1.1.2

License

ISC

Last publish

Collaborators

  • mightyiam