( ast: t.File, element: t.JSXElement, assetPath: string, )
| 1131 | }; |
| 1132 | |
| 1133 | function planReplacePlaceholder( |
| 1134 | ast: t.File, |
| 1135 | element: t.JSXElement, |
| 1136 | assetPath: string, |
| 1137 | ): PlaceholderEditPlan | { error: string } { |
| 1138 | const opening = element.openingElement; |
| 1139 | if (!t.isJSXIdentifier(opening.name) || opening.name.name !== 'ImagePlaceholder') { |
| 1140 | return { error: 'not a placeholder' }; |
| 1141 | } |
| 1142 | if (!assetPath.startsWith('./assets/') && !assetPath.startsWith('@assets/')) { |
| 1143 | return { error: 'asset path must start with ./assets/ or @assets/' }; |
| 1144 | } |
| 1145 | |
| 1146 | const hint = readJsxStringAttr(opening, 'hint') ?? ''; |
| 1147 | const width = readJsxNumberAttr(opening, 'width'); |
| 1148 | const height = readJsxNumberAttr(opening, 'height'); |
| 1149 | |
| 1150 | const { identifier, importSplice } = planAssetImport(ast, assetPath); |
| 1151 | |
| 1152 | const styleParts: string[] = []; |
| 1153 | if (width != null) styleParts.push(`width: ${width}`); |
| 1154 | else if (height != null) styleParts.push(`width: '100%'`); |
| 1155 | if (height != null) styleParts.push(`height: ${height}`); |
| 1156 | else if (width != null) styleParts.push(`height: '100%'`); |
| 1157 | styleParts.push(`objectFit: 'cover'`); |
| 1158 | styleParts.push(`objectPosition: '50% 50%'`); |
| 1159 | const replacement = |
| 1160 | `<img src={${identifier}} alt=${jsString(hint)} ` + `style={{ ${styleParts.join(', ')} }} />`; |
| 1161 | |
| 1162 | return { importSplice, elementSplice: spliceRange(element, replacement) }; |
| 1163 | } |
| 1164 | |
| 1165 | export function applyEdit( |
| 1166 | source: string, |
no test coverage detected