262 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| var __importDefault = (this && this.__importDefault) || function (mod) {
 | |
|     return (mod && mod.__esModule) ? mod : { "default": mod };
 | |
| };
 | |
| Object.defineProperty(exports, "__esModule", { value: true });
 | |
| exports.parseStyleAttributeValue = void 0;
 | |
| const template_safe_parser_1 = __importDefault(require("./template-safe-parser"));
 | |
| const postcss_1 = require("postcss");
 | |
| const compat_1 = require("../compat");
 | |
| function safeParseCss(css) {
 | |
|     try {
 | |
|         const input = new postcss_1.Input(css);
 | |
|         const parser = new template_safe_parser_1.default(input);
 | |
|         parser.parse();
 | |
|         return parser.root;
 | |
|     }
 | |
|     catch {
 | |
|         return null;
 | |
|     }
 | |
| }
 | |
| const cache = new WeakMap();
 | |
| function parseStyleAttributeValue(node, context) {
 | |
|     if (cache.has(node)) {
 | |
|         return cache.get(node) || null;
 | |
|     }
 | |
|     cache.set(node, null);
 | |
|     if (!node.value?.length) {
 | |
|         return null;
 | |
|     }
 | |
|     const startOffset = node.value[0].range[0];
 | |
|     const sourceCode = (0, compat_1.getSourceCode)(context);
 | |
|     const cssCode = node.value.map((value) => sourceCode.getText(value)).join('');
 | |
|     const root = safeParseCss(cssCode);
 | |
|     if (!root) {
 | |
|         return root;
 | |
|     }
 | |
|     const ctx = {
 | |
|         startOffset,
 | |
|         value: node.value,
 | |
|         context
 | |
|     };
 | |
|     const mustacheTags = node.value.filter((v) => v.type === 'SvelteMustacheTag');
 | |
|     const converted = convertRoot(root, mustacheTags, (e) => e.range, ctx);
 | |
|     cache.set(node, converted);
 | |
|     return converted;
 | |
| }
 | |
| exports.parseStyleAttributeValue = parseStyleAttributeValue;
 | |
| class IgnoreError extends Error {
 | |
| }
 | |
| function isStringLiteral(node) {
 | |
|     return node.type === 'Literal' && typeof node.value === 'string';
 | |
| }
 | |
| function convertRoot(root, interpolations, getRange, ctx) {
 | |
|     const nodes = [];
 | |
|     for (const child of root.nodes) {
 | |
|         const converted = convertChild(child, ctx);
 | |
|         if (!converted) {
 | |
|             return null;
 | |
|         }
 | |
|         while (interpolations[0]) {
 | |
|             const tagOrExpr = interpolations[0];
 | |
|             if (tagOrExpr.range[1] <= converted.range[0]) {
 | |
|                 nodes.push(buildSvelteStyleInline(tagOrExpr));
 | |
|                 interpolations.shift();
 | |
|                 continue;
 | |
|             }
 | |
|             if (tagOrExpr.range[0] < converted.range[1]) {
 | |
|                 try {
 | |
|                     converted.addInterpolation(tagOrExpr);
 | |
|                 }
 | |
|                 catch (e) {
 | |
|                     if (e instanceof IgnoreError)
 | |
|                         return null;
 | |
|                     throw e;
 | |
|                 }
 | |
|                 interpolations.shift();
 | |
|                 continue;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         nodes.push(converted);
 | |
|     }
 | |
|     nodes.push(...interpolations.map(buildSvelteStyleInline));
 | |
|     return {
 | |
|         type: 'root',
 | |
|         nodes
 | |
|     };
 | |
|     function buildSvelteStyleInline(tagOrExpr) {
 | |
|         const inlineStyles = new Map();
 | |
|         let range = null;
 | |
|         function getRangeForInline() {
 | |
|             if (range) {
 | |
|                 return range;
 | |
|             }
 | |
|             return range ?? (range = getRange(tagOrExpr));
 | |
|         }
 | |
|         return {
 | |
|             type: 'inline',
 | |
|             node: tagOrExpr,
 | |
|             get range() {
 | |
|                 return getRangeForInline();
 | |
|             },
 | |
|             get loc() {
 | |
|                 return toLoc(getRangeForInline(), ctx);
 | |
|             },
 | |
|             getInlineStyle(node) {
 | |
|                 return getInlineStyle(node);
 | |
|             },
 | |
|             getAllInlineStyles() {
 | |
|                 const allInlineStyles = new Map();
 | |
|                 for (const node of extractExpressions(tagOrExpr)) {
 | |
|                     const style = getInlineStyle(node);
 | |
|                     if (style) {
 | |
|                         allInlineStyles.set(node, style);
 | |
|                     }
 | |
|                 }
 | |
|                 return allInlineStyles;
 | |
|             }
 | |
|         };
 | |
|         function getInlineStyle(node) {
 | |
|             if (node.type === 'SvelteMustacheTag') {
 | |
|                 return getInlineStyle(node.expression);
 | |
|             }
 | |
|             if (inlineStyles.has(node)) {
 | |
|                 return inlineStyles.get(node) || null;
 | |
|             }
 | |
|             const sourceCode = (0, compat_1.getSourceCode)(ctx.context);
 | |
|             inlineStyles.set(node, null);
 | |
|             let converted;
 | |
|             if (isStringLiteral(node)) {
 | |
|                 const root = safeParseCss(sourceCode.getText(node).slice(1, -1));
 | |
|                 if (!root) {
 | |
|                     return null;
 | |
|                 }
 | |
|                 converted = convertRoot(root, [], () => [0, 0], {
 | |
|                     ...ctx,
 | |
|                     startOffset: node.range[0] + 1
 | |
|                 });
 | |
|             }
 | |
|             else if (node.type === 'TemplateLiteral') {
 | |
|                 const root = safeParseCss(sourceCode.getText(node).slice(1, -1));
 | |
|                 if (!root) {
 | |
|                     return null;
 | |
|                 }
 | |
|                 converted = convertRoot(root, [...node.expressions], (e) => {
 | |
|                     const index = node.expressions.indexOf(e);
 | |
|                     return [node.quasis[index].range[1] - 2, node.quasis[index + 1].range[0] + 1];
 | |
|                 }, {
 | |
|                     ...ctx,
 | |
|                     startOffset: node.range[0] + 1
 | |
|                 });
 | |
|             }
 | |
|             else {
 | |
|                 return null;
 | |
|             }
 | |
|             inlineStyles.set(node, converted);
 | |
|             return converted;
 | |
|         }
 | |
|         function* extractExpressions(node) {
 | |
|             if (node.type === 'SvelteMustacheTag') {
 | |
|                 yield* extractExpressions(node.expression);
 | |
|             }
 | |
|             else if (isStringLiteral(node)) {
 | |
|                 yield node;
 | |
|             }
 | |
|             else if (node.type === 'TemplateLiteral') {
 | |
|                 yield node;
 | |
|             }
 | |
|             else if (node.type === 'ConditionalExpression') {
 | |
|                 yield* extractExpressions(node.consequent);
 | |
|                 yield* extractExpressions(node.alternate);
 | |
|             }
 | |
|             else if (node.type === 'LogicalExpression') {
 | |
|                 yield* extractExpressions(node.left);
 | |
|                 yield* extractExpressions(node.right);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| function convertChild(node, ctx) {
 | |
|     const range = convertRange(node, ctx);
 | |
|     if (node.type === 'decl') {
 | |
|         const propRange = [range[0], range[0] + node.prop.length];
 | |
|         const declValueStartIndex = propRange[1] + (node.raws.between || '').length;
 | |
|         const valueRange = [
 | |
|             declValueStartIndex,
 | |
|             declValueStartIndex + (node.raws.value?.value || node.value).length
 | |
|         ];
 | |
|         const prop = {
 | |
|             name: node.prop,
 | |
|             range: propRange,
 | |
|             get loc() {
 | |
|                 return toLoc(propRange, ctx);
 | |
|             },
 | |
|             interpolations: []
 | |
|         };
 | |
|         const value = {
 | |
|             value: node.value,
 | |
|             range: valueRange,
 | |
|             get loc() {
 | |
|                 return toLoc(valueRange, ctx);
 | |
|             },
 | |
|             interpolations: []
 | |
|         };
 | |
|         const unknownInterpolations = [];
 | |
|         return {
 | |
|             type: 'decl',
 | |
|             prop,
 | |
|             value,
 | |
|             important: node.important,
 | |
|             range,
 | |
|             get loc() {
 | |
|                 return toLoc(range, ctx);
 | |
|             },
 | |
|             addInterpolation(tagOrExpr) {
 | |
|                 const index = tagOrExpr.range[0];
 | |
|                 if (prop.range[0] <= index && index < prop.range[1]) {
 | |
|                     prop.interpolations.push(tagOrExpr);
 | |
|                     return;
 | |
|                 }
 | |
|                 if (value.range[0] <= index && index < value.range[1]) {
 | |
|                     value.interpolations.push(tagOrExpr);
 | |
|                     return;
 | |
|                 }
 | |
|                 unknownInterpolations.push(tagOrExpr);
 | |
|             },
 | |
|             unknownInterpolations
 | |
|         };
 | |
|     }
 | |
|     if (node.type === 'comment') {
 | |
|         return {
 | |
|             type: 'comment',
 | |
|             range,
 | |
|             get loc() {
 | |
|                 return toLoc(range, ctx);
 | |
|             },
 | |
|             addInterpolation: () => {
 | |
|                 throw new IgnoreError();
 | |
|             }
 | |
|         };
 | |
|     }
 | |
|     if (node.type === 'atrule') {
 | |
|         return null;
 | |
|     }
 | |
|     if (node.type === 'rule') {
 | |
|         return null;
 | |
|     }
 | |
|     return null;
 | |
| }
 | |
| function convertRange(node, ctx) {
 | |
|     return [
 | |
|         ctx.startOffset + node.source.start.offset,
 | |
|         ctx.startOffset + node.source.end.offset + 1
 | |
|     ];
 | |
| }
 | |
| function toLoc(range, ctx) {
 | |
|     return {
 | |
|         start: (0, compat_1.getSourceCode)(ctx.context).getLocFromIndex(range[0]),
 | |
|         end: (0, compat_1.getSourceCode)(ctx.context).getLocFromIndex(range[1])
 | |
|     };
 | |
| }
 |