UNPKG

@terrazzo/parser

Version:

Parser/validator for the Design Tokens Community Group (DTCG) standard.

166 lines 6.83 kB
import { isTokenMatch } from '@terrazzo/token-tools'; import wcmatch from 'wildcard-match'; import Logger from '../logger.js'; export const SINGLE_VALUE = 'SINGLE_VALUE'; export const MULTI_VALUE = 'MULTI_VALUE'; /** Validate plugin setTransform() calls for immediate feedback */ function validateTransformParams({ params, logger, pluginName, }) { const baseMessage = { group: 'plugin', label: pluginName, message: '' }; // validate value is valid for SINGLE_VALUE or MULTI_VALUE if (!params.value || (typeof params.value !== 'string' && typeof params.value !== 'object') || Array.isArray(params.value)) { logger.error({ ...baseMessage, message: `setTransform() value expected string or object of strings, received ${Array.isArray(params.value) ? 'Array' : typeof params.value}`, }); } if (typeof params.value === 'object' && Object.values(params.value).some((v) => typeof v !== 'string')) { logger.error({ ...baseMessage, message: 'setTransform() value expected object of strings, received some non-string values', }); } } /** * Run build stage */ export default async function build(tokens, { sources, logger = new Logger(), config }) { const formats = {}; const result = { outputFiles: [] }; function getTransforms(params) { return (formats[params.format] ?? []).filter((token) => { if (params.$type) { if (typeof params.$type === 'string' && token.token.$type !== params.$type) { return false; } else if (Array.isArray(params.$type) && !params.$type.some(($type) => token.token.$type === $type)) { return false; } } if (params.id && params.id !== '*' && !isTokenMatch(token.token.id, Array.isArray(params.id) ? params.id : [params.id])) { return false; } if (params.mode && !wcmatch(params.mode)(token.mode)) { return false; } return true; }); } // transform() let transformsLocked = false; // prevent plugins from transforming after stage has ended const startTransform = performance.now(); for (const plugin of config.plugins) { if (typeof plugin.transform === 'function') { await plugin.transform({ tokens, sources, getTransforms, setTransform(id, params) { if (transformsLocked) { logger.warn({ message: 'Attempted to call setTransform() after transform step has completed.', group: 'plugin', label: plugin.name, }); return; } const token = tokens[id]; // allow `undefined` values, but remove them here const cleanValue = typeof params.value === 'string' ? params.value : { ...params.value }; if (typeof cleanValue === 'object') { for (const k of Object.keys(cleanValue)) { if (cleanValue[k] === undefined) { delete cleanValue[k]; } } } validateTransformParams({ logger, params: { ...params, value: cleanValue }, pluginName: plugin.name, }); // upsert if (!formats[params.format]) { formats[params.format] = []; } const foundTokenI = formats[params.format].findIndex((t) => id === t.id && (!params.localID || params.localID === t.localID) && (!params.mode || params.mode === t.mode)); if (foundTokenI === -1) { formats[params.format].push({ ...params, id, value: cleanValue, type: typeof cleanValue === 'string' ? SINGLE_VALUE : MULTI_VALUE, mode: params.mode || '.', token: structuredClone(token), }); } else { formats[params.format][foundTokenI].value = cleanValue; formats[params.format][foundTokenI].type = typeof cleanValue === 'string' ? SINGLE_VALUE : MULTI_VALUE; } }, }); } } transformsLocked = true; logger.debug({ group: 'parser', label: 'transform', message: 'transform() step', timing: performance.now() - startTransform, }); // build() const startBuild = performance.now(); for (const plugin of config.plugins) { if (typeof plugin.build === 'function') { const pluginBuildStart = performance.now(); await plugin.build({ tokens, sources, getTransforms, outputFile(filename, contents) { const resolved = new URL(filename, config.outDir); if (result.outputFiles.some((f) => new URL(f.filename, config.outDir).href === resolved.href)) { logger.error({ group: 'plugin', message: `Can’t overwrite file "${filename}"`, label: plugin.name, }); } result.outputFiles.push({ filename, contents, plugin: plugin.name, time: performance.now() - pluginBuildStart, }); }, }); } } logger.debug({ group: 'parser', label: 'build', message: 'build() step', timing: performance.now() - startBuild, }); // buildEnd() const startBuildEnd = performance.now(); for (const plugin of config.plugins) { if (typeof plugin.buildEnd === 'function') { await plugin.buildEnd({ outputFiles: structuredClone(result.outputFiles) }); } } logger.debug({ group: 'parser', label: 'build', message: 'buildEnd() step', timing: performance.now() - startBuildEnd, }); return result; } //# sourceMappingURL=index.js.map