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

Use Eslint form app.zetkin #10

Merged
merged 13 commits into from
Nov 30, 2023
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
27 changes: 27 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Lint
amerharb marked this conversation as resolved.
Show resolved Hide resolved

on:
push:
branches: [ "*" ]
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./webapp
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18.x
cache: 'yarn'
cache-dependency-path: ./webapp/yarn.lock
- run: yarn
- run: yarn lint
98 changes: 98 additions & 0 deletions webapp/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
module.exports = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use the same eslint config as the app.zetkin.org repo?

Copy link
Collaborator Author

@amerharb amerharb Nov 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that at the start but it doesn't work, something about prettier, it fail to read from prettier config....etc
so i just did the simple one
maybe in the future we should make one repo for common config and lint that all repos imports

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now it has been included, how ever few eslint disable has been added to be fixed later

root: true,
env: {
node: true,
es6: true,
},
parserOptions: { ecmaVersion: 8 }, // to enable features such as async/await
// We don't want to lint generated files nor node_modules, but we want to lint .prettierrc.json (ignored by default by eslint)
ignorePatterns: ['node_modules/*', '.next/*', '.out/*', '!.prettierrc.json'],
extends: [
'eslint:recommended',
'next',
'prettier',
],
settings: { react: { version: 'detect' } },
overrides: [
// This configuration will apply only to TypeScript files
{
files: ['**/*.ts', '**/*.tsx', 'src/**/*.js'],
parser: '@typescript-eslint/parser',
env: {
browser: true,
node: true,
es6: true,
},
extends: [
'plugin:@typescript-eslint/recommended', // TypeScript rules
'plugin:react-hooks/recommended', // React hooks rules
'plugin:jsx-a11y/recommended', // Accessibility rules
],
plugins: ['no-switch-statements'],
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/member-ordering': [
'error',
{
default: { memberTypes: 'never', order: 'alphabetically' },
interfaces: ['signature', 'method', 'constructor', 'field'],
},
],
curly: 'error',
'jsx-a11y/anchor-is-valid': 'off',
'no-console': 'error',
'no-switch-statements/no-switch': 'error',
'prefer-const': ['error', {}],
'react/jsx-handler-names': [
'error',
{
eventHandlerPrefix: 'on',
eventHandlerPropPrefix: 'on',
},
],
'react/jsx-no-target-blank': 'error',
'react/jsx-sort-props': [
'error',
{
ignoreCase: true,
reservedFirst: true,
},
],
'react/no-danger': 'error',
'react/no-deprecated': 'error',
'react/no-typos': 'error',
'react/no-unknown-property': 'error',
'react/no-unsafe': [
'error',
{
checkAliases: true,
},
],
'react/no-unused-prop-types': 'error',
'react/prefer-stateless-function': 'error',
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'react-hooks/exhaustive-deps': 'off',
'react/self-closing-comp': [
'error',
{
component: true,
html: true,
},
],
'sort-imports': [
'error',
{
ignoreCase: true,
allowSeparatedGroups: true,
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple'],
},
],
'sort-keys': 'error',
'sort-vars': 'error',
},
},
],
};
3 changes: 0 additions & 3 deletions webapp/.eslintrc.json

This file was deleted.

19 changes: 19 additions & 0 deletions webapp/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# auto-generated localizations
src/locale/**/*.yml

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
.nyc_output

# next.js
/.next/
/out/

# production
/build
1 change: 0 additions & 1 deletion webapp/.prettierrc

This file was deleted.

3 changes: 3 additions & 0 deletions webapp/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"singleQuote": true
}
18 changes: 15 additions & 3 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint:eslint": "eslint .",
"lint:eslint-fix": "eslint . --fix",
"lint": "next lint"
},
"dependencies": {
Expand All @@ -23,9 +25,19 @@
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "13.5.6",
"prettier": "^3.0.3",
"eslint": "^7.15.0",
"eslint-config-next": "^12.0.10",
"eslint-config-prettier": "^8.3.0",
"eslint-loader": "^4.0.2",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-next": "^0.0.0",
"eslint-plugin-no-switch-statements": "^1.0.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-yml": "^0.13.0",
"@typescript-eslint/eslint-plugin": "^6",
"@typescript-eslint/parser": "^6",
"prettier": "^2.5.1",
"typescript": "^5"
}
}
31 changes: 15 additions & 16 deletions webapp/src/app/[lang]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
"use client";
'use client';

import MessageForm from "@/components/MessageForm";
import { MessageData } from "@/utils/readTypedMessages";
import { Box, Button, Link, Typography } from "@mui/joy";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { MessageData } from '@/utils/readTypedMessages';
import MessageForm from '@/components/MessageForm';
import { Box, Button, Link, Typography } from '@mui/joy';
import { useEffect, useState } from 'react';

export default function Home({ params }: { params: { lang: string } }) {
const [messages, setMessages] = useState<MessageData[]>([]);
const [translations, setTranslations] = useState<Record<string, string>>({});
const [pullRequestUrl, setPullRequestUrl] = useState<string>("");
const [pullRequestUrl, setPullRequestUrl] = useState<string>('');

useEffect(() => {
async function loadMessages() {
const res = await fetch("/api/messages");
const res = await fetch('/api/messages');
const payload = await res.json();
setMessages(payload.data);
}
Expand All @@ -36,8 +35,8 @@ export default function Home({ params }: { params: { lang: string } }) {
<Typography level="h1">Messages</Typography>
<Button
onClick={async () => {
const res = await fetch(`/api/pull-request/`, {
method: "POST",
const res = await fetch('/api/pull-request/', {
method: 'POST',
});
const payload = await res.json();
const url = payload.pullRequestUrl;
Expand All @@ -57,23 +56,23 @@ export default function Home({ params }: { params: { lang: string } }) {
const res = await fetch(
`/api/translations/${params.lang}/${msg.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
text,
}),
headers: {
'Content-Type': 'application/json',
},
method: 'PUT',
},
);

const payload = await res.json();
await res.json();
setTranslations((cur) => ({
...cur,
[msg.id]: text,
}));
}}
translation={translations[msg.id] || ""}
translation={translations[msg.id] || ''}
/>
);
})}
Expand Down
29 changes: 15 additions & 14 deletions webapp/src/app/api/languages.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
import { envVarNotFound } from "@/utils/util";
import fs from "fs/promises";
import { parse } from "yaml";
import { simpleGit, SimpleGit, SimpleGitOptions } from "simple-git";
/* global globalThis */
import fs from 'fs/promises';
import { parse } from 'yaml';
import { envVarNotFound, logDebug } from '@/utils/util';
import { simpleGit, SimpleGit, SimpleGitOptions } from 'simple-git';

const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound("REPO_PATH");
const MAIN_BRANCH = "main";
const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH');
const MAIN_BRANCH = 'main';

export async function getLanguage(lang: string) {
let languages: Map<string, Record<string, unknown>>;
if (!globalThis.languages) {
console.debug("Initializing languages");
logDebug('Initializing languages');
const options: Partial<SimpleGitOptions> = {
baseDir: REPO_PATH,
binary: "git",
binary: 'git',
maxConcurrentProcesses: 1,
trimmed: false,
};
const git: SimpleGit = simpleGit(options);
console.debug("git checkout main pull...");
logDebug('git checkout main pull...');
await git.checkout(MAIN_BRANCH);
console.debug("git pull...");
logDebug('git pull...');
await git.pull();
console.debug("git done checkout main branch and pull");
logDebug('git done checkout main branch and pull');
languages = new Map<string, Record<string, unknown>>();
globalThis.languages = languages;
} else {
console.debug("read languages from globalThis");
logDebug('read languages from globalThis');
languages = globalThis.languages;
}

let translations: Record<string, unknown>;
if (!languages.has(lang)) {
console.debug("read languages from file");
logDebug('read languages from file');
const yamlPath = REPO_PATH + `/src/locale/${lang}.yml`;

const yamlBuf = await fs.readFile(yamlPath);
translations = parse(yamlBuf.toString()) as Record<string, unknown>;
languages.set(lang, translations);
} else {
console.debug("read languages from Memory");
logDebug('read languages from Memory');
translations = languages.get(lang) ?? throwLangNotFound(lang);
}

Expand Down
16 changes: 8 additions & 8 deletions webapp/src/app/api/messages/route.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { envVarNotFound } from "@/utils/util";
import fs from "fs/promises";
import path from "path";
import { NextResponse } from "next/server";
import { envVarNotFound } from '@/utils/util';
import fs from 'fs/promises';
import { NextResponse } from 'next/server';
import path from 'path';
import readTypedMessages, {
MessageData,
} from "@/utils/readTypedMessages";
} from '@/utils/readTypedMessages';

const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound("REPO_PATH");
const REPO_PATH = process.env.REPO_PATH ?? envVarNotFound('REPO_PATH');

export async function GET() {
const messages: MessageData[] = [];
for await (const item of getMessageFiles(REPO_PATH + "/src")) {
for await (const item of getMessageFiles(REPO_PATH + '/src')) {
messages.push(...readTypedMessages(item));
}

Expand All @@ -26,7 +26,7 @@ async function* getMessageFiles(dirPath: string): AsyncGenerator<string> {
const stats = await fs.stat(itemPath);
if (stats.isDirectory()) {
yield* getMessageFiles(itemPath);
} else if (itemPath.endsWith("messageIds.ts")) {
} else if (itemPath.endsWith('messageIds.ts')) {
yield itemPath;
}
}
Expand Down
Loading