Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@magicspace/tslint-rules

Package Overview
Dependencies
Maintainers
2
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@magicspace/tslint-rules - npm Package Compare versions

Comparing version 0.1.9 to 0.1.10

32

bld/rules/explicitReturnTypeRule.js

@@ -65,13 +65,7 @@ "use strict";

getReturnType(node) {
let type = this.typeChecker.typeToString(this.typeChecker.getTypeAtLocation(node).getCallSignatures()[0].getReturnType());
if (!this.options.complexTypeFixer && !_lang_1.BASE_TYPES.some(v => v === type)) {
console.log(type);
console.log(type);
console.log(type);
console.log(type);
console.log(type);
console.log(type);
return undefined;
}
try {
let type = this.typeChecker.typeToString(this.typeChecker.getTypeAtLocation(node).getCallSignatures()[0].getReturnType());
if (!this.options.complexTypeFixer && !_lang_1.BASE_TYPES.some(v => v === type)) {
return undefined;
}
return type;

@@ -140,20 +134,2 @@ }

}
function Z() {
let E;
(function (E) {
E[E["a"] = 0] = "a";
E[E["b"] = 1] = "b";
})(E || (E = {}));
return E;
}
let foo = () => {
return 1;
};
let a = () => { };
// let b = () => {
// return {
// name: "string"
// }
// }
let b = () => { };
//# sourceMappingURL=explicitReturnTypeRule.js.map

605

bld/rules/importGroupsRule.js
"use strict";
// import * as Path from 'path';
// import resolve = require('resolve');
// import {
// AbstractWalker,
// IOptions,
// IRuleMetadata,
// Replacement,
// RuleFailure,
// Rules,
// } from 'tslint';
// import {
// ImportKind,
// isImportDeclaration,
// isImportEqualsDeclaration,
// isTextualLiteral,
// } from 'tsutils';
// import * as TypeScript from 'typescript';
// import {Dict} from '../@lang';
// import {removeQuotes} from '../utils/path';
// const ERROR_MESSAGE_UNEXPECTED_EMPTY_LINE =
// 'Unexpected empty line within the same import group.';
// const ERROR_MESSAGE_EXPECTING_EMPTY_LINE =
// 'Expecting an empty line between different import groups.';
// const ERROR_MESSAGE_WRONG_MODULE_GROUP_ORDER =
// 'Import groups must be sorted according to given order.';
// const ERROR_MESSAGE_NOT_GROUPED = 'Imports must be grouped.';
// const resolveWithCache = ((): ((
// id: string,
// basedir: string,
// ) => string | undefined) => {
// let cache = new Map<string, string | undefined>();
// return (id: string, basedir: string): string | undefined => {
// let key = `${id}\n${basedir}`;
// if (cache.has(key)) {
// return cache.get(key);
// }
// let value: string | undefined;
// try {
// value = resolve.sync(id, {basedir});
// } catch (error) {}
// cache.set(key, value);
// return value;
// };
// })();
// const BUILT_IN_MODULE_GROUP_TESTER_DICT: Dict<ModuleGroupTester> = {
// '$node-core'(path) {
// try {
// return require.resolve(path) === path;
// } catch (error) {
// return false;
// }
// },
// '$node-modules'(modulePath, sourceFilePath) {
// let basedir = Path.dirname(sourceFilePath);
// let resolvedPath = resolveWithCache(modulePath, basedir);
// if (!resolvedPath) {
// return false;
// }
// let relativePath = Path.relative(basedir, resolvedPath);
// return /[\\/]node_modules[\\/]/.test(relativePath);
// },
// };
// interface ModuleGroupConfigItem {
// name: string;
// test: string;
// }
// interface RawOptions {
// groups: ModuleGroupConfigItem[];
// ordered?: boolean;
// }
// interface ParsedOptions {
// groups: ModuleGroup[];
// ordered: boolean;
// }
// type ModuleGroupTester = (
// modulePath: string,
// sourceFilePath: string,
// ) => boolean;
// class ModuleGroup {
// readonly name: string;
// private tester: ModuleGroupTester;
// constructor({name, test: testConfig}: ModuleGroupConfigItem) {
// this.name = name;
// this.tester = this.buildTester(testConfig);
// }
// test(modulePath: string, sourceFilePath: string): boolean {
// return this.tester(modulePath, sourceFilePath);
// }
// private buildTester(config: string): ModuleGroupTester {
// if (config.startsWith('$')) {
// return (
// BUILT_IN_MODULE_GROUP_TESTER_DICT[config] || ((): boolean => false)
// );
// } else {
// let regex = new RegExp(config);
// return (path): boolean => regex.test(path);
// }
// }
// }
// export class Rule extends Rules.AbstractRule {
// private parsedOptions: ParsedOptions;
// constructor(options: IOptions) {
// super(options);
// let {groups: groupConfigItems, ordered} = options
// .ruleArguments[0] as RawOptions;
// this.parsedOptions = {
// groups: groupConfigItems.map(item => new ModuleGroup(item)),
// ordered: !!ordered,
// };
// }
// apply(sourceFile: TypeScript.SourceFile): RuleFailure[] {
// return this.applyWithWalker(
// new ImportGroupWalker(
// sourceFile,
// Rule.metadata.ruleName,
// this.parsedOptions,
// ),
// );
// }
// static metadata: IRuleMetadata = {
// ruleName: 'import-groups',
// description: 'Validate that module imports are grouped as expected.',
// optionsDescription: '',
// options: {
// properties: {
// groups: {
// items: {
// properties: {
// name: {
// type: 'string',
// },
// test: {
// type: 'string',
// },
// },
// type: 'object',
// },
// type: 'array',
// },
// ordered: {
// type: 'boolean',
// },
// },
// type: 'object',
// },
// optionExamples: [
// [
// true,
// {
// groups: [
// {name: 'node-core', test: '$node-core'},
// {name: 'node-modules', test: '$node-modules'},
// ],
// ordered: true,
// },
// ],
// ],
// type: 'maintainability',
// hasFix: true,
// typescriptOnly: false,
// };
// }
// interface ModuleImportInfo {
// node: TypeScript.Node;
// groupIndex: number;
// /** 节点开始行. */
// startLine: number;
// /** 节点结束行. */
// endLine: number;
// }
// class ImportGroupWalker extends AbstractWalker<ParsedOptions> {
// private moduleImportInfos: ModuleImportInfo[] = [];
// private pendingStatements: TypeScript.Statement[] = [];
// walk(sourceFile: TypeScript.SourceFile): void {
// let pendingCache: TypeScript.Statement[] = [];
// let checkWithAppendModuleImport = (
// expression: TypeScript.Expression,
// ): void => {
// this.pendingStatements.push(...pendingCache);
// pendingCache = [];
// if (isTextualLiteral(expression)) {
// this.appendModuleImport(expression, sourceFile);
// }
// };
// for (let statement of sourceFile.statements) {
// if (isImportDeclaration(statement)) {
// if (ImportKind.ImportDeclaration) {
// checkWithAppendModuleImport(statement.moduleSpecifier);
// }
// } else if (isImportEqualsDeclaration(statement)) {
// if (
// ImportKind.ImportEquals &&
// statement.moduleReference.kind ===
// TypeScript.SyntaxKind.ExternalModuleReference &&
// statement.moduleReference.expression !== undefined
// ) {
// checkWithAppendModuleImport(statement.moduleReference.expression);
// }
// } else {
// pendingCache.push(statement);
// }
// }
// this.validate();
// }
// private appendModuleImport(
// expression: TypeScript.LiteralExpression,
// sourceFile: TypeScript.SourceFile,
// ): void {
// let node: TypeScript.Node = expression;
// while (node.parent.kind !== TypeScript.SyntaxKind.SourceFile) {
// node = node.parent;
// }
// let modulePath = removeQuotes(expression.getText());
// let sourceFilePath = sourceFile.fileName;
// let groups = this.options.groups;
// let index = groups.findIndex(group =>
// group.test(modulePath, sourceFilePath),
// );
// this.moduleImportInfos.push({
// node,
// // 如果没有找到匹配的分组, 则归到 "其他" 一组, groupIndex 为 groups.length.
// groupIndex: index < 0 ? groups.length : index,
// startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line,
// endLine: sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line,
// });
// }
// private validate(): void {
// let infos = this.moduleImportInfos;
// let pendingStatements = this.pendingStatements;
// if (!infos.length) {
// return;
// }
// let {ordered} = this.options;
// interface FailureItem {
// node: TypeScript.Node;
// message: string;
// }
// let failureItems: FailureItem[] = [];
// let [lastInfo, ...restInfos] = infos;
// let fixerEnabled = !pendingStatements.length;
// let appearedGroupIndexSet = new Set([lastInfo.groupIndex]);
// for (let expression of pendingStatements) {
// failureItems.push({
// node: expression,
// message: 'Unexpected code between import statements.',
// });
// }
// for (let info of restInfos) {
// let checkOrdering = ordered;
// if (info.groupIndex === lastInfo.groupIndex) {
// // 只在分组第一项检查分组顺序.
// checkOrdering = false;
// // 如果当前分组和上一份组 groupIndex 相同, 则校验是否多了空行.
// if (info.startLine - lastInfo.endLine > 1) {
// failureItems.push({
// node: info.node,
// message: ERROR_MESSAGE_UNEXPECTED_EMPTY_LINE,
// });
// }
// } else {
// // 检验该组是否已经出现过.
// if (appearedGroupIndexSet.has(info.groupIndex)) {
// checkOrdering = false;
// failureItems.push({
// node: info.node,
// message: ERROR_MESSAGE_NOT_GROUPED,
// });
// }
// // 如果未出现过则校验是否少了空行.
// else if (info.startLine - lastInfo.endLine < 2) {
// failureItems.push({
// node: info.node,
// message: ERROR_MESSAGE_EXPECTING_EMPTY_LINE,
// });
// }
// }
// if (checkOrdering) {
// // 在要求分组顺序的情况下, 如果当前分组的 groupIndex 小于上一个分组的,
// // 则说明顺序错误.
// if (info.groupIndex < lastInfo.groupIndex) {
// failureItems.push({
// node: info.node,
// message: ERROR_MESSAGE_WRONG_MODULE_GROUP_ORDER,
// });
// }
// }
// appearedGroupIndexSet.add(info.groupIndex);
// lastInfo = info;
// }
// if (failureItems.length) {
// let fixer = fixerEnabled ? this.buildFixer(infos) : undefined;
// for (let {node, message} of failureItems) {
// this.addFailureAtNode(node, message, fixer);
// }
// }
// }
// private buildFixer(infos: ModuleImportInfo[]): Replacement | undefined {
// let {ordered} = this.options;
// let startNode = infos[0].node;
// let endNode = infos[infos.length - 1].node;
// let infoGroups = groupModuleImportInfos(infos, ordered);
// let text = infoGroups
// .map(group => group.map(info => info.node.getText()).join('\n'))
// .join('\n\n');
// return new Replacement(startNode.getStart(), endNode.getEnd(), text);
// }
// }
// function groupModuleImportInfos(
// infos: ModuleImportInfo[],
// ordered: boolean,
// ): ModuleImportInfo[][] {
// // 这里利用了 Map 和 Set 枚举顺序和键加入顺序一致的特性. 如果不需要按顺序分
// // 组, 则遵照分组出现顺序.
// let infoGroupMap = new Map<number, ModuleImportInfo[]>();
// for (let info of infos) {
// let infoGroup = infoGroupMap.get(info.groupIndex);
// if (infoGroup) {
// infoGroup.push(info);
// } else {
// infoGroup = [info];
// infoGroupMap.set(info.groupIndex, infoGroup);
// }
// }
// if (ordered) {
// return Array.from(infoGroupMap.entries())
// .sort(([indexX], [indexY]) => indexX - indexY)
// .map(([, infoGroup]) => infoGroup);
// } else {
// return Array.from(infoGroupMap.values());
// }
// }
Object.defineProperty(exports, "__esModule", { value: true });
const Path = require("path");
const resolve = require("resolve");
const tslint_1 = require("tslint");
const tsutils_1 = require("tsutils");
const TypeScript = require("typescript");
const path_1 = require("../utils/path");
const ERROR_MESSAGE_UNEXPECTED_EMPTY_LINE = 'Unexpected empty line within the same import group.';
const ERROR_MESSAGE_EXPECTING_EMPTY_LINE = 'Expecting an empty line between different import groups.';
const ERROR_MESSAGE_WRONG_MODULE_GROUP_ORDER = 'Import groups must be sorted according to given order.';
const ERROR_MESSAGE_NOT_GROUPED = 'Imports must be grouped.';
const resolveWithCache = (() => {
let cache = new Map();
return (id, basedir) => {
let key = `${id}\n${basedir}`;
if (cache.has(key)) {
return cache.get(key);
}
let value;
try {
value = resolve.sync(id, { basedir });
}
catch (error) { }
cache.set(key, value);
return value;
};
})();
const BUILT_IN_MODULE_GROUP_TESTER_DICT = {
'$node-core'(path) {
try {
return require.resolve(path) === path;
}
catch (error) {
return false;
}
},
'$node-modules'(modulePath, sourceFilePath) {
let basedir = Path.dirname(sourceFilePath);
let resolvedPath = resolveWithCache(modulePath, basedir);
if (!resolvedPath) {
return false;
}
let relativePath = Path.relative(basedir, resolvedPath);
return /[\\/]node_modules[\\/]/.test(relativePath);
},
};
class ModuleGroup {
constructor({ name, test: testConfig }) {
this.name = name;
this.tester = this.buildTester(testConfig);
}
test(modulePath, sourceFilePath) {
return this.tester(modulePath, sourceFilePath);
}
buildTester(config) {
if (config.startsWith('$')) {
return (BUILT_IN_MODULE_GROUP_TESTER_DICT[config] || (() => false));
}
else {
let regex = new RegExp(config);
return (path) => regex.test(path);
}
}
}
class Rule extends tslint_1.Rules.AbstractRule {
constructor(options) {
super(options);
let { groups: groupConfigItems, ordered } = options
.ruleArguments[0];
this.parsedOptions = {
groups: groupConfigItems.map(item => new ModuleGroup(item)),
ordered: !!ordered,
};
}
apply(sourceFile) {
return this.applyWithWalker(new ImportGroupWalker(sourceFile, Rule.metadata.ruleName, this.parsedOptions));
}
}
Rule.metadata = {
ruleName: 'import-groups',
description: 'Validate that module imports are grouped as expected.',
optionsDescription: '',
options: {
properties: {
groups: {
items: {
properties: {
name: {
type: 'string',
},
test: {
type: 'string',
},
},
type: 'object',
},
type: 'array',
},
ordered: {
type: 'boolean',
},
},
type: 'object',
},
optionExamples: [
[
true,
{
groups: [
{ name: 'node-core', test: '$node-core' },
{ name: 'node-modules', test: '$node-modules' },
],
ordered: true,
},
],
],
type: 'maintainability',
hasFix: true,
typescriptOnly: false,
};
exports.Rule = Rule;
class ImportGroupWalker extends tslint_1.AbstractWalker {
constructor() {
super(...arguments);
this.moduleImportInfos = [];
this.pendingStatements = [];
}
walk(sourceFile) {
let pendingCache = [];
let checkWithAppendModuleImport = (expression) => {
this.pendingStatements.push(...pendingCache);
pendingCache = [];
if (tsutils_1.isTextualLiteral(expression)) {
this.appendModuleImport(expression, sourceFile);
}
};
for (let statement of sourceFile.statements) {
if (tsutils_1.isImportDeclaration(statement)) {
if (1 /* ImportDeclaration */) {
checkWithAppendModuleImport(statement.moduleSpecifier);
}
}
else if (tsutils_1.isImportEqualsDeclaration(statement)) {
if (2 /* ImportEquals */ &&
statement.moduleReference.kind ===
TypeScript.SyntaxKind.ExternalModuleReference &&
statement.moduleReference.expression !== undefined) {
checkWithAppendModuleImport(statement.moduleReference.expression);
}
}
else {
pendingCache.push(statement);
}
}
this.validate();
}
appendModuleImport(expression, sourceFile) {
let node = expression;
while (node.parent.kind !== TypeScript.SyntaxKind.SourceFile) {
node = node.parent;
}
let modulePath = path_1.removeQuotes(expression.getText());
let sourceFilePath = sourceFile.fileName;
let groups = this.options.groups;
let index = groups.findIndex(group => group.test(modulePath, sourceFilePath));
this.moduleImportInfos.push({
node,
// 如果没有找到匹配的分组, 则归到 "其他" 一组, groupIndex 为 groups.length.
groupIndex: index < 0 ? groups.length : index,
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line,
endLine: sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line,
});
}
validate() {
let infos = this.moduleImportInfos;
let pendingStatements = this.pendingStatements;
if (!infos.length) {
return;
}
let { ordered } = this.options;
let failureItems = [];
let [lastInfo, ...restInfos] = infos;
let fixerEnabled = !pendingStatements.length;
let appearedGroupIndexSet = new Set([lastInfo.groupIndex]);
for (let expression of pendingStatements) {
failureItems.push({
node: expression,
message: 'Unexpected code between import statements.',
});
}
for (let info of restInfos) {
let checkOrdering = ordered;
if (info.groupIndex === lastInfo.groupIndex) {
// 只在分组第一项检查分组顺序.
checkOrdering = false;
// 如果当前分组和上一份组 groupIndex 相同, 则校验是否多了空行.
if (info.startLine - lastInfo.endLine > 1) {
failureItems.push({
node: info.node,
message: ERROR_MESSAGE_UNEXPECTED_EMPTY_LINE,
});
}
}
else {
// 检验该组是否已经出现过.
if (appearedGroupIndexSet.has(info.groupIndex)) {
checkOrdering = false;
failureItems.push({
node: info.node,
message: ERROR_MESSAGE_NOT_GROUPED,
});
}
// 如果未出现过则校验是否少了空行.
else if (info.startLine - lastInfo.endLine < 2) {
failureItems.push({
node: info.node,
message: ERROR_MESSAGE_EXPECTING_EMPTY_LINE,
});
}
}
if (checkOrdering) {
// 在要求分组顺序的情况下, 如果当前分组的 groupIndex 小于上一个分组的,
// 则说明顺序错误.
if (info.groupIndex < lastInfo.groupIndex) {
failureItems.push({
node: info.node,
message: ERROR_MESSAGE_WRONG_MODULE_GROUP_ORDER,
});
}
}
appearedGroupIndexSet.add(info.groupIndex);
lastInfo = info;
}
if (failureItems.length) {
let fixer = fixerEnabled ? this.buildFixer(infos) : undefined;
for (let { node, message } of failureItems) {
this.addFailureAtNode(node, message, fixer);
}
}
}
buildFixer(infos) {
let { ordered } = this.options;
let startNode = infos[0].node;
let endNode = infos[infos.length - 1].node;
let infoGroups = groupModuleImportInfos(infos, ordered);
let text = infoGroups
.map(group => group.map(info => info.node.getText()).join('\n'))
.join('\n\n');
return new tslint_1.Replacement(startNode.getStart(), endNode.getEnd(), text);
}
}
function groupModuleImportInfos(infos, ordered) {
// 这里利用了 Map 和 Set 枚举顺序和键加入顺序一致的特性. 如果不需要按顺序分
// 组, 则遵照分组出现顺序.
let infoGroupMap = new Map();
for (let info of infos) {
let infoGroup = infoGroupMap.get(info.groupIndex);
if (infoGroup) {
infoGroup.push(info);
}
else {
infoGroup = [info];
infoGroupMap.set(info.groupIndex, infoGroup);
}
}
if (ordered) {
return Array.from(infoGroupMap.entries())
.sort(([indexX], [indexY]) => indexX - indexY)
.map(([, infoGroup]) => infoGroup);
}
else {
return Array.from(infoGroupMap.values());
}
}
//# sourceMappingURL=importGroupsRule.js.map
"use strict";
// import * as FS from 'fs';
// import * as Path from 'path';
// import * as _ from 'lodash';
// import {
// AbstractWalker,
// IOptions,
// IRuleMetadata,
// Replacement,
// RuleFailure,
// Rules,
// } from 'tslint';
// import {ImportKind, findImports} from 'tsutils';
// import * as TypeScript from 'typescript';
// import {FailureManager} from '../utils/failure-manager';
// import {removeModuleFileExtension, removeQuotes} from '../utils/path';
// const PARENT_DIRNAME = /^(?:\.{2})/;
// const RELATIVE_PATH = /^(?:\.{1,2}[\\/])+/;
// const ERROR_MESSAGE_IMPORT_OUT_OF_BASEURL =
// 'This import path must use baseUrl.';
// const ERROR_MESSAGE_IMPORT_IN_BASEURL =
// 'This import path must be a relative path.';
// interface RuleOptions {
// baseUrl: string;
// baseUrlDirSearchName: string | undefined;
// }
// export class Rule extends Rules.AbstractRule {
// private parsedOptions: RuleOptions;
// constructor(options: IOptions) {
// super(options);
// this.parsedOptions = options.ruleArguments[0];
// if (!this.parsedOptions || !this.parsedOptions.baseUrl) {
// throw new Error('Option baseUrl is required');
// }
// }
// apply(sourceFile: TypeScript.SourceFile): RuleFailure[] {
// return this.applyWithWalker(
// new ImportPathBaseUrlWalker(
// sourceFile,
// Rule.metadata.ruleName,
// this.parsedOptions,
// ),
// );
// }
// static metadata: IRuleMetadata = {
// ruleName: 'import-path-base-url',
// description: 'Check import module from baseUrl',
// optionsDescription: '',
// options: {
// properties: {
// baseUrl: {
// type: 'string',
// },
// baseUrlDirSearchName: {
// type: 'string',
// default: 'tsconfig',
// },
// },
// },
// type: 'maintainability',
// hasFix: true,
// typescriptOnly: false,
// };
// }
// export class ImportPathBaseUrlWalker extends AbstractWalker<RuleOptions> {
// private sourceDirname: string;
// private importExpressions: TypeScript.Expression[] = [];
// private failureManager = new FailureManager(this);
// constructor(
// sourceFile: TypeScript.SourceFile,
// ruleName: string,
// options: RuleOptions,
// ) {
// super(sourceFile, ruleName, options);
// this.sourceDirname = Path.dirname(sourceFile.fileName);
// }
// walk(sourceFile: TypeScript.SourceFile): void {
// for (const expression of findImports(
// sourceFile,
// ImportKind.AllStaticImports,
// )) {
// this.importExpressions.push(expression);
// }
// this.validate();
// }
// private validate(): void {
// let importExpressions = this.importExpressions;
// for (let expression of importExpressions) {
// let text = removeQuotes(expression.getText());
// if (
// !this.isModuleInbaseUrl(this.sourceDirname) &&
// this.isModuleInbaseUrl(text) &&
// RELATIVE_PATH.test(text)
// ) {
// // sourceFile 不在 baseUrl 目录下, 且模块用的是相对路径
// this.sourceFileOutOfBaseUrl(expression, text);
// } else if (
// this.isModuleInbaseUrl(this.sourceDirname) &&
// this.isModuleInbaseUrl(text) &&
// !RELATIVE_PATH.test(text)
// ) {
// // sourceFile 在 baseUrl 目录下, 且模块没有用相对路径
// this.sourceFileInBaseUrl(expression, text);
// }
// this.failureManager.throw();
// }
// }
// private sourceFileInBaseUrl(
// expression: TypeScript.Expression,
// text: string,
// ): void {
// let importPath = Path.relative(
// this.sourceDirname,
// Path.join(this.getBaseUrl(), text),
// );
// if (!PARENT_DIRNAME.test(importPath)) {
// importPath = `./${importPath}`;
// }
// this.failureManager.append({
// message: ERROR_MESSAGE_IMPORT_IN_BASEURL,
// node: expression.parent,
// fixer: new Replacement(
// expression.getStart(),
// expression.getWidth(),
// `'${importPath}'`,
// ),
// });
// }
// private sourceFileOutOfBaseUrl(
// expression: TypeScript.Expression,
// text: string,
// ): void {
// let importPath = Path.relative(
// this.getBaseUrl(),
// Path.join(this.sourceDirname, text),
// );
// this.failureManager.append({
// message: ERROR_MESSAGE_IMPORT_OUT_OF_BASEURL,
// node: expression.parent,
// fixer: new Replacement(
// expression.getStart(),
// expression.getWidth(),
// `'${importPath}'`,
// ),
// });
// }
// private getBaseUrl(): string {
// let rootPath = findProjectRootPath(
// this.sourceDirname,
// this.options.baseUrlDirSearchName,
// );
// if (!rootPath) {
// throw new Error('can not find tslint.json');
// }
// return Path.join(rootPath, this.options.baseUrl);
// }
// private isModuleInbaseUrl(filePath: string): boolean {
// if (RELATIVE_PATH.test(filePath) || Path.isAbsolute(filePath)) {
// return this.isFileInDirectory(filePath, this.getBaseUrl());
// }
// filePath = Path.join(this.getBaseUrl(), filePath);
// let basename = removeModuleFileExtension(Path.basename(filePath));
// let dirname = Path.dirname(filePath);
// let files = FS.readdirSync(dirname).map(file =>
// removeModuleFileExtension(file),
// );
// if (_.includes(files, basename)) {
// return true;
// }
// return false;
// }
// private isFileInDirectory(filePath: string, directoryPath: string): boolean {
// let modulePath = filePath;
// if (!Path.isAbsolute(filePath)) {
// modulePath = Path.join(this.sourceDirname, filePath);
// }
// return !PARENT_DIRNAME.test(Path.relative(directoryPath, modulePath));
// }
// }
// let findProjectRootPath = ((): ((
// currentPath: string,
// baseUrlDirSearchName: string | undefined,
// ) => string | undefined) => {
// let rootPathCache: string | undefined;
// return function inner(
// currentPath: string,
// baseUrlDirSearchName: string | undefined = 'tsconfig.json',
// ): string | undefined {
// if (rootPathCache) {
// return rootPathCache;
// }
// try {
// let files = FS.readdirSync(currentPath);
// if (_.includes(files, baseUrlDirSearchName)) {
// rootPathCache = currentPath;
// return currentPath;
// } else {
// return inner(Path.join(currentPath, '..'), baseUrlDirSearchName);
// }
// } catch (e) {
// throw new Error(
// `can not find such 'baseUrlDirSearchName' that ${baseUrlDirSearchName}`,
// );
// }
// };
// })();
Object.defineProperty(exports, "__esModule", { value: true });
const FS = require("fs");
const Path = require("path");
const _ = require("lodash");
const tslint_1 = require("tslint");
const tsutils_1 = require("tsutils");
const failure_manager_1 = require("../utils/failure-manager");
const path_1 = require("../utils/path");
const PARENT_DIRNAME = /^(?:\.{2})/;
const RELATIVE_PATH = /^(?:\.{1,2}[\\/])+/;
const ERROR_MESSAGE_IMPORT_OUT_OF_BASEURL = 'This import path must use baseUrl.';
const ERROR_MESSAGE_IMPORT_IN_BASEURL = 'This import path must be a relative path.';
class Rule extends tslint_1.Rules.AbstractRule {
constructor(options) {
super(options);
this.parsedOptions = options.ruleArguments[0];
if (!this.parsedOptions || !this.parsedOptions.baseUrl) {
throw new Error('Option baseUrl is required');
}
}
apply(sourceFile) {
return this.applyWithWalker(new ImportPathBaseUrlWalker(sourceFile, Rule.metadata.ruleName, this.parsedOptions));
}
}
Rule.metadata = {
ruleName: 'import-path-base-url',
description: 'Check import module from baseUrl',
optionsDescription: '',
options: {
properties: {
baseUrl: {
type: 'string',
},
baseUrlDirSearchName: {
type: 'string',
default: 'tsconfig',
},
},
},
type: 'maintainability',
hasFix: true,
typescriptOnly: false,
};
exports.Rule = Rule;
class ImportPathBaseUrlWalker extends tslint_1.AbstractWalker {
constructor(sourceFile, ruleName, options) {
super(sourceFile, ruleName, options);
this.importExpressions = [];
this.failureManager = new failure_manager_1.FailureManager(this);
this.sourceDirname = Path.dirname(sourceFile.fileName);
}
walk(sourceFile) {
for (const expression of tsutils_1.findImports(sourceFile, 3 /* AllStaticImports */)) {
this.importExpressions.push(expression);
}
this.validate();
}
validate() {
let importExpressions = this.importExpressions;
for (let expression of importExpressions) {
let text = path_1.removeQuotes(expression.getText());
if (!this.isModuleInbaseUrl(this.sourceDirname) &&
this.isModuleInbaseUrl(text) &&
RELATIVE_PATH.test(text)) {
// sourceFile 不在 baseUrl 目录下, 且模块用的是相对路径
this.sourceFileOutOfBaseUrl(expression, text);
}
else if (this.isModuleInbaseUrl(this.sourceDirname) &&
this.isModuleInbaseUrl(text) &&
!RELATIVE_PATH.test(text)) {
// sourceFile 在 baseUrl 目录下, 且模块没有用相对路径
this.sourceFileInBaseUrl(expression, text);
}
this.failureManager.throw();
}
}
sourceFileInBaseUrl(expression, text) {
let importPath = Path.relative(this.sourceDirname, Path.join(this.getBaseUrl(), text));
if (!PARENT_DIRNAME.test(importPath)) {
importPath = `./${importPath}`;
}
this.failureManager.append({
message: ERROR_MESSAGE_IMPORT_IN_BASEURL,
node: expression.parent,
fixer: new tslint_1.Replacement(expression.getStart(), expression.getWidth(), `'${importPath}'`),
});
}
sourceFileOutOfBaseUrl(expression, text) {
let importPath = Path.relative(this.getBaseUrl(), Path.join(this.sourceDirname, text));
this.failureManager.append({
message: ERROR_MESSAGE_IMPORT_OUT_OF_BASEURL,
node: expression.parent,
fixer: new tslint_1.Replacement(expression.getStart(), expression.getWidth(), `'${importPath}'`),
});
}
getBaseUrl() {
let rootPath = findProjectRootPath(this.sourceDirname, this.options.baseUrlDirSearchName);
if (!rootPath) {
throw new Error('can not find tslint.json');
}
return Path.join(rootPath, this.options.baseUrl);
}
isModuleInbaseUrl(filePath) {
if (RELATIVE_PATH.test(filePath) || Path.isAbsolute(filePath)) {
return this.isFileInDirectory(filePath, this.getBaseUrl());
}
filePath = Path.join(this.getBaseUrl(), filePath);
let basename = path_1.removeModuleFileExtension(Path.basename(filePath));
let dirname = Path.dirname(filePath);
let files = FS.readdirSync(dirname).map(file => path_1.removeModuleFileExtension(file));
if (_.includes(files, basename)) {
return true;
}
return false;
}
isFileInDirectory(filePath, directoryPath) {
let modulePath = filePath;
if (!Path.isAbsolute(filePath)) {
modulePath = Path.join(this.sourceDirname, filePath);
}
return !PARENT_DIRNAME.test(Path.relative(directoryPath, modulePath));
}
}
exports.ImportPathBaseUrlWalker = ImportPathBaseUrlWalker;
let findProjectRootPath = (() => {
let rootPathCache;
return function inner(currentPath, baseUrlDirSearchName = 'tsconfig.json') {
if (rootPathCache) {
return rootPathCache;
}
try {
let files = FS.readdirSync(currentPath);
if (_.includes(files, baseUrlDirSearchName)) {
rootPathCache = currentPath;
return currentPath;
}
else {
return inner(Path.join(currentPath, '..'), baseUrlDirSearchName);
}
}
catch (e) {
throw new Error(`can not find such 'baseUrlDirSearchName' that ${baseUrlDirSearchName}`);
}
};
})();
//# sourceMappingURL=importPathBaseUrlRule.js.map
"use strict";
// import * as Path from 'path';
// import {AbstractWalker, IRuleMetadata, RuleFailure, Rules} from 'tslint';
// import {ImportKind, findImports} from 'tsutils';
// import * as Typescript from 'typescript';
// import {removeQuotes} from '../utils/path';
// const DIRECTORY_MODULE_PATH = /^\.{1,2}(?:[\\/]\.{2})*[\\/]?$/;
// const ERROR_MESSAGE_BANNED_PARENT_IMPORT =
// 'Importing from parent directory is not allowed.';
// export class Rule extends Rules.AbstractRule {
// apply(sourceFile: Typescript.SourceFile): RuleFailure[] {
// return this.applyWithWalker(
// new NoParentImportRule(sourceFile, Rule.metadata.ruleName, undefined),
// );
// }
// static metadata: IRuleMetadata = {
// ruleName: 'no-parent-import',
// description: '',
// optionsDescription: '',
// options: undefined,
// type: 'maintainability',
// hasFix: true,
// typescriptOnly: false,
// };
// }
// class NoParentImportRule extends AbstractWalker<undefined> {
// /** 装 import 语句的容器 */
// private importExpressions: Typescript.Expression[] = [];
// walk(sourceFile: Typescript.SourceFile): void {
// for (let expression of findImports(
// sourceFile,
// ImportKind.AllStaticImports,
// )) {
// this.importExpressions.push(expression);
// }
// this.validate();
// }
// private validate(): void {
// let importExpressions = this.importExpressions;
// let sourceDirName = Path.dirname(this.sourceFile.fileName);
// for (let expression of importExpressions) {
// let modulePath: string = removeQuotes(expression.getText());
// modulePath = Path.isAbsolute(modulePath)
// ? Path.relative(sourceDirName, modulePath)
// : (modulePath = Path.relative(
// sourceDirName,
// Path.join(sourceDirName, modulePath),
// ));
// if (!DIRECTORY_MODULE_PATH.test(modulePath) && modulePath !== '') {
// continue;
// }
// this.addFailureAtNode(
// expression.parent,
// ERROR_MESSAGE_BANNED_PARENT_IMPORT,
// );
// }
// }
// }
Object.defineProperty(exports, "__esModule", { value: true });
const Path = require("path");
const tslint_1 = require("tslint");
const tsutils_1 = require("tsutils");
const path_1 = require("../utils/path");
const DIRECTORY_MODULE_PATH = /^\.{1,2}(?:[\\/]\.{2})*[\\/]?$/;
const ERROR_MESSAGE_BANNED_PARENT_IMPORT = 'Importing from parent directory is not allowed.';
class Rule extends tslint_1.Rules.AbstractRule {
apply(sourceFile) {
return this.applyWithWalker(new NoParentImportRule(sourceFile, Rule.metadata.ruleName, undefined));
}
}
Rule.metadata = {
ruleName: 'no-parent-import',
description: '',
optionsDescription: '',
options: undefined,
type: 'maintainability',
hasFix: true,
typescriptOnly: false,
};
exports.Rule = Rule;
class NoParentImportRule extends tslint_1.AbstractWalker {
constructor() {
super(...arguments);
/** 装 import 语句的容器 */
this.importExpressions = [];
}
walk(sourceFile) {
for (let expression of tsutils_1.findImports(sourceFile, 3 /* AllStaticImports */)) {
this.importExpressions.push(expression);
}
this.validate();
}
validate() {
let importExpressions = this.importExpressions;
let sourceDirName = Path.dirname(this.sourceFile.fileName);
for (let expression of importExpressions) {
let modulePath = path_1.removeQuotes(expression.getText());
modulePath = Path.isAbsolute(modulePath)
? Path.relative(sourceDirName, modulePath)
: (modulePath = Path.relative(sourceDirName, Path.join(sourceDirName, modulePath)));
if (!DIRECTORY_MODULE_PATH.test(modulePath) && modulePath !== '') {
continue;
}
this.addFailureAtNode(expression.parent, ERROR_MESSAGE_BANNED_PARENT_IMPORT);
}
}
}
//# sourceMappingURL=noParentImportRule.js.map
"use strict";
// import * as FS from 'fs';
// import * as Path from 'path';
// import _ = require('lodash');
// import {
// AbstractWalker,
// IRuleMetadata,
// Replacement,
// RuleFailure,
// Rules,
// } from 'tslint';
// import {isExportDeclaration, isImportDeclaration} from 'tsutils';
// import {
// ExportDeclaration,
// ImportDeclaration,
// SourceFile,
// isStringLiteral,
// } from 'typescript';
// import {FailureManager} from '../utils/failure-manager';
// import {removeModuleFileExtension, removeQuotes} from '../utils/path';
// const ERROR_MESSAGE_BANNED_IMPORT =
// "This module can not be imported, because it contains internal module with prefix '@' under a parallel directory.";
// const ERROR_MESSAGE_BANNED_EXPORT =
// "This module can not be exported, because it contains internal module with prefix '@' under a parallel directory.";
// const ERROR_MESSAGE_MISSING_EXPORTS =
// 'Missing modules expected to be exported.';
// const INDEX_FILE_REGEX = /(?:^|[\\/])index\.(?:js|jsx|ts|tsx|d\.ts)$/i;
// const EXPORTABLE_EXTENSION_REGEX = /\.(?:js|jsx|ts|tsx|d\.ts)$/i;
// const BANNED_IMPORT_REGEX = /^(?!(?:\.{1,2}[\\/])+@(?!.*[\\/]@)).*[\\/]@/;
// const BANNED_EXPORT_REGEX = /[\\/]@/;
// export class Rule extends Rules.AbstractRule {
// apply(sourceFile: SourceFile): RuleFailure[] {
// return this.applyWithWalker(
// new ScopedModuleWalker(sourceFile, Rule.metadata.ruleName, undefined),
// );
// }
// static metadata: IRuleMetadata = {
// ruleName: 'scoped-modules',
// description: '',
// optionsDescription: '',
// options: undefined,
// type: 'maintainability',
// hasFix: true,
// typescriptOnly: false,
// };
// }
// type ModuleStatement = ImportDeclaration | ExportDeclaration;
// type ModuleStatementInfo = ImportStatementInfo | ExportStatementInfo;
// type ModuleStatementType = ModuleStatementInfo['type'];
// interface ImportStatementInfo {
// type: 'import';
// statement: ModuleStatement;
// specifier: string;
// }
// interface ExportStatementInfo {
// type: 'export';
// statement: ModuleStatement;
// specifier: string;
// }
// class ScopedModuleWalker extends AbstractWalker<undefined> {
// private infos: ModuleStatementInfo[] = [];
// private failureManager = new FailureManager(this);
// walk(sourceFile: SourceFile): void {
// for (let statement of sourceFile.statements) {
// let type: ModuleStatementType;
// if (isImportDeclaration(statement)) {
// type = 'import';
// } else if (isExportDeclaration(statement)) {
// type = 'export';
// } else {
// continue;
// }
// let specifier = getModuleSpecifier(statement);
// if (!specifier) {
// continue;
// }
// this.infos.push({
// type,
// statement,
// specifier,
// } as ModuleStatementInfo);
// }
// this.validate();
// }
// private validateImportOrExport({
// type,
// statement,
// specifier,
// }: ModuleStatementInfo): void {
// let bannedPattern: RegExp;
// let message: string;
// if (type === 'import') {
// bannedPattern = BANNED_IMPORT_REGEX;
// message = ERROR_MESSAGE_BANNED_IMPORT;
// } else {
// bannedPattern = BANNED_EXPORT_REGEX;
// message = ERROR_MESSAGE_BANNED_EXPORT;
// }
// if (bannedPattern.test(specifier)) {
// this.failureManager.append({
// message,
// node: statement,
// fixer: buildBannedImportsAndExportsFixer(statement),
// });
// }
// }
// private validateIndexFile(exportSpecifiers: string[]): void {
// let fileName = this.sourceFile.fileName;
// if (!INDEX_FILE_REGEX.test(fileName)) {
// return;
// }
// let dirName = Path.dirname(fileName);
// let fileNames = FS.readdirSync(dirName);
// let expectedExportSpecifiers = fileNames
// .map(
// (fileName): string | undefined => {
// let entryFullPath = Path.join(dirName, fileName);
// let stats = FS.statSync(entryFullPath);
// let specifier: string;
// if (stats.isFile()) {
// if (
// INDEX_FILE_REGEX.test(fileName) ||
// !EXPORTABLE_EXTENSION_REGEX.test(fileName)
// ) {
// return undefined;
// }
// specifier = `./${fileName.replace(EXPORTABLE_EXTENSION_REGEX, '')}`;
// } else if (stats.isDirectory()) {
// let entryNamesInFolder = FS.readdirSync(entryFullPath);
// let hasIndexFile = entryNamesInFolder.some(entryNameInFolder =>
// INDEX_FILE_REGEX.test(entryNameInFolder),
// );
// if (!hasIndexFile) {
// return undefined;
// }
// specifier = `./${fileName}`;
// } else {
// return undefined;
// }
// if (BANNED_EXPORT_REGEX.test(specifier)) {
// return undefined;
// }
// return specifier;
// },
// )
// .filter((entryName): entryName is string => !!entryName);
// let missingExportIds = _.difference(
// expectedExportSpecifiers,
// exportSpecifiers,
// );
// if (missingExportIds.length) {
// this.failureManager.append({
// node: undefined,
// message: ERROR_MESSAGE_MISSING_EXPORTS,
// fixer: buildAddMissingExportsFixer(this.sourceFile, missingExportIds),
// });
// }
// }
// private validate(): void {
// let infos = this.infos;
// for (let info of infos) {
// this.validateImportOrExport(info);
// }
// let exportSpecifiers = infos
// .filter(info => info.type === 'export')
// .map(info => info.specifier);
// this.validateIndexFile(exportSpecifiers);
// this.failureManager.throw();
// }
// }
// function getModuleSpecifier({
// moduleSpecifier,
// }: ModuleStatement): string | undefined {
// return moduleSpecifier && isStringLiteral(moduleSpecifier)
// ? removeQuotes(moduleSpecifier.getText())
// : undefined;
// }
// function buildBannedImportsAndExportsFixer(node: ModuleStatement): Replacement {
// return new Replacement(node.getFullStart(), node.getFullWidth(), '');
// }
// function buildAddMissingExportsFixer(
// sourceFile: SourceFile,
// exportNodesPath: string[],
// ): Replacement {
// return new Replacement(
// sourceFile.getStart(),
// sourceFile.getEnd(),
// `${[
// sourceFile.getText().trimRight(),
// ...exportNodesPath.map(
// value => `export * from '${removeModuleFileExtension(value)}';`,
// ),
// ]
// .filter(text => !!text)
// .join('\n')}\n`,
// );
// }
Object.defineProperty(exports, "__esModule", { value: true });
const FS = require("fs");
const Path = require("path");
const _ = require("lodash");
const tslint_1 = require("tslint");
const tsutils_1 = require("tsutils");
const typescript_1 = require("typescript");
const failure_manager_1 = require("../utils/failure-manager");
const path_1 = require("../utils/path");
const ERROR_MESSAGE_BANNED_IMPORT = "This module can not be imported, because it contains internal module with prefix '@' under a parallel directory.";
const ERROR_MESSAGE_BANNED_EXPORT = "This module can not be exported, because it contains internal module with prefix '@' under a parallel directory.";
const ERROR_MESSAGE_MISSING_EXPORTS = 'Missing modules expected to be exported.';
const INDEX_FILE_REGEX = /(?:^|[\\/])index\.(?:js|jsx|ts|tsx|d\.ts)$/i;
const EXPORTABLE_EXTENSION_REGEX = /\.(?:js|jsx|ts|tsx|d\.ts)$/i;
const BANNED_IMPORT_REGEX = /^(?!(?:\.{1,2}[\\/])+@(?!.*[\\/]@)).*[\\/]@/;
const BANNED_EXPORT_REGEX = /[\\/]@/;
class Rule extends tslint_1.Rules.AbstractRule {
apply(sourceFile) {
return this.applyWithWalker(new ScopedModuleWalker(sourceFile, Rule.metadata.ruleName, undefined));
}
}
Rule.metadata = {
ruleName: 'scoped-modules',
description: '',
optionsDescription: '',
options: undefined,
type: 'maintainability',
hasFix: true,
typescriptOnly: false,
};
exports.Rule = Rule;
class ScopedModuleWalker extends tslint_1.AbstractWalker {
constructor() {
super(...arguments);
this.infos = [];
this.failureManager = new failure_manager_1.FailureManager(this);
}
walk(sourceFile) {
for (let statement of sourceFile.statements) {
let type;
if (tsutils_1.isImportDeclaration(statement)) {
type = 'import';
}
else if (tsutils_1.isExportDeclaration(statement)) {
type = 'export';
}
else {
continue;
}
let specifier = getModuleSpecifier(statement);
if (!specifier) {
continue;
}
this.infos.push({
type,
statement,
specifier,
});
}
this.validate();
}
validateImportOrExport({ type, statement, specifier, }) {
let bannedPattern;
let message;
if (type === 'import') {
bannedPattern = BANNED_IMPORT_REGEX;
message = ERROR_MESSAGE_BANNED_IMPORT;
}
else {
bannedPattern = BANNED_EXPORT_REGEX;
message = ERROR_MESSAGE_BANNED_EXPORT;
}
if (bannedPattern.test(specifier)) {
this.failureManager.append({
message,
node: statement,
fixer: buildBannedImportsAndExportsFixer(statement),
});
}
}
validateIndexFile(exportSpecifiers) {
let fileName = this.sourceFile.fileName;
if (!INDEX_FILE_REGEX.test(fileName)) {
return;
}
let dirName = Path.dirname(fileName);
let fileNames = FS.readdirSync(dirName);
let expectedExportSpecifiers = fileNames
.map((fileName) => {
let entryFullPath = Path.join(dirName, fileName);
let stats = FS.statSync(entryFullPath);
let specifier;
if (stats.isFile()) {
if (INDEX_FILE_REGEX.test(fileName) ||
!EXPORTABLE_EXTENSION_REGEX.test(fileName)) {
return undefined;
}
specifier = `./${fileName.replace(EXPORTABLE_EXTENSION_REGEX, '')}`;
}
else if (stats.isDirectory()) {
let entryNamesInFolder = FS.readdirSync(entryFullPath);
let hasIndexFile = entryNamesInFolder.some(entryNameInFolder => INDEX_FILE_REGEX.test(entryNameInFolder));
if (!hasIndexFile) {
return undefined;
}
specifier = `./${fileName}`;
}
else {
return undefined;
}
if (BANNED_EXPORT_REGEX.test(specifier)) {
return undefined;
}
return specifier;
})
.filter((entryName) => !!entryName);
let missingExportIds = _.difference(expectedExportSpecifiers, exportSpecifiers);
if (missingExportIds.length) {
this.failureManager.append({
node: undefined,
message: ERROR_MESSAGE_MISSING_EXPORTS,
fixer: buildAddMissingExportsFixer(this.sourceFile, missingExportIds),
});
}
}
validate() {
let infos = this.infos;
for (let info of infos) {
this.validateImportOrExport(info);
}
let exportSpecifiers = infos
.filter(info => info.type === 'export')
.map(info => info.specifier);
this.validateIndexFile(exportSpecifiers);
this.failureManager.throw();
}
}
function getModuleSpecifier({ moduleSpecifier, }) {
return moduleSpecifier && typescript_1.isStringLiteral(moduleSpecifier)
? path_1.removeQuotes(moduleSpecifier.getText())
: undefined;
}
function buildBannedImportsAndExportsFixer(node) {
return new tslint_1.Replacement(node.getFullStart(), node.getFullWidth(), '');
}
function buildAddMissingExportsFixer(sourceFile, exportNodesPath) {
return new tslint_1.Replacement(sourceFile.getStart(), sourceFile.getEnd(), `${[
sourceFile.getText().trimRight(),
...exportNodesPath.map(value => `export * from '${path_1.removeModuleFileExtension(value)}';`),
]
.filter(text => !!text)
.join('\n')}\n`);
}
//# sourceMappingURL=scopedModulesRule.js.map
{
"name": "@magicspace/tslint-rules",
"version": "0.1.9",
"version": "0.1.10",
"description": "Custom TSLint rules for MagicSpace.",

@@ -5,0 +5,0 @@ "repository": "https://github.com/makeflow/magicspace.git",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc