import { createSDTFEngine, isSDTFEngine, SDTFEngine, SpecifyDesignTokenFormat, TokenState, } from '@specifyapp/specify-design-token-format'; import { specifyErrors } from '../../../errors/index.js'; import { VectorDataBox } from '../../../parsersEngine/definitions/parsersEngineDataBox.js'; import { ParserToolbox } from '../../../parsersEngine/ParserToolbox.js'; import { retrieveSVGContentFromUrl } from './retrieveSVGContentFromUrl.js'; function generateVectorDataBox( tokensOrEngine: SpecifyDesignTokenFormat | SDTFEngine, toolbox: ParserToolbox, ): VectorDataBox { const sdtfEngine = isSDTFEngine(tokensOrEngine) ? tokensOrEngine : createSDTFEngine(tokensOrEngine); // @ts-ignore - Expression produces a union type that is too complex to represent. const tokenStates = sdtfEngine.query.run({ where: { token: '.*', withTypes: { include: ['vector', 'vectors'], }, select: true, }, }) as Array | TokenState<'vectors'>>; /* v8 ignore start */ const populateWarningMessageCallback = (svgVector: TokenState) => { toolbox.populateMessage({ type: 'warning', content: `The SVG vector ${svgVector.name} is not resolvable.`, errorKey: specifyErrors.PARSERS_ENGINE_PARSER_EXECUTION_FAILED.errorKey, }); return null; }; /* c8 ignore stop */ return { type: 'vector', assets: tokenStates.flatMap(tokenState => { if (!tokenState.isFullyResolvable) { populateWarningMessageCallback(tokenState); return []; } const tokensByMode = tokenState.matchJSONValueByType( { vector: v => [v], vectors: v => v.files, }, _ => undefined, ); if (!tokensByMode) return []; return Object.entries(tokensByMode).flatMap(([mode, tokens]) => tokens.map(token => ({ path: tokenState.path.toArray().slice(0), mode, url: token.url, provider: token.provider, format: token.format, token: tokenState.getJSONToken(), })), ); }, [] as Array), }; } export async function pullSvgContent( asset: VectorDataBox['assets'][number], toolbox: ParserToolbox, ) { if (asset.vector) return asset; if (asset.format === 'pdf') { toolbox.populateMessage({ type: 'warning', content: `The token at path "${asset.path.join('.')}" is a PDF, and for now they are not retrieved`, errorKey: specifyErrors.PARSERS_ENGINE_PARTIAL_OUTPUT.errorKey, }); return asset; } const rawSvg = await retrieveSVGContentFromUrl(asset.url); if (!rawSvg) { toolbox.populateMessage({ type: 'warning', content: `The SVG file at ${asset.url} could not be retrieved. It will be skipped.`, errorKey: specifyErrors.PARSERS_ENGINE_PARTIAL_OUTPUT.errorKey, }); return asset; } asset.vector = rawSvg; return asset; } export async function pullSvgContentInVectorDataBox( vectorDataBox: VectorDataBox, toolbox: ParserToolbox, ) { vectorDataBox.assets = await Promise.all( vectorDataBox.assets.map(v => pullSvgContent(v, toolbox)), ); return vectorDataBox; } export async function generateVectorDataBoxAndPullSvgsContent( tokensOrEngine: SpecifyDesignTokenFormat | SDTFEngine, toolbox: ParserToolbox, ) { return pullSvgContentInVectorDataBox(generateVectorDataBox(tokensOrEngine, toolbox), toolbox); }