Add dependency cruiser command (#2192)

* add dependency cruiser and fix all default errors

* create svg render of dependencies in command

* move configs to ts files, await font loading

* fix i18n

* fix dependencies...

* fix typedoc generation and tsconfig exclusions

* revert github-pages fix for another pr

* no-circular -> no-circular-at-runtime for type imports (future)
This commit is contained in:
Devin Korb 2024-06-16 00:26:37 -04:00 committed by GitHub
parent d9ae12ccc7
commit 728ee3809a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1225 additions and 187 deletions

384
.dependency-cruiser.cjs Normal file
View File

@ -0,0 +1,384 @@
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
forbidden: [
{
name: 'no-circular-at-runtime',
severity: 'warn',
comment:
'This dependency is part of a circular relationship. You might want to revise ' +
'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ',
from: {},
to: {
circular: true,
viaOnly: {
dependencyTypesNot: [
'type-only'
]
}
}
},
{
name: 'no-orphans',
comment:
"This is an orphan module - it's likely not used (anymore?). Either use it or " +
"remove it. If it's logical this module is an orphan (i.e. it's a config file), " +
"add an exception for it in your dependency-cruiser configuration. By default " +
"this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " +
"files (.d.ts), tsconfig.json and some of the babel and webpack configs.",
severity: 'warn',
from: {
orphan: true,
pathNot: [
'(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$', // dot files
'[.]d[.]ts$', // TypeScript declaration files
'(^|/)tsconfig[.]json$', // TypeScript config
'(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$' // other configs
]
},
to: {},
},
{
name: 'no-deprecated-core',
comment:
'A module depends on a node core module that has been deprecated. Find an alternative - these are ' +
"bound to exist - node doesn't deprecate lightly.",
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'core'
],
path: [
'^v8/tools/codemap$',
'^v8/tools/consarray$',
'^v8/tools/csvparser$',
'^v8/tools/logreader$',
'^v8/tools/profile_view$',
'^v8/tools/profile$',
'^v8/tools/SourceMap$',
'^v8/tools/splaytree$',
'^v8/tools/tickprocessor-driver$',
'^v8/tools/tickprocessor$',
'^node-inspect/lib/_inspect$',
'^node-inspect/lib/internal/inspect_client$',
'^node-inspect/lib/internal/inspect_repl$',
'^async_hooks$',
'^punycode$',
'^domain$',
'^constants$',
'^sys$',
'^_linklist$',
'^_stream_wrap$'
],
}
},
{
name: 'not-to-deprecated',
comment:
'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' +
'version of that module, or find an alternative. Deprecated modules are a security risk.',
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'deprecated'
]
}
},
{
name: 'no-non-package-json',
severity: 'error',
comment:
"This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " +
"That's problematic as the package either (1) won't be available on live (2 - worse) will be " +
"available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " +
"in your package.json.",
from: {},
to: {
dependencyTypes: [
'npm-no-pkg',
'npm-unknown'
]
}
},
{
name: 'not-to-unresolvable',
comment:
"This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " +
'module: add it to your package.json. In all other cases you likely already know what to do.',
severity: 'error',
from: {},
to: {
couldNotResolve: true
}
},
{
name: 'no-duplicate-dep-types',
comment:
"Likely this module depends on an external ('npm') package that occurs more than once " +
"in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " +
"maintenance problems later on.",
severity: 'warn',
from: {},
to: {
moreThanOneDependencyType: true,
// as it's pretty common to have a type import be a type only import
// _and_ (e.g.) a devDependency - don't consider type-only dependency
// types for this rule
dependencyTypesNot: ["type-only"]
}
},
/* rules you might want to tweak for your specific situation: */
{
name: 'not-to-spec',
comment:
'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' +
"If there's something in a spec that's of use to other modules, it doesn't have that single " +
'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.',
severity: 'error',
from: {},
to: {
path: '[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$'
}
},
{
name: 'not-to-dev-dep',
severity: 'error',
comment:
"This module depends on an npm package from the 'devDependencies' section of your " +
'package.json. It looks like something that ships to production, though. To prevent problems ' +
"with npm packages that aren't there on production declare it (only!) in the 'dependencies'" +
'section of your package.json. If this module is development only - add it to the ' +
'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration',
from: {
path: '^(src)',
pathNot: [
'[.](?:spec|test|setup|script)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$',
'src/test'
]
},
to: {
dependencyTypes: [
'npm-dev',
],
// type only dependencies are not a problem as they don't end up in the
// production code or are ignored by the runtime.
dependencyTypesNot: [
'type-only'
],
pathNot: [
'node_modules/@types/'
]
}
},
{
name: 'optional-deps-used',
severity: 'info',
comment:
"This module depends on an npm package that is declared as an optional dependency " +
"in your package.json. As this makes sense in limited situations only, it's flagged here. " +
"If you're using an optional dependency here by design - add an exception to your" +
"dependency-cruiser configuration.",
from: {},
to: {
dependencyTypes: [
'npm-optional'
]
}
},
{
name: 'peer-deps-used',
comment:
"This module depends on an npm package that is declared as a peer dependency " +
"in your package.json. This makes sense if your package is e.g. a plugin, but in " +
"other cases - maybe not so much. If the use of a peer dependency is intentional " +
"add an exception to your dependency-cruiser configuration.",
severity: 'warn',
from: {},
to: {
dependencyTypes: [
'npm-peer'
]
}
}
],
options: {
/* Which modules not to follow further when encountered */
doNotFollow: {
/* path: an array of regular expressions in strings to match against */
path: ['node_modules']
},
/* Which modules to exclude */
// exclude : {
// /* path: an array of regular expressions in strings to match against */
// path: '',
// },
/* Which modules to exclusively include (array of regular expressions in strings)
dependency-cruiser will skip everything not matching this pattern
*/
// includeOnly : [''],
/* List of module systems to cruise.
When left out dependency-cruiser will fall back to the list of _all_
module systems it knows of. It's the default because it's the safe option
It might come at a performance penalty, though.
moduleSystems: ['amd', 'cjs', 'es6', 'tsd']
As in practice only commonjs ('cjs') and ecmascript modules ('es6')
are widely used, you can limit the moduleSystems to those.
*/
// moduleSystems: ['cjs', 'es6'],
/* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/main/'
to open it on your online repo or `vscode://file/${process.cwd()}/` to
open it in visual studio code),
*/
// prefix: `vscode://file/${process.cwd()}/`,
/* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation
true: also detect dependencies that only exist before typescript-to-javascript compilation
"specify": for each dependency identify whether it only exists before compilation or also after
*/
// tsPreCompilationDeps: false,
/* list of extensions to scan that aren't javascript or compile-to-javascript.
Empty by default. Only put extensions in here that you want to take into
account that are _not_ parsable.
*/
// extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"],
/* if true combines the package.jsons found from the module up to the base
folder the cruise is initiated from. Useful for how (some) mono-repos
manage dependencies & dependency definitions.
*/
// combinedDependencies: false,
/* if true leave symlinks untouched, otherwise use the realpath */
// preserveSymlinks: false,
/* TypeScript project file ('tsconfig.json') to use for
(1) compilation and
(2) resolution (e.g. with the paths property)
The (optional) fileName attribute specifies which file to take (relative to
dependency-cruiser's current working directory). When not provided
defaults to './tsconfig.json'.
*/
tsConfig: {
fileName: 'tsconfig.json'
},
/* Webpack configuration to use to get resolve options from.
The (optional) fileName attribute specifies which file to take (relative
to dependency-cruiser's current working directory. When not provided defaults
to './webpack.conf.js'.
The (optional) `env` and `arguments` attributes contain the parameters
to be passed if your webpack config is a function and takes them (see
webpack documentation for details)
*/
// webpackConfig: {
// fileName: 'webpack.config.js',
// env: {},
// arguments: {}
// },
/* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use
for compilation
*/
// babelConfig: {
// fileName: '.babelrc',
// },
/* List of strings you have in use in addition to cjs/ es6 requires
& imports to declare module dependencies. Use this e.g. if you've
re-declared require, use a require-wrapper or use window.require as
a hack.
*/
// exoticRequireStrings: [],
/* options to pass on to enhanced-resolve, the package dependency-cruiser
uses to resolve module references to disk. The values below should be
suitable for most situations
If you use webpack: you can also set these in webpack.conf.js. The set
there will override the ones specified here.
*/
enhancedResolveOptions: {
/* What to consider as an 'exports' field in package.jsons */
exportsFields: ["exports"],
/* List of conditions to check for in the exports field.
Only works when the 'exportsFields' array is non-empty.
*/
conditionNames: ["import", "require", "node", "default", "types"],
/*
The extensions, by default are the same as the ones dependency-cruiser
can access (run `npx depcruise --info` to see which ones that are in
_your_ environment). If that list is larger than you need you can pass
the extensions you actually use (e.g. [".js", ".jsx"]). This can speed
up module resolution, which is the most expensive step.
*/
// extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"],
/* What to consider a 'main' field in package.json */
mainFields: ["module", "main", "types", "typings"],
/*
A list of alias fields in package.jsons
See [this specification](https://github.com/defunctzombie/package-browser-field-spec) and
the webpack [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields)
documentation
Defaults to an empty array (= don't use alias fields).
*/
// aliasFields: ["browser"],
},
reporterOptions: {
dot: {
/* pattern of modules that can be consolidated in the detailed
graphical dependency graph. The default pattern in this configuration
collapses everything in node_modules to one folder deep so you see
the external modules, but their innards.
*/
collapsePattern: 'node_modules/(?:@[^/]+/[^/]+|[^/]+)',
/* Options to tweak the appearance of your graph.See
https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions
for details and some examples. If you don't specify a theme
dependency-cruiser falls back to a built-in one.
*/
// theme: {
// graph: {
// /* splines: "ortho" gives straight lines, but is slow on big graphs
// splines: "true" gives bezier curves (fast, not as nice as ortho)
// */
// splines: "true"
// },
// }
},
archi: {
/* pattern of modules that can be consolidated in the high level
graphical dependency graph. If you use the high level graphical
dependency graph reporter (`archi`) you probably want to tweak
this collapsePattern to your situation.
*/
collapsePattern: '^(?:packages|src|lib(s?)|app(s?)|bin|test(s?)|spec(s?))/[^/]+|node_modules/(?:@[^/]+/[^/]+|[^/]+)',
/* Options to tweak the appearance of your graph. If you don't specify a
theme for 'archi' dependency-cruiser will use the one specified in the
dot section above and otherwise use the default one.
*/
// theme: { },
},
"text": {
"highlightFocused": true
},
}
}
};
// generated: dependency-cruiser@16.3.3 on 2024-06-13T23:26:36.169Z

1
.gitignore vendored
View File

@ -39,3 +39,4 @@ coverage
# Local Documentation
/typedoc
/dependency-graph.svg

13
dependency-graph.js Normal file
View File

@ -0,0 +1,13 @@
import { Graphviz } from "@hpcc-js/wasm/graphviz";
const graphviz = await Graphviz.load();
const inputFile = [];
for await (const chunk of process.stdin) {
inputFile.push(chunk);
}
const file = Buffer.concat(inputFile).toString("utf-8");
const svg = graphviz.dot(file, "svg");
process.stdout.write(svg);

717
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,9 @@
"test:silent": "vitest run --silent",
"eslint": "eslint --fix .",
"eslint-ci": "eslint .",
"docs": "typedoc"
"docs": "typedoc",
"depcruise": "depcruise src",
"depcruise:graph": "depcruise src --output-type dot | node dependency-graph.js > dependency-graph.svg"
},
"devDependencies": {
"@eslint/js": "^9.3.0",
@ -41,8 +43,11 @@
"vitest-canvas-mock": "^0.3.3"
},
"dependencies": {
"@hpcc-js/wasm": "^2.16.2",
"@material/material-color-utilities": "^0.2.7",
"@types/jsdom": "^21.1.7",
"crypto-js": "^4.2.0",
"dependency-cruiser": "^16.3.3",
"i18next": "^23.11.1",
"i18next-browser-languagedetector": "^7.2.1",
"i18next-korean-postposition-processor": "^1.0.0",

View File

@ -1,12 +1,12 @@
import { Type } from "./type";
import * as Utils from "../utils";
import beautify from "json-beautify";
import {pokemonEvolutions, SpeciesFormEvolution} from "./pokemon-evolutions";
import i18next from "i18next";
import { Biome } from "#enums/biome";
import { Species } from "#enums/species";
import { TimeOfDay } from "#enums/time-of-day";
import { TrainerType } from "#enums/trainer-type";
// import beautify from "json-beautify";
export function getBiomeName(biome: Biome | -1) {
if (biome === -1) {
@ -7808,57 +7808,56 @@ export function initBiomes() {
// used in a commented code
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function outputPools() {
const pokemonOutput = {};
const trainerOutput = {};
// function outputPools() {
// const pokemonOutput = {};
// const trainerOutput = {};
for (const b of Object.keys(biomePokemonPools)) {
const biome = Biome[b];
pokemonOutput[biome] = {};
trainerOutput[biome] = {};
// for (const b of Object.keys(biomePokemonPools)) {
// const biome = Biome[b];
// pokemonOutput[biome] = {};
// trainerOutput[biome] = {};
for (const t of Object.keys(biomePokemonPools[b])) {
const tier = BiomePoolTier[t];
// for (const t of Object.keys(biomePokemonPools[b])) {
// const tier = BiomePoolTier[t];
pokemonOutput[biome][tier] = {};
// pokemonOutput[biome][tier] = {};
for (const tod of Object.keys(biomePokemonPools[b][t])) {
const timeOfDay = TimeOfDay[tod];
// for (const tod of Object.keys(biomePokemonPools[b][t])) {
// const timeOfDay = TimeOfDay[tod];
pokemonOutput[biome][tier][timeOfDay] = [];
// pokemonOutput[biome][tier][timeOfDay] = [];
for (const f of biomePokemonPools[b][t][tod]) {
if (typeof f === "number") {
pokemonOutput[biome][tier][timeOfDay].push(Species[f]);
} else {
const tree = {};
// for (const f of biomePokemonPools[b][t][tod]) {
// if (typeof f === "number") {
// pokemonOutput[biome][tier][timeOfDay].push(Species[f]);
// } else {
// const tree = {};
for (const l of Object.keys(f)) {
tree[l] = f[l].map(s => Species[s]);
}
// for (const l of Object.keys(f)) {
// tree[l] = f[l].map(s => Species[s]);
// }
pokemonOutput[biome][tier][timeOfDay].push(tree);
}
}
// pokemonOutput[biome][tier][timeOfDay].push(tree);
// }
// }
}
}
// }
// }
for (const t of Object.keys(biomeTrainerPools[b])) {
const tier = BiomePoolTier[t];
// for (const t of Object.keys(biomeTrainerPools[b])) {
// const tier = BiomePoolTier[t];
trainerOutput[biome][tier] = [];
// trainerOutput[biome][tier] = [];
for (const f of biomeTrainerPools[b][t]) {
trainerOutput[biome][tier].push(TrainerType[f]);
}
}
}
// for (const f of biomeTrainerPools[b][t]) {
// trainerOutput[biome][tier].push(TrainerType[f]);
// }
// }
// }
console.log(beautify(pokemonOutput, null, 2, 180).replace(/( | (?:\{ "\d+": \[ )?| "(?:.*?)": \[ |(?:,|\[) (?:"\w+": \[ |(?:\{ )?"\d+": \[ )?)"(\w+)"(?= |,|\n)/g, "$1Species.$2").replace(/"(\d+)": /g, "$1: ").replace(/((?: )|(?:(?!\n) "(?:.*?)": \{) |\[(?: .*? )?\], )"(\w+)"/g, "$1[TimeOfDay.$2]").replace(/( )"(.*?)"/g, "$1[BiomePoolTier.$2]").replace(/( )"(.*?)"/g, "$1[Biome.$2]"));
console.log(beautify(trainerOutput, null, 2, 120).replace(/( | (?:\{ "\d+": \[ )?| "(?:.*?)": \[ |, (?:(?:\{ )?"\d+": \[ )?)"(.*?)"/g, "$1TrainerType.$2").replace(/"(\d+)": /g, "$1: ").replace(/( )"(.*?)"/g, "$1[BiomePoolTier.$2]").replace(/( )"(.*?)"/g, "$1[Biome.$2]"));
}
// console.log(beautify(pokemonOutput, null, 2, 180).replace(/( | (?:\{ "\d+": \[ )?| "(?:.*?)": \[ |(?:,|\[) (?:"\w+": \[ |(?:\{ )?"\d+": \[ )?)"(\w+)"(?= |,|\n)/g, "$1Species.$2").replace(/"(\d+)": /g, "$1: ").replace(/((?: )|(?:(?!\n) "(?:.*?)": \{) |\[(?: .*? )?\], )"(\w+)"/g, "$1[TimeOfDay.$2]").replace(/( )"(.*?)"/g, "$1[BiomePoolTier.$2]").replace(/( )"(.*?)"/g, "$1[Biome.$2]"));
// console.log(beautify(trainerOutput, null, 2, 120).replace(/( | (?:\{ "\d+": \[ )?| "(?:.*?)": \[ |, (?:(?:\{ )?"\d+": \[ )?)"(.*?)"/g, "$1TrainerType.$2").replace(/"(\d+)": /g, "$1: ").replace(/( )"(.*?)"/g, "$1[BiomePoolTier.$2]").replace(/( )"(.*?)"/g, "$1[Biome.$2]"));
// }
/*for (let pokemon of allSpecies) {
if (pokemon.speciesId >= Species.XERNEAS)

View File

@ -97,10 +97,15 @@ const fonts = [
),
];
function initFonts() {
fonts.forEach((fontFace: FontFace) => {
fontFace.load().then(f => document.fonts.add(f)).catch(e => console.error(e));
});
async function initFonts() {
const results = await Promise.allSettled(fonts.map(font => font.load()));
for (const result of results) {
if (result.status === "fulfilled") {
document.fonts?.add(result.value);
} else {
console.error(result.reason);
}
}
}
export async function initI18n(): Promise<void> {
@ -110,8 +115,6 @@ export async function initI18n(): Promise<void> {
}
isInitialized = true;
initFonts();
/**
* i18next is a localization library for maintaining and using translation resources.
*
@ -173,6 +176,8 @@ export async function initI18n(): Promise<void> {
},
postProcess: ["korean-postposition"],
});
await initFonts();
}
// Module declared to make referencing keys in the localization files type-safe.

View File

@ -1,16 +1,19 @@
import "vitest-canvas-mock";
import "#app/test/fontFace.setup";
import {initStatsKeys} from "#app/ui/game-stats-ui-handler";
import {initPokemonPrevolutions} from "#app/data/pokemon-evolutions";
import "vitest-canvas-mock";
import { initLoggedInUser } from "#app/account";
import { initAbilities } from "#app/data/ability";
import { initBiomes } from "#app/data/biomes";
import { initEggMoves } from "#app/data/egg-moves";
import { initMoves } from "#app/data/move";
import { initPokemonPrevolutions } from "#app/data/pokemon-evolutions";
import { initPokemonForms } from "#app/data/pokemon-forms";
import { initSpecies } from "#app/data/pokemon-species";
import {initMoves} from "#app/data/move";
import {initAbilities} from "#app/data/ability";
import { initAchievements } from "#app/system/achv.js";
import { initVouchers } from "#app/system/voucher.js";
import {initLoggedInUser} from "#app/account";
import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
import { beforeAll } from "vitest";
initVouchers();
initAchievements();
@ -25,3 +28,12 @@ initAbilities();
initLoggedInUser();
global.testFailed = false;
beforeAll(() => {
Object.defineProperty(document, "fonts", {
writable: true,
value: {
add: () => {},
}
});
});

View File

@ -23,5 +23,11 @@
"entryPoints": ["src/"],
"entryPointStrategy": "expand",
"out": "typedoc",
}
},
"exclude": [
"node_modules",
"dist",
"vite.config.ts",
"vitest.config.ts"
]
}

View File

@ -1,34 +0,0 @@
import { resolve } from 'path';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
// import fs from 'vite-plugin-fs';
export default defineConfig(({ mode }) => {
return {
plugins: [tsconfigPaths()],
server: { host: '0.0.0.0', port: 8000 },
clearScreen: false,
build: {
minify: 'esbuild',
sourcemap: false,
},
rollupOptions: {
onwarn(warning, warn) {
// Suppress "Module level directives cause errors when bundled" warnings
if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
return;
}
warn(warning);
},
},
resolve: {
alias: {
"#enums": resolve('./src/enums')
}
},
esbuild: {
pure: mode === 'production' ? [ 'console.log' ] : [],
keepNames: true,
},
}
})

30
vite.config.ts Normal file
View File

@ -0,0 +1,30 @@
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
export const defaultConfig = {
plugins: [tsconfigPaths() as any],
server: { host: '0.0.0.0', port: 8000 },
clearScreen: false,
build: {
minify: 'esbuild' as const,
sourcemap: false,
},
rollupOptions: {
onwarn(warning, warn) {
// Suppress "Module level directives cause errors when bundled" warnings
if (warning.code === "MODULE_LEVEL_DIRECTIVE") {
return;
}
warn(warning);
},
}
};
export default defineConfig(({mode}) => ({
...defaultConfig,
esbuild: {
pure: mode === 'production' ? [ 'console.log' ] : [],
keepNames: true,
},
}));

View File

@ -1,48 +0,0 @@
import { defineConfig } from 'vite';
import path from 'path';
// import fs from 'vite-plugin-fs';
export default defineConfig(({ mode }) => {
return {
test: {
setupFiles: ['./src/test/vitest.setup.ts'],
environment: 'jsdom',
deps: {
optimizer: {
web: {
include: ['vitest-canvas-mock'],
}
}
},
threads: false,
trace: true,
restoreMocks: true,
environmentOptions: {
jsdom: {
resources: 'usable',
},
},
coverage: {
provider: 'istanbul',
reportsDirectory: 'coverage',
reporters: ['text-summary', 'html'],
},
},
plugins: [/*fs()*/],
server: { host: '0.0.0.0', port: 8000 },
clearScreen: false,
build: {
minify: 'esbuild',
sourcemap: true
},
resolve: {
alias: {
"#enums": path.resolve('./src/enums')
}
},
esbuild: {
pure: mode === 'production' ? [ 'console.log' ] : [],
keepNames: true,
},
}
})

38
vitest.config.ts Normal file
View File

@ -0,0 +1,38 @@
import { defineConfig } from 'vitest/config';
import { defaultConfig } from './vite.config';
export default defineConfig(({mode}) => ({
...defaultConfig,
test: {
setupFiles: ['./src/test/vitest.setup.ts'],
server: {
deps: {
inline: ['vitest-canvas-mock'],
optimizer: {
web: {
include: ['vitest-canvas-mock'],
}
}
}
},
environment: 'jsdom' as const,
environmentOptions: {
jsdom: {
resources: 'usable',
},
},
threads: false,
trace: true,
restoreMocks: true,
watch: false,
coverage: {
provider: 'istanbul' as const,
reportsDirectory: 'coverage' as const,
reporters: ['text-summary', 'html'],
},
},
esbuild: {
pure: mode === 'production' ? [ 'console.log' ] : [],
keepNames: true,
},
}))