diff --git a/package-lock.json b/package-lock.json index dc91e38b..4c599e49 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,8 @@ "typescript": "^5.3.3", "vite": "^5.0.9", "vite-plugin-checker": "^0.6.2", + "vite-plugin-full-reload": "^1.1.0", + "vite-tsconfig-paths": "^4.2.2", "vitest": "^1.0.4", "webdriverio": "^8.26.1" } @@ -4509,6 +4511,11 @@ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==" }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -8092,6 +8099,25 @@ } } }, + "node_modules/tsconfck": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", + "integrity": "sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^14.13.1 || ^16 || >=18" + }, + "peerDependencies": { + "typescript": "^4.3.5 || ^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", @@ -8499,6 +8525,33 @@ "node": ">= 10.0.0" } }, + "node_modules/vite-plugin-full-reload": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz", + "integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/vite-tsconfig-paths": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.2.tgz", + "integrity": "sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^2.1.0" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, "node_modules/vite/node_modules/@esbuild/android-arm": { "version": "0.19.6", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.6.tgz", @@ -12571,6 +12624,11 @@ "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==" }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -15118,6 +15176,12 @@ "yn": "3.1.1" } }, + "tsconfck": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", + "integrity": "sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==", + "requires": {} + }, "tslib": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", @@ -15561,6 +15625,25 @@ } } }, + "vite-plugin-full-reload": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.1.0.tgz", + "integrity": "sha512-3cObNDzX6DdfhD9E7kf6w2mNunFpD7drxyNgHLw+XwIYAgb+Xt16SEXo0Up4VH+TMf3n+DSVJZtW2POBGcBYAA==", + "requires": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "vite-tsconfig-paths": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.2.tgz", + "integrity": "sha512-dq0FjyxHHDnp0uS3P12WEOX2W7NeuLzX9AWP38D7Zw2CTbFErapwQVlCiT5DMJcVWKQ1MMdTe92PZl/rBQ7qcw==", + "requires": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^2.1.0" + } + }, "vitest": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.0.4.tgz", diff --git a/package.json b/package.json index 10fa35f3..9974c4f4 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,8 @@ "typescript": "^5.3.3", "vite": "^5.0.9", "vite-plugin-checker": "^0.6.2", + "vite-plugin-full-reload": "^1.1.0", + "vite-tsconfig-paths": "^4.2.2", "vitest": "^1.0.4", "webdriverio": "^8.26.1" } diff --git a/src/react-env.d.ts b/react-env.d.ts similarity index 100% rename from src/react-env.d.ts rename to react-env.d.ts diff --git a/src/components/Root/index.tsx b/src/components/Root/index.tsx index e0df7f17..43d37cc2 100644 --- a/src/components/Root/index.tsx +++ b/src/components/Root/index.tsx @@ -1,5 +1,4 @@ import { Component } from 'react'; -import { StrictMode } from 'react'; import App from '../App'; import { store } from '../../redux'; import { Provider } from 'react-redux'; @@ -9,13 +8,11 @@ import { Props, State } from './types'; export default class Root extends Component { render() { return ( - - - - - - - + + + + + ); } } diff --git a/src/index.tsx b/src/index.tsx index 06fd96aa..d2fb719f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,7 +1,7 @@ import './index.scss'; import Root from './components/Root'; import { createRoot } from 'react-dom/client'; -import { setPrototypes } from './tools'; +import { setPrototypes } from './prototypes'; function setHotReload() { if (!import.meta.hot) return; diff --git a/src/tools/prototypes/Array.ts b/src/prototypes/Array.ts similarity index 68% rename from src/tools/prototypes/Array.ts rename to src/prototypes/Array.ts index f3d357b1..a6abc025 100644 --- a/src/tools/prototypes/Array.ts +++ b/src/prototypes/Array.ts @@ -1,4 +1,4 @@ -import { isEqual } from '..'; +import { isEqual } from '../tools'; export function setArrayPrototype() { Array.prototype.isEqual = isEqual; diff --git a/src/tools/prototypes/Boolean.ts b/src/prototypes/Boolean.ts similarity index 70% rename from src/tools/prototypes/Boolean.ts rename to src/prototypes/Boolean.ts index 5a0522ed..668d4e3d 100644 --- a/src/tools/prototypes/Boolean.ts +++ b/src/prototypes/Boolean.ts @@ -1,4 +1,4 @@ -import { isEqual } from '..'; +import { isEqual } from '../tools'; export function setBooleanPrototype() { Boolean.prototype.isEqual = isEqual; diff --git a/src/tools/prototypes/Number.ts b/src/prototypes/Number.ts similarity index 69% rename from src/tools/prototypes/Number.ts rename to src/prototypes/Number.ts index 4c6a79f2..6ebcf2ec 100644 --- a/src/tools/prototypes/Number.ts +++ b/src/prototypes/Number.ts @@ -1,4 +1,4 @@ -import { isEqual } from '..'; +import { isEqual } from '../tools'; export function setNumberPrototype() { Number.prototype.isEqual = isEqual; diff --git a/src/tools/prototypes/Object.ts b/src/prototypes/Object.ts similarity index 69% rename from src/tools/prototypes/Object.ts rename to src/prototypes/Object.ts index ffccf4a6..bc7ad141 100644 --- a/src/tools/prototypes/Object.ts +++ b/src/prototypes/Object.ts @@ -1,4 +1,4 @@ -import { isEqual } from '..'; +import { isEqual } from '../tools'; export function setObjectPrototype() { Object.prototype.isEqual = isEqual; diff --git a/src/tools/prototypes/String.ts b/src/prototypes/String.ts similarity index 69% rename from src/tools/prototypes/String.ts rename to src/prototypes/String.ts index 19d10c20..80e15cc9 100644 --- a/src/tools/prototypes/String.ts +++ b/src/prototypes/String.ts @@ -1,4 +1,4 @@ -import { isEqual } from '..'; +import { isEqual } from '../tools'; export function setStringPrototype() { String.prototype.isEqual = isEqual; diff --git a/src/tools/prototypes/index.ts b/src/prototypes/index.ts similarity index 100% rename from src/tools/prototypes/index.ts rename to src/prototypes/index.ts diff --git a/src/prototypes/types.d.ts b/src/prototypes/types.d.ts new file mode 100644 index 00000000..bb5b108a --- /dev/null +++ b/src/prototypes/types.d.ts @@ -0,0 +1,19 @@ +export {}; + +declare global { + interface Object { + isEqual(this: object, target: unknown): boolean; + } + interface Array { + isEqual(this: Array, target: unknown): boolean; + } + interface String { + isEqual(this: string, target: unknown): boolean; + } + interface Number { + isEqual(this: number, target: unknown): boolean; + } + interface Boolean { + isEqual(this: boolean, target: unknown): boolean; + } +} diff --git a/src/tools/index.ts b/src/tools/index.ts index ce535dae..48c161d2 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,2 +1 @@ -export * from './methods'; -export * from './prototypes'; \ No newline at end of file +export * from './methods'; \ No newline at end of file diff --git a/src/tools/methods/isEqual.ts b/src/tools/methods/isEqual.ts index 5250364e..8bf4232e 100644 --- a/src/tools/methods/isEqual.ts +++ b/src/tools/methods/isEqual.ts @@ -1,63 +1,22 @@ export function isEqual(this: unknown, target: unknown) { - let leftChain: Array = []; - let rightChain: Array = []; - function compare2Objects(x: unknown, y: unknown) { - let p; - if (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)) return true; - if (x === y) return true; - if ( - (typeof x === 'function' && typeof y === 'function') || - (x instanceof Date && y instanceof Date) || - (x instanceof RegExp && y instanceof RegExp) || - (x instanceof String && y instanceof String) || - (x instanceof Number && y instanceof Number) - ) - return x.toString() === y.toString(); - if (!(x instanceof Object && y instanceof Object)) return false; - if (Object.prototype.isPrototypeOf.call(x, y) || Object.prototype.isPrototypeOf.call(y, x)) - return false; - if (x.constructor !== y.constructor) return false; - if (Object.prototype.valueOf.call(x) !== Object.prototype.valueOf.call(y)) return false; - if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) return false; - const a = (x as Record) - const b = (y as Record) - for (const p in b) { - if ( - Object.prototype.hasOwnProperty.call(y, p) !== - Object.prototype.hasOwnProperty.call(x, p) - ) { - return false; - } else if (typeof b[p] !== typeof a[p]) { - return false; - } + if (this === target) return true; + // this verification for primitives (number, string, boolean, etc.) + if (this == null || target == null || (typeof this !== 'object' && typeof target !== 'object')) + return this === target; + // deep object/array comparison + // keep shallow copy of comparator + const remainder = { + ...target + }; + for (const key in this) { + if (this[key].isEqual(target[key])) { + // remove properties from shallow copy to keep track of verified children + delete remainder[key]; + continue; } - for (p in x) { - if ( - Object.prototype.hasOwnProperty.call(y, p) !== - Object.prototype.hasOwnProperty.call(x, p) - ) - return false; - else if (typeof b[p] !== typeof b[p]) return false; - switch (typeof a[p]) { - case 'object': - case 'function': { - leftChain.push(x); - rightChain.push(y); - if (!compare2Objects(a[p], b[p])) return false; - leftChain.pop(); - rightChain.pop(); - break; - } - default: { - if (a[p] !== b[p]) return false; - break; - } - } - } - return true; + // if one property of comparator is not equal to itself on the original + return false; } - leftChain = []; - rightChain = []; - if (!compare2Objects(this, target)) return false; - return true; + // check if comparator has the same properties than original + return Object.entries(remainder).length === 0; } diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 5579c080..8c65a264 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,21 +1,6 @@ export {}; declare global { - interface Object { - isEqual(this: unknown, target: unknown): boolean; - } - interface Array { - isEqual(this: Array, target: unknown): boolean; - } - interface String { - isEqual(this: string, target: unknown): boolean; - } - interface Number { - isEqual(this: number, target: unknown): boolean; - } - interface Boolean { - isEqual(this: boolean, target: unknown): boolean; - } namespace Window { // } diff --git a/tsconfig.json b/tsconfig.json index e5836fb6..6faead2e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,8 +3,8 @@ "target": "ESNext", "lib": ["DOM", "DOM.Iterable", "ESNext"], "module": "ESNext", - "skipLibCheck": true, // this causes .d.ts files to not have type check - "allowJs": true, + "skipLibCheck": false, // If true causes .d.ts files to not have type check + "allowJs": false, "esModuleInterop": true, "allowSyntheticDefaultImports": true, @@ -18,19 +18,31 @@ /* Linting */ "strict": true, + "noImplicitAny": false, "noUnusedLocals": false, "noUnusedParameters": false, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "useDefineForClassFields": true, + /* Paths*/ + "baseUrl": ".", + "paths": { + "resources/*": ["src/resources/*"], + "proto": ["src/proto/index.ts"], + "proto/*": ["src/proto/*"], + "enums": ["src/enums/index.ts"], + "enums/*": ["src/enums/*"] + }, + /* Type overrides */ - "types": ["vite/client", "./src/react-env.d.ts", "vitest"] + "types": ["vite/client", "./react-env.d.ts", "vitest"] }, "include": ["*", "src"], + "exclude": ["node_modules", "dist", "build", "scripts"], "references": [ { "path": "./tsconfig.node.json" } ] -} +} \ No newline at end of file diff --git a/tsconfig.node.json b/tsconfig.node.json index d883fd73..a5540d94 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -8,4 +8,4 @@ "esModuleInterop": true }, "include": ["vite.config.ts"] -} +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index 7dfc2991..d0804f50 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from 'vite'; -import config from './vite.shared'; +import shared from './vite.shared'; // https://vitejs.dev/config/ export default defineConfig(async () => { - return config; -}); + return shared; +}); \ No newline at end of file diff --git a/vite.shared.ts b/vite.shared.ts index b5738b8a..ce623adf 100644 --- a/vite.shared.ts +++ b/vite.shared.ts @@ -1,29 +1,41 @@ import react from '@vitejs/plugin-react-swc'; import checker from 'vite-plugin-checker'; import { UserConfig } from 'vite'; -import pkg from './package.json' +import fullReload from 'vite-plugin-full-reload'; +import tsconfigPaths from 'vite-tsconfig-paths'; const config: UserConfig = { resolve: { extensions: ['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json', '.d.ts'] }, - base: pkg.homepage, + // clear screen is needed for styles to be reloaded properly clearScreen: true, + base: './', esbuild: { jsxFactory: 'h', jsxFragment: 'Fragment', jsxInject: `import React from 'react'` }, + define: { + global: {} + }, plugins: [ + tsconfigPaths(), react({}), checker({ eslint: { lintCommand: 'eslint "./src/**/*.{ts,tsx}"' }, typescript: true + }), + fullReload('./**/*', { + root: __dirname, + delay: 0, + always: true }) ], server: { + host: true, hmr: { overlay: false }, @@ -57,4 +69,4 @@ const config: UserConfig = { } }; -export default config; +export default config; \ No newline at end of file diff --git a/vitest.config.ts b/vitest.config.ts index 95b48477..cbff61ca 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,18 +3,15 @@ import { mergeConfig } from 'vitest/config'; import shared from './vite.shared'; -export default mergeConfig( - shared, - { - test: { - dom: true, - typecheck: { - checker: 'tsc' - }, - css: true, - globals: true, - setupFiles: ['./src/setupTests.ts'], - environment: 'happy-dom' - } +export default mergeConfig(shared, { + test: { + dom: true, + typecheck: { + checker: 'tsc' + }, + css: true, + globals: true, + setupFiles: ['./src/setupTests.ts'], + environment: 'happy-dom' } -); +}); \ No newline at end of file