UNPKG

purgetss

Version:

A package that simplifies mobile app creation for Titanium developers.

135 lines (125 loc) 4.53 kB
/** * PurgeTSS - TSS reader for the SVG pipeline * * Lightweight parser for the controlled TSS output PurgeTSS generates. Used by * the SVG image pipeline to resolve final width/height per class after a * regular purge run. NOT a general-purpose TSS parser — only the shapes the * purger emits are recognized. * * Recognized line shape: * '.classname': { prop: value, prop: value } * * Tag selectors (e.g. 'View': { ... }, 'ImageView[platform=ios]': { ... }) and * '#id': { ... } selectors are skipped — the SVG pipeline resolves only by * class cascade in V1. * * @fileoverview Class → properties map extracted from purged app.tss * @author César Estrada */ const CLASS_LINE = /^\s*'\.([^']+)'\s*:\s*\{([^}]*)\}\s*$/ /** * Parse the controlled TSS string emitted by the purger into a class → props * map. Values are returned in a normalized form: * * - number → finite numeric value (e.g. 128) * - 'auto' → Ti.UI.SIZE * - 'fill' → Ti.UI.FILL * - 'percent' → percentage or any other non-resolvable value * * Only the `width` and `height` properties are normalized; other props are * captured verbatim as the raw RHS string (callers don't currently need them). * * @param {string} tssContent - The full purged TSS (in-memory string). * @returns {Map<string, { width?: number|'auto'|'fill'|'percent', height?: number|'auto'|'fill'|'percent', _raw: string }>} */ export function parseTssMap(tssContent) { const map = new Map() if (typeof tssContent !== 'string' || !tssContent) return map const lines = tssContent.split(/\r?\n/) for (const line of lines) { const stripped = stripLineComment(line) const match = stripped.match(CLASS_LINE) if (!match) continue const className = match[1] const body = match[2] const props = parsePropBody(body) map.set(className, { ...props, _raw: body.trim() }) } return map } // Drop trailing `// comment` so it doesn't break the brace-matching regex. function stripLineComment(line) { let inSingle = false let inDouble = false for (let i = 0; i < line.length - 1; i++) { const c = line[i] if (c === '\'' && !inDouble) inSingle = !inSingle else if (c === '"' && !inSingle) inDouble = !inDouble else if (c === '/' && line[i + 1] === '/' && !inSingle && !inDouble) { return line.slice(0, i) } } return line } function parsePropBody(body) { const out = {} for (const pair of splitTopLevelCommas(body)) { const colon = findTopLevelColon(pair) if (colon === -1) continue const key = pair.slice(0, colon).trim() const value = pair.slice(colon + 1).trim() if (key === 'width' || key === 'height') { out[key] = normalizeDimensionValue(value) } } return out } function splitTopLevelCommas(body) { const out = [] let depth = 0 let inSingle = false let inDouble = false let last = 0 for (let i = 0; i < body.length; i++) { const c = body[i] if (c === '\'' && !inDouble) inSingle = !inSingle else if (c === '"' && !inSingle) inDouble = !inDouble else if (!inSingle && !inDouble) { if (c === '(' || c === '[' || c === '{') depth++ else if (c === ')' || c === ']' || c === '}') depth-- else if (c === ',' && depth === 0) { out.push(body.slice(last, i)) last = i + 1 } } } out.push(body.slice(last)) return out.filter(s => s.trim().length > 0) } function findTopLevelColon(pair) { let inSingle = false let inDouble = false let depth = 0 for (let i = 0; i < pair.length; i++) { const c = pair[i] if (c === '\'' && !inDouble) inSingle = !inSingle else if (c === '"' && !inSingle) inDouble = !inDouble else if (!inSingle && !inDouble) { if (c === '(' || c === '[' || c === '{') depth++ else if (c === ')' || c === ']' || c === '}') depth-- else if (c === ':' && depth === 0) return i } } return -1 } // Recognize: numeric literals, Ti.UI.SIZE/FILL, quoted percentages or other // non-numeric strings. Anything else falls into 'percent' (the catch-all for // "this can't be turned into a dp number"). The label is historic — it // originally only meant "string with %" — keeping it avoids ripple changes. function normalizeDimensionValue(raw) { const trimmed = raw.trim() if (/^-?\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed) if (/^Ti\.UI\.SIZE$/i.test(trimmed)) return 'auto' if (/^Ti\.UI\.FILL$/i.test(trimmed)) return 'fill' return 'percent' }