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