mirror of
https://github.com/chenasraf/DefinitelyTyped-tools.git
synced 2026-05-18 01:49:03 +00:00
Port no import default of export equals (#657)
* Port no-import-default-of-export-equals First draft and 2/3 of the original tests. The test of modules, the one that matters, doesn't work with typescript-eslint's test infrastructure though. * Port no-import-default-of-export-equals Also update TS to 5.0 and Jest to 29. Associated minor changes to jest config (no more top-level ts-node) and some tslint rules (switch to a combined decorator/modifier list, but only on select nodes). * yarn format * Fix lint and update new jest baseline format More like real JSON! * also update dts-critic (??)
This commit is contained in:
committed by
GitHub
parent
cb0d73d177
commit
eba074f006
@@ -3,10 +3,10 @@ module.exports = {
|
||||
testEnvironment: "node",
|
||||
modulePathIgnorePatterns: ["packages\\/publisher\\/output"],
|
||||
testMatch: ["<rootDir>/packages/*/test/**/*.test.ts", "<rootDir>/packages/dts-critic/index.test.ts"],
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
tsConfig: "<rootDir>/tsconfig.test.json",
|
||||
transform: {
|
||||
"^.+\\.tsx?$": ["ts-jest", {
|
||||
tsconfig: "<rootDir>/tsconfig.test.json",
|
||||
diagnostics: false
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"retag": "node packages/retag/dist/retag.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^25.1.3",
|
||||
"@types/jest": "^29.5.0",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/yargs": "^15.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.55.0",
|
||||
@@ -22,12 +22,12 @@
|
||||
"eslint": "^7.31.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-jsdoc": "^30.7.8",
|
||||
"jest": "^25.1.0",
|
||||
"jest": "^29.5.0",
|
||||
"lerna": "^4.0.0",
|
||||
"prettier": "^2.6.2",
|
||||
"ts-jest": "^25.2.1",
|
||||
"ts-jest": "^29.0.5",
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-microsoft-contrib": "^6.2.0",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "^5.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -829,8 +829,8 @@ function isExportConstruct(node: ts.Node): boolean {
|
||||
}
|
||||
|
||||
function hasExportModifier(node: ts.Node): boolean {
|
||||
if (node.modifiers) {
|
||||
return node.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
||||
if (ts.canHaveModifiers(node)) {
|
||||
return !!ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -39,14 +39,14 @@
|
||||
"yargs": "^15.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">= 3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev || >= 3.8.0-dev || >= 3.9.0-dev || >= 4.0.0-dev"
|
||||
"typescript": ">= 3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev || >= 3.8.0-dev || >= 3.9.0-dev || >= 4.0.0-dev || >=5.0.0-dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/eslint": "^8.4.2",
|
||||
"@types/fs-extra": "^5.0.2",
|
||||
"@types/json-stable-stringify": "^1.0.32",
|
||||
"@types/strip-json-comments": "^0.0.28",
|
||||
"typescript": "4.7.4"
|
||||
"typescript": "^5.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { createRule } from "../util";
|
||||
import { ESLintUtils } from "@typescript-eslint/utils";
|
||||
import * as ts from "typescript";
|
||||
|
||||
const rule = createRule({
|
||||
name: "no-import-default-of-export-equals",
|
||||
defaultOptions: [],
|
||||
meta: {
|
||||
type: "problem",
|
||||
docs: {
|
||||
description: "Forbid a default import to reference an `export =` module.",
|
||||
recommended: "error",
|
||||
},
|
||||
messages: {
|
||||
noImportDefaultOfExportEquals: `The module {{moduleName}} uses \`export = \`. Import with \`import {{importName}} = require({{moduleName}})\`.`,
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create(context) {
|
||||
const parserServices = ESLintUtils.getParserServices(context);
|
||||
const checker = parserServices.program.getTypeChecker();
|
||||
return {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
ImportDeclaration(node) {
|
||||
const defaultName = node.specifiers.find((spec) => spec.type === "ImportDefaultSpecifier")?.local;
|
||||
if (!defaultName) {
|
||||
return;
|
||||
}
|
||||
const importName = defaultName.name;
|
||||
const source = parserServices.esTreeNodeToTSNodeMap.get(node.source);
|
||||
const sym = checker.getSymbolAtLocation(source);
|
||||
if (
|
||||
sym?.declarations?.some((d) => getStatements(d)?.some((s) => ts.isExportAssignment(s) && !!s.isExportEquals))
|
||||
) {
|
||||
context.report({
|
||||
messageId: "noImportDefaultOfExportEquals",
|
||||
data: { moduleName: node.source, importName },
|
||||
node: defaultName,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
function getStatements(decl: ts.Declaration): readonly ts.Statement[] | undefined {
|
||||
return ts.isSourceFile(decl)
|
||||
? decl.statements
|
||||
: ts.isModuleDeclaration(decl)
|
||||
? getModuleDeclarationStatements(decl)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function getModuleDeclarationStatements(node: ts.ModuleDeclaration): readonly ts.Statement[] | undefined {
|
||||
let { body } = node;
|
||||
while (body && body.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
body = body.body;
|
||||
}
|
||||
return body && ts.isModuleBlock(body) ? body.statements : undefined;
|
||||
}
|
||||
|
||||
export = rule;
|
||||
@@ -1,58 +0,0 @@
|
||||
import * as Lint from "tslint";
|
||||
import * as ts from "typescript";
|
||||
|
||||
import { eachModuleStatement, failure, getModuleDeclarationStatements } from "../util";
|
||||
|
||||
export class Rule extends Lint.Rules.TypedRule {
|
||||
static metadata: Lint.IRuleMetadata = {
|
||||
ruleName: "no-import-default-of-export-equals",
|
||||
description: "Forbid a default import to reference an `export =` module.",
|
||||
optionsDescription: "Not configurable.",
|
||||
options: null,
|
||||
type: "functionality",
|
||||
typescriptOnly: true,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
static FAILURE_STRING(importName: string, moduleName: string): string {
|
||||
return failure(
|
||||
Rule.metadata.ruleName,
|
||||
`The module ${moduleName} uses \`export = \`. Import with \`import ${importName} = require(${moduleName})\`.`
|
||||
);
|
||||
}
|
||||
|
||||
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
|
||||
return this.applyWithFunction(sourceFile, (ctx) => walk(ctx, program.getTypeChecker()));
|
||||
}
|
||||
}
|
||||
|
||||
function walk(ctx: Lint.WalkContext<void>, checker: ts.TypeChecker): void {
|
||||
eachModuleStatement(ctx.sourceFile, (statement) => {
|
||||
if (!ts.isImportDeclaration(statement)) {
|
||||
return;
|
||||
}
|
||||
const defaultName = statement.importClause && statement.importClause.name;
|
||||
if (!defaultName) {
|
||||
return;
|
||||
}
|
||||
const sym = checker.getSymbolAtLocation(statement.moduleSpecifier);
|
||||
if (
|
||||
sym &&
|
||||
sym.declarations &&
|
||||
sym.declarations.some((d) => {
|
||||
const statements = getStatements(d);
|
||||
return statements !== undefined && statements.some((s) => ts.isExportAssignment(s) && !!s.isExportEquals);
|
||||
})
|
||||
) {
|
||||
ctx.addFailureAtNode(defaultName, Rule.FAILURE_STRING(defaultName.text, statement.moduleSpecifier.getText()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getStatements(decl: ts.Declaration): readonly ts.Statement[] | undefined {
|
||||
return ts.isSourceFile(decl)
|
||||
? decl.statements
|
||||
: ts.isModuleDeclaration(decl)
|
||||
? getModuleDeclarationStatements(decl)
|
||||
: undefined;
|
||||
}
|
||||
@@ -222,7 +222,8 @@ function removeTypeExpression(
|
||||
function isInAmbientContext(node: ts.Node): boolean {
|
||||
return ts.isSourceFile(node)
|
||||
? node.isDeclarationFile
|
||||
: Lint.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword) || isInAmbientContext(node.parent!);
|
||||
: (ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword)) ||
|
||||
isInAmbientContext(node.parent!);
|
||||
}
|
||||
|
||||
const redundantTags = new Set([
|
||||
|
||||
@@ -54,7 +54,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
// `declare global` and `declare module "foo"` OK. `declare namespace N` not OK, should be `export namespace`.
|
||||
if (!isDeclareGlobalOrExternalModuleDeclaration(node)) {
|
||||
if (isDeclare(node)) {
|
||||
fail(mod(node, ts.SyntaxKind.DeclareKeyword), "'declare' keyword is redundant here.");
|
||||
fail(mod(node, ts.SyntaxKind.DeclareKeyword)!, "'declare' keyword is redundant here.");
|
||||
}
|
||||
if (autoExportEnabled && !isExport(node)) {
|
||||
fail(
|
||||
@@ -76,7 +76,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
node.kind === ts.SyntaxKind.InterfaceDeclaration ||
|
||||
node.kind === ts.SyntaxKind.TypeAliasDeclaration
|
||||
) {
|
||||
fail(mod(node, ts.SyntaxKind.DeclareKeyword), "'declare' keyword is redundant here.");
|
||||
fail(mod(node, ts.SyntaxKind.DeclareKeyword)!, "'declare' keyword is redundant here.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,8 +85,8 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
ctx.addFailureAtNode(node, failure(Rule.metadata.ruleName, reason));
|
||||
}
|
||||
|
||||
function mod(node: ts.Statement, kind: ts.SyntaxKind): ts.Node {
|
||||
return node.modifiers!.find((m) => m.kind === kind)!;
|
||||
function mod(node: ts.Statement, kind: ts.SyntaxKind): ts.Node | undefined {
|
||||
return ts.canHaveModifiers(node) ? ts.getModifiers(node)?.find((m) => m.kind === kind) : undefined;
|
||||
}
|
||||
|
||||
function checkModule(moduleDeclaration: ts.ModuleDeclaration): void {
|
||||
@@ -110,7 +110,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
|
||||
// Compiler will error for 'declare' here anyway, so just check for 'export'.
|
||||
if (isExport(s) && autoExportEnabled && !isDefault(s)) {
|
||||
fail(
|
||||
mod(s, ts.SyntaxKind.ExportKeyword),
|
||||
mod(s, ts.SyntaxKind.ExportKeyword)!,
|
||||
"'export' keyword is redundant here because " +
|
||||
"all declarations in this module are exported automatically. " +
|
||||
"If you have a good reason to export some declarations and not others, " +
|
||||
@@ -138,15 +138,15 @@ function isModuleDeclaration(node: ts.Node): node is ts.ModuleDeclaration {
|
||||
}
|
||||
|
||||
function isDeclare(node: ts.Node): boolean {
|
||||
return Lint.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword);
|
||||
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword);
|
||||
}
|
||||
|
||||
function isExport(node: ts.Node): boolean {
|
||||
return Lint.hasModifier(node.modifiers, ts.SyntaxKind.ExportKeyword);
|
||||
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
|
||||
}
|
||||
|
||||
function isDefault(node: ts.Node): boolean {
|
||||
return Lint.hasModifier(node.modifiers, ts.SyntaxKind.DefaultKeyword);
|
||||
return ts.canHaveModifiers(node) && !!ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:max-line-length
|
||||
|
||||
@@ -31,33 +31,6 @@ export function getCommonDirectoryName(files: readonly string[]): string {
|
||||
return basename(minDir);
|
||||
}
|
||||
|
||||
export function eachModuleStatement(sourceFile: ts.SourceFile, action: (statement: ts.Statement) => void): void {
|
||||
if (!sourceFile.isDeclarationFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const node of sourceFile.statements) {
|
||||
if (ts.isModuleDeclaration(node)) {
|
||||
const statements = getModuleDeclarationStatements(node);
|
||||
if (statements) {
|
||||
for (const statement of statements) {
|
||||
action(statement);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
action(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getModuleDeclarationStatements(node: ts.ModuleDeclaration): readonly ts.Statement[] | undefined {
|
||||
let { body } = node;
|
||||
while (body && body.kind === ts.SyntaxKind.ModuleDeclaration) {
|
||||
body = body.body;
|
||||
}
|
||||
return body && ts.isModuleBlock(body) ? body.statements : undefined;
|
||||
}
|
||||
|
||||
export async function getCompilerOptions(dirPath: string): Promise<ts.CompilerOptions> {
|
||||
const tsconfigPath = join(dirPath, "tsconfig.json");
|
||||
if (!(await pathExists(tsconfigPath))) {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { ESLintUtils } from "@typescript-eslint/utils";
|
||||
|
||||
import * as noImportDefaultOfExportEquals from "../src/rules/no-import-default-of-export-equals";
|
||||
|
||||
const ruleTester = new ESLintUtils.RuleTester({
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018,
|
||||
tsconfigRootDir: __dirname,
|
||||
project: "./tsconfig.no-import-default-of-export-equals.json",
|
||||
},
|
||||
});
|
||||
|
||||
ruleTester.run("no-import-default-of-export-equals", noImportDefaultOfExportEquals, {
|
||||
invalid: [
|
||||
{
|
||||
filename: "index.d.ts",
|
||||
code: `declare module "a" {
|
||||
interface I {}
|
||||
export = I;
|
||||
}
|
||||
|
||||
declare module "b" {
|
||||
import a from "a";
|
||||
}`,
|
||||
errors: [
|
||||
{
|
||||
line: 7,
|
||||
messageId: "noImportDefaultOfExportEquals",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
valid: [
|
||||
{
|
||||
filename: "index.d.ts",
|
||||
code: `declare module "a" {
|
||||
interface I {}
|
||||
export default I;
|
||||
}
|
||||
|
||||
declare module "b" {
|
||||
import a from "a";
|
||||
}
|
||||
`,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
declare module "a" {
|
||||
interface I {}
|
||||
export = I;
|
||||
}
|
||||
|
||||
declare module "b" {
|
||||
import a from "a";
|
||||
~ [0]
|
||||
}
|
||||
|
||||
[0]: The module "a" uses `export = `. Import with `import a = require("a")`. See: https://github.com/microsoft/DefinitelyTyped-tools/blob/master/packages/dtslint/docs/no-import-default-of-export-equals.md
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"rulesDirectory": ["../../../dist/rules"],
|
||||
"rules": {
|
||||
"no-import-default-of-export-equals": true
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
declare function foo(): void;
|
||||
export = foo;
|
||||
@@ -1,4 +0,0 @@
|
||||
import D from "./a";
|
||||
~ [0]
|
||||
|
||||
[0]: The module "./a" uses `export = `. Import with `import D = require("./a")`. See: https://github.com/microsoft/DefinitelyTyped-tools/blob/master/packages/dtslint/docs/no-import-default-of-export-equals.md
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"rulesDirectory": ["../../../dist/rules"],
|
||||
"rules": {
|
||||
"no-import-default-of-export-equals": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
declare module "a" {
|
||||
interface I {}
|
||||
export default I;
|
||||
}
|
||||
|
||||
declare module "b" {
|
||||
import a from "a";
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"rulesDirectory": ["../../../dist/rules"],
|
||||
"rules": {
|
||||
"no-import-default-of-export-equals": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "esnext"
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"file.ts"
|
||||
]
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
"@types/node": "^12.12.29",
|
||||
"markdown-table": "^1.1.3",
|
||||
"semver": "^7.3.7",
|
||||
"typescript": "^4.1.0",
|
||||
"typescript": "^5.0.2",
|
||||
"yargs": "^15.4.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`analysis summarizePackageBenchmarks snapshot 1`] = `
|
||||
Object {
|
||||
{
|
||||
"batchRunStart": "2019-06-05T21:02:57.283Z",
|
||||
"benchmarkDuration": 6146.653569,
|
||||
"completions": Object {
|
||||
"completions": {
|
||||
"mean": 148.38288194,
|
||||
"meanCoefficientOfVariation": 0.23178183452633877,
|
||||
"median": 146.9316702,
|
||||
"standardDeviation": 14.526224099473964,
|
||||
"trials": 20,
|
||||
"worst": Object {
|
||||
"completionsDurations": Array [
|
||||
"worst": {
|
||||
"completionsDurations": [
|
||||
260.302261,
|
||||
161.823348,
|
||||
209.062858,
|
||||
@@ -23,7 +23,7 @@ Object {
|
||||
"identifierText": "abbrev",
|
||||
"line": 12,
|
||||
"offset": 15,
|
||||
"quickInfoDurations": Array [
|
||||
"quickInfoDurations": [
|
||||
144.181488,
|
||||
128.053511,
|
||||
181.161561,
|
||||
@@ -38,14 +38,14 @@ Object {
|
||||
"packageName": "abbrev",
|
||||
"packageVersionMajor": 1,
|
||||
"packageVersionMinor": 1,
|
||||
"quickInfo": Object {
|
||||
"quickInfo": {
|
||||
"mean": 140.33250924,
|
||||
"meanCoefficientOfVariation": 0.21107195517210994,
|
||||
"median": 139.3310676,
|
||||
"standardDeviation": 12.553366997435981,
|
||||
"trials": 20,
|
||||
"worst": Object {
|
||||
"completionsDurations": Array [
|
||||
"worst": {
|
||||
"completionsDurations": [
|
||||
188.397316,
|
||||
111.76699,
|
||||
154.360542,
|
||||
@@ -57,7 +57,7 @@ Object {
|
||||
"identifierText": "ReadonlyArray",
|
||||
"line": 11,
|
||||
"offset": 14,
|
||||
"quickInfoDurations": Array [
|
||||
"quickInfoDurations": [
|
||||
209.811926,
|
||||
229.587525,
|
||||
166.220571,
|
||||
@@ -67,7 +67,7 @@ Object {
|
||||
"start": 262,
|
||||
},
|
||||
},
|
||||
"relationCacheSizes": Object {
|
||||
"relationCacheSizes": {
|
||||
"assignable": 104,
|
||||
"identity": 0,
|
||||
"subtype": 0,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"pacote": "^13.6.1",
|
||||
"semver": "^7.3.7",
|
||||
"source-map-support": "^0.4.0",
|
||||
"typescript": "^4.1.0",
|
||||
"typescript": "^5.0.2",
|
||||
"yargs": "15.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
Reference in New Issue
Block a user