Skip to content

Commit

Permalink
refactor: migrate all remaining rules (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
JounQin committed Mar 18, 2024
1 parent de896f4 commit f4ca4b5
Show file tree
Hide file tree
Showing 70 changed files with 2,633 additions and 2,000 deletions.
5 changes: 5 additions & 0 deletions .changeset/blue-dolls-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"eslint-plugin-import-x": patch
---

refactor: migrate all remaining rules
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,11 @@ module.exports = {
'import-x/default': 0,
},
},
{
files: 'global.d.ts',
rules: {
'import-x/no-extraneous-dependencies': 0,
},
},
],
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@changesets/cli": "^2.27.1",
"@eslint/import-test-order-redirect-scoped": "link:./test/fixtures/order-redirect-scoped",
"@test-scope/some-module": "link:./test/fixtures/symlinked-module",
"@total-typescript/ts-reset": "^0.5.1",
"@types/debug": "^4.1.12",
"@types/doctrine": "^0.0.9",
"@types/eslint": "^8.56.5",
Expand Down
2 changes: 2 additions & 0 deletions src/core/import-type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,5 @@ function typeTest(name: string, context: RuleContext, path?: string | null) {
export function importType(name: string, context: RuleContext) {
return typeTest(name, context, resolve(name, context))
}

export type ImportType = ReturnType<typeof importType>
2 changes: 1 addition & 1 deletion src/docs-url.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - We're using commonjs
// @ts-ignore - The structures of `lib` and `src` are same
import pkg from '../package.json'

const repoUrl = 'https://github.com/un-es/eslint-plugin-import-x'
Expand Down
1 change: 1 addition & 0 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '@total-typescript/ts-reset'
72 changes: 48 additions & 24 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ 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'
import noCommonjs from './rules/no-commonjs'
import noAmd from './rules/no-amd'
import noDuplicates from './rules/no-duplicates'
import first from './rules/first'
import maxDependencies from './rules/max-dependencies'
import noExtraneousDependencies from './rules/no-extraneous-dependencies'
import noAbsolutePath from './rules/no-absolute-path'
import noNodejsModules from './rules/no-nodejs-modules'
import noWebpackLoaderSyntax from './rules/no-webpack-loader-syntax'
import order from './rules/order'
import newlineAfterImport from './rules/newline-after-import'
import preferDefaultExport from './rules/prefer-default-export'
import noDefaultExport from './rules/no-default-export'
import noNamedExport from './rules/no-named-export'
import noDynamicRequire from './rules/no-dynamic-require'
import unambiguous from './rules/unambiguous'
import noUnassignedImport from './rules/no-unassigned-import'
import noUselessPathSegments from './rules/no-useless-path-segments'
import dynamicImportChunkname from './rules/dynamic-import-chunkname'
import noImportModuleExports from './rules/no-import-module-exports'
import noEmptyNamedBlocks from './rules/no-empty-named-blocks'
import exportsLast from './rules/exports-last'
import noDeprecated from './rules/no-deprecated'
import importsFirst from './rules/imports-first'

// configs
import recommended from './config/recommended'
Expand Down Expand Up @@ -59,36 +83,36 @@ export const rules = {
'no-anonymous-default-export': noAnonymousDefaultExport,
'no-unused-modules': noUnusedModules,

'no-commonjs': require('./rules/no-commonjs'),
'no-amd': require('./rules/no-amd'),
'no-duplicates': require('./rules/no-duplicates'),
first: require('./rules/first'),
'max-dependencies': require('./rules/max-dependencies'),
'no-extraneous-dependencies': require('./rules/no-extraneous-dependencies'),
'no-absolute-path': require('./rules/no-absolute-path'),
'no-nodejs-modules': require('./rules/no-nodejs-modules'),
'no-webpack-loader-syntax': require('./rules/no-webpack-loader-syntax'),
order: require('./rules/order'),
'newline-after-import': require('./rules/newline-after-import'),
'prefer-default-export': require('./rules/prefer-default-export'),
'no-default-export': require('./rules/no-default-export'),
'no-named-export': require('./rules/no-named-export'),
'no-dynamic-require': require('./rules/no-dynamic-require'),
unambiguous: require('./rules/unambiguous'),
'no-unassigned-import': require('./rules/no-unassigned-import'),
'no-useless-path-segments': require('./rules/no-useless-path-segments'),
'dynamic-import-chunkname': require('./rules/dynamic-import-chunkname'),
'no-import-module-exports': require('./rules/no-import-module-exports'),
'no-empty-named-blocks': require('./rules/no-empty-named-blocks'),
'no-commonjs': noCommonjs,
'no-amd': noAmd,
'no-duplicates': noDuplicates,
first,
'max-dependencies': maxDependencies,
'no-extraneous-dependencies': noExtraneousDependencies,
'no-absolute-path': noAbsolutePath,
'no-nodejs-modules': noNodejsModules,
'no-webpack-loader-syntax': noWebpackLoaderSyntax,
order,
'newline-after-import': newlineAfterImport,
'prefer-default-export': preferDefaultExport,
'no-default-export': noDefaultExport,
'no-named-export': noNamedExport,
'no-dynamic-require': noDynamicRequire,
unambiguous,
'no-unassigned-import': noUnassignedImport,
'no-useless-path-segments': noUselessPathSegments,
'dynamic-import-chunkname': dynamicImportChunkname,
'no-import-module-exports': noImportModuleExports,
'no-empty-named-blocks': noEmptyNamedBlocks,

// export
'exports-last': require('./rules/exports-last'),
'exports-last': exportsLast,

// metadata-based
'no-deprecated': require('./rules/no-deprecated'),
'no-deprecated': noDeprecated,

// deprecated aliases to rules
'imports-first': require('./rules/imports-first'),
'imports-first': importsFirst,
} satisfies Record<string, TSESLint.RuleModule<string, readonly unknown[]>>

export const configs = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import vm from 'vm'
import { docsUrl } from '../docs-url'

module.exports = {
import { createRule } from '../utils'
import { TSESTree } from '@typescript-eslint/utils'

type Options = {
allowEmpty?: boolean
importFunctions?: readonly string[]
webpackChunknameFormat?: string
}

type MessageId =
| 'leadingComment'
| 'blockComment'
| 'paddedSpaces'
| 'webpackComment'
| 'chunknameFormat'

export = createRule<[Options?], MessageId>({
name: 'dynamic-import-chunkname',
meta: {
type: 'suggestion',
docs: {
category: 'Style guide',
description:
'Enforce a leading comment with the webpackChunkName for dynamic imports.',
url: docsUrl('dynamic-import-chunkname'),
},
schema: [
{
Expand All @@ -30,32 +45,41 @@ module.exports = {
},
},
],
messages: {
leadingComment:
'dynamic imports require a leading comment with the webpack chunkname',
blockComment:
'dynamic imports require a /* foo */ style comment, not a // foo comment',
paddedSpaces:
'dynamic imports require a block comment padded with spaces - /* foo */',
webpackComment:
'dynamic imports require a "webpack" comment with valid syntax',
chunknameFormat:
'dynamic imports require a leading comment in the form /*{{format}}*/',
},
},

defaultOptions: [],
create(context) {
const config = context.options[0]
const { importFunctions = [], allowEmpty = false } = config || {}
const {
importFunctions = [],
allowEmpty = false,
webpackChunknameFormat = '([0-9a-zA-Z-_/.]|\\[(request|index)\\])+',
} = config || {}
} = context.options[0] || {}

const paddedCommentRegex = /^ (\S[\s\S]+\S) $/
const commentStyleRegex =
/^( ((webpackChunkName: .+)|((webpackPrefetch|webpackPreload): (true|false|-?[0-9]+))|(webpackIgnore: (true|false))|((webpackInclude|webpackExclude): \/.*\/)|(webpackMode: ["'](lazy|lazy-once|eager|weak)["'])|(webpackExports: (['"]\w+['"]|\[(['"]\w+['"], *)+(['"]\w+['"]*)\]))),?)+ $/
const chunkSubstrFormat = ` webpackChunkName: ["']${webpackChunknameFormat}["'],? `
const chunkSubstrRegex = new RegExp(chunkSubstrFormat)

function run(node, arg) {
function run(node: TSESTree.Node, arg: TSESTree.Node) {
const sourceCode = context.getSourceCode()
const leadingComments = sourceCode.getCommentsBefore
? sourceCode.getCommentsBefore(arg) // This method is available in ESLint >= 4.
: sourceCode.getComments(arg).leading // This method is deprecated in ESLint 7.
const leadingComments = sourceCode.getCommentsBefore(arg)

if ((!leadingComments || leadingComments.length === 0) && !allowEmpty) {
context.report({
node,
message:
'dynamic imports require a leading comment with the webpack chunkname',
messageId: 'leadingComment',
})
return
}
Expand All @@ -66,16 +90,15 @@ module.exports = {
if (comment.type !== 'Block') {
context.report({
node,
message:
'dynamic imports require a /* foo */ style comment, not a // foo comment',
messageId: 'blockComment',
})
return
}

if (!paddedCommentRegex.test(comment.value)) {
context.report({
node,
message: `dynamic imports require a block comment padded with spaces - /* foo */`,
messageId: 'paddedSpaces',
})
return
}
Expand All @@ -86,15 +109,15 @@ module.exports = {
} catch (error) {
context.report({
node,
message: `dynamic imports require a "webpack" comment with valid syntax`,
messageId: 'webpackComment',
})
return
}

if (!commentStyleRegex.test(comment.value)) {
context.report({
node,
message: `dynamic imports require a "webpack" comment with valid syntax`,
messageId: 'webpackComment',
})
return
}
Expand All @@ -107,7 +130,10 @@ module.exports = {
if (!isChunknamePresent && !allowEmpty) {
context.report({
node,
message: `dynamic imports require a leading comment in the form /*${chunkSubstrFormat}*/`,
messageId: 'chunknameFormat',
data: {
format: chunkSubstrFormat,
},
})
}
}
Expand All @@ -119,7 +145,9 @@ module.exports = {

CallExpression(node) {
if (
// @ts-expect-error - legacy parser type
node.callee.type !== 'Import' &&
'name' in node.callee &&
importFunctions.indexOf(node.callee.name) < 0
) {
return
Expand All @@ -129,4 +157,4 @@ module.exports = {
},
}
},
}
})
20 changes: 12 additions & 8 deletions src/rules/exports-last.js → src/rules/exports-last.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import { docsUrl } from '../docs-url'
import type { TSESTree } from '@typescript-eslint/utils'

function isNonExportStatement({ type }) {
import { createRule } from '../utils'

function isNonExportStatement({ type }: TSESTree.Node) {
return (
type !== 'ExportDefaultDeclaration' &&
type !== 'ExportNamedDeclaration' &&
type !== 'ExportAllDeclaration'
)
}

module.exports = {
export = createRule({
name: 'exports-last',
meta: {
type: 'suggestion',
docs: {
category: 'Style guide',
description: 'Ensure all exports appear after other statements.',
url: docsUrl('exports-last'),
},
schema: [],
messages: {
end: 'Export statements should appear at the end of the file',
},
},

defaultOptions: [],
create(context) {
return {
Program({ body }) {
Expand All @@ -30,13 +35,12 @@ module.exports = {
if (!isNonExportStatement(node)) {
context.report({
node,
message:
'Export statements should appear at the end of the file',
messageId: 'end',
})
}
})
}
},
}
},
}
})
Loading

0 comments on commit f4ca4b5

Please sign in to comment.