Skip to content
Draft
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
6 changes: 6 additions & 0 deletions packages/react-core-linter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Ideas for rules

- T component static children
- string registration functions static args
- untranslated content warning (off by default)
- Branch must have branch prop, and plural must have n prop
82 changes: 82 additions & 0 deletions packages/react-core-linter/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"name": "@generaltranslation/react-core-linter",
"version": "0.0.0",
"description": "ESLint plugin for General Translation React Core integration",
"type": "module",
"main": "dist/index.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"files": [
"dist"
],
"keywords": [
"eslint",
"eslintplugin",
"eslint-plugin",
"react-core",
"translation",
"internationalization",
"i18n",
"jsx",
"react",
"typescript"
],
"author": "General Translation, Inc.",
"license": "FSL-1.1-ALv2",
"repository": {
"type": "git",
"url": "git+https://github.com/generaltranslation/gt.git",
"directory": "packages/react-core-linter"
},
"bugs": {
"url": "https://github.com/generaltranslation/gt/issues"
},
"homepage": "https://generaltranslation.com/",
"engines": {
"node": ">=18.0.0"
},
"scripts": {
"patch": "pnpm version patch",
"transpile": "tsc",
"build": "pnpm run transpile",
"build:clean": "sh ../../scripts/clean.sh && pnpm run build",
"build:release": "pnpm run build:clean",
"dev": "tsc --watch",
"release": "pnpm run build:clean && pnpm publish",
"release:alpha": "pnpm run build:clean && pnpm publish --tag alpha",
"release:beta": "pnpm run build:clean && pnpm publish --tag beta",
"release:latest": "pnpm run build:clean && pnpm publish --tag latest",
"lint": "eslint \"src/**/*.{js,ts,tsx}\" \"**/__tests__/**/*.{js,ts,tsx}\"",
"lint:fix": "eslint \"src/**/*.{js,ts,tsx}\" \"**/__tests__/**/*.{js,ts,tsx}\" --fix",
"test": "vitest run",
"test:watch": "vitest",
"prepublishOnly": "npm run build:clean"
},
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^8.0.0 || ^9.0.0"
},
"devDependencies": {
"@types/eslint": "^8.56.0",
"@types/estree": "^1.0.8",
"@types/estree-jsx": "^1.0.5",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"@typescript-eslint/rule-tester": "^8.50.1",
"eslint": "^9.0.0",
"typescript": "^5.0.0",
"vitest": "^2.0.0"
},
"dependencies": {
"@typescript-eslint/utils": "^8.50.1"
}
}
10 changes: 10 additions & 0 deletions packages/react-core-linter/src/configs/recommended.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Recommended ESLint configuration for React Core
*/

export const recommended = {
plugins: ['@generaltranslation/react-core-linter'],
rules: {
// Add recommended rules here as they are implemented
},
};

Check failure on line 10 in packages/react-core-linter/src/configs/recommended.ts

View workflow job for this annotation

GitHub Actions / tests

Insert `⏎`
35 changes: 35 additions & 0 deletions packages/react-core-linter/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* React Core ESLint Plugin
*
* Provides ESLint rules for General Translation React Core integration.
* This plugin helps ensure correct usage of React Core i18n components
* and translation patterns.
*/

import type { ESLint, Rule } from 'eslint';
import { staticJsx } from './rules/static-jsx/index.js';
import { staticString } from './rules/static-string/index.js';

const plugin: ESLint.Plugin = {
meta: {
name: '@generaltranslation/react-core-linter',
version: '0.0.0',
},
rules: {
'static-jsx': staticJsx as unknown as Rule.RuleModule,
'static-string': staticString as unknown as Rule.RuleModule,
},
configs: {
recommended: {
plugins: ['@generaltranslation/react-core-linter'],
rules: {
'@generaltranslation/react-core-linter/static-jsx': 'error',
'@generaltranslation/react-core-linter/static-string': 'error',
},
},
},
};

plugin

Check failure on line 33 in packages/react-core-linter/src/index.ts

View workflow job for this annotation

GitHub Actions / tests

Insert `;`

export default plugin;
47 changes: 47 additions & 0 deletions packages/react-core-linter/src/rules/static-jsx/ScopeStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
export type Scope =
| 'no-T' // No check
| 'T' // Check static
| 'Branch' // Check attrs
| 'Plural' // Check attrs
| 'Branching-Attribute'; // Handle JsxExpressionContainers different

/**
* Utility for tracking scope in JSX tree
*/
export class ScopeStack {
private stack: Scope[] = [];

constructor() {
this.stack.push('no-T');
}

push(scope: Scope) {
this.stack.push(scope);
}

pop() {
this.stack.pop();
}

inTranslatableContent(): boolean {
const scope = this.stack[this.stack.length - 1];
return scope === 'T';
}
inBranchingComponent(): boolean {
const scope = this.stack[this.stack.length - 1];
return scope === 'Branch' || scope === 'Plural';
}
inBranchingAttribute(): boolean {
return this.stack[this.stack.length - 1] === 'Branching-Attribute';
}
inBranchT(): boolean {
return (
this.stack.length >= 2 &&
this.stack[this.stack.length - 1] === 'T' &&
this.stack[this.stack.length - 2] === 'Branching-Attribute'
);
}
getScope() {
return this.stack[this.stack.length - 1];
}
}
Loading
Loading