1export function pathToHtmlSafeName(path: string) { 2 return path.replace(/[^a-zA-Z0-9_]/g, '_'); 3} 4 5export function getHotReplaceTemplate(id: string) { 6 // In dev mode, we need to replace the style tag instead of appending it 7 // use the path as the expo-css-hmr attribute to find the style tag 8 // to replace. 9 const attr = JSON.stringify(pathToHtmlSafeName(id)); 10 return `style.setAttribute('data-expo-css-hmr', ${attr}); 11 const previousStyle = document.querySelector('[data-expo-css-hmr=${attr}]'); 12 if (previousStyle) { 13 previousStyle.parentNode.removeChild(previousStyle); 14 }`; 15} 16 17export function wrapDevelopmentCSS(props: { src: string; filename: string }) { 18 const withBackTicksEscaped = escapeBackticksAndOctals(props.src); 19 return `(() => { 20 if (typeof document === 'undefined') { 21 return 22 } 23 const head = document.head || document.getElementsByTagName('head')[0]; 24 const style = document.createElement('style'); 25 ${getHotReplaceTemplate(props.filename)} 26 style.setAttribute('data-expo-loader', 'css'); 27 head.appendChild(style); 28 const css = \`${withBackTicksEscaped}\`; 29 if (style.styleSheet){ 30 style.styleSheet.cssText = css; 31 } else { 32 style.appendChild(document.createTextNode(css)); 33 } 34})();`; 35} 36 37export function escapeBackticksAndOctals(str: string) { 38 if (typeof str !== 'string') { 39 return ''; 40 } 41 42 return str 43 .replace(/\\/g, '\\\\') 44 .replace(/`/g, '\\`') 45 .replace(/[\0-\7]/g, (match) => `\\0${match.charCodeAt(0).toString(8)}`); 46} 47