This commit is contained in:
@@ -0,0 +1 @@
|
||||
tailwind.config.cjs
|
||||
@@ -0,0 +1,159 @@
|
||||
/* eslint-env node */
|
||||
const tailwindCssConfig = `${__dirname}/../admin/src/index.css`;
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'plugin:ghost/ts',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended'
|
||||
],
|
||||
plugins: [
|
||||
'ghost',
|
||||
'react-refresh',
|
||||
'tailwindcss'
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
},
|
||||
tailwindcss: {
|
||||
config: tailwindCssConfig
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// ----------------------
|
||||
// Rules COPIED from base config, remove these when the config is fixed
|
||||
|
||||
// Style Rules
|
||||
// Require 4 spaces
|
||||
indent: ['error', 4],
|
||||
// Require single quotes for strings & properties (allows template literals)
|
||||
quotes: ['error', 'single', {allowTemplateLiterals: true}],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
// Require semi colons, always at the end of a line
|
||||
semi: ['error', 'always'],
|
||||
'semi-style': ['error', 'last'],
|
||||
// Don't allow dangling commas
|
||||
'comma-dangle': ['error', 'never'],
|
||||
// Always require curly braces, and position them at the end and beginning of lines
|
||||
curly: 'error',
|
||||
'brace-style': ['error', '1tbs'],
|
||||
// Don't allow padding inside of blocks
|
||||
'padded-blocks': ['error', 'never'],
|
||||
// Require objects to be consistently formatted with newlines
|
||||
'object-curly-newline': ['error', {consistent: true}],
|
||||
// Don't allow more than 1 consecutive empty line or an empty 1st line
|
||||
'no-multiple-empty-lines': ['error', {max: 1, maxBOF: 0}],
|
||||
// Variables must be camelcase, but properties are not checked
|
||||
camelcase: ['error', {properties: 'never'}],
|
||||
// Allow newlines before dots, not after e.g. .then goes on a new line
|
||||
'dot-location': ['error', 'property'],
|
||||
// Prefer dot notation over array notation
|
||||
'dot-notation': ['error'],
|
||||
|
||||
// Spacing rules
|
||||
// Don't allow multiple spaces anywhere
|
||||
'no-multi-spaces': 'error',
|
||||
// Anonymous functions have a sape, named functions never do
|
||||
'space-before-function-paren': ['error', {anonymous: 'always', named: 'never'}],
|
||||
// Don't put spaces inside of objects or arrays
|
||||
'object-curly-spacing': ['error', 'never'],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
// Allow a max of one space between colons and values
|
||||
'key-spacing': ['error', {mode: 'strict'}],
|
||||
// Require spaces before and after keywords like if, else, try, catch etc
|
||||
'keyword-spacing': 'error',
|
||||
// No spaces around semis
|
||||
'semi-spacing': 'error',
|
||||
// 1 space around arrows
|
||||
'arrow-spacing': 'error',
|
||||
// Don't allow spaces inside parenthesis
|
||||
'space-in-parens': ['error', 'never'],
|
||||
// Require single spaces either side of operators
|
||||
'space-unary-ops': 'error',
|
||||
'space-infix-ops': 'error',
|
||||
|
||||
// Best practice rules
|
||||
// Require === / !==
|
||||
eqeqeq: ['error', 'always'],
|
||||
// Don't allow ++ and --
|
||||
'no-plusplus': ['error', {allowForLoopAfterthoughts: true}],
|
||||
// Don't allow eval
|
||||
'no-eval': 'error',
|
||||
// Throw errors for unnecessary usage of .call or .apply
|
||||
'no-useless-call': 'error',
|
||||
// Don't allow console.* calls
|
||||
'no-console': 'error',
|
||||
// Prevent [variable shadowing](https://en.wikipedia.org/wiki/Variable_shadowing)
|
||||
'no-shadow': ['error'],
|
||||
|
||||
// Return rules
|
||||
// Prevent missing return statements in array functions like map & reduce
|
||||
'array-callback-return': 'error',
|
||||
'no-constructor-return': 'error',
|
||||
'no-promise-executor-return': 'error',
|
||||
|
||||
// Arrow function styles
|
||||
// Do not enforce single lines when using arrow functions.
|
||||
// https://eslint.org/docs/rules/arrow-body-style
|
||||
'arrow-body-style': 'off',
|
||||
'arrow-parens': ['error', 'as-needed', {requireForBlockBody: true}],
|
||||
'implicit-arrow-linebreak': 'error',
|
||||
'no-confusing-arrow': 'error',
|
||||
|
||||
// ----------------------
|
||||
// Rules NOT COPIED from base config, keep these
|
||||
|
||||
// sort multiple import lines into alphabetical groups
|
||||
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
|
||||
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
|
||||
}],
|
||||
'no-restricted-imports': ['error', {
|
||||
paths: [{
|
||||
name: '@tryghost/shade',
|
||||
message: 'Import from layered subpaths instead (components/primitives/patterns/utils/app/tokens).'
|
||||
}]
|
||||
}],
|
||||
|
||||
// Enforce kebab-case (lowercase with hyphens) for all filenames
|
||||
'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false],
|
||||
|
||||
// TODO: enable this when we have the time to retroactively go and fix the issues
|
||||
'prefer-const': 'off',
|
||||
|
||||
// TODO: re-enable this (maybe fixed fast refresh?)
|
||||
'react-refresh/only-export-components': 'off',
|
||||
|
||||
// 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',
|
||||
|
||||
// TODO: re-enable this because otherwise we're just skirting TS
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
|
||||
// TODO: re-enable these if deemed useful
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
|
||||
// custom react rules
|
||||
'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',
|
||||
|
||||
'tailwindcss/classnames-order': 'error',
|
||||
'tailwindcss/enforces-negative-arbitrary-values': 'warn',
|
||||
'tailwindcss/enforces-shorthand': 'warn',
|
||||
'tailwindcss/migration-from-tailwind-2': 'warn',
|
||||
'tailwindcss/no-arbitrary-value': 'off',
|
||||
'tailwindcss/no-custom-classname': 'off',
|
||||
'tailwindcss/no-contradicting-classname': 'error'
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
# Admin X Settings
|
||||
|
||||
Ghost Admin Settings micro-frontend.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
- Run `pnpm` in Ghost monorepo root
|
||||
|
||||
## Running the app
|
||||
|
||||
### Running the development version
|
||||
|
||||
Run `pnpm dev` (in this package folder) to start the development server to test/develop the settings standalone. This will generate a demo site from the `index.html` file which renders the app and makes it available on http://localhost:5173
|
||||
|
||||
### Running inside Admin
|
||||
|
||||
Run `pnpm dev` from the top-level repo. This starts all frontend apps via Docker backend + host dev servers, and AdminX will automatically rebuild when you make changes.
|
||||
|
||||
## 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:acceptance` - runs acceptance tests
|
||||
- `pnpm test:unit` - runs unit tests
|
||||
- `pnpm test:acceptance path/to/test` - runs a specific test
|
||||
- `pnpm test:acceptance:slowmo` - runs acceptance tests in slow motion and headed mode, useful for debugging and developing tests
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Settings - Admin</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* This is used by vite to resolve node builtins. See resolve.alias in vite.config.js
|
||||
*/
|
||||
module.exports = {};
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"name": "@tryghost/admin-x-settings",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TryGhost/Ghost/tree/main/packages/admin-x-settings"
|
||||
},
|
||||
"author": "Ghost Foundation",
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"dist/"
|
||||
],
|
||||
"main": "./dist/admin-x-settings.umd.cjs",
|
||||
"module": "./dist/admin-x-settings.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/admin-x-settings.js",
|
||||
"require": "./dist/admin-x-settings.umd.cjs",
|
||||
"types": "./src/index.tsx"
|
||||
},
|
||||
"./src/*": {
|
||||
"import": "./src/*.tsx",
|
||||
"require": "./src/*.tsx"
|
||||
}
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"dev:start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "pnpm run lint:js",
|
||||
"lint:js": "eslint --ext .js,.ts,.cjs,.tsx --cache src test",
|
||||
"test": "pnpm test:unit",
|
||||
"test:unit": "vitest run --config vitest.config.ts",
|
||||
"test:acceptance": "NODE_OPTIONS='--experimental-specifier-resolution=node --no-warnings' playwright test",
|
||||
"test:acceptance:slowmo": "TIMEOUT=100000 PLAYWRIGHT_SLOWMO=100 pnpm test:acceptance --headed",
|
||||
"test:acceptance:full": "ALL_BROWSERS=1 pnpm test:acceptance",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "6.4.11",
|
||||
"@dnd-kit/sortable": "7.0.2",
|
||||
"@ebay/nice-modal-react": "1.2.13",
|
||||
"@sentry/react": "7.120.4",
|
||||
"@tanstack/react-query": "4.36.1",
|
||||
"@tryghost/color-utils": "0.2.16",
|
||||
"@tryghost/i18n": "workspace:*",
|
||||
"@tryghost/kg-unsplash-selector": "0.3.26",
|
||||
"@tryghost/limit-service": "1.5.2",
|
||||
"@tryghost/nql": "0.12.10",
|
||||
"@tryghost/timezone-data": "0.4.18",
|
||||
"@uiw/react-codemirror": "4.25.2",
|
||||
"clsx": "2.1.1",
|
||||
"lucide-react": "0.577.0",
|
||||
"mingo": "2.5.3",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hot-toast": "2.6.0",
|
||||
"react-select": "5.10.2",
|
||||
"sonner": "2.0.7",
|
||||
"validator": "13.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "1.59.1",
|
||||
"@testing-library/jest-dom": "^6",
|
||||
"@testing-library/react": "14.3.1",
|
||||
"@tryghost/admin-x-design-system": "workspace:*",
|
||||
"@tryghost/admin-x-framework": "workspace:*",
|
||||
"@tryghost/custom-fonts": "1.0.8",
|
||||
"@tryghost/shade": "workspace:*",
|
||||
"@types/node": "22.19.17",
|
||||
"@types/react": "18.3.28",
|
||||
"@types/react-dom": "18.3.7",
|
||||
"@types/validator": "13.15.10",
|
||||
"@vitejs/plugin-react": "4.7.0",
|
||||
"eslint": "catalog:",
|
||||
"eslint-plugin-react-hooks": "4.6.2",
|
||||
"eslint-plugin-react-refresh": "0.4.24",
|
||||
"eslint-plugin-tailwindcss": "4.0.0-beta.0",
|
||||
"stylelint": "15.11.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
"vite": "5.4.21",
|
||||
"vite-plugin-css-injected-by-js": "3.5.2",
|
||||
"vite-plugin-svgr": "3.3.0",
|
||||
"vitest": "1.6.1"
|
||||
},
|
||||
"nx": {
|
||||
"targets": {
|
||||
"dev": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
},
|
||||
"test:acceptance": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import {adminXPlaywrightConfig} from '@tryghost/admin-x-framework/playwright';
|
||||
|
||||
export default adminXPlaywrightConfig();
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = require('@tryghost/admin-x-design-system/postcss.config.cjs');
|
||||
@@ -0,0 +1,38 @@
|
||||
import MainContent from './main-content';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import SettingsAppProvider, {type UpgradeStatusType} from './components/providers/settings-app-provider';
|
||||
import SettingsRouter, {loadModals, modalPaths} from './components/providers/settings-router';
|
||||
import {DesignSystemApp, type DesignSystemAppProps} from '@tryghost/admin-x-design-system';
|
||||
import {FrameworkProvider, type TopLevelFrameworkProps} from '@tryghost/admin-x-framework';
|
||||
import {RoutingProvider} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
interface AppProps {
|
||||
designSystem: DesignSystemAppProps;
|
||||
upgradeStatus?: UpgradeStatusType;
|
||||
}
|
||||
|
||||
export function App({designSystem, upgradeStatus}: AppProps) {
|
||||
return (
|
||||
<SettingsAppProvider upgradeStatus={upgradeStatus}>
|
||||
{/* NOTE: we need to have an extra NiceModal.Provider here because the one inside DesignSystemApp
|
||||
is loaded too late for possible modals in RoutingProvider, and it's quite hard to change it at
|
||||
this point */}
|
||||
<NiceModal.Provider>
|
||||
<RoutingProvider basePath='settings' modals={{paths: modalPaths, load: loadModals}}>
|
||||
<DesignSystemApp className='admin-x-settings' {...designSystem}>
|
||||
<SettingsRouter />
|
||||
<MainContent />
|
||||
</DesignSystemApp>
|
||||
</RoutingProvider>
|
||||
</NiceModal.Provider>
|
||||
</SettingsAppProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export function StandaloneApp({framework, designSystem, upgradeStatus}: AppProps & {framework: TopLevelFrameworkProps}) {
|
||||
return (
|
||||
<FrameworkProvider {...framework}>
|
||||
<App designSystem={designSystem} upgradeStatus={upgradeStatus} />
|
||||
</FrameworkProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import './styles/index.css';
|
||||
import {StandaloneApp} from './app.tsx';
|
||||
|
||||
export {
|
||||
StandaloneApp as AdminXApp
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
import ExitSettingsButton from './components/exit-settings-button';
|
||||
import Settings from './components/settings';
|
||||
import Sidebar from './components/sidebar';
|
||||
import Users from './components/settings/general/users';
|
||||
import {Heading, confirmIfDirty, topLevelBackdropClasses, useGlobalDirtyState} from '@tryghost/admin-x-design-system';
|
||||
import {type ReactNode, useEffect} from 'react';
|
||||
import {canAccessSettings, isEditorUser} from '@tryghost/admin-x-framework/api/users';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useGlobalData} from './components/providers/global-data-provider';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
const EMPTY_KEYWORDS: string[] = [];
|
||||
|
||||
const Page: React.FC<{children: ReactNode}> = ({children}) => {
|
||||
return <>
|
||||
<div className='fixed top-2 right-0 z-50 m-8 flex justify-end bg-transparent tablet:fixed tablet:top-0' id="done-button-container">
|
||||
<ExitSettingsButton />
|
||||
</div>
|
||||
<div className="fixed top-0 left-0 flex size-full dark:bg-grey-975" id="admin-x-settings-content">
|
||||
{children}
|
||||
</div>
|
||||
</>;
|
||||
};
|
||||
|
||||
const MainContent: React.FC = () => {
|
||||
const {currentUser} = useGlobalData();
|
||||
const {loadingModal} = useRouting();
|
||||
const {isDirty} = useGlobalDirtyState();
|
||||
|
||||
const navigateAway = (escLocation: string) => {
|
||||
window.location.hash = escLocation;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key === 'Escape') {
|
||||
// Don't navigate away if a modal is open - let the modal handle ESC
|
||||
const modalBackdrop = document.getElementById('modal-backdrop');
|
||||
if (modalBackdrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
confirmIfDirty(isDirty, () => {
|
||||
navigateAway('/');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
}, [isDirty]);
|
||||
|
||||
useEffect(() => {
|
||||
// resets any toasts that may have been left open on initial load
|
||||
toast.remove();
|
||||
}, []);
|
||||
|
||||
// Contributors/Authors only see their profile modal (rendered via routing)
|
||||
// Don't render the main settings content for them
|
||||
if (!canAccessSettings(currentUser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isEditorUser(currentUser)) {
|
||||
return (
|
||||
<Page>
|
||||
<div className='flex-1 overflow-y-auto bg-white dark:bg-grey-975' id="admin-x-settings-scroller">
|
||||
<div className='mx-auto max-w-5xl px-[5vmin] tablet:mt-16 xl:mt-10'>
|
||||
<Heading className='mb-[5vmin]'>Settings</Heading>
|
||||
<Users highlight={false} keywords={EMPTY_KEYWORDS} />
|
||||
</div>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Page>
|
||||
{loadingModal && <div className={`fixed inset-0 z-40 h-[calc(100vh-55px)] w-[100vw] tablet:h-[100vh] ${topLevelBackdropClasses}`} />}
|
||||
<div className="fixed inset-x-0 top-0 z-[35] max-w-[calc(100%-16px)] flex-1 basis-[320px] bg-white p-8 tablet:relative tablet:inset-x-auto tablet:top-auto tablet:h-full tablet:overflow-y-scroll tablet:bg-grey-50 tablet:py-0 dark:bg-grey-975 dark:tablet:bg-[#101114]" id="admin-x-settings-sidebar-scroller">
|
||||
<div className="relative w-full">
|
||||
<Sidebar />
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative h-full flex-1 overflow-y-scroll bg-white pt-12 tablet:basis-[800px] dark:bg-grey-975 dark:tablet:bg-black" id="admin-x-settings-scroller">
|
||||
<Settings />
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
export default MainContent;
|
||||
@@ -0,0 +1,6 @@
|
||||
import './styles/index.css';
|
||||
import renderStandaloneApp from '@tryghost/admin-x-framework/test/render';
|
||||
import {StandaloneApp} from './app.tsx';
|
||||
|
||||
renderStandaloneApp(StandaloneApp, {
|
||||
});
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
declare module '@tryghost/limit-service'
|
||||
declare module '@tryghost/nql'
|
||||
|
||||
declare module '*.svg' {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
import React = require('react');
|
||||
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
import {setupShadeMocks} from '@tryghost/admin-x-framework/test/setup';
|
||||
|
||||
// Set up common mocks for shade components
|
||||
setupShadeMocks();
|
||||
@@ -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,33 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"types": ["node", "vitest/globals", "@testing-library/jest-dom/vitest"],
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Path mapping */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@src/*": ["./src/*"],
|
||||
"@test/*": ["./test/*"]
|
||||
},
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src", "test"]
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import adminXViteConfig from '@tryghost/admin-x-framework/vite';
|
||||
import pkg from './package.json';
|
||||
import {resolve} from 'path';
|
||||
import {createRequire} from 'node:module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default (function viteConfig() {
|
||||
return adminXViteConfig({
|
||||
packageName: pkg.name,
|
||||
entry: resolve(__dirname, 'src/index.tsx'),
|
||||
overrides: {
|
||||
define: {
|
||||
'process.env.DEBUG': false // Shim env var utilized by the @tryghost/nql package
|
||||
},
|
||||
resolve: {
|
||||
// Shim node modules utilized by the @tryghost/nql package
|
||||
alias: {
|
||||
'@src': resolve(__dirname, 'src'),
|
||||
'@test': resolve(__dirname, 'test'),
|
||||
fs: 'node-shim.cjs',
|
||||
path: 'node-shim.cjs',
|
||||
util: 'node-shim.cjs',
|
||||
// @TODO: Remove this when @tryghost/nql is updated
|
||||
mingo: require.resolve('mingo/dist/mingo.js')
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['@tryghost/kg-unsplash-selector', '@tryghost/custom-fonts']
|
||||
}
|
||||
},
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
include: [/ghost\/custom-fonts/]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import {createVitestConfig} from '@tryghost/admin-x-framework/test/vitest-config';
|
||||
|
||||
export default createVitestConfig();
|
||||
Reference in New Issue
Block a user