Skip to content

Commit

Permalink
Add initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
xemlock committed Feb 16, 2019
1 parent a114ef9 commit 4e14c91
Show file tree
Hide file tree
Showing 9 changed files with 412 additions and 0 deletions.
21 changes: 21 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org

root = true


[*]

# Change these settings to your own preference
indent_style = space
indent_size = 2

# We recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,15 @@ typings/

# next.js build output
.next

# System files
.DS_Store
Thumbs.db
[Dd]esktop.ini

# Lock files
package-lock.json
yarn.lock

# Release
dist
10 changes: 10 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.vscode
.idea
node_modules
src
.editorconfig
.gitignore
.npmignore
.travis.yml
tsconfig.json
tslint.json
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# TSLint Rule: whitespace-before-colon

TSLint provides no means of controlling spaces before colon in object literals and destructuring assignments. `whitespace.check-type` checks only for space after colon, and `typedef-whitespace` works only for type definitions.

This rules determines if a space is required or not before the colon in object literals and destructuring assignments.

Related to [palantir/tslint#991](https://github.com/palantir/tslint/issues/991).

## Usage

Install with NPM or Yarn to your dev dependencies:

```
npm install --save-dev tslint-whitespace-before-colon
```

and enable it in your project's `tslint.json` file:

```json
{
"extends": [
"tslint-whitespace-before-colon"
],
"rules": {
"whitespace-before-colon": [true, "nospace"]
}
}
```

## Configuration

Rule expects a single string option indicating the required number of spaces before colon:

* `"nospace"` requires no space
* `"onespace"` requires exactly one space
* `"space"` requires one or more spaces

If none of the above is provided, the rule will have no effect.

If a newline character (`"\n"`) is present, the rule validates regardless of the configured option, just as `typedef-whitespace` rule does.

## License

MIT
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
rulesDirectory: "./dist",
};
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "tslint-whitespace-before-colon",
"version": "0.1.0",
"description": "TSLint rule for determining if a space is required or not before the colon in object literals and destructuring assignments",
"main": "index.js",
"scripts": {
"build": "tsc --project tsconfig.json --outDir dist",
"prepublish": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "tslint --project tsconfig.json"
},
"repository": {
"type": "git",
"url": "https://github.com/xemlock/tslint-whitespace-before-colon.git"
},
"author": "xemlock <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/xemlock/tslint-whitespace-before-colon/issues"
},
"homepage": "https://github.com/xemlock/tslint-whitespace-before-colon#readme",
"keywords": [
"tslint",
"tslint-plugin",
"custom-rules",
"rules",
"lint",
"linter",
"linting"
],
"devDependencies": {
"@types/node": "^8.10.40",
"tslint": "^5.12.1",
"typescript": "^2.9.2"
},
"peerDependencies": {
"tslint": "^5.0.0",
"typescript": "^2.2.0 || ^3.0.0"
}
}
108 changes: 108 additions & 0 deletions src/whitespaceBeforeColonRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';

type Option = 'nospace' | 'onespace' | 'space';

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
ruleName: 'whitespace-before-colon',
description: Lint.Utils.dedent`
Determines if a space is required or not before the colon in object literals
and destructuring assignments.
If a newline character (\`"\\n"\`) is present, the rule validates regardless
of the configured option, just as \`typedef-whitespace\` rule does.
`,
rationale: Lint.Utils.dedent`
TSLint provides no means of controlling spaces before colon in object literals
and destructuring assignments. \`whitespace.check-type\` checks only for space
after colon, and \`typedef-whitespace\` works only for type definitions.
Related to [palantir/tslint#991](https://github.com/palantir/tslint/issues/991).
`,
optionsDescription: Lint.Utils.dedent`
An option indicating the required number of spaces before colon must be provided:
* \`"nospace"\` requires no space
* \`"onespace"\` requires exactly one space
* \`"space"\` requires one or more spaces
`,
options: {
type: 'string',
enum: ['nospace', 'onespace', 'space'],
},
optionExamples: [
[true, 'onespace'],
],
type: 'style',
typescriptOnly: false,
hasFix: true,
};

public static FAILURE_STRING(option: string) {
return `expected ${option} before colon`;
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const walker = new RuleWalker(sourceFile, this.getOptions());
return this.applyWithWalker(walker);
}
}

class RuleWalker extends Lint.RuleWalker {
private option?: Option;

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
super(sourceFile, options);

const [option] = this.getOptions();

if (['nospace', 'onespace', 'space'].indexOf(option) !== -1) {
this.option = option;
}
}

public visitNode(node: ts.Node) {
if (
node.kind === ts.SyntaxKind.PropertyAssignment ||
node.kind === ts.SyntaxKind.BindingElement
) {
for (const child of node.getChildren()) {
if (child.kind === ts.SyntaxKind.ColonToken) {
this.checkWhitespace(child);
}
}
}
super.visitNode(node);
}

private checkWhitespace(colon: ts.Node) {
const start = colon.pos; // position of whitespace before ':' in ColonToken
const end = colon.end; // position of ':'
const text = this.getSourceFile().text.substring(start, end);

const match = text.match(/^(\s+)/);
const whitespace = match && match[1] || '';

if (whitespace.includes('\n')) {
return;
}

const { option } = this;

if (!option || option === 'space' && whitespace.length) {
return;
}

const requiredLength = option === 'nospace' ? 0 : 1;

if (whitespace.length !== requiredLength) {
this.addFailureAt(
start,
whitespace.length,
Rule.FAILURE_STRING(option),
Lint.Replacement.replaceFromTo(start, start + whitespace.length, ' '.repeat(requiredLength))
);
}
}
}
26 changes: 26 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"compileOnSave": false,
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"baseUrl": "src",
"removeComments": true,
"sourceMap": false,
"target": "es5",
"typeRoots": [
"node_modules/@types",
],
"lib": [
"es2017"
],
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmitOnError": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true
},
"exclude": [
"node_modules"
]
}
Loading

0 comments on commit 4e14c91

Please sign in to comment.