19b2597baSEvan Baconexport function pathToHtmlSafeName(path: string) { 29b2597baSEvan Bacon return path.replace(/[^a-zA-Z0-9_]/g, '_'); 39b2597baSEvan Bacon} 49b2597baSEvan Bacon 59b2597baSEvan Baconexport function getHotReplaceTemplate(id: string) { 69b2597baSEvan Bacon // In dev mode, we need to replace the style tag instead of appending it 79b2597baSEvan Bacon // use the path as the expo-css-hmr attribute to find the style tag 89b2597baSEvan Bacon // to replace. 99b2597baSEvan Bacon const attr = JSON.stringify(pathToHtmlSafeName(id)); 109b2597baSEvan Bacon return `style.setAttribute('data-expo-css-hmr', ${attr}); 119b2597baSEvan Bacon const previousStyle = document.querySelector('[data-expo-css-hmr=${attr}]'); 129b2597baSEvan Bacon if (previousStyle) { 139b2597baSEvan Bacon previousStyle.parentNode.removeChild(previousStyle); 149b2597baSEvan Bacon }`; 159b2597baSEvan Bacon} 169b2597baSEvan Bacon 179b2597baSEvan Baconexport function wrapDevelopmentCSS(props: { src: string; filename: string }) { 18*793fa844SEvan Bacon const withBackTicksEscaped = escapeBackticksAndOctals(props.src); 199b2597baSEvan Bacon return `(() => { 209b2597baSEvan Bacon if (typeof document === 'undefined') { 219b2597baSEvan Bacon return 229b2597baSEvan Bacon } 239b2597baSEvan Bacon const head = document.head || document.getElementsByTagName('head')[0]; 249b2597baSEvan Bacon const style = document.createElement('style'); 259b2597baSEvan Bacon ${getHotReplaceTemplate(props.filename)} 269b2597baSEvan Bacon style.setAttribute('data-expo-loader', 'css'); 279b2597baSEvan Bacon head.appendChild(style); 2879310320SEvan Bacon const css = \`${withBackTicksEscaped}\`; 299b2597baSEvan Bacon if (style.styleSheet){ 309b2597baSEvan Bacon style.styleSheet.cssText = css; 319b2597baSEvan Bacon } else { 329b2597baSEvan Bacon style.appendChild(document.createTextNode(css)); 339b2597baSEvan Bacon } 349b2597baSEvan Bacon})();`; 359b2597baSEvan Bacon} 36*793fa844SEvan Bacon 37*793fa844SEvan Baconexport function escapeBackticksAndOctals(str: string) { 38*793fa844SEvan Bacon if (typeof str !== 'string') { 39*793fa844SEvan Bacon return ''; 40*793fa844SEvan Bacon } 41*793fa844SEvan Bacon 42*793fa844SEvan Bacon return str 43*793fa844SEvan Bacon .replace(/\\/g, '\\\\') 44*793fa844SEvan Bacon .replace(/`/g, '\\`') 45*793fa844SEvan Bacon .replace(/[\0-\7]/g, (match) => `\\0${match.charCodeAt(0).toString(8)}`); 46*793fa844SEvan Bacon} 47