Migrate from create react app to vite

This commit is contained in:
Przemek Więch 2025-02-05 17:05:34 +01:00
parent 99a217191b
commit b3f5ae1c25
22 changed files with 3751 additions and 21797 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
build dist
node_modules node_modules
src/react-app-env.d.ts src/react-app-env.d.ts
cypress/fixtures/example.json cypress/fixtures/example.json

View File

@ -96,7 +96,7 @@ cd topola-viewer
npm install npm install
npm run build npm run build
``` ```
Now, take the contents of the `build/` folder and host it on your own server. Now, take the contents of the `dist/` folder and host it on your own server.
### Use an existing package ### Use an existing package
@ -107,18 +107,18 @@ These are the exact files that are hosted on GitHub pages.
### Build for your own data only ### Build for your own data only
You can run Topola Viewer in a "single tree mode" that displays only the GEDCOM you specify. Specify the URL to a GEDCOM file in the `REACT_APP_STATIC_URL` environment variable when building and running the application. You can run Topola Viewer in a "single tree mode" that displays only the GEDCOM you specify. Specify the URL to a GEDCOM file in the `VITE_STATIC_URL` environment variable when building and running the application.
Run locally with the specified data URL: Run locally with the specified data URL:
``` ```
REACT_APP_STATIC_URL=https://example.org/sample.ged npm start VITE_STATIC_URL=https://example.org/sample.ged npm start
``` ```
Build with the specified data URL: Build with the specified data URL:
``` ```
REACT_APP_STATIC_URL=https://example.org/sample.ged npm run build VITE_STATIC_URL=https://example.org/sample.ged npm run build
``` ```
The `build/` folder will contain files that can be hosted on a Web server. The `dist/` folder will contain files that can be hosted on a Web server.
### Alternative build ### Alternative build

View File

@ -4,7 +4,7 @@ set -e
echo Copying files... echo Copying files...
lftp --env-password sftp://wiech13@apps.wikitree.com << EOF lftp --env-password sftp://wiech13@apps.wikitree.com << EOF
mkdir www/topola-viewer.new mkdir www/topola-viewer.new
mirror -R -p build/ www/topola-viewer.new/ mirror -R -p dist/ www/topola-viewer.new/
rm -r www/topola-viewer.old rm -r www/topola-viewer.old
mv www/topola-viewer www/topola-viewer.old mv www/topola-viewer www/topola-viewer.old
mv www/topola-viewer.new www/topola-viewer mv www/topola-viewer.new www/topola-viewer

View File

@ -19,5 +19,6 @@
You need to enable JavaScript to run this app. You need to enable JavaScript to run this app.
</noscript> </noscript>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body> </body>
</html> </html>

21
jest.config.ts Normal file
View File

@ -0,0 +1,21 @@
import type { Config } from '@jest/types';
const config: Config.InitialOptions = {
testEnvironment: "node",
transform: {
"^.+.tsx?$": ["ts-jest", {}],
},
moduleNameMapper: {
"d3-array": "<rootDir>/node_modules/d3-array/dist/d3-array.js",
"d3-color": "<rootDir>/node_modules/d3-color/dist/d3-color.js",
"d3-ease": "<rootDir>/node_modules/d3-ease/dist/d3-ease.js",
"d3-dispatch": "<rootDir>/node_modules/d3-dispatch/dist/d3-dispatch.js",
"d3-interpolate": "<rootDir>/node_modules/d3-interpolate/dist/d3-interpolate.js",
"d3-hierarchy": "<rootDir>/node_modules/d3-hierarchy/dist/d3-hierarchy.js",
"d3-selection": "<rootDir>/node_modules/d3-selection/dist/d3-selection.js",
"d3-timer": "<rootDir>/node_modules/d3-timer/dist/d3-timer.js",
"d3-transition": "<rootDir>/node_modules/d3-transition/dist/d3-transition.js",
},
};
export default config;

25350
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -45,6 +45,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@formatjs/fast-memoize": "^2.2.6", "@formatjs/fast-memoize": "^2.2.6",
"@formatjs/intl": "^2.10.15", "@formatjs/intl": "^2.10.15",
"@jest/globals": "^29.7.0",
"@types/adm-zip": "^0.5.0", "@types/adm-zip": "^0.5.0",
"@types/array.prototype.flatmap": "^1.2.2", "@types/array.prototype.flatmap": "^1.2.2",
"@types/d3-array": "^3.2.1", "@types/d3-array": "^3.2.1",
@ -63,30 +64,36 @@
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^4.19.0", "@typescript-eslint/eslint-plugin": "^4.19.0",
"@typescript-eslint/parser": "^4.19.0", "@typescript-eslint/parser": "^4.19.0",
"@vitejs/plugin-react": "^4.3.4",
"cypress": "^13.17.0", "cypress": "^13.17.0",
"gh-pages": "^6.3.0", "gh-pages": "^6.3.0",
"jest": "^29.7.0",
"jsdom": "^26.0.0",
"prettier": "^3.4.2", "prettier": "^3.4.2",
"react-scripts": "^5.0.1",
"run-script-os": "^1.1.6", "run-script-os": "^1.1.6",
"start-server-and-test": "^2.0.9", "start-server-and-test": "^2.0.9",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"tslint-config-prettier": "^1.18.0", "tslint-config-prettier": "^1.18.0",
"typescript": "^4.9.5" "typescript": "^5.7.3",
"vite": "^6.0.11",
"vite-tsconfig-paths": "^5.1.4"
}, },
"scripts": { "scripts": {
"start": "run-script-os", "start": "run-script-os",
"start:default": "GENERATE_SOURCEMAP=false REACT_APP_CHANGELOG=`cat CHANGELOG.md` REACT_APP_GIT_SHA=`git rev-parse --short HEAD` REACT_APP_GIT_TIME=`git log -1 --format=%ci` react-scripts start", "start:default": "GENERATE_SOURCEMAP=false VITE_CHANGELOG=`cat CHANGELOG.md` VITE_GIT_SHA=`git rev-parse --short HEAD` VITE_GIT_TIME=`git log -1 --format=%ci` vite",
"start:windows": "set \"GENERATE_SOURCEMAP=false\" && react-scripts start", "start:windows": "set \"GENERATE_SOURCEMAP=false\" && vite",
"build": "run-script-os", "build": "run-script-os",
"build:default": "GENERATE_SOURCEMAP=false REACT_APP_CHANGELOG=`cat CHANGELOG.md` REACT_APP_GIT_SHA=`git rev-parse --short HEAD` REACT_APP_GIT_TIME=`git log -1 --format=%ci` react-scripts build", "build:default": "GENERATE_SOURCEMAP=false VITE_CHANGELOG=`cat CHANGELOG.md` VITE_GIT_SHA=`git rev-parse --short HEAD` VITE_GIT_TIME=`git log -1 --format=%ci` tsc && vite build",
"build:windows": "set \"GENERATE_SOURCEMAP=false\" && react-scripts build", "build:windows": "set \"GENERATE_SOURCEMAP=false\" && tsc && vite build",
"test": "react-scripts test --env=jsdom", "test": "jest",
"prettier": "prettier --write src/**/*.{ts,tsx,json} --end-of-line lf && prettier --write src/*.{ts,tsx,json} --end-of-line lf", "prettier": "prettier --write src/**/*.{ts,tsx,json} --end-of-line lf && prettier --write src/*.{ts,tsx,json} --end-of-line lf",
"predeploy": "npm run build", "predeploy": "npm run build",
"deploy": "gh-pages -d build", "deploy": "gh-pages -d dist",
"predeploy-wikitree": "npm run build", "predeploy-wikitree": "npm run build",
"deploy-wikitree": "./deploy-wikitree.sh", "deploy-wikitree": "./deploy-wikitree.sh",
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:start-and-run": "BROWSER=none start-server-and-test start 3000 cy:run" "cy:start-and-run": "BROWSER=none start-server-and-test start localhost:3000 cy:run"
}, },
"homepage": ".", "homepage": ".",
"browserslist": [ "browserslist": [
@ -100,17 +107,5 @@
"@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "off",
"@typescript-eslint/no-use-before-define": "off" "@typescript-eslint/no-use-before-define": "off"
} }
},
"jest": {
"moduleNameMapper": {
"d3-array": "<rootDir>/node_modules/d3-array/dist/d3-array.js",
"d3-color": "<rootDir>/node_modules/d3-color/dist/d3-color.js",
"d3-ease": "<rootDir>/node_modules/d3-ease/dist/d3-ease.js",
"d3-hierarchy": "<rootDir>/node_modules/d3-hierarchy/dist/d3-hierarchy.js",
"d3-interpolate": "<rootDir>/node_modules/d3-interpolate/dist/d3-interpolate.js",
"d3-selection": "<rootDir>/node_modules/d3-selection/dist/d3-selection.js",
"d3-timer": "<rootDir>/node_modules/d3-timer/dist/d3-timer.js",
"d3-transition": "<rootDir>/node_modules/d3-transition/dist/d3-transition.js"
}
} }
} }

View File

@ -50,12 +50,12 @@ import {
import {DonatsoChart} from './donatso-chart'; import {DonatsoChart} from './donatso-chart';
/** /**
* Load GEDCOM URL from REACT_APP_STATIC_URL environment variable. * Load GEDCOM URL from VITE_STATIC_URL environment variable.
* *
* If this environment variable is provided, the viewer is switched to * If this environment variable is provided, the viewer is switched to
* single-tree mode without the option to load other data. * single-tree mode without the option to load other data.
*/ */
const staticUrl = process.env.REACT_APP_STATIC_URL; const staticUrl = import.meta.env.VITE_STATIC_URL;
/** Shows an error message in the middle of the screen. */ /** Shows an error message in the middle of the screen. */
function ErrorMessage(props: {message?: string}) { function ErrorMessage(props: {message?: string}) {

View File

@ -19,8 +19,10 @@ export async function getChangelog(maxVersions: number, seenVersion?: string) {
? Date.parse(seenVersion.slice(0, 10)) ? Date.parse(seenVersion.slice(0, 10))
: 0; : 0;
const changelog = import.meta.env.VITE_CHANGELOG as string;
const changes = const changes =
process.env.REACT_APP_CHANGELOG?.split('##') changelog
.split('##')
.slice(1, maxVersions + 1) .slice(1, maxVersions + 1)
.map((notes) => { .map((notes) => {
const date = Date.parse(notes.split('\n')[0].trim()); const date = Date.parse(notes.split('\n')[0].trim());
@ -40,7 +42,7 @@ export async function getChangelog(maxVersions: number, seenVersion?: string) {
/** Stores in local storage the current app version as the last seen version. */ /** Stores in local storage the current app version as the last seen version. */
export function updateSeenVersion() { export function updateSeenVersion() {
localStorage.setItem(LAST_SEEN_VERSION_KEY, process.env.REACT_APP_GIT_TIME!); localStorage.setItem(LAST_SEEN_VERSION_KEY, import.meta.env.VITE_GIT_TIME!);
} }
/** /**
@ -54,7 +56,7 @@ export function Changelog() {
useEffect(() => { useEffect(() => {
(async () => { (async () => {
const seenVersion = localStorage.getItem(LAST_SEEN_VERSION_KEY); const seenVersion = localStorage.getItem(LAST_SEEN_VERSION_KEY);
const currentVersion = process.env.REACT_APP_GIT_TIME!; const currentVersion = import.meta.env.VITE_GIT_TIME!;
if (!seenVersion || seenVersion === currentVersion) { if (!seenVersion || seenVersion === currentVersion) {
return; return;
} }

View File

@ -1,3 +1,4 @@
import {expect, describe, it} from '@jest/globals';
import {loadFile} from './load_data'; import {loadFile} from './load_data';
import {readFileSync} from 'fs'; import {readFileSync} from 'fs';
import {Blob} from 'buffer'; import {Blob} from 'buffer';

View File

@ -1 +0,0 @@
declare module '*.jpg';

View File

@ -1,3 +1,4 @@
import f3 from 'family-chart';
import {useEffect, useRef} from 'react'; import {useEffect, useRef} from 'react';
import {IntlShape, useIntl} from 'react-intl'; import {IntlShape, useIntl} from 'react-intl';
import {IndiInfo, JsonFam, JsonGedcomData} from 'topola'; import {IndiInfo, JsonFam, JsonGedcomData} from 'topola';
@ -5,8 +6,6 @@ import {IndiInfo, JsonFam, JsonGedcomData} from 'topola';
import {formatDateOrRange} from './util/date_util'; import {formatDateOrRange} from './util/date_util';
import {usePrevious} from './util/previous-hook'; import {usePrevious} from './util/previous-hook';
const f3: any = require('family-chart');
export interface DonatsoChartProps { export interface DonatsoChartProps {
data: JsonGedcomData; data: JsonGedcomData;
selection: IndiInfo; selection: IndiInfo;

7
src/family-chart.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
// Data type definitions for the family-chart library.
declare module 'family-chart' {
export function createStore(args: any): any;
export function createSvg(args: any): any;
export function view(arg1: any, arg2: any, arg3: any, arg4: any): any;
export const elements: any;
}

2
src/imports.d.ts vendored
View File

@ -1,2 +1,2 @@
declare module '*.jng'; declare module '*.jpg';
declare module '*.png'; declare module '*.png';

View File

@ -126,11 +126,11 @@ function Contents() {
/> />
<p className="ui right aligned version"> <p className="ui right aligned version">
version: {formatBuildDate(process.env.REACT_APP_GIT_TIME!)} ( version: {formatBuildDate(import.meta.env.VITE_GIT_TIME!)} (
<a <a
href={`https://github.com/PeWu/topola-viewer/commit/${process.env.REACT_APP_GIT_SHA}`} href={`https://github.com/PeWu/topola-viewer/commit/${import.meta.env.VITE_GIT_SHA}`}
> >
{process.env.REACT_APP_GIT_SHA} {import.meta.env.VITE_GIT_SHA}
</a> </a>
) )
</p> </p>

7
src/lunr-languages.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
declare module 'lunr-languages/lunr.*' {
import lunr from 'lunr';
function register(l: typeof lunr): void;
export = register;
}

View File

@ -3,12 +3,17 @@ import naturalSort from 'javascript-natural-sort';
import {idToFamMap, idToIndiMap} from '../util/gedcom_util'; import {idToFamMap, idToIndiMap} from '../util/gedcom_util';
import {JsonFam, JsonGedcomData, JsonIndi} from 'topola'; import {JsonFam, JsonGedcomData, JsonIndi} from 'topola';
// TODO: Add type declarations and use import instead of require. import lunrStemmer from 'lunr-languages/lunr.stemmer.support';
require('lunr-languages/lunr.stemmer.support')(lunr); import lunrDe from 'lunr-languages/lunr.de';
require('lunr-languages/lunr.de')(lunr); import lunrFr from 'lunr-languages/lunr.fr';
require('lunr-languages/lunr.fr')(lunr); import lunrIt from 'lunr-languages/lunr.it';
require('lunr-languages/lunr.it')(lunr); import lunrRu from 'lunr-languages/lunr.ru';
require('lunr-languages/lunr.ru')(lunr);
lunrStemmer(lunr);
lunrDe(lunr);
lunrFr(lunr);
lunrIt(lunr);
lunrRu(lunr);
const MAX_RESULTS = 8; const MAX_RESULTS = 8;

View File

@ -1,4 +1,4 @@
import expect from 'expect'; import {expect, describe, it} from '@jest/globals';
import {createIntl} from 'react-intl'; import {createIntl} from 'react-intl';
import {calcAge} from './age_util'; import {calcAge} from './age_util';

View File

@ -1,3 +1,4 @@
import {expect, describe, it} from '@jest/globals';
import {getName, normalizeGedcom} from './gedcom_util'; import {getName, normalizeGedcom} from './gedcom_util';
describe('normalizeGedcom()', () => { describe('normalizeGedcom()', () => {

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

@ -0,0 +1 @@
/// <reference types="vite/client" />

View File

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es2020",
"lib": [ "lib": [
"dom", "dom",
"esnext" "esnext"
@ -11,7 +11,7 @@
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"noEmit": true, "noEmit": true,

15
vite.config.mts Normal file
View File

@ -0,0 +1,15 @@
import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
import viteTsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig({
// depending on your application, base can also be "/"
base: '',
plugins: [react(), viteTsconfigPaths()],
server: {
// this ensures that the browser opens upon server start
open: true,
// this sets a default port to 3000
port: 3000,
},
});