css-gradient-parser
Version:
a css gradient parser
1 lines • 14.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/linear.ts","../src/radial.ts","../src/conic.ts"],"sourcesContent":["export * from './linear.js'\nexport * from './radial.js'\nexport * from './conic.js'\nexport * from './type.js'","import { ColorStop } from \"./type.js\"\n\nexport function split(\n input: string,\n separator: string | RegExp = ','\n): string[] {\n const result = []\n let l = 0\n let parenCount = 0\n separator = new RegExp(separator)\n\n for (let i = 0; i < input.length; i++) {\n if (input[i] === '(') {\n parenCount++\n } else if (input[i] === ')') {\n parenCount--\n }\n\n if (parenCount === 0 && separator.test(input[i])) {\n result.push(input.slice(l, i).trim())\n l = i + 1\n }\n }\n\n result.push(input.slice(l).trim())\n\n return result\n}\n\nexport function resolveStops(v: string[]): ColorStop[] {\n const stops: ColorStop[] = []\n\n for (let i = 0, n = v.length; i < n;) {\n const [color, offset] = split(v[i], /\\s+/)\n\n if (isHint(v[i + 1])) {\n stops.push({\n color,\n offset: resolveLength(offset),\n hint: resolveLength(v[i + 1])\n })\n i += 2\n } else {\n stops.push({\n color,\n offset: resolveLength(offset)\n })\n i++\n }\n }\n\n return stops\n}\n\nconst REGEX = /^(-?\\d+\\.?\\d*)(%|vw|vh|px|em|rem|deg|rad|grad|turn)$/\n\nfunction isHint(v: string) {\n return REGEX.test(v)\n}\n\nexport function resolveLength(v?: string) {\n if (!v) return undefined\n\n const [, value, unit] = v.trim().match(REGEX) || []\n\n return { value, unit }\n}","import { split, resolveStops,resolveLength } from \"./utils.js\"\nimport { ColorStop } from './type.js'\n\ntype LinearOrientation = {\n type: 'directional'\n value: string\n} | {\n type: 'angular'\n value: { unit: string; value: string }\n}\n\nexport interface LinearResult {\n orientation: LinearOrientation\n repeating: boolean\n stops: ColorStop[]\n}\n\nexport function parseLinearGradient(input: string): LinearResult {\n if (!/^(repeating-)?linear-gradient/.test(input)) throw new SyntaxError(`could not find syntax for this item: ${input}`)\n\n let [, repeating, props] = input.match(/(repeating-)?linear-gradient\\((.+)\\)/)\n const result: LinearResult = {\n orientation: { type: 'directional', value: 'bottom' },\n repeating: Boolean(repeating),\n stops: []\n }\n\n const properties: string[] = split(props)\n const orientation = resolveLinearOrientation(properties[0])\n if (orientation) {\n result.orientation = orientation\n properties.shift()\n }\n\n return { ...result, stops: resolveStops(properties) }\n}\n\nfunction resolveLinearOrientation(angle: string): LinearOrientation {\n if (angle.startsWith('to ')) {\n return {\n type: 'directional',\n value: angle.replace('to ', '')\n }\n }\n\n if (['turn', 'deg', 'grad', 'rad'].some(unit => angle.endsWith(unit))) {\n return {\n type: 'angular',\n value: resolveLength(angle)\n }\n }\n\n return null\n}","import { resolveStops, split, resolveLength } from './utils.js'\nimport { ColorStop } from './type.js'\n\nexport type RgExtentKeyword = 'closest-corner' | 'closest-side' | 'farthest-corner' | 'farthest-side'\n\nexport type RadialPropertyValue = {\n type: 'keyword'\n value: string\n} | {\n type: 'length'\n value: { unit: string; value: string }\n}\n\nexport interface RadialResult {\n shape: 'circle' | 'ellipse'\n repeating: boolean\n size: RadialPropertyValue[]\n position: {\n x: RadialPropertyValue\n y: RadialPropertyValue\n }\n stops: ColorStop[]\n}\n\nconst rgExtentKeyword = new Set<RgExtentKeyword>([\n 'closest-corner',\n 'closest-side',\n 'farthest-corner',\n 'farthest-side'\n])\n\ntype PositionKeyWord = 'center' | 'left' | 'right' | 'top' | 'bottom'\n\nconst positionKeyword = new Set<PositionKeyWord>([\n 'center',\n 'left',\n 'top',\n 'right',\n 'bottom'\n])\n\n//eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction isRgExtentKeyword(v: any): v is RgExtentKeyword {\n return rgExtentKeyword.has(v)\n}\n\n//eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction isPositionKeyWord(v: any): v is PositionKeyWord {\n return positionKeyword.has(v)\n}\n\nfunction extendPosition(v: string[]) {\n const res = Array(2).fill('')\n for (let i = 0; i < 2; i++) {\n if (!v[i]) res[i] = 'center'\n else res[i] = v[i]\n }\n\n return res\n}\n\nexport function parseRadialGradient(input: string): RadialResult {\n if (!/(repeating-)?radial-gradient/.test(input)) throw new SyntaxError(`could not find syntax for this item: ${input}`)\n \n const [, repeating, props] = input.match(/(repeating-)?radial-gradient\\((.+)\\)/)\n const result: RadialResult = {\n shape: 'ellipse',\n repeating: Boolean(repeating),\n size: [{\n type: 'keyword',\n value: 'farthest-corner'\n }],\n position: {\n x: { type: 'keyword', value: 'center' },\n y: { type: 'keyword', value: 'center' }\n },\n stops: []\n }\n\n const properties = split(props)\n // handle like radial-gradient(rgba(0,0,0,0), #ee7621)\n if (isColor(properties[0])) {\n return { ...result, stops: resolveStops(properties) }\n }\n\n const prefix = properties[0].split('at').map(v => v.trim())\n\n const shape = ((prefix[0] || '').match(/(circle|ellipse)/) || [])[1]\n const size: string[] = (prefix[0] || '').match(/(-?\\d+\\.?\\d*(vw|vh|px|em|rem|%|rad|grad|turn|deg)?|closest-corner|closest-side|farthest-corner|farthest-side)/g) || []\n const position = extendPosition((prefix[1] || '').split(' '))\n\n if (!shape) {\n if (size.length === 1 && !isRgExtentKeyword(size[0])) {\n result.shape = 'circle'\n } else {\n result.shape = 'ellipse'\n }\n } else {\n result.shape = shape as RadialResult['shape']\n }\n\n if (size.length === 0) {\n size.push('farthest-corner')\n }\n\n result.size = size.map(v => {\n if (isRgExtentKeyword(v)) {\n return { type: 'keyword', value: v }\n } else {\n return { type: 'length', value: resolveLength(v) }\n }\n })\n\n result.position.x = isPositionKeyWord(position[0])\n ? { type: 'keyword', value: position[0] }\n : { type: 'length', value: resolveLength(position[0]) }\n\n result.position.y = isPositionKeyWord(position[1])\n ? { type: 'keyword', value: position[1] }\n : { type: 'length', value: resolveLength(position[1]) }\n\n if (shape || size.length > 0 || prefix[1]) properties.shift()\n\n return {\n ...result,\n stops: resolveStops(properties)\n }\n}\n\nfunction isColor(v: string) {\n if (/(circle|ellipse|at)/.test(v)) return false\n return /^(rgba?|hwb|hsl|lab|lch|oklab|color|#|[a-zA-Z]+)/.test(v)\n}","import { resolveStops, split } from \"./utils.js\"\nimport { ColorStop } from './type.js'\n\ntype RectColorSpace = 'srgb' | 'srgb-linear' | 'lab' | 'oklab' | 'xyz' | 'xyz-d50' | 'xyz-d65'\ntype PolarColorSpace = 'hsl' | 'hwb' | 'lch' | 'oklch'\ntype HueInterpolationMethod = `${'shorter' | 'longer' | 'increasing' | 'decreasing'} hue`\n\ninterface ConicGradient {\n angle: string\n repeating: boolean\n position: string\n color?: Color\n stops: ColorStop[]\n}\n\ntype Color = {\n space: RectColorSpace | PolarColorSpace\n method?: HueInterpolationMethod\n}\n\nconst set = new Set(['from', 'in', 'at'])\n\nexport function parseConicGradient(input: string): ConicGradient {\n if (!/(repeating-)?conic-gradient/.test(input)) throw new SyntaxError(`could not find syntax for this item: ${input}`)\n\n let [, repeating, props] = input.match(/(repeating-)?conic-gradient\\((.+)\\)/)\n const result: ConicGradient = {\n angle: '0deg',\n repeating: Boolean(repeating),\n position: 'center',\n stops: []\n }\n\n const properties = split(props).map(v => v.trim())\n\n const prefix = split(properties[0], /\\s+/)\n\n let k = ''\n let j = 0\n for (let i = 0, n = prefix.length; i < n; i++) {\n if (set.has(prefix[i])) {\n if (i > 0) {\n Object.assign(result, resolvePrefix(k, prefix, j, i))\n }\n k = prefix[i]\n j = i + 1\n }\n }\n\n if (k) {\n Object.assign(result, resolvePrefix(k, prefix, j, prefix.length))\n properties.shift()\n }\n\n return { ...result, stops: resolveStops(properties) }\n}\n\nfunction resolvePrefix(k: string, props: string[], start: number, end: number) {\n switch (k) {\n case 'from':\n return { angle: props.slice(start, end).join(' ') }\n case 'at':\n return { position: props.slice(start, end).join(' ') }\n case 'in': {\n const [ space, ...method ] = props.slice(start, end)\n return {\n color: {\n space: space as Color['space'],\n method: method.length > 0 ? method.join(' ') as HueInterpolationMethod : undefined\n }\n }\n }\n }\n}"],"mappings":"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","names":["src_exports","__export","parseConicGradient","parseLinearGradient","parseRadialGradient","__toCommonJS","split","input","separator","result","l","parenCount","resolveStops","v","stops","i","n","color","offset","isHint","resolveLength","REGEX","value","unit","parseLinearGradient","input","repeating","props","result","properties","split","orientation","resolveLinearOrientation","resolveStops","angle","unit","resolveLength","rgExtentKeyword","positionKeyword","isRgExtentKeyword","v","isPositionKeyWord","extendPosition","res","i","parseRadialGradient","input","repeating","props","result","properties","split","isColor","resolveStops","prefix","shape","size","position","resolveLength","set","parseConicGradient","input","repeating","props","result","properties","split","v","prefix","k","j","i","n","resolvePrefix","resolveStops","start","end","space","method"]}