import type { DeriveBuiltInParserHandlerFromDefinition } from '../../internals/createBuiltInParserDefinition.js'; import type { ToSvgFileParserDefinition } from './definition.js'; import { SpecifyError, specifyErrors } from '../../../errors/index.js'; import { generateVectorDataBoxAndPullSvgsContent, pullSvgContent, } from '../../shared/to-svg-file/generateVectorDataBoxFiles.js'; import { createSDTFEngine, deepSetJSONValue, SDTFEngine, SpecifyDesignTokenFormat, TreePath, } from '@specifyapp/specify-design-token-format'; import { DEFAULT_FILENAME_TEMPLATE_WITH_MODES, DEFAULT_FILENAME_TEMPLATE_WITHOUT_MODES, makeFilename, } from '../../utils/filenameTemplateVariables.js'; import type { FilesOutput } from '../../../parsersEngine/definitions/parserOutput.js'; import type { VectorDataBox } from '../../../parsersEngine/index.js'; export const toSvgFileHandler: DeriveBuiltInParserHandlerFromDefinition< ToSvgFileParserDefinition > = async (previousDataBox, toolbox, parserOptions, outputOptions, _context) => { let tokenTree: SpecifyDesignTokenFormat; let sdtfEngine: SDTFEngine; let vectorAssets: VectorDataBox['assets']; switch (previousDataBox.type) { case 'vector': { vectorAssets = await Promise.all(previousDataBox.assets.map(v => pullSvgContent(v, toolbox))); tokenTree = vectorAssets.reduce((acc, svgData) => { deepSetJSONValue(acc, svgData.path, svgData.token); return acc; }, {} as SpecifyDesignTokenFormat); sdtfEngine = createSDTFEngine(tokenTree); break; } case 'SDTF': { tokenTree = previousDataBox.graph; sdtfEngine = createSDTFEngine(previousDataBox.graph); vectorAssets = (await generateVectorDataBoxAndPullSvgsContent(previousDataBox.graph, toolbox)) .assets; break; } case 'SDTF Engine': { tokenTree = previousDataBox.engine.renderJSONTree(); sdtfEngine = previousDataBox.engine; vectorAssets = ( await generateVectorDataBoxAndPullSvgsContent( previousDataBox.engine.renderJSONTree(), toolbox, ) ).assets; break; } default: throw new SpecifyError({ errorKey: specifyErrors.PARSERS_ENGINE_INVALID_PARSER_INPUT.errorKey, publicMessage: `${ (previousDataBox as any).type } is not a valid input for the to-svg-file parser.`, }); } if (outputOptions?.type !== 'directory') { throw new SpecifyError({ errorKey: specifyErrors.PARSERS_ENGINE_INVALID_OUTPUT_TYPE.errorKey, publicMessage: `The output type ${outputOptions?.type} is not supported by the to-svg-file parser.`, }); } const directoryPath = outputOptions.directoryPath.endsWith('/') ? outputOptions.directoryPath : `${outputOptions.directoryPath}/`; let output: FilesOutput['files'] = []; const filenamesSet = new Set(); for (const asset of vectorAssets) { if (asset.format === 'pdf') continue; const tokenState = sdtfEngine.query.getTokenState(new TreePath(asset.path)); if (!tokenState) throw new SpecifyError({ errorKey: specifyErrors.PARSERS_ENGINE_INVALID_PARSER_INPUT.errorKey, publicMessage: `Token path ${asset.path.join('.')} couldn't be found.`, }); const doesContainMultipleModes = tokenState.modes.length > 1; const filenameTemplate = parserOptions?.filenameTemplate ? parserOptions?.filenameTemplate : doesContainMultipleModes ? DEFAULT_FILENAME_TEMPLATE_WITH_MODES : DEFAULT_FILENAME_TEMPLATE_WITHOUT_MODES; const filename = makeFilename(filenameTemplate, tokenState, asset.mode, 'a.svg'); if (filenamesSet.has(filename)) { toolbox.populateMessage({ type: 'warning', content: `Duplicate filename "${filename}" from token "${tokenState.path.toString()}". File will not be added to result.`, errorKey: specifyErrors.PARSERS_ENGINE_PARSER_EXECUTION_FAILED.errorKey, }); } else { filenamesSet.add(filename); output.push({ path: `${directoryPath}${filename}`, content: { type: 'text', // Because we can only have svg here, it'll we be pulled, so we can force non-nullable text: (await pullSvgContent(asset, toolbox)).vector!, }, }); } } toolbox.populateOutput({ type: 'files', files: output, }); return { type: 'vector', assets: vectorAssets, }; };