mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
Updated AdminX to load via ES Modules to enable code splitting (#17971)
refs https://github.com/TryGhost/Product/issues/3349
This commit is contained in:
parent
8a02d54326
commit
9e45afddb8
12 changed files with 199 additions and 136 deletions
4
.github/scripts/dev.js
vendored
4
.github/scripts/dev.js
vendored
|
@ -71,9 +71,9 @@ if (DASH_DASH_ARGS.includes('admin-x') || DASH_DASH_ARGS.includes('adminx') || D
|
|||
// https://localhost:41740 {
|
||||
// reverse_proxy http://localhost:4174
|
||||
// }
|
||||
COMMAND_GHOST.env['adminX__url'] = 'https://localhost:41740/admin-x-settings.umd.js';
|
||||
COMMAND_GHOST.env['adminX__url'] = 'https://localhost:41740/admin-x-settings.js';
|
||||
} else {
|
||||
COMMAND_GHOST.env['adminX__url'] = 'http://localhost:4174/admin-x-settings.umd.js';
|
||||
COMMAND_GHOST.env['adminX__url'] = 'http://localhost:4174/admin-x-settings.js';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
"stylelint": "15.10.3",
|
||||
"tailwindcss": "3.3.3",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.0",
|
||||
"vite-plugin-svgr": "3.2.0",
|
||||
"vitest": "0.34.3"
|
||||
}
|
||||
|
|
|
@ -1,68 +1,15 @@
|
|||
import CodeMirror, {ReactCodeMirrorProps, ReactCodeMirrorRef} from '@uiw/react-codemirror';
|
||||
import Heading from '../Heading';
|
||||
import Hint from '../Hint';
|
||||
import React, {forwardRef, useId} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {EditorView} from '@codemirror/view';
|
||||
import {Extension} from '@codemirror/state';
|
||||
import React, {Suspense, forwardRef} from 'react';
|
||||
import type {CodeEditorProps} from './CodeEditorView.tsx';
|
||||
import type {ReactCodeMirrorRef} from '@uiw/react-codemirror';
|
||||
|
||||
export interface CodeEditorProps extends Omit<ReactCodeMirrorProps, 'value' | 'onChange'> {
|
||||
title?: string;
|
||||
value?: string;
|
||||
height?: string;
|
||||
error?: boolean;
|
||||
hint?: React.ReactNode;
|
||||
clearBg?: boolean;
|
||||
extensions: Extension[];
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const theme = EditorView.theme({
|
||||
'& .cm-scroller': {
|
||||
fontFamily: 'Consolas, Liberation Mono, Menlo, Courier, monospace'
|
||||
},
|
||||
|
||||
'& .cm-activeLine, & .cm-activeLineGutter': {
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
});
|
||||
|
||||
const CodeEditor = forwardRef<ReactCodeMirrorRef, CodeEditorProps>(function CodeEditor({
|
||||
title,
|
||||
value,
|
||||
height = '200px',
|
||||
error,
|
||||
hint,
|
||||
clearBg = true,
|
||||
extensions,
|
||||
onChange,
|
||||
...props
|
||||
}, ref) {
|
||||
const id = useId();
|
||||
|
||||
let styles = clsx(
|
||||
'peer order-2 overflow-hidden rounded-sm border',
|
||||
clearBg ? 'bg-transparent' : 'bg-grey-75',
|
||||
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 focus:border-grey-800',
|
||||
title && 'mt-2',
|
||||
height === 'full' && 'h-full'
|
||||
);
|
||||
// Imported asynchronously to avoid including CodeMirror in the main bundle
|
||||
const CodeEditorView = React.lazy(() => import('./CodeEditorView.tsx'));
|
||||
|
||||
const CodeEditor = forwardRef<ReactCodeMirrorRef, CodeEditorProps>(function CodeEditor(props, ref) {
|
||||
return (
|
||||
<div className={height === 'full' ? 'h-full' : ''}>
|
||||
<CodeMirror
|
||||
ref={ref}
|
||||
className={styles}
|
||||
extensions={extensions}
|
||||
height={height === 'full' ? '100%' : height}
|
||||
theme={theme}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
/>
|
||||
{title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
|
||||
</div>
|
||||
<Suspense fallback={null}>
|
||||
<CodeEditorView {...props} ref={ref} />
|
||||
</Suspense>
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
import CodeMirror, {ReactCodeMirrorProps, ReactCodeMirrorRef} from '@uiw/react-codemirror';
|
||||
import Heading from '../Heading';
|
||||
import Hint from '../Hint';
|
||||
import React, {forwardRef, useEffect, useId} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {EditorView} from '@codemirror/view';
|
||||
import {Extension} from '@codemirror/state';
|
||||
|
||||
export interface CodeEditorProps extends Omit<ReactCodeMirrorProps, 'value' | 'onChange' | 'extensions'> {
|
||||
title?: string;
|
||||
value?: string;
|
||||
height?: string;
|
||||
error?: boolean;
|
||||
hint?: React.ReactNode;
|
||||
clearBg?: boolean;
|
||||
extensions: Array<Extension | Promise<Extension>>;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const theme = EditorView.theme({
|
||||
'& .cm-scroller': {
|
||||
fontFamily: 'Consolas, Liberation Mono, Menlo, Courier, monospace'
|
||||
},
|
||||
|
||||
'& .cm-activeLine, & .cm-activeLineGutter': {
|
||||
backgroundColor: 'transparent'
|
||||
}
|
||||
});
|
||||
|
||||
// Meant to be imported asynchronously to avoid including CodeMirror in the main bundle
|
||||
const CodeEditorView = forwardRef<ReactCodeMirrorRef, CodeEditorProps>(function CodeEditorView({
|
||||
title,
|
||||
value,
|
||||
height = '200px',
|
||||
error,
|
||||
hint,
|
||||
clearBg = true,
|
||||
extensions,
|
||||
onChange,
|
||||
...props
|
||||
}, ref) {
|
||||
const id = useId();
|
||||
const [resolvedExtensions, setResolvedExtensions] = React.useState<Extension[] | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all(extensions).then(setResolvedExtensions);
|
||||
}, [extensions]);
|
||||
|
||||
let styles = clsx(
|
||||
'peer order-2 overflow-hidden rounded-sm border',
|
||||
clearBg ? 'bg-transparent' : 'bg-grey-75',
|
||||
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 focus:border-grey-800',
|
||||
title && 'mt-2',
|
||||
height === 'full' && 'h-full'
|
||||
);
|
||||
|
||||
if (!resolvedExtensions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={height === 'full' ? 'h-full' : ''}>
|
||||
<CodeMirror
|
||||
ref={ref}
|
||||
className={styles}
|
||||
extensions={resolvedExtensions}
|
||||
height={height === 'full' ? '100%' : height}
|
||||
theme={theme}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
{...props}
|
||||
/>
|
||||
{title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default CodeEditorView;
|
|
@ -1,28 +1,6 @@
|
|||
import AddIntegrationModal from '../settings/advanced/integrations/AddIntegrationModal';
|
||||
import AddNewsletterModal from '../settings/email/newsletters/AddNewsletterModal';
|
||||
import AddRecommendationModal from '../settings/site/recommendations/AddRecommendationModal';
|
||||
import AmpModal from '../settings/advanced/integrations/AmpModal';
|
||||
import AnnouncementBarModal from '../settings/site/AnnouncementBarModal';
|
||||
import ChangeThemeModal from '../settings/site/ThemeModal';
|
||||
import CustomIntegrationModal from '../settings/advanced/integrations/CustomIntegrationModal';
|
||||
import DesignModal from '../settings/site/DesignModal';
|
||||
import EditRecommendationModal from '../settings/site/recommendations/EditRecommendationModal';
|
||||
import EmbedSignupFormModal from '../settings/membership/EmbedSignupFormModal';
|
||||
import FirstpromoterModal from '../settings/advanced/integrations/FirstPromoterModal';
|
||||
import HistoryModal from '../settings/advanced/HistoryModal';
|
||||
import InviteUserModal from '../settings/general/InviteUserModal';
|
||||
import NavigationModal from '../settings/site/NavigationModal';
|
||||
import NewsletterDetailModal from '../settings/email/newsletters/NewsletterDetailModal';
|
||||
import NiceModal, {NiceModalHocProps} from '@ebay/nice-modal-react';
|
||||
import PinturaModal from '../settings/advanced/integrations/PinturaModal';
|
||||
import PortalModal from '../settings/membership/portal/PortalModal';
|
||||
|
||||
import React, {createContext, useCallback, useEffect, useState} from 'react';
|
||||
import SlackModal from '../settings/advanced/integrations/SlackModal';
|
||||
import StripeConnectModal from '../settings/membership/stripe/StripeConnectModal';
|
||||
import TierDetailModal from '../settings/membership/tiers/TierDetailModal';
|
||||
import UnsplashModal from '../settings/advanced/integrations/UnsplashModal';
|
||||
import UserDetailModal from '../settings/general/UserDetailModal';
|
||||
import ZapierModal from '../settings/advanced/integrations/ZapierModal';
|
||||
|
||||
export type RouteParams = {[key: string]: string}
|
||||
|
||||
|
@ -57,7 +35,31 @@ export type RoutingModalProps = {
|
|||
params?: Record<string, string>
|
||||
}
|
||||
|
||||
const modalPaths: {[key: string]: React.FC<NiceModalHocProps & RoutingModalProps>} = {
|
||||
const AddIntegrationModal = () => import('../settings/advanced/integrations/AddIntegrationModal');
|
||||
const AddNewsletterModal = () => import('../settings/email/newsletters/AddNewsletterModal');
|
||||
const AddRecommendationModal = () => import('../settings/site/recommendations/AddRecommendationModal');
|
||||
const AmpModal = () => import('../settings/advanced/integrations/AmpModal');
|
||||
const ChangeThemeModal = () => import('../settings/site/ThemeModal');
|
||||
const CustomIntegrationModal = () => import('../settings/advanced/integrations/CustomIntegrationModal');
|
||||
const DesignModal = () => import('../settings/site/DesignModal');
|
||||
const EditRecommendationModal = () => import('../settings/site/recommendations/EditRecommendationModal');
|
||||
const FirstpromoterModal = () => import('../settings/advanced/integrations/FirstPromoterModal');
|
||||
const HistoryModal = () => import('../settings/advanced/HistoryModal');
|
||||
const InviteUserModal = () => import('../settings/general/InviteUserModal');
|
||||
const NavigationModal = () => import('../settings/site/NavigationModal');
|
||||
const NewsletterDetailModal = () => import('../settings/email/newsletters/NewsletterDetailModal');
|
||||
const PinturaModal = () => import('../settings/advanced/integrations/PinturaModal');
|
||||
const PortalModal = () => import('../settings/membership/portal/PortalModal');
|
||||
const SlackModal = () => import('../settings/advanced/integrations/SlackModal');
|
||||
const StripeConnectModal = () => import('../settings/membership/stripe/StripeConnectModal');
|
||||
const TierDetailModal = () => import('../settings/membership/tiers/TierDetailModal');
|
||||
const UnsplashModal = () => import('../settings/advanced/integrations/UnsplashModal');
|
||||
const UserDetailModal = () => import('../settings/general/UserDetailModal');
|
||||
const ZapierModal = () => import('../settings/advanced/integrations/ZapierModal');
|
||||
const AnnouncementBarModal = () => import('../settings/site/AnnouncementBarModal');
|
||||
const EmbedSignupFormModal = () => import('../settings/membership/EmbedSignupFormModal');
|
||||
|
||||
const modalPaths: {[key: string]: () => Promise<{default: React.FC<NiceModalHocProps & RoutingModalProps>}>} = {
|
||||
'design/edit/themes': ChangeThemeModal,
|
||||
'design/edit': DesignModal,
|
||||
'navigation/edit': NavigationModal,
|
||||
|
@ -119,7 +121,7 @@ const handleNavigation = (scroll: boolean = true) => {
|
|||
const [path, modal] = Object.entries(modalPaths).find(([modalPath]) => matchRoute(pathName, modalPath)) || [];
|
||||
|
||||
if (path && modal) {
|
||||
NiceModal.show(modal, {params: matchRoute(pathName, path)});
|
||||
modal().then(({default: component}) => NiceModal.show(component, {params: matchRoute(pathName, path)}));
|
||||
}
|
||||
|
||||
if (scroll) {
|
||||
|
|
|
@ -2,13 +2,12 @@ import Button from '../../../admin-x-ds/global/Button';
|
|||
import CodeEditor from '../../../admin-x-ds/global/form/CodeEditor';
|
||||
import CodeModal from './code/CodeModal';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React, {useRef, useState} from 'react';
|
||||
import React, {useMemo, useRef, useState} from 'react';
|
||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {ReactCodeMirrorRef} from '@uiw/react-codemirror';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {html} from '@codemirror/lang-html';
|
||||
|
||||
const CodeInjection: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -28,15 +27,17 @@ const CodeInjection: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
const headerEditorRef = useRef<ReactCodeMirrorRef>(null);
|
||||
const footerEditorRef = useRef<ReactCodeMirrorRef>(null);
|
||||
|
||||
const html = useMemo(() => import('@codemirror/lang-html').then(module => module.html()), []);
|
||||
|
||||
const headerProps = {
|
||||
extensions: [html()],
|
||||
extensions: [html],
|
||||
hint: 'Code here will be injected into the {{ghost_head}} tag on every page of the site',
|
||||
value: headerContent,
|
||||
onChange: (value: string) => updateSetting('codeinjection_head', value)
|
||||
};
|
||||
|
||||
const footerProps = {
|
||||
extensions: [html()],
|
||||
extensions: [html],
|
||||
hint: 'Code here will be injected into the {{ghost_foot}} tag on every page of the site',
|
||||
value: footerContent,
|
||||
onChange: (value: string) => updateSetting('codeinjection_foot', value)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import CodeEditor from '../../../../admin-x-ds/global/form/CodeEditor';
|
||||
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import React from 'react';
|
||||
import {html} from '@codemirror/lang-html';
|
||||
import React, {useMemo} from 'react';
|
||||
|
||||
interface CodeModalProps {
|
||||
hint?: React.ReactNode;
|
||||
|
@ -14,13 +13,15 @@ interface CodeModalProps {
|
|||
const CodeModal: React.FC<CodeModalProps> = ({hint, value, onChange, afterClose}) => {
|
||||
const modal = useModal();
|
||||
|
||||
const html = useMemo(() => import('@codemirror/lang-html').then(module => module.html()), []);
|
||||
|
||||
const onOk = () => {
|
||||
modal.remove();
|
||||
afterClose?.();
|
||||
};
|
||||
|
||||
return <Modal afterClose={afterClose} cancelLabel='' okColor='grey' okLabel='Done' size='full' testId='modal-code' onOk={onOk}>
|
||||
<CodeEditor extensions={[html()]} height='full' hint={hint} value={value} autoFocus onChange={onChange} />
|
||||
<CodeEditor extensions={[html]} height='full' hint={hint} value={value} autoFocus onChange={onChange} />
|
||||
</Modal>;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import LimitService from '@tryghost/limit-service';
|
||||
import useStaffUsers from './useStaffUsers';
|
||||
import {useBrowseMembers} from '../api/members';
|
||||
import {useBrowseNewsletters} from '../api/newsletters';
|
||||
import {useEffect, useMemo, useState} from 'react';
|
||||
import {useGlobalData} from '../components/providers/GlobalDataProvider';
|
||||
import {useMemo} from 'react';
|
||||
|
||||
const limitServiceImport = import('@tryghost/limit-service');
|
||||
|
||||
export class LimitError extends Error {
|
||||
public readonly errorType: string;
|
||||
|
@ -48,6 +49,11 @@ interface LimiterLimits {
|
|||
|
||||
export const useLimiter = () => {
|
||||
const {config} = useGlobalData();
|
||||
const [LimitService, setLimitService] = useState<typeof import('@tryghost/limit-service') | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
limitServiceImport.then(exports => setLimitService(() => exports.default));
|
||||
}, []);
|
||||
|
||||
const {users, contributorUsers, invites, isLoading} = useStaffUsers();
|
||||
const {refetch: fetchMembers} = useBrowseMembers({
|
||||
|
@ -70,7 +76,7 @@ export const useLimiter = () => {
|
|||
return useMemo(() => {
|
||||
const limits = config.hostSettings?.limits as LimiterLimits;
|
||||
|
||||
if (!limits || isLoading) {
|
||||
if (!LimitService || !limits || isLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -114,5 +120,5 @@ export const useLimiter = () => {
|
|||
errorIfWouldGoOverLimit: (limitName: string, metadata: Record<string, unknown> = {}): Promise<void> => limiter.errorIfWouldGoOverLimit(limitName, metadata),
|
||||
errorIfIsOverLimit: (limitName: string): Promise<void> => limiter.errorIfIsOverLimit(limitName)
|
||||
};
|
||||
}, [config.hostSettings?.limits, contributorUsers, fetchMembers, fetchNewsletters, helpLink, invites, isLoading, users]);
|
||||
}, [LimitService, config.hostSettings?.limits, contributorUsers, fetchMembers, fetchNewsletters, helpLink, invites, isLoading, users]);
|
||||
};
|
||||
|
|
|
@ -19,19 +19,19 @@ test.describe('Announcement Bar', async () => {
|
|||
});
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
|
||||
const section = page.getByTestId('announcement-bar');
|
||||
|
||||
|
||||
await section.getByRole('button', {name: 'Customize'}).click();
|
||||
|
||||
|
||||
const modal = page.getByTestId('announcement-bar-modal');
|
||||
|
||||
|
||||
// // Homepage and post preview
|
||||
|
||||
|
||||
await expect(modal.frameLocator('[data-testid="announcement-bar-preview"]').getByText('homepage preview')).toHaveCount(1);
|
||||
|
||||
|
||||
await modal.getByTestId('design-toolbar').getByRole('tab', {name: 'Post'}).click();
|
||||
|
||||
|
||||
await expect(modal.frameLocator('[data-testid="announcement-bar-preview"]').getByText('post preview')).toHaveCount(1);
|
||||
});
|
||||
|
||||
|
@ -51,9 +51,9 @@ test.describe('Announcement Bar', async () => {
|
|||
// await page.goto('/');
|
||||
|
||||
// const section = page.getByTestId('announcement-bar');
|
||||
|
||||
|
||||
// await section.getByRole('button', {name: 'Customize'}).click();
|
||||
|
||||
|
||||
// const modal = page.getByTestId('announcement-bar-modal');
|
||||
|
||||
// await expect(modal.frameLocator('[data-testid="announcement-bar-preview"]').getByText('homepage preview')).toHaveCount(1);
|
||||
|
@ -73,26 +73,26 @@ test.describe('Announcement Bar', async () => {
|
|||
|
||||
await section.getByRole('button', {name: 'Customize'}).click();
|
||||
|
||||
const labelElement = await page.$('label:text("Background color")');
|
||||
const labelElement = page.locator('label:text("Background color")');
|
||||
|
||||
expect(labelElement).not.toBeNull();
|
||||
await expect(labelElement).toHaveCount(1);
|
||||
|
||||
const modal = page.getByTestId('announcement-bar-modal');
|
||||
|
||||
// Check the titles of the buttons.
|
||||
// Get the parent div of the label
|
||||
const parentDiv = await labelElement?.$('xpath=..');
|
||||
const parentDiv = labelElement.locator('..');
|
||||
|
||||
// Then get the div that follows the label within the parent div
|
||||
const buttonContainer = await parentDiv?.$('div');
|
||||
const buttonContainer = parentDiv.locator('div');
|
||||
|
||||
const darkButton = await buttonContainer?.$('button[title="Dark"]');
|
||||
const lightButton = await buttonContainer?.$('button[title="Light"]');
|
||||
const accentButton = await buttonContainer?.$('button[title="Accent"]');
|
||||
const darkButton = buttonContainer.locator('button[title="Dark"]');
|
||||
const lightButton = buttonContainer.locator('button[title="Light"]');
|
||||
const accentButton = buttonContainer.locator('button[title="Accent"]');
|
||||
|
||||
expect(darkButton).not.toBeNull();
|
||||
expect(lightButton).not.toBeNull();
|
||||
expect(accentButton).not.toBeNull();
|
||||
await expect(darkButton).toHaveCount(1);
|
||||
await expect(lightButton).toHaveCount(1);
|
||||
await expect(accentButton).toHaveCount(1);
|
||||
|
||||
await lightButton?.click();
|
||||
|
||||
|
@ -119,9 +119,9 @@ test.describe('Announcement Bar', async () => {
|
|||
|
||||
await section.getByRole('button', {name: 'Customize'}).click();
|
||||
|
||||
const labelElement = await page.$('h6:text("Visibility")');
|
||||
const labelElement = page.locator('h6:text("Visibility")');
|
||||
|
||||
expect(labelElement).not.toBeNull();
|
||||
await expect(labelElement).toHaveCount(1);
|
||||
|
||||
const modal = page.getByTestId('announcement-bar-modal');
|
||||
|
||||
|
@ -142,4 +142,4 @@ test.describe('Announcement Bar', async () => {
|
|||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,17 +1,49 @@
|
|||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
import pkg from './package.json';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import svgr from 'vite-plugin-svgr';
|
||||
import {PluginOption} from 'vite';
|
||||
import {defineConfig} from 'vitest/config';
|
||||
import {resolve} from 'path';
|
||||
|
||||
const outputFileName = pkg.name[0] === '@' ? pkg.name.slice(pkg.name.indexOf('/') + 1) : pkg.name;
|
||||
|
||||
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 viteConfig() {
|
||||
return defineConfig({
|
||||
plugins: [
|
||||
svgr(),
|
||||
react()
|
||||
react(),
|
||||
externalPlugin({
|
||||
externals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM'
|
||||
}
|
||||
}),
|
||||
cssInjectedByJsPlugin()
|
||||
],
|
||||
define: {
|
||||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
|
||||
|
@ -23,8 +55,8 @@ export default (function viteConfig() {
|
|||
build: {
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
cssCodeSplit: true,
|
||||
lib: {
|
||||
formats: ['es'],
|
||||
entry: resolve(__dirname, 'src/index.tsx'),
|
||||
name: pkg.name,
|
||||
fileName(format) {
|
||||
|
@ -35,15 +67,6 @@ export default (function viteConfig() {
|
|||
return `${outputFileName}.js`;
|
||||
}
|
||||
},
|
||||
rollupOptions: {
|
||||
external: ['react', 'react-dom'],
|
||||
output: {
|
||||
globals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM'
|
||||
}
|
||||
}
|
||||
},
|
||||
commonjsOptions: {
|
||||
include: [/packages/, /node_modules/]
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import config from 'ghost-admin/config/environment';
|
|||
import {action} from '@ember/object';
|
||||
import {inject} from 'ghost-admin/decorators/inject';
|
||||
import {inject as service} from '@ember/service';
|
||||
import {tracked} from '@glimmer/tracking';
|
||||
|
||||
// TODO: Long term move asset management directly in AdminX
|
||||
const officialThemes = [{
|
||||
|
@ -213,9 +214,9 @@ const fetchKoenig = function () {
|
|||
const url = new URL(urlTemplate.replace('{version}', urlVersion));
|
||||
|
||||
if (url.protocol === 'http:') {
|
||||
await import(`http://${url.host}${url.pathname}`);
|
||||
window['@tryghost/admin-x-settings'] = await import(`http://${url.host}${url.pathname}`);
|
||||
} else {
|
||||
await import(`https://${url.host}${url.pathname}`);
|
||||
window['@tryghost/admin-x-settings'] = await import(`https://${url.host}${url.pathname}`);
|
||||
}
|
||||
|
||||
return window['@tryghost/admin-x-settings'];
|
||||
|
@ -264,6 +265,8 @@ export default class AdminXSettings extends Component {
|
|||
|
||||
@inject config;
|
||||
|
||||
@tracked display = 'none';
|
||||
|
||||
@action
|
||||
onError(error) {
|
||||
// ensure we're still showing errors in development
|
||||
|
|
|
@ -31242,7 +31242,7 @@ vite-plugin-commonjs@0.9.0:
|
|||
magic-string "^0.30.1"
|
||||
vite-plugin-dynamic-import "^1.5.0"
|
||||
|
||||
vite-plugin-css-injected-by-js@3.3.0:
|
||||
vite-plugin-css-injected-by-js@3.3.0, vite-plugin-css-injected-by-js@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vite-plugin-css-injected-by-js/-/vite-plugin-css-injected-by-js-3.3.0.tgz#c19480a9e42a95c5bced976a9dde1446f9bd91ff"
|
||||
integrity sha512-xG+jyHNCmUqi/TXp6q88wTJGeAOrNLSyUUTp4qEQ9QZLGcHWQQsCsSSKa59rPMQr8sOzfzmWDd8enGqfH/dBew==
|
||||
|
|
Loading…
Add table
Reference in a new issue