extract import path detection into util

This commit is contained in:
Johannes Rieken 2019-12-30 13:01:33 +01:00
parent b05b481265
commit 808865cf57
12 changed files with 273 additions and 327 deletions

View file

@ -5,6 +5,7 @@
*--------------------------------------------------------------------------------------------*/
const path_1 = require("path");
const minimatch = require("minimatch");
const utils_1 = require("./utils");
module.exports = new class {
constructor() {
this.meta = {
@ -19,26 +20,12 @@ module.exports = new class {
const configs = context.options;
for (const config of configs) {
if (minimatch(context.getFilename(), config.target)) {
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);
}
}
};
return utils_1.createImportRuleListener((node, value) => this._checkImport(context, config, node, value));
}
}
return {};
}
_checkImport(context, config, node, path) {
if (typeof path !== 'string') {
return;
}
// resolve relative paths
if (path[0] === '.') {
path = path_1.join(context.getFilename(), path);

View file

@ -7,6 +7,7 @@ import * as eslint from 'eslint';
import * as estree from 'estree';
import { join } from 'path';
import * as minimatch from 'minimatch';
import { createImportRuleListener } from './utils';
interface ImportPatternsConfig {
target: string;
@ -29,28 +30,14 @@ export = new class implements eslint.Rule.RuleModule {
for (const config of configs) {
if (minimatch(context.getFilename(), config.target)) {
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);
}
}
};
return createImportRuleListener((node, value) => this._checkImport(context, config, node, value));
}
}
return {};
}
private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: estree.Node, path: any) {
if (typeof path !== 'string') {
return;
}
private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: estree.Node, path: string) {
// resolve relative paths
if (path[0] === '.') {

View file

@ -4,6 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path_1 = require("path");
const utils_1 = require("./utils");
module.exports = new class {
constructor() {
this.meta = {
@ -37,45 +38,30 @@ module.exports = new class {
// 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);
return utils_1.createImportRuleListener((node, path) => {
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;
}
}
};
}
_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;
}
}
});
}
};

View file

@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
import * as estree from 'estree';
import { join, dirname } from 'path';
import { createImportRuleListener } from './utils';
type Config = {
allowed: Set<string>;
@ -49,51 +49,34 @@ export = new class implements eslint.Rule.RuleModule {
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);
return createImportRuleListener((node, path) => {
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;
}
}
};
}
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;
}
}
});
}
};

View file

@ -4,6 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path_1 = require("path");
const utils_1 = require("./utils");
module.exports = new class NoNlsInStandaloneEditorRule {
constructor() {
this.meta = {
@ -16,39 +17,24 @@ module.exports = new class NoNlsInStandaloneEditorRule {
}
create(context) {
const fileName = context.getFilename();
if (!(/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName)
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName))) {
return {};
}
return {
ImportDeclaration: (node) => {
this._checkImport(context, 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, node, args[0].value);
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)) {
return utils_1.createImportRuleListener((node, path) => {
// resolve relative paths
if (path[0] === '.') {
path = path_1.join(context.getFilename(), path);
}
if (/vs(\/|\\)nls/.test(path)) {
context.report({
node,
messageId: 'noNls'
});
}
}
};
}
_checkImport(context, node, path) {
if (typeof path !== 'string') {
return;
}
// resolve relative paths
if (path[0] === '.') {
path = path_1.join(context.getFilename(), path);
}
if (/vs(\/|\\)nls/.test(path)) {
context.report({
node,
messageId: 'noNls'
});
}
return {};
}
};

View file

@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
import * as estree from 'estree';
import { join } from 'path';
import { createImportRuleListener } from './utils';
export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule {
@ -20,48 +20,31 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
const fileName = context.getFilename();
if (!(
if (
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(fileName)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(fileName)
)) {
return {};
}
return {
ImportDeclaration: (node: estree.Node) => {
this._checkImport(context, 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, node, (<estree.Literal>args[0]).value);
}
}
};
}
private _checkImport(context: eslint.Rule.RuleContext, node: estree.Node, path: any) {
if (typeof path !== 'string') {
return;
}
// resolve relative paths
if (path[0] === '.') {
path = join(context.getFilename(), path);
}
if (
/vs(\/|\\)nls/.test(path)
) {
context.report({
node,
messageId: 'noNls'
return createImportRuleListener((node, path) => {
// resolve relative paths
if (path[0] === '.') {
path = join(context.getFilename(), path);
}
if (
/vs(\/|\\)nls/.test(path)
) {
context.report({
node,
messageId: 'noNls'
});
}
});
}
return {};
}
};

View file

@ -4,6 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
const path_1 = require("path");
const utils_1 = require("./utils");
module.exports = new class NoNlsInStandaloneEditorRule {
constructor() {
this.meta = {
@ -19,36 +20,21 @@ module.exports = new class NoNlsInStandaloneEditorRule {
// the vs/editor folder is allowed to use the standalone editor
return {};
}
return {
ImportDeclaration: (node) => {
this._checkImport(context, 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, node, args[0].value);
}
return utils_1.createImportRuleListener((node, path) => {
// resolve relative paths
if (path[0] === '.') {
path = path_1.join(context.getFilename(), path);
}
};
}
_checkImport(context, node, path) {
if (typeof path !== 'string') {
return;
}
// resolve relative paths
if (path[0] === '.') {
path = path_1.join(context.getFilename(), path);
}
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) {
context.report({
node,
messageId: 'badImport'
});
}
if (/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)) {
context.report({
node,
messageId: 'badImport'
});
}
});
}
};

View file

@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import * as eslint from 'eslint';
import * as estree from 'estree';
import { join } from 'path';
import { createImportRuleListener } from './utils';
export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule {
@ -24,41 +24,26 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule
return {};
}
return {
ImportDeclaration: (node: estree.Node) => {
this._checkImport(context, 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, node, (<estree.Literal>args[0]).value);
}
return createImportRuleListener((node, path) => {
// resolve relative paths
if (path[0] === '.') {
path = join(context.getFilename(), path);
}
};
}
private _checkImport(context: eslint.Rule.RuleContext, node: estree.Node, path: any) {
if (typeof path !== 'string') {
return;
}
// resolve relative paths
if (path[0] === '.') {
path = join(context.getFilename(), path);
}
if (
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)
) {
context.report({
node,
messageId: 'badImport'
});
}
if (
/vs(\/|\\)editor(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)common(\/|\\)standalone(\/|\\)/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.api/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.main/.test(path)
|| /vs(\/|\\)editor(\/|\\)editor.worker/.test(path)
) {
context.report({
node,
messageId: 'badImport'
});
}
});
}
};

View file

@ -5,6 +5,7 @@
*--------------------------------------------------------------------------------------------*/
var _a;
const fs_1 = require("fs");
const utils_1 = require("./utils");
module.exports = new (_a = class TranslationRemind {
constructor() {
this.meta = {
@ -16,51 +17,41 @@ module.exports = new (_a = class TranslationRemind {
};
}
create(context) {
return {
ImportDeclaration: (node) => {
this._checkImport(context, 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, node, args[0].value);
}
}
};
return utils_1.createImportRuleListener((node, path) => this._checkImport(context, node, path));
}
_checkImport(context, node, path) {
if (path === TranslationRemind.NLS_MODULE) {
const currentFile = context.getFilename();
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
if (!matchService && !matchPart) {
if (path !== TranslationRemind.NLS_MODULE) {
return;
}
const currentFile = context.getFilename();
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
if (!matchService && !matchPart) {
return;
}
const resource = matchService ? matchService[0] : matchPart[0];
let resourceDefined = false;
let json;
try {
json = fs_1.readFileSync('./build/lib/i18n.resources.json', 'utf8');
}
catch (e) {
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
return;
}
const workbenchResources = JSON.parse(json).workbench;
workbenchResources.forEach((existingResource) => {
if (existingResource.name === resource) {
resourceDefined = true;
return;
}
const resource = matchService ? matchService[0] : matchPart[0];
let resourceDefined = false;
let json;
try {
json = fs_1.readFileSync('./build/lib/i18n.resources.json', 'utf8');
}
catch (e) {
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
return;
}
const workbenchResources = JSON.parse(json).workbench;
workbenchResources.forEach((existingResource) => {
if (existingResource.name === resource) {
resourceDefined = true;
return;
}
});
if (!resourceDefined) {
context.report({
node,
messageId: 'missing',
data: { resource }
});
if (!resourceDefined) {
context.report({
node,
messageId: 'missing',
data: { resource }
});
}
}
}
},

View file

@ -6,6 +6,7 @@
import * as eslint from 'eslint';
import * as estree from 'estree';
import { readFileSync } from 'fs';
import { createImportRuleListener } from './utils';
export = new class TranslationRemind implements eslint.Rule.RuleModule {
@ -21,59 +22,48 @@ export = new class TranslationRemind implements eslint.Rule.RuleModule {
};
create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {
return {
ImportDeclaration: (node: estree.Node) => {
this._checkImport(context, 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, node, (<estree.Literal>args[0]).value);
}
}
};
return createImportRuleListener((node, path) => this._checkImport(context, node, path));
}
private _checkImport(context: eslint.Rule.RuleContext, node: estree.Node, path: any) {
private _checkImport(context: eslint.Rule.RuleContext, node: estree.Node, path: string) {
if (path === TranslationRemind.NLS_MODULE) {
const currentFile = context.getFilename();
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
if (!matchService && !matchPart) {
return;
}
const resource = matchService ? matchService[0] : matchPart![0];
let resourceDefined = false;
let json;
try {
json = readFileSync('./build/lib/i18n.resources.json', 'utf8');
} catch (e) {
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
return;
}
const workbenchResources = JSON.parse(json).workbench;
workbenchResources.forEach((existingResource: any) => {
if (existingResource.name === resource) {
resourceDefined = true;
return;
}
});
if (!resourceDefined) {
context.report({
node,
messageId: 'missing',
data: { resource }
});
}
if (path !== TranslationRemind.NLS_MODULE) {
return;
}
const currentFile = context.getFilename();
const matchService = currentFile.match(/vs\/workbench\/services\/\w+/);
const matchPart = currentFile.match(/vs\/workbench\/contrib\/\w+/);
if (!matchService && !matchPart) {
return;
}
const resource = matchService ? matchService[0] : matchPart![0];
let resourceDefined = false;
let json;
try {
json = readFileSync('./build/lib/i18n.resources.json', 'utf8');
} catch (e) {
console.error('[translation-remind rule]: File with resources to pull from Transifex was not found. Aborting translation resource check for newly defined workbench part/service.');
return;
}
const workbenchResources = JSON.parse(json).workbench;
workbenchResources.forEach((existingResource: any) => {
if (existingResource.name === resource) {
resourceDefined = true;
return;
}
});
if (!resourceDefined) {
context.report({
node,
messageId: 'missing',
data: { resource }
});
}
}
};

36
build/lib/eslint/utils.js Normal file
View file

@ -0,0 +1,36 @@
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
function createImportRuleListener(validateImport) {
function _checkImport(node) {
if (node && node.type === 'Literal' && typeof node.value === 'string') {
validateImport(node, node.value);
}
}
return {
// import ??? from 'module'
ImportDeclaration: (node) => {
_checkImport(node.source);
},
// import('module').then(...)
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') {
_checkImport(args[0]);
}
},
// export ?? from 'module'
ExportAllDeclaration: (node) => {
_checkImport(node.source);
},
// export {foo} from 'module'
ExportNamedDeclaration: (node) => {
_checkImport(node.source);
}
};
}
exports.createImportRuleListener = createImportRuleListener;

46
build/lib/eslint/utils.ts Normal file
View file

@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* 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';
//https://github.com/estree/estree/blob/master/es2015.md#exportnameddeclaration
declare interface ExportNamedDeclaration {
type: "ExportNamedDeclaration";
declaration: estree.Declaration | null;
specifiers: [estree.ExportSpecifier];
source: estree.Literal | null;
}
export function createImportRuleListener(validateImport: (node: estree.SimpleLiteral, value: string) => any): eslint.Rule.RuleListener {
function _checkImport(node: estree.Literal | null) {
if (node && node.type === 'Literal' && typeof node.value === 'string') {
validateImport(<estree.SimpleLiteral>node, node.value);
}
}
return {
// import ??? from 'module'
ImportDeclaration: (node: estree.Node) => {
_checkImport((<estree.ImportDeclaration>node).source);
},
// import('module').then(...)
CallExpression: (node: estree.Node) => {
const { callee, arguments: args } = <estree.CallExpression>node;
if ((<any>callee.type) === 'Import' && args[0]?.type === 'Literal') {
_checkImport(<estree.SimpleLiteral>args[0]);
}
},
// export ?? from 'module'
ExportAllDeclaration: (node: estree.Node) => {
_checkImport((<estree.ExportAllDeclaration>node).source);
},
// export {foo} from 'module'
ExportNamedDeclaration: (node: estree.Node) => {
_checkImport((<ExportNamedDeclaration>node).source);
}
};
}