This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'plugin:ghost/ts',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended'
|
||||
],
|
||||
plugins: [
|
||||
'ghost',
|
||||
'react-refresh',
|
||||
'tailwindcss'
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// suppress errors for missing 'import React' in JSX files, as we don't need it
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
// ignore prop-types for now
|
||||
'react/prop-types': 'off',
|
||||
'no-restricted-imports': ['error', {
|
||||
paths: [{
|
||||
name: '@tryghost/shade',
|
||||
message: 'Import from layered subpaths instead (components/primitives/patterns/utils/app/tokens).'
|
||||
}]
|
||||
}],
|
||||
|
||||
'react/jsx-sort-props': ['error', {
|
||||
reservedFirst: true,
|
||||
callbacksLast: true,
|
||||
shorthandLast: true,
|
||||
locale: 'en'
|
||||
}],
|
||||
'react/button-has-type': 'error',
|
||||
'react/no-array-index-key': 'error',
|
||||
'react/jsx-key': 'off',
|
||||
|
||||
// Enforce kebab-case (lowercase with hyphens) for all filenames
|
||||
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false]
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
types
|
||||
@@ -0,0 +1,22 @@
|
||||
# Admin X Framework
|
||||
|
||||
Ghost Shared Framework that is used by all the micro-frontends for common functionality like data fetching and routing.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
- Run `pnpm` in Ghost monorepo root
|
||||
|
||||
## Develop
|
||||
|
||||
This is a monorepo package.
|
||||
|
||||
Follow the instructions for the top-level repo.
|
||||
1. `git clone` this repo & `cd` into it as usual
|
||||
2. Run `pnpm` to install top-level dependencies.
|
||||
|
||||
## Test
|
||||
|
||||
- `pnpm lint` - run just eslint
|
||||
- `pnpm test` - runs acceptance tests
|
||||
|
||||
In package.json you can find other related running options too.
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"name": "@tryghost/admin-x-framework",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"repository": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-framework",
|
||||
"author": "Ghost Foundation",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./errors": {
|
||||
"import": "./dist/errors.js",
|
||||
"require": "./dist/errors.cjs",
|
||||
"types": "./types/errors.d.ts"
|
||||
},
|
||||
"./helpers": {
|
||||
"import": "./dist/helpers.js",
|
||||
"require": "./dist/helpers.cjs",
|
||||
"types": "./types/helpers.d.ts"
|
||||
},
|
||||
"./hooks": {
|
||||
"import": "./dist/hooks.js",
|
||||
"require": "./dist/hooks.cjs",
|
||||
"types": "./types/hooks.d.ts"
|
||||
},
|
||||
"./routing": {
|
||||
"import": "./dist/routing.js",
|
||||
"require": "./dist/routing.cjs",
|
||||
"types": "./types/routing.d.ts"
|
||||
},
|
||||
"./api/*": {
|
||||
"import": "./dist/api/*.js",
|
||||
"require": "./dist/api/*.cjs",
|
||||
"types": "./types/api/*.d.ts"
|
||||
},
|
||||
"./utils/post-utils": {
|
||||
"import": "./dist/utils/post-utils.js",
|
||||
"require": "./dist/utils/post-utils.cjs",
|
||||
"types": "./types/utils/post-utils.d.ts"
|
||||
},
|
||||
"./vite": {
|
||||
"import": "./dist/vite.js",
|
||||
"require": "./dist/vite.cjs",
|
||||
"types": "./types/vite.d.ts"
|
||||
},
|
||||
"./playwright": {
|
||||
"import": "./dist/playwright.js",
|
||||
"require": "./dist/playwright.cjs",
|
||||
"types": "./types/playwright.d.ts"
|
||||
},
|
||||
"./test/*": {
|
||||
"import": "./dist/test/*.js",
|
||||
"require": "./dist/test/*.cjs",
|
||||
"types": "./types/test/*.d.ts"
|
||||
}
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "tsc -p tsconfig.declaration.json && vite build",
|
||||
"test": "pnpm test:types && pnpm test:unit",
|
||||
"test:types": "tsc --noEmit",
|
||||
"test:unit": "vitest run --coverage",
|
||||
"lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache",
|
||||
"lint": "pnpm lint:code && pnpm lint:test",
|
||||
"lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"types"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.59.1",
|
||||
"@testing-library/jest-dom": "5.17.0",
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@tryghost/koenig-lexical": "1.7.30",
|
||||
"@types/react": "18.3.28",
|
||||
"@types/react-dom": "18.3.7",
|
||||
"@vitejs/plugin-react": "4.7.0",
|
||||
"@vitest/coverage-v8": "^1.6.1",
|
||||
"c8": "10.1.3",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"eslint-plugin-react-refresh": "0.4.24",
|
||||
"glob": "^10.5.0",
|
||||
"jsdom": "28.1.0",
|
||||
"msw": "2.12.14",
|
||||
"sinon": "18.0.1",
|
||||
"typescript": "5.9.3",
|
||||
"vite": "5.4.21",
|
||||
"vite-plugin-css-injected-by-js": "3.5.2",
|
||||
"vite-plugin-svgr": "3.3.0",
|
||||
"vitest": "1.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ebay/nice-modal-react": "1.2.13",
|
||||
"@sentry/react": "7.120.4",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tinybirdco/charts": "0.2.4",
|
||||
"@tryghost/admin-x-design-system": "workspace:*",
|
||||
"@tryghost/shade": "workspace:*",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hot-toast": "2.6.0",
|
||||
"react-router": "7.14.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:unit": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './utils/errors';
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './utils/helpers';
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export {default as useFilterableApi} from './hooks/use-filterable-api';
|
||||
export {default as useForm} from './hooks/use-form';
|
||||
export type {Dirtyable, ErrorMessages, FormHook, OkProps, SaveHandler, SaveState} from './hooks/use-form';
|
||||
export {default as useHandleError} from './hooks/use-handle-error';
|
||||
export {usePermission} from './hooks/use-permissions';
|
||||
export {useKoenigFileUpload, koenigFileUploadTypes} from './hooks/use-koenig-file-upload';
|
||||
export {useKoenigFetchEmbed} from './hooks/use-koenig-fetch-embed';
|
||||
export type {KoenigFileUploadType} from './hooks/use-koenig-file-upload';
|
||||
export {useKoenigLinkSuggestions} from './hooks/use-koenig-link-suggestions';
|
||||
export {usePinturaConfig} from './hooks/use-pintura-config';
|
||||
@@ -0,0 +1,58 @@
|
||||
// Framework
|
||||
export type {StatsConfig, FrameworkContextType, FrameworkProviderProps, TopLevelFrameworkProps} from './providers/framework-provider';
|
||||
export {FrameworkProvider, useFramework} from './providers/framework-provider';
|
||||
|
||||
// App Context
|
||||
export type {AppSettings, BaseAppProps, AppContextType, AppProviderProps} from './providers/app-provider';
|
||||
export {AppContext, AppProvider, useAppContext} from './providers/app-provider';
|
||||
|
||||
// Hooks
|
||||
export {useActiveVisitors} from './hooks/use-active-visitors';
|
||||
export {default as useForm} from './hooks/use-form';
|
||||
export type {Dirtyable, ErrorMessages, FormHook, OkProps, SaveHandler, SaveState} from './hooks/use-form';
|
||||
export {default as useHandleError} from './hooks/use-handle-error';
|
||||
export {default as useFilterableApi} from './hooks/use-filterable-api';
|
||||
export {useTinybirdToken} from './hooks/use-tinybird-token';
|
||||
export type {UseTinybirdTokenResult} from './hooks/use-tinybird-token';
|
||||
export {useTinybirdQuery} from './hooks/use-tinybird-query';
|
||||
export type {UseTinybirdQueryOptions} from './hooks/use-tinybird-query';
|
||||
export {useKoenigFileUpload, koenigFileUploadTypes} from './hooks/use-koenig-file-upload';
|
||||
export {useKoenigFetchEmbed} from './hooks/use-koenig-fetch-embed';
|
||||
export type {KoenigFileUploadType} from './hooks/use-koenig-file-upload';
|
||||
export {useKoenigLinkSuggestions} from './hooks/use-koenig-link-suggestions';
|
||||
|
||||
// Currency utilities
|
||||
export {getSymbol} from './utils/currency';
|
||||
|
||||
// Stats utilities
|
||||
export {getStatEndpointUrl, getToken} from './utils/stats-config';
|
||||
|
||||
// Post utilities
|
||||
export type {Post} from './api/posts';
|
||||
export {hasBeenEmailed} from './utils/post-utils';
|
||||
export {isEmailOnly, isPublishedOnly, isPublishedAndEmailed, getPostMetricsToDisplay} from './utils/post-helpers';
|
||||
export {focusKoenigEditorOnBottomClick} from './utils/focus-koenig-editor-on-bottom-click';
|
||||
|
||||
// Source utilities
|
||||
export {SOURCE_DOMAIN_MAP, getFaviconDomain, extractDomain, isDomainOrSubdomain, processSources, extendSourcesWithPercentages, normalizeSource} from './utils/source-utils';
|
||||
export type {BaseSourceData, ProcessedSourceData, ExtendSourcesOptions} from './utils/source-utils';
|
||||
|
||||
// Routing
|
||||
export type {RouteObject} from 'react-router';
|
||||
export type {RouterProviderProps, NavigateOptions} from './providers/router-provider';
|
||||
export {RouterProvider, useNavigate, useBaseRoute, useRouteHasParams, resetScrollPosition, ScrollRestoration, Navigate} from './providers/router-provider';
|
||||
export {useNavigationStack} from './providers/navigation-stack-provider';
|
||||
export {Link, NavLink, Outlet, useLocation, useParams, useSearchParams, redirect, matchRoutes, matchPath, useMatch, useMatches} from 'react-router';
|
||||
|
||||
// Lazy component loader
|
||||
export {lazyComponent} from './utils/lazy-component';
|
||||
|
||||
// Data fetching
|
||||
export type {InfiniteData} from '@tanstack/react-query';
|
||||
export {useQueryClient} from '@tanstack/react-query';
|
||||
|
||||
// API
|
||||
export type {TinybirdToken, TinybirdTokenResponseType} from './api/tinybird';
|
||||
export {getTinybirdToken} from './api/tinybird';
|
||||
export type {FeaturebaseToken, FeaturebaseTokenResponseType} from './api/featurebase';
|
||||
export {getFeaturebaseToken} from './api/featurebase';
|
||||
@@ -0,0 +1,62 @@
|
||||
import {defineConfig, devices, PlaywrightTestConfig} from '@playwright/test';
|
||||
|
||||
export const E2E_PORT = 5173;
|
||||
|
||||
export function adminXPlaywrightConfig(overrides: Partial<PlaywrightTestConfig> = {}) {
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
return defineConfig({
|
||||
testDir: './test/acceptance',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Hardcode to use all cores in CI */
|
||||
workers: process.env.CI ? '100%' : (process.env.PLAYWRIGHT_SLOWMO ? 1 : undefined),
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
baseURL: `http://localhost:${E2E_PORT}`,
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
launchOptions: {
|
||||
slowMo: parseInt(process.env.PLAYWRIGHT_SLOWMO ?? '') || 0,
|
||||
// force GPU hardware acceleration
|
||||
// (even in headless mode)
|
||||
args: ['--use-gl=egl']
|
||||
}
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {...devices['Desktop Chrome']}
|
||||
},
|
||||
|
||||
...(process.env.ALL_BROWSERS ? [{
|
||||
name: 'firefox',
|
||||
use: {...devices['Desktop Firefox']}
|
||||
},
|
||||
|
||||
{
|
||||
name: 'webkit',
|
||||
use: {...devices['Desktop Safari']}
|
||||
}] : [])
|
||||
],
|
||||
|
||||
/* Run local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: `pnpm dev:start`,
|
||||
url: `http://localhost:${E2E_PORT}`,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
timeout: 10000
|
||||
},
|
||||
|
||||
...overrides
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export {RoutingProvider, useRouteChangeCallback, useRouting} from './providers/routing-provider';
|
||||
export type {ExternalLink, InternalLink, ModalComponent, RoutingModalProps} from './providers/routing-provider';
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import {PluginOption, UserConfig, mergeConfig} from 'vite';
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
import {defineConfig} from 'vitest/config';
|
||||
|
||||
const externalPlugin = ({externals}: { externals: Record<string, string> }): PluginOption => {
|
||||
return {
|
||||
name: 'external-globals',
|
||||
apply: 'build',
|
||||
enforce: 'pre',
|
||||
resolveId(id) {
|
||||
if (Object.keys(externals).includes(id)) {
|
||||
// Naming convention for IDs that will be resolved by a plugin
|
||||
return `\0${id}`;
|
||||
}
|
||||
},
|
||||
async load(id) {
|
||||
const [originalId, externalName] = Object.entries(externals).find(([key]) => id === `\0${key}`) || [];
|
||||
|
||||
if (originalId) {
|
||||
const module = await import(originalId);
|
||||
|
||||
return Object.keys(module).map(key => (key === 'default' ? `export default ${externalName};` : `export const ${key} = ${externalName}.${key};`)).join('\n');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default function adminXViteConfig({packageName, entry, overrides}: {packageName: string; entry: string; overrides?: UserConfig}) {
|
||||
const outputFileName = packageName[0] === '@' ? packageName.slice(packageName.indexOf('/') + 1) : packageName;
|
||||
|
||||
const defaultConfig = defineConfig({
|
||||
logLevel: process.env.CI ? 'info' : 'warn',
|
||||
plugins: [
|
||||
svgr(),
|
||||
react(),
|
||||
externalPlugin({
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM'
|
||||
}
|
||||
}),
|
||||
cssInjectedByJsPlugin() as PluginOption // Cast to avoid type conflicts
|
||||
],
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
'process.env.VITEST_SEGFAULT_RETRY': 3,
|
||||
'import.meta.env.GHOST_BUILD_VERSION': JSON.stringify(process.env.GHOST_BUILD_VERSION || '')
|
||||
},
|
||||
preview: {
|
||||
port: 4174
|
||||
},
|
||||
build: {
|
||||
reportCompressedSize: false,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
lib: {
|
||||
formats: ['es'],
|
||||
entry,
|
||||
name: packageName,
|
||||
fileName(format) {
|
||||
if (format === 'umd') {
|
||||
return `${outputFileName}.umd.js`;
|
||||
}
|
||||
|
||||
return `${outputFileName}.js`;
|
||||
}
|
||||
},
|
||||
commonjsOptions: {
|
||||
include: [/packages/, /node_modules/]
|
||||
}
|
||||
},
|
||||
test: {
|
||||
globals: true, // required for @testing-library/jest-dom extensions
|
||||
environment: 'jsdom',
|
||||
include: ['./test/unit/**/*'],
|
||||
testTimeout: process.env.TIMEOUT ? parseInt(process.env.TIMEOUT) : 10000,
|
||||
...(process.env.CI && { // https://github.com/vitest-dev/vitest/issues/1674
|
||||
minThreads: 1,
|
||||
maxThreads: 2
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
return mergeConfig(defaultConfig, overrides || {});
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
module.exports = {
|
||||
plugins: ['ghost'],
|
||||
extends: [
|
||||
'plugin:ghost/ts-test'
|
||||
],
|
||||
rules: {
|
||||
'ghost/mocha/no-mocha-arrows': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
// Enforce kebab-case (lowercase with hyphens) for all filenames
|
||||
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false]
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
/// <reference types="vitest/globals" />
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
// This file ensures TypeScript knows about vitest globals
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"composite": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationDir": "./types",
|
||||
"emitDeclarationOnly": true,
|
||||
"tsBuildInfoFile": "./types/tsconfig.tsbuildinfo",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["src/**/*.stories.tsx", "src/**/*.test.ts", "src/**/*.test.tsx"]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client", "vitest/globals"],
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src", "test"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
},
|
||||
"include": ["vite.config.ts", "package.json"]
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import path from 'path';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import {globSync} from 'glob';
|
||||
import {resolve} from 'path';
|
||||
import {defineConfig} from 'vitest/config';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default (function viteConfig() {
|
||||
return defineConfig({
|
||||
logLevel: process.env.CI ? 'info' : 'warn',
|
||||
plugins: [
|
||||
react()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './src')
|
||||
}
|
||||
},
|
||||
preview: {
|
||||
port: 4174
|
||||
},
|
||||
build: {
|
||||
reportCompressedSize: false,
|
||||
minify: false,
|
||||
sourcemap: true,
|
||||
outDir: 'dist',
|
||||
lib: {
|
||||
formats: ['es', 'cjs'],
|
||||
entry: globSync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, libpath) => {
|
||||
if (libpath.endsWith('.d.ts')) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
const outPath = libpath.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, '');
|
||||
entries[outPath] = libpath;
|
||||
return entries;
|
||||
}, {} as Record<string, string>)
|
||||
},
|
||||
commonjsOptions: {
|
||||
include: [/packages/, /node_modules/]
|
||||
},
|
||||
rollupOptions: {
|
||||
external: (source) => {
|
||||
if (source.startsWith('.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (source.includes('node_modules')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !source.includes(__dirname);
|
||||
}
|
||||
}
|
||||
},
|
||||
test: {
|
||||
globals: true, // required for @testing-library/jest-dom extensions
|
||||
environment: 'jsdom',
|
||||
include: ['./test/unit/**/*'],
|
||||
setupFiles: ['./test/setup.ts'],
|
||||
testTimeout: process.env.TIMEOUT ? parseInt(process.env.TIMEOUT) : 10000,
|
||||
...(process.env.CI && { // https://github.com/vitest-dev/vitest/issues/1674
|
||||
minThreads: 1,
|
||||
maxThreads: 2
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user