diorama-js 0.1.1 → 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/react.d.cts CHANGED
@@ -66,6 +66,11 @@ interface RenderOptions {
66
66
  projectType?: ProjectType;
67
67
  /** Override entry point detection. */
68
68
  entryPoint?: string;
69
+ /**
70
+ * Tailwind CSS handling. `'auto'` (default) loads Tailwind's Play CDN when the
71
+ * project is detected to use Tailwind; `true` forces it on; `false` disables it.
72
+ */
73
+ tailwind?: 'auto' | boolean;
69
74
  /** Glob patterns of files to include. */
70
75
  include?: string[];
71
76
  /** Glob patterns of files to exclude. */
@@ -125,6 +130,11 @@ interface DioramaProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad' | '
125
130
  frame?: FrameStyle;
126
131
  /** Allow click-to-expand to fill viewport. Default: `false`. */
127
132
  expand?: boolean;
133
+ /**
134
+ * Tailwind CSS handling. `'auto'` (default) loads Tailwind's Play CDN when the
135
+ * project uses Tailwind; `true` forces it on; `false` disables it.
136
+ */
137
+ tailwind?: 'auto' | boolean;
128
138
  /** Called when rendering completes. */
129
139
  onLoad?: () => void;
130
140
  /** Called on error. */
package/dist/react.d.ts CHANGED
@@ -66,6 +66,11 @@ interface RenderOptions {
66
66
  projectType?: ProjectType;
67
67
  /** Override entry point detection. */
68
68
  entryPoint?: string;
69
+ /**
70
+ * Tailwind CSS handling. `'auto'` (default) loads Tailwind's Play CDN when the
71
+ * project is detected to use Tailwind; `true` forces it on; `false` disables it.
72
+ */
73
+ tailwind?: 'auto' | boolean;
69
74
  /** Glob patterns of files to include. */
70
75
  include?: string[];
71
76
  /** Glob patterns of files to exclude. */
@@ -125,6 +130,11 @@ interface DioramaProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onLoad' | '
125
130
  frame?: FrameStyle;
126
131
  /** Allow click-to-expand to fill viewport. Default: `false`. */
127
132
  expand?: boolean;
133
+ /**
134
+ * Tailwind CSS handling. `'auto'` (default) loads Tailwind's Play CDN when the
135
+ * project uses Tailwind; `true` forces it on; `false` disables it.
136
+ */
137
+ tailwind?: 'auto' | boolean;
128
138
  /** Called when rendering completes. */
129
139
  onLoad?: () => void;
130
140
  /** Called on error. */
package/dist/react.js CHANGED
@@ -624,6 +624,7 @@ function analyzeProject(project, overrideType, overrideEntry) {
624
624
  hasBareImports: hasBareImports(files),
625
625
  isVite
626
626
  });
627
+ const usesTailwind = usesTailwindCSS(files);
627
628
  return {
628
629
  type,
629
630
  entryPoint: htmlEntry,
@@ -632,7 +633,8 @@ function analyzeProject(project, overrideType, overrideEntry) {
632
633
  hasJSX,
633
634
  hasTypeScript,
634
635
  jsEntryPoint: jsEntry,
635
- isVite
636
+ isVite,
637
+ usesTailwind
636
638
  };
637
639
  }
638
640
  function detectProjectType(input) {
@@ -654,6 +656,17 @@ function hasBareImports(files) {
654
656
  }
655
657
  return false;
656
658
  }
659
+ function usesTailwindCSS(files) {
660
+ for (const [path, content] of files) {
661
+ if (/(?:^|\/)tailwind\.config\.(?:js|cjs|mjs|ts)$/.test(path)) {
662
+ return true;
663
+ }
664
+ if (path.endsWith(".css") && /@tailwind\b|@apply\b/.test(content)) {
665
+ return true;
666
+ }
667
+ }
668
+ return false;
669
+ }
657
670
 
658
671
  // src/core/sandbox.ts
659
672
  function buildErrorHTML(message) {
@@ -1738,20 +1751,25 @@ var ASSET_IMPORT_EXTENSIONS = {
1738
1751
  ".otf": "font/otf"
1739
1752
  };
1740
1753
  function assembleHTML(options) {
1741
- const { project, config, transformedFiles, cdnProvider } = options;
1754
+ const { project, config, transformedFiles, cdnProvider, tailwind = "auto" } = options;
1742
1755
  const files = transformedFiles ?? project.files;
1756
+ let result;
1743
1757
  switch (config.type) {
1744
1758
  case "static":
1745
- return assembleStatic(files, project, config);
1759
+ result = assembleStatic(files, project, config);
1760
+ break;
1746
1761
  case "static-esm":
1747
1762
  case "jsx":
1748
1763
  case "typescript":
1749
1764
  case "jsx-typescript":
1750
1765
  case "vite":
1751
- return assembleESM(files, project, config, cdnProvider);
1766
+ result = assembleESM(files, project, config, cdnProvider);
1767
+ break;
1752
1768
  default:
1753
1769
  throw new AssemblyError(`Unsupported project type: ${config.type}`);
1754
1770
  }
1771
+ result.html = applyTailwindRuntime(result.html, project, config, tailwind);
1772
+ return result;
1755
1773
  }
1756
1774
  function assembleStatic(files, project, config) {
1757
1775
  let html = files.get(config.entryPoint);
@@ -2077,6 +2095,94 @@ ${css}`);
2077
2095
  }
2078
2096
  return { css: cssChunks.join("\n\n"), links };
2079
2097
  }
2098
+ var TAILWIND_DIRECTIVE_RE = /@tailwind\b|@apply\b/;
2099
+ var TAILWIND_CDN_URL = "https://cdn.tailwindcss.com";
2100
+ function hasTailwindDirectives(css) {
2101
+ return TAILWIND_DIRECTIVE_RE.test(css);
2102
+ }
2103
+ function applyTailwindRuntime(html, project, config, tailwind) {
2104
+ const enabled = tailwind === true || tailwind === "auto" && (config.usesTailwind ?? usesTailwindCSS(project.files));
2105
+ if (!enabled) return html;
2106
+ html = html.replace(
2107
+ /<style>([\s\S]*?)<\/style>/g,
2108
+ (match, css) => hasTailwindDirectives(css) ? `<style type="text/tailwindcss">${css}</style>` : match
2109
+ );
2110
+ let injection = `<script src="${TAILWIND_CDN_URL}"></script>`;
2111
+ const configObject = extractTailwindConfig(project.files);
2112
+ if (configObject) {
2113
+ injection += `
2114
+ <script>tailwind.config = ${configObject};</script>`;
2115
+ }
2116
+ return injectIntoHead(html, injection);
2117
+ }
2118
+ function extractTailwindConfig(files) {
2119
+ let source;
2120
+ for (const [path, content] of files) {
2121
+ if (/(?:^|\/)tailwind\.config\.(?:js|cjs|mjs|ts)$/.test(path)) {
2122
+ source = content;
2123
+ break;
2124
+ }
2125
+ }
2126
+ if (!source) return null;
2127
+ const object = extractBalancedObject(source);
2128
+ if (!object || !isSafeConfigObject(object)) return null;
2129
+ return object;
2130
+ }
2131
+ function extractBalancedObject(source) {
2132
+ const opener = source.match(/(?:export\s+default|module\.exports\s*=)\s*\{/);
2133
+ if (!opener || opener.index === void 0) return null;
2134
+ const start = opener.index + opener[0].length - 1;
2135
+ let depth = 0;
2136
+ let str = null;
2137
+ let lineComment = false;
2138
+ let blockComment = false;
2139
+ for (let i = start; i < source.length; i++) {
2140
+ const c = source[i];
2141
+ const n = source[i + 1];
2142
+ if (lineComment) {
2143
+ if (c === "\n") lineComment = false;
2144
+ continue;
2145
+ }
2146
+ if (blockComment) {
2147
+ if (c === "*" && n === "/") {
2148
+ blockComment = false;
2149
+ i++;
2150
+ }
2151
+ continue;
2152
+ }
2153
+ if (str) {
2154
+ if (c === "\\") {
2155
+ i++;
2156
+ continue;
2157
+ }
2158
+ if (c === str) str = null;
2159
+ continue;
2160
+ }
2161
+ if (c === "/" && n === "/") {
2162
+ lineComment = true;
2163
+ i++;
2164
+ continue;
2165
+ }
2166
+ if (c === "/" && n === "*") {
2167
+ blockComment = true;
2168
+ i++;
2169
+ continue;
2170
+ }
2171
+ if (c === '"' || c === "'" || c === "`") {
2172
+ str = c;
2173
+ continue;
2174
+ }
2175
+ if (c === "{") depth++;
2176
+ else if (c === "}") {
2177
+ depth--;
2178
+ if (depth === 0) return source.slice(start, i + 1);
2179
+ }
2180
+ }
2181
+ return null;
2182
+ }
2183
+ function isSafeConfigObject(object) {
2184
+ 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));
2185
+ }
2080
2186
  function injectImportMetaEnv(files) {
2081
2187
  const shim = `if(!import.meta.env){Object.defineProperty(import.meta,'env',{value:{MODE:'production',BASE_URL:'/',PROD:true,DEV:false,SSR:false}});}`;
2082
2188
  for (const [path, content] of files) {
@@ -2187,7 +2293,8 @@ var Diorama = class {
2187
2293
  project,
2188
2294
  config,
2189
2295
  transformedFiles: files,
2190
- cdnProvider: this.options.cdnProvider
2296
+ cdnProvider: this.options.cdnProvider,
2297
+ tailwind: options.tailwind ?? "auto"
2191
2298
  });
2192
2299
  return { html: html2, usesESM: usesESM2, repoName: repoName2 };
2193
2300
  };
@@ -2300,6 +2407,7 @@ var DioramaPreview = forwardRef(
2300
2407
  height = "500px",
2301
2408
  frame,
2302
2409
  expand,
2410
+ tailwind,
2303
2411
  onLoad,
2304
2412
  onError,
2305
2413
  options,
@@ -2326,6 +2434,7 @@ var DioramaPreview = forwardRef(
2326
2434
  height,
2327
2435
  frame,
2328
2436
  expand,
2437
+ tailwind,
2329
2438
  onLoad,
2330
2439
  onError: (err) => {
2331
2440
  onError?.(err);
@@ -2346,7 +2455,7 @@ var DioramaPreview = forwardRef(
2346
2455
  instanceRef.current?.destroy();
2347
2456
  instanceRef.current = null;
2348
2457
  };
2349
- }, [repo, branch, subdirectory, loading, placeholder, height, frame, expand]);
2458
+ }, [repo, branch, subdirectory, loading, placeholder, height, frame, expand, tailwind]);
2350
2459
  const containerStyle = {
2351
2460
  width: "100%",
2352
2461
  minHeight: height,