diff --git a/docs/src/modules/sandbox/CodeSandbox.test.js b/docs/src/modules/sandbox/CodeSandbox.test.js index 03a05904cf719d..e03269b5604239 100644 --- a/docs/src/modules/sandbox/CodeSandbox.test.js +++ b/docs/src/modules/sandbox/CodeSandbox.test.js @@ -72,7 +72,7 @@ describe('CodeSandbox', () => {
- + \n `, }, 'src/Demo.js': { @@ -129,12 +129,12 @@ ReactDOM.createRoot(document.querySelector("#root")).render( 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@types/react': 'latest', - '@types/react-dom': 'latest', typescript: 'latest', }, devDependencies: { 'react-scripts': 'latest', + '@types/react': 'latest', + '@types/react-dom': 'latest', }, main: 'index.tsx', scripts: { @@ -167,7 +167,7 @@ ReactDOM.createRoot(document.querySelector("#root")).render(
- + \n `, }, 'src/Demo.tsx': { @@ -234,14 +234,14 @@ ReactDOM.createRoot(document.querySelector("#root")!).render( '@emotion/styled': 'latest', // #npm-tag-reference '@mui/material': 'latest', - '@types/react': 'latest', - '@types/react-dom': 'latest', react: 'latest', 'react-dom': 'latest', typescript: 'latest', }); expect(result.devDependencies).to.deep.equal({ 'react-scripts': 'latest', + '@types/react': 'latest', + '@types/react-dom': 'latest', }); }); diff --git a/docs/src/modules/sandbox/CodeSandbox.ts b/docs/src/modules/sandbox/CodeSandbox.ts index 26f51d384a47e2..cfa766e4e269cb 100644 --- a/docs/src/modules/sandbox/CodeSandbox.ts +++ b/docs/src/modules/sandbox/CodeSandbox.ts @@ -7,6 +7,10 @@ import getFileExtension from 'docs/src/modules/sandbox/FileExtension'; import flattenRelativeImports from 'docs/src/modules/sandbox/FlattenRelativeImports'; import { DemoData, CodeVariant, CodeStyling } from 'docs/src/modules/sandbox/types'; +const CSB_DEV_DEPENDENCIES = { + 'react-scripts': 'latest', +}; + function compress(object: any) { return LZString.compressToBase64(JSON.stringify(object)) .replace(/\+/g, '-') // Convert '+' to '-' @@ -71,6 +75,7 @@ function createReactApp(demoData: DemoData) { const { dependencies, devDependencies } = SandboxDependencies(demoData, { commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: CSB_DEV_DEPENDENCIES, }); files['package.json'] = { @@ -165,6 +170,7 @@ ReactDOM.createRoot(document.querySelector("#root")${type}).render( }, { commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: CSB_DEV_DEPENDENCIES, }, ); @@ -254,6 +260,7 @@ ReactDOM.createRoot(document.querySelector("#root")${type}).render( }, { commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: CSB_DEV_DEPENDENCIES, }, ); diff --git a/docs/src/modules/sandbox/CreateReactApp.ts b/docs/src/modules/sandbox/CreateReactApp.ts index 81cce8b1a12cc5..5c259ca4872aaa 100644 --- a/docs/src/modules/sandbox/CreateReactApp.ts +++ b/docs/src/modules/sandbox/CreateReactApp.ts @@ -5,11 +5,13 @@ export const getHtml = ({ language, codeStyling, raw, + main, }: { title: string; language: string; codeStyling?: 'Tailwind' | 'MUI System'; raw?: string; + main?: string; }) => { return ` @@ -84,6 +86,7 @@ export const getHtml = ({
+ ${main ? `` : ''} `; }; diff --git a/docs/src/modules/sandbox/Dependencies.test.js b/docs/src/modules/sandbox/Dependencies.test.js index f9e016c90eec20..00a4100cdba248 100644 --- a/docs/src/modules/sandbox/Dependencies.test.js +++ b/docs/src/modules/sandbox/Dependencies.test.js @@ -138,7 +138,7 @@ import 'exceljs'; }); it('can collect required @types packages', () => { - const { dependencies } = SandboxDependencies({ + const { dependencies, devDependencies } = SandboxDependencies({ raw: s1, codeVariant: 'TS', }); @@ -153,16 +153,19 @@ import 'exceljs'; // #npm-tag-reference '@mui/material': 'latest', '@mui/base': 'latest', + typescript: 'latest', + }); + + expect(devDependencies).to.deep.equal({ '@types/foo-bar__bip': 'latest', '@types/prop-types': 'latest', '@types/react-dom': 'latest', '@types/react': 'latest', - typescript: 'latest', }); }); it('should handle @types correctly', () => { - const { dependencies } = SandboxDependencies({ + const { dependencies, devDependencies } = SandboxDependencies({ raw: `import utils from '../utils';`, codeVariant: 'TS', }); @@ -174,9 +177,12 @@ import 'exceljs'; '@emotion/styled': 'latest', // #npm-tag-reference '@mui/material': 'latest', + typescript: 'latest', + }); + + expect(devDependencies).to.deep.equal({ '@types/react-dom': 'latest', '@types/react': 'latest', - typescript: 'latest', }); }); @@ -533,8 +539,6 @@ export default function EmailExample() { '@mui/joy': 'latest', '@mui/material': 'latest', '@mui/system': 'latest', - '@types/react': 'latest', - '@types/react-dom': 'latest', typescript: 'latest', }); }); diff --git a/docs/src/modules/sandbox/Dependencies.ts b/docs/src/modules/sandbox/Dependencies.ts index f22e654ce872c5..e3b76448c9520a 100644 --- a/docs/src/modules/sandbox/Dependencies.ts +++ b/docs/src/modules/sandbox/Dependencies.ts @@ -19,7 +19,7 @@ const muiNpmOrgs = ['@mui', '@base_ui', '@pigment-css', '@toolpad']; * * @param deps - list of dependency as `name => version` */ -function addTypeDeps(deps: Record): void { +function addTypeDeps(deps: Record, devDeps: Record): void { const packagesWithDTPackage = Object.keys(deps) .filter((name) => !packagesWithBundledTypes.includes(name)) // All the MUI packages come with bundled types @@ -33,14 +33,17 @@ function addTypeDeps(deps: Record): void { resolvedName = name.slice(1).replace('/', '__'); } - deps[`@types/${resolvedName}`] = 'latest'; + devDeps[`@types/${resolvedName}`] = 'latest'; }); } type Demo = Pick; -export default function SandboxDependencies(demo: Demo, options?: { commitRef?: string }) { - const { commitRef } = options || {}; +export default function SandboxDependencies( + demo: Demo, + options?: { commitRef?: string; devDeps?: Record }, +) { + const { commitRef, devDeps = {} } = options || {}; /** * @param packageName - The name of a package living inside this repository. @@ -149,11 +152,6 @@ export default function SandboxDependencies(demo: Demo, options?: { commitRef?: const dependencies = extractDependencies(); - if (demo.codeVariant === CODE_VARIANTS.TS) { - addTypeDeps(dependencies); - dependencies.typescript = 'latest'; - } - if (!demo.productId && !dependencies['@mui/material']) { // The `index.js` imports StyledEngineProvider from '@mui/material', so we need to make sure we have it as a dependency const name = '@mui/material'; @@ -163,9 +161,12 @@ export default function SandboxDependencies(demo: Demo, options?: { commitRef?: dependencies[name] = versions[name] ? versions[name] : 'latest'; } - const devDependencies = { - 'react-scripts': 'latest', - }; + const devDependencies: Record = { ...devDeps }; + + if (demo.codeVariant === CODE_VARIANTS.TS) { + addTypeDeps(dependencies, devDependencies); + dependencies.typescript = 'latest'; + } return { dependencies, devDependencies }; } diff --git a/docs/src/modules/sandbox/StackBlitz.test.js b/docs/src/modules/sandbox/StackBlitz.test.js index a688a4bb833eee..d313dd40c7da7c 100644 --- a/docs/src/modules/sandbox/StackBlitz.test.js +++ b/docs/src/modules/sandbox/StackBlitz.test.js @@ -52,9 +52,32 @@ describe('StackBlitz', () => {
+ `, - 'Demo.js': `import * as React from 'react'; + 'package.json': `{ + "name": "mui-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "latest", + "@mui/material": "latest", + "react-dom": "latest", + "@emotion/react": "latest", + "@emotion/styled": "latest" + }, + "devDependencies": { + "@vitejs/plugin-react": "latest", + "vite": "latest" + } +}`, + 'src/Demo.js': `import * as React from 'react'; import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; @@ -68,7 +91,7 @@ export default function BasicButtons() { ); } `, - 'index.js': `import * as React from 'react'; + 'src/index.js': `import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { StyledEngineProvider } from '@mui/material/styles'; import Demo from './Demo'; @@ -80,6 +103,15 @@ ReactDOM.createRoot(document.querySelector("#root")).render( );`, + 'vite.config.js': ` +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + define: { 'process.env': {} }, +});`, }, dependencies: { react: 'latest', @@ -90,7 +122,8 @@ ReactDOM.createRoot(document.querySelector("#root")).render( '@emotion/styled': 'latest', }, devDependencies: { - 'react-scripts': 'latest', + '@vitejs/plugin-react': 'latest', + vite: 'latest', }, }); }); @@ -130,9 +163,35 @@ ReactDOM.createRoot(document.querySelector("#root")).render(
+ `, - 'Demo.tsx': `import * as React from 'react'; + 'package.json': `{ + "name": "mui-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "latest", + "@mui/material": "latest", + "react-dom": "latest", + "@emotion/react": "latest", + "@emotion/styled": "latest", + "typescript": "latest" + }, + "devDependencies": { + "@vitejs/plugin-react": "latest", + "vite": "latest", + "@types/react": "latest", + "@types/react-dom": "latest" + } +}`, + 'src/Demo.tsx': `import * as React from 'react'; import Stack from '@mui/material/Stack'; import Button from '@mui/material/Button'; @@ -146,7 +205,7 @@ export default function BasicButtons() { ); } `, - 'index.tsx': `import * as React from 'react'; + 'src/index.tsx': `import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { StyledEngineProvider } from '@mui/material/styles'; import Demo from './Demo'; @@ -158,32 +217,47 @@ ReactDOM.createRoot(document.querySelector("#root")!).render( );`, + 'tsconfig.json': `{ "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react" + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true }, - "include": [ - "src" - ] -} -`, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +}`, + 'tsconfig.node.json': `{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +}`, + 'vite.config.ts': ` +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + define: { 'process.env': {} }, +});`, }, dependencies: { react: 'latest', @@ -192,12 +266,13 @@ ReactDOM.createRoot(document.querySelector("#root")!).render( 'react-dom': 'latest', '@emotion/react': 'latest', '@emotion/styled': 'latest', - '@types/react': 'latest', - '@types/react-dom': 'latest', typescript: 'latest', }, devDependencies: { - 'react-scripts': 'latest', + '@types/react': 'latest', + '@types/react-dom': 'latest', + '@vitejs/plugin-react': 'latest', + vite: 'latest', }, }); }); diff --git a/docs/src/modules/sandbox/StackBlitz.ts b/docs/src/modules/sandbox/StackBlitz.ts index 5002b015ea93bc..aaf5664e70869f 100644 --- a/docs/src/modules/sandbox/StackBlitz.ts +++ b/docs/src/modules/sandbox/StackBlitz.ts @@ -1,41 +1,38 @@ import addHiddenInput from 'docs/src/modules/utils/addHiddenInput'; -import { CODE_VARIANTS } from 'docs/src/modules/constants'; import SandboxDependencies from 'docs/src/modules/sandbox/Dependencies'; -import * as CRA from 'docs/src/modules/sandbox/CreateReactApp'; import getFileExtension from 'docs/src/modules/sandbox/FileExtension'; import flattenRelativeImports from 'docs/src/modules/sandbox/FlattenRelativeImports'; import { CodeStyling, CodeVariant, DemoData } from 'docs/src/modules/sandbox/types'; +import * as CRA from 'docs/src/modules/sandbox/CreateReactApp'; + +function ensureExtension(file: string, extension: string): string { + return file.endsWith(`.${extension}`) ? file : `${file}.${extension}`; +} + +const VITE_DEV_DEPENDENCIES = { + '@vitejs/plugin-react': 'latest', + vite: 'latest', +}; function openStackBlitz({ title, description, - dependencies, - devDependencies, files, - codeVariant, initialFile, }: { title: string; description: string; - dependencies: Record; - devDependencies: Record; files: Record; - codeVariant: string; initialFile: string; }) { - const extension = codeVariant === CODE_VARIANTS.TS ? '.tsx' : '.js'; // ref: https://developer.stackblitz.com/docs/platform/post-api/ const form = document.createElement('form'); form.method = 'POST'; form.target = '_blank'; - form.action = `https://stackblitz.com/run?file=${initialFile}${ - initialFile.match(/(\.tsx|\.ts|\.js)$/) ? '' : extension - }`; - addHiddenInput(form, 'project[template]', 'create-react-app'); + form.action = `https://stackblitz.com/run?file=${initialFile}`; + addHiddenInput(form, 'project[template]', 'node'); addHiddenInput(form, 'project[title]', title); addHiddenInput(form, 'project[description]', `# ${title}\n${description}`); - addHiddenInput(form, 'project[dependencies]', JSON.stringify(dependencies)); - addHiddenInput(form, 'project[devDependencies]', JSON.stringify(devDependencies)); Object.keys(files).forEach((key) => { const value = files[key]; addHiddenInput(form, `project[files][${key}]`, value); @@ -45,55 +42,162 @@ function openStackBlitz({ document.body.removeChild(form); } -function createReactApp(demoData: DemoData) { +/** + * Create a Vite project config for StackBlitz + */ +function createViteFiles( + demoData: DemoData, + dependencies: Record = {}, + devDependencies: Record = {}, +): Record { const ext = getFileExtension(demoData.codeVariant); - const { title, githubLocation: description } = demoData; + return { + [`vite.config.${demoData.codeVariant === 'TS' ? 'ts' : 'js'}`]: ` +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; - const files: Record = { - 'index.html': CRA.getHtml(demoData), - [`index.${ext}`]: CRA.getRootIndex(demoData), - [`Demo.${ext}`]: flattenRelativeImports(demoData.raw), - // Spread the relative modules - ...(demoData.relativeModules && - // Transform the relative modules array into an object - demoData.relativeModules.reduce( - (acc, curr) => ({ - ...acc, - // Remove the path and keep the filename - [`${curr.module.replace(/^.*[\\/]/g, '')}`]: flattenRelativeImports(curr.raw), - }), - {}, - )), +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + define: { 'process.env': {} }, +});`, + 'index.html': CRA.getHtml({ ...demoData, main: `/src/index.${ext}` }), + 'package.json': JSON.stringify( + { + name: 'mui-demo', + private: true, + version: '0.0.0', + type: 'module', + scripts: { + dev: 'vite', + build: 'vite build', + preview: 'vite preview', + }, + dependencies, + devDependencies, + }, + null, + 2, + ), ...(demoData.codeVariant === 'TS' && { - 'tsconfig.json': CRA.getTsconfig(), + 'tsconfig.json': `{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +}`, + 'tsconfig.node.json': `{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +}`, }), }; +} + +/** + * Create a Material Template for StackBlitz using the SDK and Vite + */ +function createJoyTemplate(templateData: { + title: string; + files: Record; + githubLocation: string; + codeVariant: CodeVariant; + codeStyling?: CodeStyling; +}) { + const ext = getFileExtension(templateData.codeVariant); + const { title, githubLocation: description } = templateData; + const raw = Object.entries(templateData.files ?? {}).reduce( + (prev, curr) => `${prev}\n${curr}`, + '', + ); + + const demoData: DemoData = { codeStyling: 'MUI System', ...templateData, raw, language: 'en' }; + // Get dependencies const { dependencies, devDependencies } = SandboxDependencies(demoData, { - // Waiting for https://github.com/stackblitz/core/issues/437 - // commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: VITE_DEV_DEPENDENCIES, }); + // Create base Vite files with dependencies + const viteFiles = createViteFiles(demoData, dependencies, devDependencies); + + // Restructure template files to be under src/ + const templateSourceFiles = templateData.files + ? Object.fromEntries( + Object.entries(templateData.files).map(([key, value]) => [`src/${key}`, value]), + ) + : {}; + + // document.querySelector returns 'Element | null' but createRoot expects 'Element | DocumentFragment'. + const type = templateData.codeVariant === 'TS' ? '!' : ''; + + // Create a proper React 18 index file for Vite + const indexContent = `import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { StyledEngineProvider } from '@mui/joy/styles'; +import App from './App'; + +ReactDOM.createRoot(document.querySelector("#root")${type}).render( + + + + + +);`; + + // Combine all files + const files = { + ...viteFiles, + [`src/index.${ext}`]: indexContent, + ...templateSourceFiles, + }; + return { title, - description, files, dependencies, devDependencies, - openSandbox: (initialFile = `Demo.${ext}`) => { + replaceContent(updater: (content: string | Record, filePath: string) => string) { + Object.keys(files).forEach((filePath) => { + files[filePath] = updater(files[filePath], filePath); + }); + return this; + }, + openStackBlitz: (initialFile: string = `src/App`) => { openStackBlitz({ title, description, - dependencies, - devDependencies, files, - codeVariant: demoData.codeVariant, - initialFile, + initialFile: ensureExtension(initialFile, ext), }); }, }; } - +/** + * Create a Material Template for StackBlitz using the SDK and Vite + */ function createMaterialTemplate(templateData: { title: string; files: Record; @@ -103,44 +207,50 @@ function createMaterialTemplate(templateData: { }) { const ext = getFileExtension(templateData.codeVariant); const { title, githubLocation: description } = templateData; + const raw = Object.entries(templateData.files ?? {}).reduce( + (prev, curr) => `${prev}\n${curr}`, + '', + ); - // document.querySelector returns 'Element | null' but createRoot expects 'Element | DocumentFragment'. - const type = templateData.codeVariant === 'TS' ? '!' : ''; + const demoData: DemoData = { codeStyling: 'MUI System', ...templateData, raw, language: 'en' }; - const files: Record = { - 'index.html': CRA.getHtml({ - title: templateData.title, - language: 'en', - codeStyling: templateData.codeStyling ?? 'MUI System', - }), - [`index.${ext}`]: `import * as React from 'react'; + // Get dependencies + const { dependencies, devDependencies } = SandboxDependencies(demoData, { + commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: VITE_DEV_DEPENDENCIES, + }); + + // Create base Vite files with dependencies + const viteFiles = createViteFiles(demoData, dependencies, devDependencies); + + // Restructure template files to be under src/ + const templateSourceFiles = templateData.files + ? Object.fromEntries( + Object.entries(templateData.files).map(([key, value]) => [`src/${key}`, value]), + ) + : {}; + + // Create a proper React 18 index file for Vite + const indexContent = ` +import * as React from 'react'; import * as ReactDOM from 'react-dom/client'; import { StyledEngineProvider } from '@mui/material/styles'; import App from './App'; -ReactDOM.createRoot(document.querySelector("#root")${type}).render( +ReactDOM.createRoot(document.getElementById('root')${templateData.codeVariant === 'TS' ? '!' : ''}).render( -);`, - ...templateData.files, - ...(templateData.codeVariant === 'TS' && { - 'tsconfig.json': CRA.getTsconfig(), - }), - }; +);`; - const { dependencies, devDependencies } = SandboxDependencies( - { - codeVariant: templateData.codeVariant, - raw: Object.entries(templateData.files).reduce((prev, curr) => `${prev}\n${curr}`, ''), - }, - { - // Waiting for https://github.com/stackblitz/core/issues/437 - // commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, - }, - ); + // Combine all files + const files = { + ...viteFiles, + [`src/index.${ext}`]: indexContent, + ...templateSourceFiles, + }; return { title, @@ -153,20 +263,76 @@ ReactDOM.createRoot(document.querySelector("#root")${type}).render( }); return this; }, - openStackBlitz: (initialFile: string = '/App') => + openStackBlitz: (initialFile: string = `src/App`) => { openStackBlitz({ - title: templateData.title, + title, + description, files, + initialFile: ensureExtension(initialFile, ext), + }); + }, + }; +} + +/** + * Create a React App for StackBlitz using the SDK and Vite + * This maintains similar structure to the original createReactApp but uses Vite + */ +function createReactApp(demoData: DemoData) { + const ext = getFileExtension(demoData.codeVariant); + const { title, githubLocation: description } = demoData; + + // Get dependencies + const { dependencies, devDependencies } = SandboxDependencies(demoData, { + commitRef: process.env.PULL_REQUEST_ID ? process.env.COMMIT_REF : undefined, + devDeps: VITE_DEV_DEPENDENCIES, + }); + + const viteFiles = createViteFiles(demoData, dependencies, devDependencies); + + const demoFiles: Record = { + [`src/Demo.${ext}`]: flattenRelativeImports(demoData.raw), + }; + + // Add relative modules if any + const relativeModuleFiles = demoData.relativeModules + ? demoData.relativeModules.reduce( + (acc, curr) => ({ + ...acc, + // Add files to src directory but preserve original names + [`src/${curr.module.replace(/^.*[\\/]/g, '')}`]: flattenRelativeImports(curr.raw), + }), + {}, + ) + : {}; + + // Combine all files + const files = { + ...viteFiles, + [`src/index.${ext}`]: CRA.getRootIndex(demoData), + ...demoFiles, + ...relativeModuleFiles, + }; + + return { + title, + description, + files, + dependencies, + devDependencies, + openSandbox: (initialFile = 'src/Demo') => { + openStackBlitz({ + title, description, - dependencies, - devDependencies, - codeVariant: templateData.codeVariant, - initialFile, - }), + files, + initialFile: ensureExtension(initialFile, ext), + }); + }, }; } export default { + createJoyTemplate, createReactApp, createMaterialTemplate, };