Build with Vite (#308)

This commit is contained in:
Luyu Cheng 2023-02-09 15:53:53 +08:00 committed by GitHub
parent 11e338c873
commit b6b2becbda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2370 additions and 9255 deletions

View File

@ -1,7 +0,0 @@
module.exports = {
style: {
postcss: {
plugins: [require('tailwindcss'), require('autoprefixer')],
},
},
}

2
.env
View File

@ -1 +1 @@
GENERATE_SOURCEMAP = false
GENERATE_SOURCEMAP=false

View File

@ -5,7 +5,7 @@
FROM node:14
MAINTAINER sevenyoungairye<lel.ng.top@gmail.com>
LABEL maintainer="sevenyoungairye <lel.ng.top@gmail.com>"
WORKDIR /app/qwerty-learner

View File

@ -2,7 +2,7 @@ version: "3"
services:
qwertylearner:
image: "node:14"
image: "node:16"
user: "root"
working_dir: "/app/qwerty-learner"
ports:

View File

@ -64,21 +64,12 @@
name="description"
content="为键盘工作者设计的单词记忆与英语肌肉记忆锻炼软件 / Words learning and English muscle memory training software designed for keyboard workers"
/>
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<link rel="manifest" href="/manifest.json" />
<title>Qwerty Learner</title>
<script>
// Dark mode init
@ -91,17 +82,11 @@
</script>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<noscript>
<div>You need to enable JavaScript to run QWERTY Learner.</div>
<div>你需要启用 JavaScript 来运行 QWERTY Learner。</div>
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
<script type="module" src="/src/index.tsx"></script>
</body>
</html>

View File

@ -4,7 +4,6 @@
"private": true,
"homepage": ".",
"dependencies": {
"@craco/craco": "^6.0.0",
"@floating-ui/react": "^0.19.1",
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/free-brands-svg-icons": "^6.1.1",
@ -19,28 +18,27 @@
"@types/howler": "^2.2.3",
"@types/jest": "^26.0.15",
"@types/lodash": "^4.14.168",
"@types/node": "^12.0.0",
"@types/node": "16",
"@types/react": "^16.9.53",
"@types/react-dom": "^16.9.8",
"autoprefixer": "^9",
"autoprefixer": "^10.4.13",
"classnames": "^2.3.2",
"eslint": "^7.18.0",
"eslint-config-prettier": "^7.2.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"howler": "^2.2.3",
"husky": "^4.3.8",
"lint-staged": "^10.5.3",
"lodash": "^4.17.20",
"postcss": "^7",
"postcss": "^8.4.21",
"prettier": "^2.2.1",
"react": "^17.0.1",
"react-app-polyfill": "^2.0.0",
"react-dom": "^17.0.1",
"react-hotkeys-hook": "^3.0.3",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.1",
"react-timer-hook": "^2.0.7",
"react-use": "^17.1.0",
"source-map-explorer": "^2.5.2",
@ -50,11 +48,11 @@
"web-vitals": "^0.2.4"
},
"scripts": {
"start": "craco start",
"build": "cross-env CI=false craco build",
"test": "craco test",
"dev": "vite",
"start": "vite",
"build": "cross-env CI=false vite build",
"test": "echo \"No tests\"",
"lint": "eslint .",
"eject": "react-scripts eject",
"analyze": "source-map-explorer 'build/static/js/*.js'"
},
"eslintConfig": {
@ -107,7 +105,11 @@
},
"devDependencies": {
"@types/react-router-dom": "^5.1.7",
"@vitejs/plugin-react": "^3.1.0",
"cross-env": "^7.0.3",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
"eslint-config-react-app": "^7.0.1",
"tailwindcss": "^3.2.6",
"vite": "^4.1.1",
"vite-plugin-svgr": "^2.4.0"
}
}

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -1,8 +1,8 @@
import React, { useState } from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import InfoPanel from 'components/InfoPanel'
import alipay from 'assets/alipay.png'
import vscLogo from 'assets/vsc-logo.svg'
import InfoPanel from '@/components/InfoPanel'
import alipay from '@/assets/alipay.png'
import vscLogo from '@/assets/vsc-logo.svg'
const Footer: React.FC = () => {
const [showModal, setShowModal] = useState<boolean>(false)

View File

@ -1,6 +1,6 @@
import React from 'react'
import { NavLink } from 'react-router-dom'
import { ReactComponent as Logo } from 'assets/logo.svg'
import { ReactComponent as Logo } from '@/assets/logo.svg'
const Header: React.FC = ({ children }) => {
return (

View File

@ -2,7 +2,7 @@ import React, { MouseEvent } from 'react'
import { Transition } from '@headlessui/react'
import { useHotkeys } from 'react-hotkeys-hook'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import Tooltip from 'components/Tooltip'
import Tooltip from '@/components/Tooltip'
type ModalsProps = {
state: boolean

View File

@ -1,6 +1,6 @@
import { Transition } from '@headlessui/react'
import Tooltip from 'components/Tooltip'
import { useWordList } from 'pages/Typing/hooks/useWordList'
import Tooltip from '@/components/Tooltip'
import { useWordList } from '@/pages/Typing/hooks/useWordList'
import { useMemo } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import ConclusionBar from './ConclusionBar'

View File

@ -1,4 +1,4 @@
import { ResultSpeedInfo } from 'components/ResultScreen'
import { ResultSpeedInfo } from '@/components/ResultScreen'
import React, { useEffect } from 'react'
import { useStopwatch } from 'react-timer-hook'
import InfoBox from './InfoBox'

View File

@ -1,9 +1,9 @@
import React, { useEffect, useState, useCallback, useLayoutEffect } from 'react'
import Letter, { LetterState } from './Letter'
import { isLegal, isChineseSymbol } from '../../utils/utils'
import useSounds from 'hooks/useSounds'
import useSounds from '@/hooks/useSounds'
import style from './index.module.css'
import WordSound from 'components/WordSound'
import WordSound from '@/components/WordSound'
import { useAppState } from '../../store/AppState'
const EXPLICIT_SPACE = '␣'

View File

@ -1,6 +1,6 @@
import { SoundIcon, SoundIconProps } from 'components/SoundIcon'
import Tooltip from 'components/Tooltip'
import usePronunciationSound from 'hooks/usePronunciation'
import { SoundIcon, SoundIconProps } from '@/components/SoundIcon'
import Tooltip from '@/components/Tooltip'
import usePronunciationSound from '@/hooks/usePronunciation'
import React, { useEffect, useCallback } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'

View File

@ -1,6 +1,6 @@
import { Howl } from 'howler'
import { useEffect, useState } from 'react'
import { PronunciationType, useAppState } from 'store/AppState'
import { PronunciationType, useAppState } from '@/store/AppState'
import useSound from 'use-sound'
import { HookOptions } from 'use-sound/dist/types'
import { addHowlListener } from '../utils/utils'

View File

@ -1,9 +1,9 @@
import useSound from 'use-sound'
import clickSoundFileUrl from 'assets/click.wav'
import beepSoundFileUrl from 'assets/beep.wav'
import hintSoundFileUrl from 'assets/hint.wav'
import clickSoundFileUrl from '@/assets/click.wav'
import beepSoundFileUrl from '@/assets/beep.wav'
import hintSoundFileUrl from '@/assets/hint.wav'
import { noop } from 'lodash'
import { useAppState } from 'store/AppState'
import { useAppState } from '@/store/AppState'
export type PlayFunction = ReturnType<typeof useSound>[0]

View File

@ -50,33 +50,11 @@ body,
background: transparent;
}
.rounded-xl {
border-radius: 1rem;
}
.rounded-b-xl {
border-bottom-right-radius: 1rem;
border-bottom-left-radius: 1rem;
}
.rounded-3xl {
border-radius: 1.75rem;
}
.bg-indigo-50 {
--tw-bg-opacity: 1;
background-color: rgba(247, 245, 255, var(--tw-bg-opacity));
}
.rounded-full {
border-radius: 100%;
}
@layer components {
.word-chip {
@apply border-indigo-400 dark:border-blue-gray-800 border-solid border-2
@apply border-indigo-400 dark:border-slate-800 border-solid border-2
rounded-md bg-white
dark:bg-blue-gray-700 hover:bg-indigo-100 dark:hover:bg-blue-gray-600
dark:bg-slate-700 hover:bg-indigo-100 dark:hover:bg-slate-600
w-auto h-10 md:h-12 px-2 md:px-5 py-0.5 md:py-1 flex flex-row
cursor-pointer transition-colors duration-100
items-center justify-center;

View File

@ -4,7 +4,7 @@ import './index.css'
import './icon'
import reportWebVitals from './reportWebVitals'
import 'react-app-polyfill/stable'
import { AppStateProvider } from 'store/AppState'
import { AppStateProvider } from '@/store/AppState'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import GalleryPage from './pages/Gallery'
import TypingPage from './pages/Typing'
@ -12,7 +12,7 @@ import TypingPage from './pages/Typing'
ReactDOM.render(
<React.StrictMode>
<AppStateProvider>
<Router basename={process.env.REACT_APP_DEPLOY_ENV === 'travis' ? '/qwerty-learner' : ''}>
<Router basename={REACT_APP_DEPLOY_ENV === 'travis' ? '/qwerty-learner' : ''}>
<Switch>
<Route path="/gallery">
<GalleryPage />

View File

@ -1,6 +1,6 @@
import React from 'react'
import { range } from 'lodash'
import { useSelectedChapter } from 'store/AppState'
import { useSelectedChapter } from '@/store/AppState'
import ChapterButton from './ChapterButton'
const ChapterGroup: React.FC<ChapterGroupProps> = ({ totalWords }) => {

View File

@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import React, { useEffect, useRef } from 'react'
import { Dictionary } from 'resources/dictionary'
import { useSelectedDictionary, useSetDictionary } from 'store/AppState'
import { Dictionary } from '@/resources/dictionary'
import { useSelectedDictionary, useSetDictionary } from '@/store/AppState'
const DictionaryCard: React.FC<DictionaryCardProps> = ({ dictionary }) => {
const buttonRef = useRef<HTMLButtonElement>(null)

View File

@ -1,11 +1,11 @@
import React from 'react'
import { Dictionary } from 'resources/dictionary'
import { Dictionary } from '@/resources/dictionary'
import DictionaryCard from './DictionaryCard'
const DictionaryGroup: React.FC<DictionaryGroupProps> = ({ title, dictionaries }) => {
return (
<section className="mb-4 mr-1">
<h3 className="sticky top-0 pb-2 text-sm font-bold text-gray-600 z-50 bg-indigo-50 dark:bg-blue-gray-800 dark:text-white dark:text-opacity-60">
<h3 className="sticky top-0 pb-2 text-sm font-bold text-gray-600 z-50 bg-indigo-50 dark:bg-slate-800 dark:text-white dark:text-opacity-60">
{title}
</h3>
<main className="grid gap-4 rounded-md sm:grid-cols-1 md:grid-cols-2">

View File

@ -1,13 +1,13 @@
import React from 'react'
import Layout from 'components/Layout'
import Layout from '@/components/Layout'
import DictionaryGroup from './DictionaryGroup'
import Header from 'components/Header'
import Header from '@/components/Header'
import { NavLink, useHistory } from 'react-router-dom'
import { useDictionaries, useSelectedDictionary } from 'store/AppState'
import { useDictionaries, useSelectedDictionary } from '@/store/AppState'
import { groupBy } from 'lodash'
import { useHotkeys } from 'react-hotkeys-hook'
import ChapterGroup from './ChapterGroup'
import Tooltip from 'components/Tooltip'
import Tooltip from '@/components/Tooltip'
const GalleryPage: React.FC = () => {
const dictionaries = useDictionaries()
@ -28,7 +28,7 @@ const GalleryPage: React.FC = () => {
</Tooltip>
</Header>
<div className="mt-auto mb-auto flex w-auto space-x-4 overflow-y-auto">
<div className="bg-indigo-50 dark:bg-blue-gray-800 rounded-lg p-6 space-y-2 overflow-y-auto flex flex-col">
<div className="bg-indigo-50 dark:bg-slate-800 rounded-lg p-6 space-y-2 overflow-y-auto flex flex-col">
<h2 className="sticky top-0 mb-2 font-bold text-lg text-gray-700 dark:text-white dark:text-opacity-70 text-shadow z-10">
</h2>
@ -38,7 +38,7 @@ const GalleryPage: React.FC = () => {
))}
</div>
</div>
<div className="p-6 overflow-y-auto bg-indigo-50 dark:bg-blue-gray-800 rounded-lg flex flex-col">
<div className="p-6 overflow-y-auto bg-indigo-50 dark:bg-slate-800 rounded-lg flex flex-col">
<h2 className="sticky top-0 mb-4 font-bold text-lg text-gray-700 dark:text-white dark:text-opacity-70 text-shadow z-10">
</h2>

View File

@ -2,7 +2,7 @@ import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { SwitcherStateType, SwitcherDispatchType } from '../hooks/useSwitcherState'
import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from 'components/Tooltip'
import Tooltip from '@/components/Tooltip'
export type SwitcherPropsType = {
state: SwitcherStateType

View File

@ -1,4 +1,4 @@
import { useSetPronunciationState, PronunciationType } from 'store/AppState'
import { useSetPronunciationState, PronunciationType } from '@/store/AppState'
export type SwitcherDispatchType = (newStatus?: string) => void

View File

@ -1,5 +1,5 @@
import { useState } from 'react'
import { useSetSoundState, useDarkMode, useRandomState, useSetLoopState, usePhoneticState, useSetSoundLoopState } from 'store/AppState'
import { useSetSoundState, useDarkMode, useRandomState, useSetLoopState, usePhoneticState, useSetSoundLoopState } from '@/store/AppState'
export type SwitcherStateType = {
phonetic: boolean

View File

@ -1,7 +1,7 @@
import cet4 from 'assets/CET4_T.json'
import cet4 from '@/assets/CET4_T.json'
import { shuffle } from 'lodash'
import { useMemo } from 'react'
import { useSelectedChapter, useSelectedDictionary, useRandomState } from 'store/AppState'
import { useSelectedChapter, useSelectedDictionary, useRandomState } from '@/store/AppState'
import useSWR from 'swr'
export type Word = {

View File

@ -1,13 +1,13 @@
import React, { useCallback, useEffect, useState } from 'react'
import Header from 'components/Header'
import Main from 'components/Main'
import Word from 'components/Word'
import Translation from 'components/Translation'
import Speed from 'components/Speed'
import Loading from 'components/Loading'
import Phonetic from 'components/Phonetic'
import Header from '@/components/Header'
import Main from '@/components/Main'
import Word from '@/components/Word'
import Translation from '@/components/Translation'
import Speed from '@/components/Speed'
import Loading from '@/components/Loading'
import Phonetic from '@/components/Phonetic'
import PronunciationSwitcher from './PronunciationSwitcher'
import { isLegal, IsDesktop } from 'utils/utils'
import { isLegal, IsDesktop } from '@/utils/utils'
import { useHotkeys } from 'react-hotkeys-hook'
import useSwitcherState from './hooks/useSwitcherState'
import Switcher from './Switcher'
@ -15,10 +15,10 @@ import { useWordList } from './hooks/useWordList'
import Layout from '../../components/Layout'
import { NavLink } from 'react-router-dom'
import usePronunciation from './hooks/usePronunciation'
import Tooltip from 'components/Tooltip'
import { useRandomState } from 'store/AppState'
import Tooltip from '@/components/Tooltip'
import { useRandomState } from '@/store/AppState'
import Progress from './Progress'
import ResultScreen, { IncorrectInfo, ResultSpeedInfo } from 'components/ResultScreen'
import ResultScreen, { IncorrectInfo, ResultSpeedInfo } from '@/components/ResultScreen'
const App: React.FC = () => {
const [order, setOrder] = useState<number>(0)

View File

@ -1 +0,0 @@
/// <reference types="react-scripts" />

View File

@ -1,7 +1,7 @@
import { omit } from 'lodash'
import React, { useCallback, useContext } from 'react'
import { useLocalStorage } from 'react-use'
import { dictionaries, Dictionary } from 'resources/dictionary'
import { dictionaries, Dictionary } from '@/resources/dictionary'
export type PronunciationType = 'us' | 'uk' | 'jap' | false

2
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference types="vite/client" />
declare const REACT_APP_DEPLOY_ENV: string

View File

@ -1,24 +1,19 @@
const colors = require('tailwindcss/colors')
module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
content: ['./src/**/*.{js,jsx,ts,tsx}', './index.html'],
darkMode: 'class',
theme: {
extend: {
colors: {
'blue-gray': colors.blueGray,
},
transitionDuration: {
0: '0ms',
},
padding: {
0.8: '0.2rem',
},
},
borderRadius: {
large: '0.75rem',
lg: '0.5rem',
md: '0.375rem',
borderRadius: {
large: '0.75rem',
lg: '0.5rem',
md: '0.375rem',
},
},
},
variants: {

View File

@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
@ -15,7 +19,18 @@
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src"
"baseUrl": "src",
"paths": {
"@/assets/*": ["assets/*"],
"@/components/*": ["components/*"],
"@/hooks/*": ["hooks/*"],
"@/pages/*": ["pages/*"],
"@/resources/*": ["resources/*"],
"@/store/*": ["store/*"],
"@/utils/*": ["utils/*"]
},
},
"include": ["src"]
"include": [
"src"
]
}

9
tsconfig.node.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

20
vite.config.ts Normal file
View File

@ -0,0 +1,20 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'node:path'
import svgr from 'vite-plugin-svgr'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), svgr()],
build: {
outDir: 'build',
},
define: {
REACT_APP_DEPLOY_ENV: JSON.stringify(process.env.REACT_APP_DEPLOY_ENV),
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
})

11359
yarn.lock

File diff suppressed because it is too large Load Diff