Skip to content

Commit

Permalink
add eslint-rules "module" and use it in eslintrc-file
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Dec 24, 2019
1 parent 5c9a0af commit 556475c
Show file tree
Hide file tree
Showing 10 changed files with 1,560 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/insane/**
**/marked/**
**/test/**/*.js
49 changes: 35 additions & 14 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
{
"$schema": "https://json.schemastore.org/eslintrc",
"root": true,
"env": {
"node": true,
"es6": true
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"project": "./src/tsconfig.json"
},
"plugins": [
"code-oss-eslint"
],
"rules": {
"no-console": 0,
"no-cond-assign": 0,
"no-unused-vars": 1,
"no-extra-semi": "warn",
"semi": "warn"
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
"no-extra-semi": "off",
"no-unused-vars": "off",
"semi": "warn",
"eqeqeq": "warn",
"no-duplicate-imports": "warn",
"code-oss-eslint/layering": [
"warn",
{
"common": [],
"node": [
"common"
],
"browser": [
"common"
],
"electron-main": [
"common",
"node"
],
"electron-browser": [
"common",
"browser",
"node"
]
}
]
}
}
10 changes: 10 additions & 0 deletions build/lib/eslint/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const layering = require('./rules/layering');
const rules = {
'layering': layering
};
module.exports = { rules };
12 changes: 12 additions & 0 deletions build/lib/eslint/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

const layering = require('./rules/layering');

const rules = {
'layering': layering
};

export = { rules };
81 changes: 81 additions & 0 deletions build/lib/eslint/lib/rules/layering.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path_1 = require("path");
module.exports = new class {
constructor() {
this.meta = {
type: 'problem',
schema: {},
messages: {
layerbreaker: 'Bad layering. You are not allowed to access {{from}} from here, allowed layers are: [{{allowed}}]'
}
};
}
create(context) {
const fileDirname = path_1.dirname(context.getFilename());
const parts = fileDirname.split(/\\|\//);
const ruleArgs = context.options[0];
let config;
for (let i = parts.length - 1; i >= 0; i--) {
if (ruleArgs[parts[i]]) {
config = {
allowed: new Set(ruleArgs[parts[i]]).add(parts[i]),
disallowed: new Set()
};
Object.keys(ruleArgs).forEach(key => {
if (!config.allowed.has(key)) {
config.disallowed.add(key);
}
});
break;
}
}
if (!config) {
// nothing
return {};
}
return {
ImportDeclaration: (node) => {
this._checkImport(context, config, node, node.source.value);
},
CallExpression: (node) => {
var _a;
const { callee, arguments: args } = node;
if (callee.type === 'Import' && ((_a = args[0]) === null || _a === void 0 ? void 0 : _a.type) === 'Literal') {
this._checkImport(context, config, node, args[0].value);
}
}
};
}
_checkImport(context, config, node, path) {
if (typeof path !== 'string') {
return;
}
if (path[0] === '.') {
path = path_1.join(path_1.dirname(context.getFilename()), path);
}
const parts = path_1.dirname(path).split(/\\|\//);
for (let i = parts.length - 1; i >= 0; i--) {
const part = parts[i];
if (config.allowed.has(part)) {
// GOOD - same layer
break;
}
if (config.disallowed.has(part)) {
// BAD - wrong layer
context.report({
node,
messageId: 'layerbreaker',
data: {
from: part,
allowed: [...config.allowed.keys()].join(', ')
}
});
break;
}
}
}
};
99 changes: 99 additions & 0 deletions build/lib/eslint/lib/rules/layering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as eslint from 'eslint';
import * as estree from 'estree';
import { join, dirname } from 'path';

type Config = {
allowed: Set<string>;
disallowed: Set<string>;
};

export = new class implements eslint.Rule.RuleModule {

meta = {
type: 'problem',
schema: {},
messages: {
layerbreaker: 'Bad layering. You are not allowed to access {{from}} from here, allowed layers are: [{{allowed}}]'
}
};

create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {

const fileDirname = dirname(context.getFilename());
const parts = fileDirname.split(/\\|\//);
const ruleArgs = <Record<string, string[]>>context.options[0];

let config: Config | undefined;
for (let i = parts.length - 1; i >= 0; i--) {
if (ruleArgs[parts[i]]) {
config = {
allowed: new Set(ruleArgs[parts[i]]).add(parts[i]),
disallowed: new Set()
};
Object.keys(ruleArgs).forEach(key => {
if (!config!.allowed.has(key)) {
config!.disallowed.add(key);
}
});
break;
}
}

if (!config) {
// nothing
return {};
}

return {
ImportDeclaration: (node: estree.Node) => {
this._checkImport(context, config!, node, (<estree.ImportDeclaration>node).source.value);
},
CallExpression: (node: estree.Node) => {
const { callee, arguments: args } = <estree.CallExpression>node;
if ((<any>callee.type) === 'Import' && args[0]?.type === 'Literal') {
this._checkImport(context, config!, node, (<estree.Literal>args[0]).value);
}
}
};
}

private _checkImport(context: eslint.Rule.RuleContext, config: Config, node: estree.Node, path: any) {
if (typeof path !== 'string') {
return;
}

if (path[0] === '.') {
path = join(dirname(context.getFilename()), path);
}

const parts = dirname(path).split(/\\|\//);
for (let i = parts.length - 1; i >= 0; i--) {
const part = parts[i];

if (config!.allowed.has(part)) {
// GOOD - same layer
break;
}

if (config!.disallowed.has(part)) {
// BAD - wrong layer
context.report({
node,
messageId: 'layerbreaker',
data: {
from: part,
allowed: [...config!.allowed.keys()].join(', ')
}
});

break;
}
}
}
};

20 changes: 20 additions & 0 deletions build/lib/eslint/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "code-oss-eslint",
"version": "1.0.0",
"license": "MIT",
"description": "ESLint rules for code oss",
"engines": {
"node": ">=0.10.0"
},
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin"
],
"author": "vscode",
"main": "lib/index.js",
"devDependencies": {
"@types/eslint": "4.16.1",
"eslint": "~3.9.1"
}
}
Loading

0 comments on commit 556475c

Please sign in to comment.