diorama-js 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/svelte.cjs CHANGED
@@ -623,6 +623,7 @@ function analyzeProject(project, overrideType, overrideEntry) {
623
623
  hasBareImports: hasBareImports(files),
624
624
  isVite
625
625
  });
626
+ const usesTailwind = usesTailwindCSS(files);
626
627
  return {
627
628
  type,
628
629
  entryPoint: htmlEntry,
@@ -631,7 +632,8 @@ function analyzeProject(project, overrideType, overrideEntry) {
631
632
  hasJSX,
632
633
  hasTypeScript,
633
634
  jsEntryPoint: jsEntry,
634
- isVite
635
+ isVite,
636
+ usesTailwind
635
637
  };
636
638
  }
637
639
  function detectProjectType(input) {
@@ -653,6 +655,17 @@ function hasBareImports(files) {
653
655
  }
654
656
  return false;
655
657
  }
658
+ function usesTailwindCSS(files) {
659
+ for (const [path, content] of files) {
660
+ if (/(?:^|\/)tailwind\.config\.(?:js|cjs|mjs|ts)$/.test(path)) {
661
+ return true;
662
+ }
663
+ if (path.endsWith(".css") && /@tailwind\b|@apply\b/.test(content)) {
664
+ return true;
665
+ }
666
+ }
667
+ return false;
668
+ }
656
669
 
657
670
  // src/core/sandbox.ts
658
671
  function buildErrorHTML(message) {
@@ -1507,6 +1520,18 @@ function getCDNProvider(name) {
1507
1520
  return esmShProvider;
1508
1521
  }
1509
1522
  }
1523
+ function buildCSSURL(specifier, options = {}) {
1524
+ const { dependencies = {}, cdnProvider = "esm.sh" } = options;
1525
+ const clean = specifier.replace(/[?#].*$/, "");
1526
+ const { packageName, subpath } = parseSpecifier(clean);
1527
+ const version = dependencies[packageName];
1528
+ const ver = version ? `@${version}` : "";
1529
+ const sub = subpath ? `/${subpath}` : "";
1530
+ if (cdnProvider === "unpkg") {
1531
+ return `https://unpkg.com/${packageName}${ver}${sub}`;
1532
+ }
1533
+ return `https://esm.sh/${packageName}${ver}${sub}`;
1534
+ }
1510
1535
  function rewriteImports(source, options = {}) {
1511
1536
  const {
1512
1537
  dependencies = {},
@@ -1520,6 +1545,9 @@ function rewriteImports(source, options = {}) {
1520
1545
  if (raw.startsWith(".") || raw.startsWith("/") || /^https?:\/\//.test(raw)) {
1521
1546
  return raw;
1522
1547
  }
1548
+ if (/\.css(?:[?#]|$)/.test(raw)) {
1549
+ return raw;
1550
+ }
1523
1551
  const { packageName, subpath } = parseSpecifier(raw);
1524
1552
  if (NODE_BUILTINS.has(packageName) || NODE_BUILTINS.has(packageName.replace("node:", ""))) {
1525
1553
  throw new NodeBuiltinError(packageName);
@@ -1722,20 +1750,25 @@ var ASSET_IMPORT_EXTENSIONS = {
1722
1750
  ".otf": "font/otf"
1723
1751
  };
1724
1752
  function assembleHTML(options) {
1725
- const { project, config, transformedFiles } = options;
1753
+ const { project, config, transformedFiles, cdnProvider, tailwind = "auto" } = options;
1726
1754
  const files = transformedFiles ?? project.files;
1755
+ let result;
1727
1756
  switch (config.type) {
1728
1757
  case "static":
1729
- return assembleStatic(files, project, config);
1758
+ result = assembleStatic(files, project, config);
1759
+ break;
1730
1760
  case "static-esm":
1731
1761
  case "jsx":
1732
1762
  case "typescript":
1733
1763
  case "jsx-typescript":
1734
1764
  case "vite":
1735
- return assembleESM(files, project, config);
1765
+ result = assembleESM(files, project, config, cdnProvider);
1766
+ break;
1736
1767
  default:
1737
1768
  throw new AssemblyError(`Unsupported project type: ${config.type}`);
1738
1769
  }
1770
+ result.html = applyTailwindRuntime(result.html, project, config, tailwind);
1771
+ return result;
1739
1772
  }
1740
1773
  function assembleStatic(files, project, config) {
1741
1774
  let html = files.get(config.entryPoint);
@@ -1749,10 +1782,13 @@ function assembleStatic(files, project, config) {
1749
1782
  html = inlineAssets(html, project, config.entryPoint);
1750
1783
  return { html, usesESM: false };
1751
1784
  }
1752
- function assembleESM(files, project, config) {
1785
+ function assembleESM(files, project, config, cdnProvider) {
1753
1786
  const isGenerated = config.entryPoint === "__generated__/index.html";
1754
1787
  rewriteAssetImports(files, project);
1755
- const cssFromJS = extractCSSImports(files);
1788
+ const { css: cssFromJS, links: cssLinks } = extractCSSImports(files, {
1789
+ dependencies: config.dependencies,
1790
+ cdnProvider
1791
+ });
1756
1792
  if (config.isVite) {
1757
1793
  injectImportMetaEnv(files);
1758
1794
  }
@@ -1771,6 +1807,9 @@ function assembleESM(files, project, config) {
1771
1807
  html = rewriteScriptSrcsToImportMap(html, config.entryPoint);
1772
1808
  html = inlineAssets(html, project, config.entryPoint);
1773
1809
  }
1810
+ for (const href of cssLinks) {
1811
+ html = injectIntoHead(html, `<link rel="stylesheet" href="${href}">`);
1812
+ }
1774
1813
  if (cssFromJS) {
1775
1814
  html = injectIntoHead(html, `<style>
1776
1815
  ${cssFromJS}
@@ -2020,20 +2059,32 @@ function rewriteAssetImports(files, project) {
2020
2059
  }
2021
2060
  }
2022
2061
  }
2023
- function extractCSSImports(files) {
2062
+ function extractCSSImports(files, options = {}) {
2024
2063
  const cssChunks = [];
2025
- const cssImportRe = /import\s+['"]([^'"]+\.css)['"]\s*;?/g;
2064
+ const links = [];
2065
+ const seenLinks = /* @__PURE__ */ new Set();
2066
+ const cssImportRe = /import\s+['"]([^'"]+\.css(?:[?#][^'"]*)?)['"]\s*;?/g;
2026
2067
  for (const [path, content] of files) {
2027
2068
  if (!/\.(js|ts|jsx|tsx|mjs)$/.test(path)) continue;
2028
2069
  let modified = content;
2029
2070
  let match;
2071
+ cssImportRe.lastIndex = 0;
2030
2072
  while ((match = cssImportRe.exec(content)) !== null) {
2031
- const cssPath = match[1];
2032
- const resolved = resolvePath(directoryOf(path), cssPath);
2033
- const css = files.get(resolved);
2034
- if (css) {
2035
- cssChunks.push(`/* ${resolved} */
2073
+ const specifier = match[1];
2074
+ const bare = specifier.replace(/[?#].*$/, "");
2075
+ if (bare.startsWith(".") || bare.startsWith("/")) {
2076
+ const resolved = resolvePath(directoryOf(path), bare);
2077
+ const css = files.get(resolved);
2078
+ if (css) {
2079
+ cssChunks.push(`/* ${resolved} */
2036
2080
  ${css}`);
2081
+ }
2082
+ } else {
2083
+ const href = /^https?:\/\//.test(bare) ? bare : buildCSSURL(bare, options);
2084
+ if (!seenLinks.has(href)) {
2085
+ seenLinks.add(href);
2086
+ links.push(href);
2087
+ }
2037
2088
  }
2038
2089
  modified = modified.replace(match[0], "");
2039
2090
  }
@@ -2041,7 +2092,95 @@ ${css}`);
2041
2092
  files.set(path, modified);
2042
2093
  }
2043
2094
  }
2044
- return cssChunks.join("\n\n");
2095
+ return { css: cssChunks.join("\n\n"), links };
2096
+ }
2097
+ var TAILWIND_DIRECTIVE_RE = /@tailwind\b|@apply\b/;
2098
+ var TAILWIND_CDN_URL = "https://cdn.tailwindcss.com";
2099
+ function hasTailwindDirectives(css) {
2100
+ return TAILWIND_DIRECTIVE_RE.test(css);
2101
+ }
2102
+ function applyTailwindRuntime(html, project, config, tailwind) {
2103
+ const enabled = tailwind === true || tailwind === "auto" && (config.usesTailwind ?? usesTailwindCSS(project.files));
2104
+ if (!enabled) return html;
2105
+ html = html.replace(
2106
+ /<style>([\s\S]*?)<\/style>/g,
2107
+ (match, css) => hasTailwindDirectives(css) ? `<style type="text/tailwindcss">${css}</style>` : match
2108
+ );
2109
+ let injection = `<script src="${TAILWIND_CDN_URL}"></script>`;
2110
+ const configObject = extractTailwindConfig(project.files);
2111
+ if (configObject) {
2112
+ injection += `
2113
+ <script>tailwind.config = ${configObject};</script>`;
2114
+ }
2115
+ return injectIntoHead(html, injection);
2116
+ }
2117
+ function extractTailwindConfig(files) {
2118
+ let source;
2119
+ for (const [path, content] of files) {
2120
+ if (/(?:^|\/)tailwind\.config\.(?:js|cjs|mjs|ts)$/.test(path)) {
2121
+ source = content;
2122
+ break;
2123
+ }
2124
+ }
2125
+ if (!source) return null;
2126
+ const object = extractBalancedObject(source);
2127
+ if (!object || !isSafeConfigObject(object)) return null;
2128
+ return object;
2129
+ }
2130
+ function extractBalancedObject(source) {
2131
+ const opener = source.match(/(?:export\s+default|module\.exports\s*=)\s*\{/);
2132
+ if (!opener || opener.index === void 0) return null;
2133
+ const start = opener.index + opener[0].length - 1;
2134
+ let depth = 0;
2135
+ let str = null;
2136
+ let lineComment = false;
2137
+ let blockComment = false;
2138
+ for (let i = start; i < source.length; i++) {
2139
+ const c = source[i];
2140
+ const n = source[i + 1];
2141
+ if (lineComment) {
2142
+ if (c === "\n") lineComment = false;
2143
+ continue;
2144
+ }
2145
+ if (blockComment) {
2146
+ if (c === "*" && n === "/") {
2147
+ blockComment = false;
2148
+ i++;
2149
+ }
2150
+ continue;
2151
+ }
2152
+ if (str) {
2153
+ if (c === "\\") {
2154
+ i++;
2155
+ continue;
2156
+ }
2157
+ if (c === str) str = null;
2158
+ continue;
2159
+ }
2160
+ if (c === "/" && n === "/") {
2161
+ lineComment = true;
2162
+ i++;
2163
+ continue;
2164
+ }
2165
+ if (c === "/" && n === "*") {
2166
+ blockComment = true;
2167
+ i++;
2168
+ continue;
2169
+ }
2170
+ if (c === '"' || c === "'" || c === "`") {
2171
+ str = c;
2172
+ continue;
2173
+ }
2174
+ if (c === "{") depth++;
2175
+ else if (c === "}") {
2176
+ depth--;
2177
+ if (depth === 0) return source.slice(start, i + 1);
2178
+ }
2179
+ }
2180
+ return null;
2181
+ }
2182
+ function isSafeConfigObject(object) {
2183
+ return !(/\brequire\s*\(/.test(object) || /\bimport\b/.test(object) || /=>/.test(object) || /\bfunction\b/.test(object) || /`/.test(object) || /\bprocess\b/.test(object) || /\b__dirname\b|\b__filename\b/.test(object));
2045
2184
  }
2046
2185
  function injectImportMetaEnv(files) {
2047
2186
  const shim = `if(!import.meta.env){Object.defineProperty(import.meta,'env',{value:{MODE:'production',BASE_URL:'/',PROD:true,DEV:false,SSR:false}});}`;
@@ -2152,7 +2291,9 @@ var Diorama = class {
2152
2291
  const { html: html2, usesESM: usesESM2 } = assembleHTML({
2153
2292
  project,
2154
2293
  config,
2155
- transformedFiles: files
2294
+ transformedFiles: files,
2295
+ cdnProvider: this.options.cdnProvider,
2296
+ tailwind: options.tailwind ?? "auto"
2156
2297
  });
2157
2298
  return { html: html2, usesESM: usesESM2, repoName: repoName2 };
2158
2299
  };
@@ -2272,6 +2413,7 @@ function dioramaAction(node, params) {
2272
2413
  height: opts.height ?? "500px",
2273
2414
  frame: opts.frame,
2274
2415
  expand: opts.expand,
2416
+ tailwind: opts.tailwind,
2275
2417
  onLoad: opts.onLoad,
2276
2418
  onError: opts.onError
2277
2419
  });