Upgrade ESLint to v9 with flat config
- Upgrade eslint from v8 to v9.18.0 - Migrate from .eslintrc.js to eslint.config.mjs (flat config) - Upgrade typescript-eslint to v8.20.0 (unified package) - Replace eslint-plugin-import with eslint-plugin-import-x for flat config support - Add globals package for environment globals - Update eslint-plugin-bsky-internal with proper meta objects for ESLint v9 - Fix deprecated context.getScope() API usage - Update bskyembed to use flat config - Remove deprecated --ext flag from lint scripts - Configure rules to maintain previous behavior while using new ESLint version
This commit is contained in:
-127
@@ -1,127 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
extends: [
|
|
||||||
'@react-native',
|
|
||||||
'plugin:react/recommended',
|
|
||||||
'plugin:react/jsx-runtime',
|
|
||||||
'plugin:react-native-a11y/ios',
|
|
||||||
'prettier',
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: [
|
|
||||||
'@typescript-eslint',
|
|
||||||
'react',
|
|
||||||
'lingui',
|
|
||||||
'simple-import-sort',
|
|
||||||
'bsky-internal',
|
|
||||||
'eslint-plugin-react-compiler',
|
|
||||||
'import',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'react/no-unescaped-entities': 0,
|
|
||||||
'react/prop-types': 0,
|
|
||||||
'react-native/no-inline-styles': 0,
|
|
||||||
'bsky-internal/avoid-unwrapped-text': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
impliedTextComponents: [
|
|
||||||
'H1',
|
|
||||||
'H2',
|
|
||||||
'H3',
|
|
||||||
'H4',
|
|
||||||
'H5',
|
|
||||||
'H6',
|
|
||||||
'P',
|
|
||||||
'Admonition',
|
|
||||||
'Admonition.Admonition',
|
|
||||||
'Toast.Action',
|
|
||||||
'toast.Action',
|
|
||||||
'AgeAssuranceAdmonition',
|
|
||||||
'Span',
|
|
||||||
'StackedButton',
|
|
||||||
],
|
|
||||||
impliedTextProps: [],
|
|
||||||
suggestedTextWrappers: {
|
|
||||||
Button: 'ButtonText',
|
|
||||||
'ToggleButton.Button': 'ToggleButton.ButtonText',
|
|
||||||
'SegmentedControl.Item': 'SegmentedControl.ItemText',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'bsky-internal/use-exact-imports': 'error',
|
|
||||||
'bsky-internal/use-typed-gates': 'error',
|
|
||||||
'bsky-internal/use-prefixed-imports': 'error',
|
|
||||||
'simple-import-sort/imports': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
groups: [
|
|
||||||
// Side effect imports.
|
|
||||||
['^\\u0000'],
|
|
||||||
// Node.js builtins prefixed with `node:`.
|
|
||||||
['^node:'],
|
|
||||||
// Packages.
|
|
||||||
// Things that start with a letter (or digit or underscore), or `@` followed by a letter.
|
|
||||||
// React/React Native priortized, followed by expo
|
|
||||||
// Followed by all packages excluding unprefixed relative ones
|
|
||||||
[
|
|
||||||
'^(react\\/(.*)$)|^(react$)|^(react-native(.*)$)',
|
|
||||||
'^(expo(.*)$)|^(expo$)',
|
|
||||||
'^(?!(?:alf|components|lib|locale|logger|platform|screens|state|view)(?:$|\\/))@?\\w',
|
|
||||||
],
|
|
||||||
// Relative imports.
|
|
||||||
// Ideally, anything that starts with a dot or #
|
|
||||||
// due to unprefixed relative imports being used, we whitelist the relative paths we use
|
|
||||||
// (?:$|\\/) matches end of string or /
|
|
||||||
[
|
|
||||||
'^(?:#\\/)?(?:lib|state|logger|platform|locale)(?:$|\\/)',
|
|
||||||
'^(?:#\\/)?view(?:$|\\/)',
|
|
||||||
'^(?:#\\/)?screens(?:$|\\/)',
|
|
||||||
'^(?:#\\/)?alf(?:$|\\/)',
|
|
||||||
'^(?:#\\/)?components(?:$|\\/)',
|
|
||||||
'^#\\/',
|
|
||||||
'^\\.',
|
|
||||||
],
|
|
||||||
// anything else - hopefully we don't have any of these
|
|
||||||
['^'],
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'simple-import-sort/exports': 'error',
|
|
||||||
'react-compiler/react-compiler': 'warn',
|
|
||||||
'no-unused-vars': 'off',
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{argsIgnorePattern: '^_', varsIgnorePattern: '^_.+'},
|
|
||||||
],
|
|
||||||
'@typescript-eslint/consistent-type-imports': [
|
|
||||||
'warn',
|
|
||||||
{prefer: 'type-imports', fixStyle: 'inline-type-imports'},
|
|
||||||
],
|
|
||||||
'import/consistent-type-specifier-style': ['warn', 'prefer-inline'],
|
|
||||||
},
|
|
||||||
ignorePatterns: [
|
|
||||||
'**/__mocks__/*.ts',
|
|
||||||
'src/platform/polyfills.ts',
|
|
||||||
'src/third-party',
|
|
||||||
'ios',
|
|
||||||
'android',
|
|
||||||
'coverage',
|
|
||||||
'*.lock',
|
|
||||||
'.husky',
|
|
||||||
'patches',
|
|
||||||
'*.html',
|
|
||||||
'bskyweb',
|
|
||||||
'bskyembed',
|
|
||||||
'src/locale/locales/_build/',
|
|
||||||
'src/locale/locales/**/*.js',
|
|
||||||
'*.e2e.ts',
|
|
||||||
'*.e2e.tsx',
|
|
||||||
],
|
|
||||||
settings: {
|
|
||||||
componentWrapperFunctions: ['observer'],
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
plugins: ['@typescript-eslint', 'simple-import-sort'],
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'preact',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
|
||||||
],
|
|
||||||
rules: {
|
|
||||||
'simple-import-sort/imports': 'warn',
|
|
||||||
'simple-import-sort/exports': 'warn',
|
|
||||||
'no-else-return': 'off',
|
|
||||||
},
|
|
||||||
parserOptions: {
|
|
||||||
sourceType: 'module',
|
|
||||||
ecmaVersion: 'latest',
|
|
||||||
project: ['./tsconfig.json'],
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// @ts-check
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import simpleImportSort from 'eslint-plugin-simple-import-sort'
|
||||||
|
import globals from 'globals'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
// Global ignores
|
||||||
|
{
|
||||||
|
ignores: ['dist/**', 'node_modules/**'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Base JS recommended rules
|
||||||
|
js.configs.recommended,
|
||||||
|
|
||||||
|
// TypeScript rules with type checking
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
|
||||||
|
// Main configuration
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||||
|
plugins: {
|
||||||
|
'simple-import-sort': simpleImportSort,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'simple-import-sort/imports': 'warn',
|
||||||
|
'simple-import-sort/exports': 'warn',
|
||||||
|
'no-else-return': 'off',
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrors: 'none',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"dev-snippet": "tsc --project tsconfig.snippet.json && serve -s dist -p 3000 -n",
|
"dev-snippet": "tsc --project tsconfig.snippet.json && serve -s dist -p 3000 -n",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"build-snippet": "tsc --project tsconfig.snippet.json",
|
"build-snippet": "tsc --project tsconfig.snippet.json",
|
||||||
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx src",
|
"lint": "eslint --cache src",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -17,11 +17,13 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@preact/preset-vite": "^2.10.2",
|
"@preact/preset-vite": "^2.10.2",
|
||||||
"@vitejs/plugin-legacy": "^7.0.0",
|
"@vitejs/plugin-legacy": "^7.0.0",
|
||||||
|
"@eslint/js": "^9.18.0",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-config-preact": "^1.3.0",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
"globals": "^15.14.0",
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
|
"typescript-eslint": "^8.20.0",
|
||||||
"serve": "^14.2.5",
|
"serve": "^14.2.5",
|
||||||
"tailwindcss": "^3.4.3",
|
"tailwindcss": "^3.4.3",
|
||||||
"terser": "^5.43.1",
|
"terser": "^5.43.1",
|
||||||
|
|||||||
+305
-1347
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,213 @@
|
|||||||
|
// @ts-check
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactNative from 'eslint-plugin-react-native'
|
||||||
|
import reactNativeA11y from 'eslint-plugin-react-native-a11y'
|
||||||
|
import simpleImportSort from 'eslint-plugin-simple-import-sort'
|
||||||
|
import importX from 'eslint-plugin-import-x'
|
||||||
|
import lingui from 'eslint-plugin-lingui'
|
||||||
|
import reactCompiler from 'eslint-plugin-react-compiler'
|
||||||
|
import bskyInternal from 'eslint-plugin-bsky-internal'
|
||||||
|
import globals from 'globals'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
// Global ignores
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
'**/__mocks__/*.ts',
|
||||||
|
'src/platform/polyfills.ts',
|
||||||
|
'src/third-party/**',
|
||||||
|
'ios/**',
|
||||||
|
'android/**',
|
||||||
|
'coverage/**',
|
||||||
|
'*.lock',
|
||||||
|
'.husky/**',
|
||||||
|
'patches/**',
|
||||||
|
'*.html',
|
||||||
|
'bskyweb/**',
|
||||||
|
'bskyembed/**',
|
||||||
|
'src/locale/locales/_build/**',
|
||||||
|
'src/locale/locales/**/*.js',
|
||||||
|
'*.e2e.ts',
|
||||||
|
'*.e2e.tsx',
|
||||||
|
'eslint.config.mjs',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
// Base JS recommended rules
|
||||||
|
js.configs.recommended,
|
||||||
|
|
||||||
|
// TypeScript rules
|
||||||
|
...tseslint.configs.recommended,
|
||||||
|
|
||||||
|
// Main configuration for all JS/TS/JSX/TSX files
|
||||||
|
{
|
||||||
|
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||||
|
plugins: {
|
||||||
|
react,
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-native': reactNative,
|
||||||
|
'react-native-a11y': reactNativeA11y,
|
||||||
|
'simple-import-sort': simpleImportSort,
|
||||||
|
'import-x': importX,
|
||||||
|
lingui,
|
||||||
|
'react-compiler': reactCompiler,
|
||||||
|
'bsky-internal': bskyInternal,
|
||||||
|
},
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
globals: {
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
...globals.es2021,
|
||||||
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
componentWrapperFunctions: ['observer'],
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// React rules
|
||||||
|
...react.configs.recommended.rules,
|
||||||
|
...react.configs['jsx-runtime'].rules,
|
||||||
|
'react/no-unescaped-entities': 'off',
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
|
||||||
|
// React Hooks rules
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
|
||||||
|
// React Native rules
|
||||||
|
'react-native/no-inline-styles': 'off',
|
||||||
|
|
||||||
|
// React Native A11y rules (iOS focused)
|
||||||
|
...reactNativeA11y.configs.ios.rules,
|
||||||
|
|
||||||
|
// Bsky internal rules
|
||||||
|
'bsky-internal/avoid-unwrapped-text': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
impliedTextComponents: [
|
||||||
|
'H1',
|
||||||
|
'H2',
|
||||||
|
'H3',
|
||||||
|
'H4',
|
||||||
|
'H5',
|
||||||
|
'H6',
|
||||||
|
'P',
|
||||||
|
'Admonition',
|
||||||
|
'Admonition.Admonition',
|
||||||
|
'Toast.Action',
|
||||||
|
'AgeAssuranceAdmonition',
|
||||||
|
'Span',
|
||||||
|
'StackedButton',
|
||||||
|
],
|
||||||
|
impliedTextProps: [],
|
||||||
|
suggestedTextWrappers: {
|
||||||
|
Button: 'ButtonText',
|
||||||
|
'ToggleButton.Button': 'ToggleButton.ButtonText',
|
||||||
|
'SegmentedControl.Item': 'SegmentedControl.ItemText',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'bsky-internal/use-exact-imports': 'error',
|
||||||
|
'bsky-internal/use-typed-gates': 'error',
|
||||||
|
'bsky-internal/use-prefixed-imports': 'error',
|
||||||
|
|
||||||
|
// Import sorting
|
||||||
|
'simple-import-sort/imports': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
groups: [
|
||||||
|
// Side effect imports.
|
||||||
|
['^\\u0000'],
|
||||||
|
// Node.js builtins prefixed with `node:`.
|
||||||
|
['^node:'],
|
||||||
|
// Packages.
|
||||||
|
// Things that start with a letter (or digit or underscore), or `@` followed by a letter.
|
||||||
|
// React/React Native prioritized, followed by expo
|
||||||
|
// Followed by all packages excluding unprefixed relative ones
|
||||||
|
[
|
||||||
|
'^(react\\/(.*)$)|^(react$)|^(react-native(.*)$)',
|
||||||
|
'^(expo(.*)$)|^(expo$)',
|
||||||
|
'^(?!(?:alf|components|lib|locale|logger|platform|screens|state|view)(?:$|\\/))@?\\w',
|
||||||
|
],
|
||||||
|
// Relative imports.
|
||||||
|
// Ideally, anything that starts with a dot or #
|
||||||
|
// due to unprefixed relative imports being used, we whitelist the relative paths we use
|
||||||
|
// (?:$|\\/) matches end of string or /
|
||||||
|
[
|
||||||
|
'^(?:#\\/)?(?:lib|state|logger|platform|locale)(?:$|\\/)',
|
||||||
|
'^(?:#\\/)?view(?:$|\\/)',
|
||||||
|
'^(?:#\\/)?screens(?:$|\\/)',
|
||||||
|
'^(?:#\\/)?alf(?:$|\\/)',
|
||||||
|
'^(?:#\\/)?components(?:$|\\/)',
|
||||||
|
'^#\\/',
|
||||||
|
'^\\.',
|
||||||
|
],
|
||||||
|
// anything else - hopefully we don't have any of these
|
||||||
|
['^'],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'simple-import-sort/exports': 'error',
|
||||||
|
|
||||||
|
// React Compiler
|
||||||
|
'react-compiler/react-compiler': 'warn',
|
||||||
|
|
||||||
|
// TypeScript rules
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
argsIgnorePattern: '^_',
|
||||||
|
varsIgnorePattern: '^_',
|
||||||
|
caughtErrors: 'none',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/consistent-type-imports': [
|
||||||
|
'warn',
|
||||||
|
{prefer: 'type-imports', fixStyle: 'inline-type-imports'},
|
||||||
|
],
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
// Maintain previous behavior - these are stricter in typescript-eslint v8
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/ban-ts-comment': 'off',
|
||||||
|
'@typescript-eslint/no-empty-object-type': 'off',
|
||||||
|
'@typescript-eslint/no-unused-expressions': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
||||||
|
'@typescript-eslint/no-wrapper-object-types': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||||
|
|
||||||
|
// Import rules
|
||||||
|
'import-x/consistent-type-specifier-style': ['warn', 'prefer-inline'],
|
||||||
|
|
||||||
|
// Turn off rules that weren't enforced in previous config
|
||||||
|
'no-empty-pattern': 'off',
|
||||||
|
'no-async-promise-executor': 'off',
|
||||||
|
'no-constant-binary-expression': 'warn',
|
||||||
|
'prefer-const': 'off',
|
||||||
|
'no-empty': 'off',
|
||||||
|
'no-unsafe-optional-chaining': 'off',
|
||||||
|
'no-prototype-builtins': 'off',
|
||||||
|
'no-var': 'off',
|
||||||
|
'prefer-rest-params': 'off',
|
||||||
|
'no-case-declarations': 'off',
|
||||||
|
'no-irregular-whitespace': 'off',
|
||||||
|
'no-useless-escape': 'off',
|
||||||
|
'no-sparse-arrays': 'off',
|
||||||
|
'no-fallthrough': 'off',
|
||||||
|
'no-control-regex': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -29,7 +29,34 @@ function getTagName(node) {
|
|||||||
return reversedIdentifiers.reverse().join('.')
|
return reversedIdentifiers.reverse().join('.')
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = function create(context) {
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description: 'Enforce text strings are wrapped in <Text> components',
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
impliedTextComponents: {
|
||||||
|
type: 'array',
|
||||||
|
items: {type: 'string'},
|
||||||
|
},
|
||||||
|
impliedTextProps: {
|
||||||
|
type: 'array',
|
||||||
|
items: {type: 'string'},
|
||||||
|
},
|
||||||
|
suggestedTextWrappers: {
|
||||||
|
type: 'object',
|
||||||
|
additionalProperties: {type: 'string'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalProperties: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
const options = context.options[0] || {}
|
const options = context.options[0] || {}
|
||||||
const impliedTextProps = options.impliedTextProps ?? []
|
const impliedTextProps = options.impliedTextProps ?? []
|
||||||
const impliedTextComponents = options.impliedTextComponents ?? []
|
const impliedTextComponents = options.impliedTextComponents ?? []
|
||||||
@@ -297,7 +324,7 @@ exports.create = function create(context) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ReturnStatement(node) {
|
ReturnStatement(node) {
|
||||||
let fnScope = context.getScope()
|
let fnScope = context.sourceCode.getScope(node)
|
||||||
while (fnScope && fnScope.type !== 'function') {
|
while (fnScope && fnScope.type !== 'function') {
|
||||||
fnScope = fnScope.upper
|
fnScope = fnScope.upper
|
||||||
}
|
}
|
||||||
@@ -328,4 +355,5 @@ exports.create = function create(context) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,6 +1,10 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
module.exports = {
|
const plugin = {
|
||||||
|
meta: {
|
||||||
|
name: 'eslint-plugin-bsky-internal',
|
||||||
|
version: '1.0.0',
|
||||||
|
},
|
||||||
rules: {
|
rules: {
|
||||||
'avoid-unwrapped-text': require('./avoid-unwrapped-text'),
|
'avoid-unwrapped-text': require('./avoid-unwrapped-text'),
|
||||||
'use-exact-imports': require('./use-exact-imports'),
|
'use-exact-imports': require('./use-exact-imports'),
|
||||||
@@ -8,3 +12,5 @@ module.exports = {
|
|||||||
'use-prefixed-imports': require('./use-prefixed-imports'),
|
'use-prefixed-imports': require('./use-prefixed-imports'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = plugin
|
||||||
|
|||||||
@@ -3,7 +3,15 @@ const BANNED_IMPORTS = [
|
|||||||
'@fortawesome/free-solid-svg-icons',
|
'@fortawesome/free-solid-svg-icons',
|
||||||
]
|
]
|
||||||
|
|
||||||
exports.create = function create(context) {
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
type: 'suggestion',
|
||||||
|
docs: {
|
||||||
|
description: 'Prevent importing entire icon packages',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
return {
|
return {
|
||||||
ImportDeclaration(node) {
|
ImportDeclaration(node) {
|
||||||
const source = node.source
|
const source = node.source
|
||||||
@@ -19,4 +27,5 @@ exports.create = function create(context) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ const BANNED_IMPORT_PREFIXES = [
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
meta: {
|
meta: {
|
||||||
type: 'suggestion',
|
type: 'suggestion',
|
||||||
|
docs: {
|
||||||
|
description: 'Enforce using prefixed imports for internal paths',
|
||||||
|
},
|
||||||
fixable: 'code',
|
fixable: 'code',
|
||||||
|
schema: [],
|
||||||
},
|
},
|
||||||
create(context) {
|
create(context) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
exports.create = function create(context) {
|
module.exports = {
|
||||||
|
meta: {
|
||||||
|
type: 'suggestion',
|
||||||
|
docs: {
|
||||||
|
description:
|
||||||
|
'Enforce using internal statsig wrapper instead of npm package',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
return {
|
return {
|
||||||
ImportSpecifier(node) {
|
ImportSpecifier(node) {
|
||||||
if (
|
if (
|
||||||
@@ -28,4 +37,5 @@ exports.create = function create(context) {
|
|||||||
// TODO: Verify gate() call results aren't stored in variables.
|
// TODO: Verify gate() call results aren't stored in variables.
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-10
@@ -41,7 +41,7 @@
|
|||||||
"test-watch": "NODE_ENV=test jest --watchAll",
|
"test-watch": "NODE_ENV=test jest --watchAll",
|
||||||
"test-ci": "NODE_ENV=test jest --ci --forceExit --reporters=default --reporters=jest-junit",
|
"test-ci": "NODE_ENV=test jest --ci --forceExit --reporters=default --reporters=jest-junit",
|
||||||
"test-coverage": "NODE_ENV=test jest --coverage",
|
"test-coverage": "NODE_ENV=test jest --coverage",
|
||||||
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx src",
|
"lint": "eslint --cache src",
|
||||||
"lint-native": "swiftlint ./modules && ktlint ./modules",
|
"lint-native": "swiftlint ./modules && ktlint ./modules",
|
||||||
"lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules",
|
"lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules",
|
||||||
"typecheck": "tsc --project ./tsconfig.check.json",
|
"typecheck": "tsc --project ./tsconfig.check.json",
|
||||||
@@ -248,22 +248,24 @@
|
|||||||
"@types/psl": "^1.1.1",
|
"@types/psl": "^1.1.1",
|
||||||
"@types/react": "^19.1.12",
|
"@types/react": "^19.1.12",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.1.9",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@typescript-eslint/parser": "^7.18.0",
|
"typescript-eslint": "^8.20.0",
|
||||||
"babel-jest": "^29.7.0",
|
"babel-jest": "^29.7.0",
|
||||||
"babel-plugin-macros": "^3.1.0",
|
"babel-plugin-macros": "^3.1.0",
|
||||||
"babel-plugin-module-resolver": "^5.0.2",
|
"babel-plugin-module-resolver": "^5.0.2",
|
||||||
"babel-plugin-react-compiler": "^19.1.0-rc.3",
|
"babel-plugin-react-compiler": "^19.1.0-rc.3",
|
||||||
"babel-preset-expo": "~54.0.0",
|
"babel-preset-expo": "~54.0.0",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-plugin-bsky-internal": "link:./eslint",
|
"eslint-plugin-bsky-internal": "link:./eslint",
|
||||||
"eslint-plugin-ft-flow": "^2.0.3",
|
"eslint-plugin-import-x": "^4.6.1",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-lingui": "^0.3.0",
|
||||||
"eslint-plugin-lingui": "^0.2.0",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
|
||||||
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
"eslint-plugin-react-compiler": "^19.1.0-rc.2",
|
||||||
"eslint-plugin-react-native-a11y": "^3.3.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
"eslint-plugin-simple-import-sort": "^12.0.0",
|
"eslint-plugin-react-native": "^5.0.0",
|
||||||
|
"eslint-plugin-react-native-a11y": "^3.4.1",
|
||||||
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
|
"globals": "^15.14.0",
|
||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"is-ci": "^3.0.1",
|
"is-ci": "^3.0.1",
|
||||||
|
|||||||
@@ -215,18 +215,18 @@ export function Controls({
|
|||||||
|
|
||||||
const seekLeft = useCallback(() => {
|
const seekLeft = useCallback(() => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const currentTime = videoRef.current.currentTime
|
const currentTime = videoRef.current.currentTime
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const duration = videoRef.current.duration || 0
|
const duration = videoRef.current.duration || 0
|
||||||
onSeek(clamp(currentTime - 5, 0, duration))
|
onSeek(clamp(currentTime - 5, 0, duration))
|
||||||
}, [onSeek, videoRef])
|
}, [onSeek, videoRef])
|
||||||
|
|
||||||
const seekRight = useCallback(() => {
|
const seekRight = useCallback(() => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const currentTime = videoRef.current.currentTime
|
const currentTime = videoRef.current.currentTime
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
const duration = videoRef.current.duration || 0
|
const duration = videoRef.current.duration || 0
|
||||||
onSeek(clamp(currentTime + 5, 0, duration))
|
onSeek(clamp(currentTime + 5, 0, duration))
|
||||||
}, [onSeek, videoRef])
|
}, [onSeek, videoRef])
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import {
|
|||||||
getInfoAsync,
|
getInfoAsync,
|
||||||
readDirectoryAsync,
|
readDirectoryAsync,
|
||||||
} from 'expo-file-system/legacy'
|
} from 'expo-file-system/legacy'
|
||||||
|
import {type ImagePickerResult} from 'expo-image-picker'
|
||||||
import ExpoImageCropTool, {
|
import ExpoImageCropTool, {
|
||||||
type OpenCropperOptions,
|
type OpenCropperOptions,
|
||||||
} from '@bsky.app/expo-image-crop-tool'
|
} from '@bsky.app/expo-image-crop-tool'
|
||||||
|
|
||||||
import {compressIfNeeded} from './manip'
|
import {compressIfNeeded} from './manip'
|
||||||
import {type PickerImage} from './picker.shared'
|
import {type PickerImage} from './picker.shared'
|
||||||
import {ImagePickerResult} from 'expo-image-picker'
|
|
||||||
|
|
||||||
async function getFile() {
|
async function getFile() {
|
||||||
const imagesDir = documentDirectory!
|
const imagesDir = documentDirectory!
|
||||||
|
|||||||
@@ -138,11 +138,10 @@ function ChatListItemReady({
|
|||||||
|
|
||||||
const {lastMessage, lastMessageSentAt, latestReportableMessage} =
|
const {lastMessage, lastMessageSentAt, latestReportableMessage} =
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
let lastMessage = _(msg`No messages yet`)
|
let lastMessage = _(msg`No messages yet`)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
let lastMessageSentAt: string | null = null
|
let lastMessageSentAt: string | null = null
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined
|
let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined
|
||||||
|
|
||||||
if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
|
if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) {
|
||||||
|
|||||||
@@ -64,7 +64,6 @@ export function PostThread({uri}: {uri: string}) {
|
|||||||
*/
|
*/
|
||||||
const thread = usePostThread({anchor: uri})
|
const thread = usePostThread({anchor: uri})
|
||||||
const {anchor, hasParents} = useMemo(() => {
|
const {anchor, hasParents} = useMemo(() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
let hasParents = false
|
let hasParents = false
|
||||||
for (const item of thread.data.items) {
|
for (const item of thread.data.items) {
|
||||||
if (item.type === 'threadPost' && item.depth === 0) {
|
if (item.type === 'threadPost' && item.depth === 0) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable no-labels */
|
|
||||||
import {AppBskyUnspeccedDefs, type ModerationOpts} from '@atproto/api'
|
import {AppBskyUnspeccedDefs, type ModerationOpts} from '@atproto/api'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -437,7 +437,6 @@ let PostFeed = ({
|
|||||||
for (const page of data.pages) {
|
for (const page of data.pages) {
|
||||||
for (const slice of page.slices) {
|
for (const slice of page.slices) {
|
||||||
const item = slice.items.find(
|
const item = slice.items.find(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
||||||
item => item.uri === slice.feedPostUri,
|
item => item.uri === slice.feedPostUri,
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {LogBox, Pressable, View, TextInput} from 'react-native'
|
import {LogBox, Pressable, TextInput, View} from 'react-native'
|
||||||
import {useQueryClient} from '@tanstack/react-query'
|
import {useQueryClient} from '@tanstack/react-query'
|
||||||
|
|
||||||
import {BLUESKY_PROXY_HEADER} from '#/lib/constants'
|
import {BLUESKY_PROXY_HEADER} from '#/lib/constants'
|
||||||
import {useSessionApi, useAgent} from '#/state/session'
|
import {useAgent, useSessionApi} from '#/state/session'
|
||||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||||
import {useOnboardingDispatch} from '#/state/shell/onboarding'
|
import {useOnboardingDispatch} from '#/state/shell/onboarding'
|
||||||
import {navigate} from '../../../Navigation'
|
import {navigate} from '../../../Navigation'
|
||||||
@@ -50,6 +50,8 @@ export function TestCtrls() {
|
|||||||
return (
|
return (
|
||||||
<View style={{position: 'absolute', top: 100, right: 0, zIndex: 100}}>
|
<View style={{position: 'absolute', top: 100, right: 0, zIndex: 100}}>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
accessibilityLabel="Text input field"
|
||||||
|
accessibilityHint="Enter proxy header"
|
||||||
testID="e2eProxyHeaderInput"
|
testID="e2eProxyHeaderInput"
|
||||||
onChangeText={val => setProxyHeader(val as any)}
|
onChangeText={val => setProxyHeader(val as any)}
|
||||||
onSubmitEditing={() => {
|
onSubmitEditing={() => {
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import {type AlertButton, type AlertStatic} from 'react-native'
|
|||||||
class WebAlert implements Pick<AlertStatic, 'alert'> {
|
class WebAlert implements Pick<AlertStatic, 'alert'> {
|
||||||
public alert(title: string, message?: string, buttons?: AlertButton[]): void {
|
public alert(title: string, message?: string, buttons?: AlertButton[]): void {
|
||||||
if (buttons === undefined || buttons.length === 0) {
|
if (buttons === undefined || buttons.length === 0) {
|
||||||
// eslint-disable-next-line no-alert
|
|
||||||
window.alert([title, message].filter(Boolean).join('\n'))
|
window.alert([title, message].filter(Boolean).join('\n'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-alert
|
|
||||||
const result = window.confirm([title, message].filter(Boolean).join('\n'))
|
const result = window.confirm([title, message].filter(Boolean).join('\n'))
|
||||||
|
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
|
|||||||
Reference in New Issue
Block a user