Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate several rules #50

Merged
merged 8 commits into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/odd-chicken-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-import-x": patch
---

refactor: migrate several rules
35 changes: 24 additions & 11 deletions patches/@typescript-eslint+utils+5.62.0.patch
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
diff --git a/node_modules/@typescript-eslint/utils/dist/ts-eslint/Rule.d.ts b/node_modules/@typescript-eslint/utils/dist/ts-eslint/Rule.d.ts
index 9a3a1fd..6a2e2dd 100644
index 9a3a1fd..c6ed412 100644
--- a/node_modules/@typescript-eslint/utils/dist/ts-eslint/Rule.d.ts
+++ b/node_modules/@typescript-eslint/utils/dist/ts-eslint/Rule.d.ts
@@ -7,15 +7,13 @@ import type { SourceCode } from './SourceCode';
@@ -6,6 +6,10 @@ import type { Scope } from './Scope';
import type { SourceCode } from './SourceCode';
export type RuleRecommendation = 'error' | 'strict' | 'warn' | false;
interface RuleMetaDataDocs {
/**
- * Concise description of the rule
+ /**
+ * The category the rule falls under
*/
- description: string;
+ */
+ category?: string;
/**
- * The recommendation level for the rule.
- * Used by the build tools to generate the recommended and strict configs.
- * Set to false to not include it as a recommendation
+ * Concise description of the rule
* Concise description of the rule
*/
@@ -15,7 +19,7 @@ interface RuleMetaDataDocs {
* Used by the build tools to generate the recommended and strict configs.
* Set to false to not include it as a recommendation
*/
- recommended: 'error' | 'strict' | 'warn' | false;
+ description: string;
+ recommended?: 'error' | 'strict' | 'warn' | boolean;
/**
* The URL of the rule's docs
*/
diff --git a/node_modules/@typescript-eslint/utils/dist/ts-eslint/RuleTester.d.ts b/node_modules/@typescript-eslint/utils/dist/ts-eslint/RuleTester.d.ts
index c8afefe..d629d04 100644
--- a/node_modules/@typescript-eslint/utils/dist/ts-eslint/RuleTester.d.ts
+++ b/node_modules/@typescript-eslint/utils/dist/ts-eslint/RuleTester.d.ts
@@ -115,7 +115,7 @@ interface RunTests<TMessageIds extends string, TOptions extends Readonly<unknown
readonly invalid: readonly InvalidTestCase<TMessageIds, TOptions>[];
}
interface RuleTesterConfig extends Linter.Config {
- readonly parser: string;
+ readonly parser?: string;
readonly parserOptions?: Readonly<ParserOptions>;
}
declare class RuleTesterBase {
57 changes: 32 additions & 25 deletions src/export-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ export interface DeclarationMetadata {
isOnlyImportingTypes?: boolean
}

export interface ModuleNamespace {
doc?: Annotation
namespace?: ExportMap | null
}

export interface ModuleImport {
getter: () => ExportMap | null
declarations: Set<DeclarationMetadata>
}

export class ExportMap {
static for(context: ChildContext) {
const { path } = context
Expand Down Expand Up @@ -216,18 +226,25 @@ export class ExportMap {
return ExportMap.for(childContext(rp, context))
}

function getNamespace(identifier: TSESTree.Identifier) {
if (!namespaces.has(identifier.name)) {
function getNamespace(identifier: TSESTree.Identifier | string) {
const namespace =
typeof identifier === 'string' ? identifier : identifier.name
if (!namespaces.has(namespace)) {
return
}

return function () {
return resolveImport(namespaces.get(identifier.name))
return resolveImport(namespaces.get(namespace))
}
}

function addNamespace(object: object, identifier: TSESTree.Identifier) {
const nsfn = getNamespace(identifier)
function addNamespace(
object: object,
identifier: TSESTree.Identifier | string,
) {
const namespace =
typeof identifier === 'string' ? identifier : identifier.name
const nsfn = getNamespace(namespace)
if (nsfn) {
Object.defineProperty(object, 'namespace', { get: nsfn })
}
Expand All @@ -247,7 +264,7 @@ export class ExportMap {
n.source &&
(n.source as TSESTree.StringLiteral).value) as string

const exportMeta = {}
const exportMeta: ModuleNamespace = {}

let local: string

Expand All @@ -273,11 +290,7 @@ export class ExportMap {
s.exported!.name ||
// @ts-expect-error - legacy parser type
s.exported!.value,
addNamespace(
exportMeta,
// @ts-expect-error -- FIXME: no idea yet
s.source.value,
),
addNamespace(exportMeta, s.source.value),
)
return
case 'ExportSpecifier':
Expand Down Expand Up @@ -625,7 +638,7 @@ export class ExportMap {
return m
}

namespace = new Map()
namespace = new Map<string, ModuleNamespace>()

// todo: restructure to key on path, value is resolver + map of names
reexports = new Map<
Expand All @@ -644,19 +657,13 @@ export class ExportMap {
/**
* dependencies of this module that are not explicitly re-exported
*/
imports = new Map<
string,
{
getter: () => ExportMap | null
declarations: Set<DeclarationMetadata>
}
>()
imports = new Map<string, ModuleImport>()

errors: ParseError[] = []

parseGoal: 'ambiguous' | 'Module' | 'Script' = 'ambiguous'

private declare visitorKeys: TSESLint.SourceCode.VisitorKeys | null
declare visitorKeys: TSESLint.SourceCode.VisitorKeys | null

private declare mtime: Date

Expand Down Expand Up @@ -770,7 +777,7 @@ export class ExportMap {
return { found: false, path: [this] }
}

get<T = unknown>(name: string): T | null | undefined {
get(name: string): ModuleNamespace | null | undefined {
if (this.namespace.has(name)) {
return this.namespace.get(name)
}
Expand Down Expand Up @@ -808,15 +815,15 @@ export class ExportMap {

const innerValue = innerMap.get(name)
if (innerValue !== undefined) {
return innerValue as T
return innerValue
}
}
}
}

forEach<T>(
forEach(
callback: (
value: T | null | undefined,
value: ModuleNamespace | null | undefined,
name: string,
map: ExportMap,
) => void,
Expand All @@ -839,7 +846,7 @@ export class ExportMap {
return
}

d.forEach<T>((v, n) => {
d.forEach((v, n) => {
if (n !== 'default') {
callback.call(thisArg, v, n, this)
}
Expand Down
21 changes: 14 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ import groupExports from './rules/group-exports'
import noRelativePackages from './rules/no-relative-packages'
import noRelativeParentImports from './rules/no-relative-parent-imports'
import consistentTypeSpecifierStyle from './rules/consistent-type-specifier-style'
import noSelfImport from './rules/no-self-import'
import noCycle from './rules/no-cycle'
import noNamedDefault from './rules/no-named-default'
import noNamedAsDefault from './rules/no-named-as-default'
import noNamedAsDefaultMember from './rules/no-named-as-default-member'
import noAnonymousDefaultExport from './rules/no-anonymous-default-export'
import noUnusedModules from './rules/no-unused-modules'

// configs
import recommended from './config/recommended'
Expand Down Expand Up @@ -44,13 +51,13 @@ export const rules = {
'no-relative-parent-imports': noRelativeParentImports,
'consistent-type-specifier-style': consistentTypeSpecifierStyle,

'no-self-import': require('./rules/no-self-import'),
'no-cycle': require('./rules/no-cycle'),
'no-named-default': require('./rules/no-named-default'),
'no-named-as-default': require('./rules/no-named-as-default'),
'no-named-as-default-member': require('./rules/no-named-as-default-member'),
'no-anonymous-default-export': require('./rules/no-anonymous-default-export'),
'no-unused-modules': require('./rules/no-unused-modules'),
'no-self-import': noSelfImport,
'no-cycle': noCycle,
'no-named-default': noNamedDefault,
'no-named-as-default': noNamedAsDefault,
'no-named-as-default-member': noNamedAsDefaultMember,
'no-anonymous-default-export': noAnonymousDefaultExport,
'no-unused-modules': noUnusedModules,

'no-commonjs': require('./rules/no-commonjs'),
'no-amd': require('./rules/no-amd'),
Expand Down
16 changes: 6 additions & 10 deletions src/rules/namespace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Options = {

function processBodyStatement(
context: RuleContext<MessageId>,
namespaces: Map<string, ExportMap>,
namespaces: Map<string, ExportMap | null>,
declaration: TSESTree.ProgramStatement,
) {
if (declaration.type !== 'ImportDeclaration') {
Expand Down Expand Up @@ -56,7 +56,7 @@ function processBodyStatement(
break
case 'ImportDefaultSpecifier':
case 'ImportSpecifier': {
const meta = imports.get<{ namespace?: ExportMap }>(
const meta = imports.get(
'imported' in specifier && specifier.imported
? specifier.imported.name ||
// @ts-expect-error - legacy parser node
Expand Down Expand Up @@ -136,7 +136,7 @@ export = createRule<[Options], MessageId>({
// read options
const { allowComputed } = context.options[0] || {}

const namespaces = new Map<string, ExportMap>()
const namespaces = new Map<string, ExportMap | null>()

return {
// pick up all imports at body entry time, to properly respect hoisting
Expand Down Expand Up @@ -231,9 +231,7 @@ export = createRule<[Options], MessageId>({
break
}

const exported = namespace.get<{ namespace: ExportMap }>(
deref.property.name,
)
const exported = namespace.get(deref.property.name)

if (exported == null) {
return
Expand Down Expand Up @@ -268,7 +266,7 @@ export = createRule<[Options], MessageId>({
// DFS traverse child namespaces
function testKey(
pattern: TSESTree.Node,
namespace?: ExportMap,
namespace?: ExportMap | null,
path: string[] = [initName],
) {
if (!(namespace instanceof ExportMap)) {
Expand Down Expand Up @@ -304,9 +302,7 @@ export = createRule<[Options], MessageId>({

path.push(property.key.name)

const dependencyExportMap = namespace.get<{ namespace: ExportMap }>(
property.key.name,
)
const dependencyExportMap = namespace.get(property.key.name)

// could be null when ignored or ambiguous
if (dependencyExportMap != null) {
Expand Down
7 changes: 1 addition & 6 deletions src/rules/newline-after-import.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/**
* @fileoverview Rule to enforce new line after import not followed by another import.
* @author Radek Benkel
* Rule to enforce new line after import not followed by another import.
*/

import { isStaticRequire } from '../core/static-require'
Expand All @@ -9,10 +8,6 @@ import { docsUrl } from '../docs-url'
import debug from 'debug'
const log = debug('eslint-plugin-import-x:rules:newline-after-import')

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

function containsNodeOrEqual(outerNode, innerNode) {
return (
outerNode.range[0] <= innerNode.range[0] &&
Expand Down
7 changes: 1 addition & 6 deletions src/rules/no-amd.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
/**
* @fileoverview Rule to prefer imports to AMD
* @author Jamund Ferguson
* Rule to prefer imports to AMD
*/

import { docsUrl } from '../docs-url'

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = {
meta: {
type: 'suggestion',
Expand Down
Loading
Loading