import { useCallback, useEffect, useMemo, useState } from 'react'; import { applyStyle } from '@boost/cli'; import { Input, MultiSelect, Select, type SelectOptionLike } from '@boost/cli/react'; import { toArray } from '@boost/common'; import { BROWSER_TARGETS, DEFAULT_FORMATS, DEFAULT_INPUT, DEFAULT_SUPPORT, ELECTRON_TARGETS, NATIVE_TARGETS, NODE_SUPPORTED_VERSIONS, } from '../../constants'; import type { Format, PackemonPackageConfig, Platform, Support } from '../../types'; export interface PackageFormProps { onSubmit: (config: PackemonPackageConfig) => void; } export function getVersionsCombo(platforms: Platform[], support: Support): Set { const versions = new Set(); platforms.forEach((platform) => { switch (platform) { case 'native': versions.add(`Native (${NATIVE_TARGETS[support]}+)`); break; case 'node': versions.add(`Node v${NODE_SUPPORTED_VERSIONS[support]}+`); break; case 'electron': versions.add(`Electron v${ELECTRON_TARGETS[support]}+`); break; case 'browser': { const targets = support === 'experimental' ? ['last 2 versions'] : toArray(BROWSER_TARGETS[support]); versions.add(`Browser (${targets[0]})`); break; } // no default } }); return versions; } function getSupportVersions(platforms: Platform[], support: Support): string { return applyStyle([...getVersionsCombo(platforms, support)].sort().join(', '), 'muted'); } export function PackageForm({ onSubmit }: PackageFormProps) { const [platform, setPlatform] = useState([]); const [support, setSupport] = useState(DEFAULT_SUPPORT); const [format, setFormat] = useState('lib'); const [input, setInput] = useState(''); const [namespace, setNamespace] = useState(''); // Submit once all values are acceptable useEffect(() => { const hasUMD = format.includes('umd'); if ( platform.length > 0 && format && support && input && ((hasUMD && namespace) || (!hasUMD && !namespace)) ) { const result = { format, inputs: { index: input }, namespace, platform, support, }; // Delay submission or focus API crashes // https://github.com/vadimdemedes/ink/pull/404 setTimeout(() => { onSubmit(result); }, 100); // Reset state for the next package setPlatform([]); setSupport(DEFAULT_SUPPORT); setFormat('lib'); setInput(''); setNamespace(''); } }, [format, input, namespace, onSubmit, platform, support]); // PLATFORM const platformOptions = useMemo[]>( () => [ { label: 'Browsers', value: 'browser' }, { label: 'Electron', value: 'electron' }, { label: 'Node', value: 'node' }, { label: 'React Native', value: 'native' }, ], [], ); const validatePlatform = useCallback((value: Platform[]) => { if (value.length === 0) { throw new Error('Please select at least 1 platform'); } }, []); // SUPPORT const supportOptions = useMemo[]>( () => [ { label: `Legacy ${getSupportVersions(platform, 'legacy')}`, value: 'legacy', }, { label: `Stable ${getSupportVersions(platform, 'stable')}`, value: 'stable', }, { label: `Current ${getSupportVersions(platform, 'current')}`, value: 'current', }, { label: `Experimental ${getSupportVersions(platform, 'experimental')}`, value: 'experimental', }, ], [platform], ); const validateSupport = useCallback((value: Support) => { if (!value) { throw new Error('Please select a supported environment'); } }, []); // FORMAT const formatOptions = useMemo(() => { const options: SelectOptionLike[] = [{ label: 'Shared CommonJS', value: 'lib' }]; if (platform.includes('node')) { options.push( { label: `Node - CommonJS ${applyStyle('(.cjs)', 'muted')}`, value: 'cjs' }, { label: `Node - ECMAScript ${applyStyle('(.mjs)', 'muted')}`, value: 'mjs' }, ); } if (platform.includes('electron')) { options.push({ label: 'Electron - ECMAScript', value: 'esm' }); } if (platform.includes('browser')) { options.push( { label: 'Browser - ECMAScript', value: 'esm' }, { label: 'Browser - UMD', value: 'umd' }, ); } return options; }, [platform]); const validateFormat = useCallback((value: Format) => { if (!value) { throw new Error('Please select a format'); } }, []); // INPUT const validateInput = useCallback((value: string) => { if (!value?.match(/[-_a-z\d./\\]+/iu)) { throw new Error('Must be a valid file path'); } }, []); // NAMESPACE const validateNamespace = useCallback((value: string) => { if (!value?.match(/[a-z]\w+/iu)) { throw new Error('Must be in pascal-case or camel-case and start with an alpha character'); } }, []); return ( <> label="Platforms to target?" options={platformOptions} validate={validatePlatform} onSubmit={setPlatform} /> defaultSelected={DEFAULT_SUPPORT} label="Environment to support?" options={supportOptions} validate={validateSupport} onSubmit={setSupport} /> defaultSelected={DEFAULT_FORMATS[platform[0]]} label="Format to build?" options={formatOptions} validate={validateFormat} onSubmit={setFormat} /> {format.includes('umd') && ( )} ); }