├── .gitignore ├── README.md ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.js ├── App.test.js ├── challenges │ └── templates.js ├── deprecated │ ├── constants.js │ ├── getDemoConfig.js │ └── helpers.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js ├── tailwind.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn refatoring 2 | 3 | 👋 สวัสดี, repo นี้จะเป็นการฝึก refactor โค้ดคับ เหมาะทั้ง frontend และ backend engineer เลย 4 | 5 | - เขียน javascript พอได้ 6 | - ไม่จำเป็นต้องมีความรู้ React 7 | - ไม่จำเป็นต้องมีความรู้ CSS 8 | 9 | ## Introduction 10 | 11 | ผมตั้งใจสร้าง repo นี้ขึ้นมาเนื่องจากเป็นโค้ดที่ผ่านการใช้งานมาจริงๆ และเป็น business requirement ที่เกิดขึ้นจริง เหมาะสำหรับการฝึกฝนมากๆ หวังว่าจะเป็นประโยชน์กับทุกๆท่านที่ผ่านมาเห็นนะครับ 12 | 13 | โค้ดทั้งหมดผมได้มาจาก [MUI repository](https://github.com/mui/material-ui/blob/master/docs/src/modules/utils/getDemoConfig.js) 14 | 15 | หลังจาก clone โปรเจคแล้วลองรัน `yarn && yarn start` 16 | 17 | จะได้หน้าจอแบบนี้ขึ้นมา 18 | 19 | Screen Shot 2565-05-14 at 21 29 36 20 | 21 | ## How to practice 22 | 23 | เพื่อให้เสมือนจริงมากที่สุด ให้เริ่มทำความเข้าใจโค้ดเองทั้งหมดว่า application นี้เกี่ยวกับอะไร ทำงานอย่างไร จากนั้นให้อ่าน [Challenges](#challenges) แล้วเริ่มลงมือทำ 24 | 25 | ถ้าไม่เข้าใจจริงๆ ให้อ่าน hint ได้ 26 | 27 |
28 | 29 | 💡 hint 30 | 31 | Application นี้เป็นเว็บไซต์สำหรับนักพัฒนาเพื่อเข้ามาดูโค้ดตัวอย่างแล้วนำไปใช้ ในกรณีที่ต้องการทดสอบโค้ดตัวอย่างสามารถเลือกเปิด interactive demo ได้สองแบบผ่าน [CodeSandbox](https://codesandbox.io/) หรือ [StackBlitz](https://stackblitz.com/). 32 | 33 | **การทำงาน** 34 | 35 | - การทำงานทั้งหมดจะเริ่มจากไฟล์ `src/App.js` เมื่อ user กดปุ่ม ข้อมูลจะถูกประมวลผลให้อยู่ในรูปแบบที่ CodeSandbox หรือ StackBlitz ต้องการ 36 | - ข้อมูลนั้นจะนำมาใช้ในการเรียก API เพื่อสร้าง sandbox 37 | - [CodeSanbox API](https://codesandbox.io/docs/api/#define-api) 38 | - [StackBlitz API](https://developer.stackblitz.com/docs/platform/post-api/) 39 |
40 | 41 | ## Challenges 42 | 43 | ### 1. Bug fix 44 | 45 | เมื่อกดปุ่ม StackBlitz จะเห็นว่า demo ไม่สามารถทำงานได้ 46 | 47 | https://user-images.githubusercontent.com/18292247/168430133-8f3f5f06-65dc-4997-b5b2-549eadcc6899.mov 48 | 49 | **Requirement** 50 | 51 | ให้เพิ่ม `@babel/runtime` เข้าไปใน dependencies ของ StackBlitz เท่านั้น เมื่อกดปุ่ม StackBlitz จะต้องเห็น demo ที่ทำงานได้ถูกต้องทันที 52 | 53 | ### 2. Multiple products 54 | 55 | โค้ดตัวอย่างที่เห็นอยู่นั้นเป็น demo ของ Material UI ซึ่งเป็นหนึ่งในไลบรารี่ที่เรามี ทางบริษัทต้องการเพิ่มไลบรารี่ให้มากขึ้นโดยแต่ละไลบรารี่จะต้องมีไฟล์ template ที่แตกต่างกัน หากเปิดดูโค้ดที่อยู่ในไฟล์ `getDemoConfig.js` จะเห็นว่าฟังก์ชัน `tsDemo()` และ `jsDemo()` มี config เกี่ยวกับไฟล์ `index.js` ด้วยซึ่งเป็นส่วนหนึ่งของ template. 56 | 57 | ไฟล์ `src/challenges/templates.js` มีโค้ดตัวอย่างของอีกหนึ่งไลบรารี่ที่ชื่อ Joy UI. 58 | 59 | โจทย์คือให้ refactor โค้ด เพื่อนำ Joy UI มาแสดงเป็น demo และเปิด CodeSandbox กับ StackBlitz ได้ถูกต้อง 60 | 61 | > ในอนาคตจะมีไลบรารี่เพิ่มขึ้นอีกแน่นอน ฉะนั้นให้คำนึงถึงเรื่องนี้ตอน refactor โค้ดด้วย 62 | 63 | 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "learn-refactoring", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^13.0.0", 8 | "@testing-library/user-event": "^13.2.1", 9 | "@types/lz-string": "1.3.34", 10 | "lz-string": "1.4.4", 11 | "prism-react-renderer": "1.3.1", 12 | "react": "^18.1.0", 13 | "react-dom": "^18.1.0", 14 | "react-scripts": "5.0.1", 15 | "web-vitals": "^2.1.0" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "autoprefixer": "10.4.7", 43 | "postcss": "8.4.13", 44 | "tailwindcss": "3.0.24" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/React-in-Thai/learn-refactoring/b7511b6c9a242dbc04b82fdb374ce728ed81832b/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LZString from "lz-string"; 3 | import Highlight, { defaultProps } from "prism-react-renderer"; 4 | import getDemoConfig from "./deprecated/getDemoConfig"; 5 | 6 | function compress(object) { 7 | return LZString.compressToBase64(JSON.stringify(object)) 8 | .replace(/\+/g, "-") // Convert '+' to '-' 9 | .replace(/\//g, "_") // Convert '/' to '_' 10 | .replace(/=+$/, ""); // Remove ending '=' 11 | } 12 | 13 | function addHiddenInput(form, name, value) { 14 | const input = document.createElement("input"); 15 | input.type = "hidden"; 16 | input.name = name; 17 | input.value = value; 18 | form.appendChild(input); 19 | } 20 | 21 | const jsCode = `import * as React from 'react'; 22 | import Stack from '@mui/material/Stack'; 23 | import Button from '@mui/material/Button'; 24 | 25 | export default function BasicButtons() { 26 | return ( 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | }`; 34 | 35 | const tsCode = `import * as React from 'react'; 36 | import Stack from '@mui/material/Stack'; 37 | import Button from '@mui/material/Button'; 38 | 39 | export default function BasicButtons(): React.ReactElement { 40 | return ( 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | }`; 48 | 49 | const CODE = { 50 | JS: jsCode, 51 | TS: tsCode, 52 | }; 53 | 54 | function App() { 55 | const [codeVariant, setCodeVariant] = React.useState("JS"); 56 | const demoData = { 57 | title: "A demo", 58 | githubLocation: "", 59 | language: "en", 60 | codeVariant, 61 | raw: CODE[codeVariant], 62 | }; 63 | const handleCodeSandboxClick = () => { 64 | const demoConfig = getDemoConfig(demoData); 65 | const parameters = compress({ 66 | files: { 67 | "package.json": { 68 | content: { 69 | name: demoConfig.title, 70 | description: demoConfig.description, 71 | dependencies: demoConfig.dependencies, 72 | devDependencies: { 73 | "react-scripts": "latest", 74 | ...demoConfig.devDependencies, 75 | }, 76 | main: demoConfig.main, 77 | scripts: demoConfig.scripts, 78 | // We used `title` previously but only inference from `name` is documented. 79 | // TODO revisit once https://github.com/codesandbox/codesandbox-client/issues/4983 is resolved. 80 | title: demoConfig.title, 81 | }, 82 | }, 83 | ...Object.keys(demoConfig.files).reduce((files, name) => { 84 | files[name] = { content: demoConfig.files[name] }; 85 | return files; 86 | }, {}), 87 | }, 88 | }); 89 | 90 | const form = document.createElement("form"); 91 | form.method = "POST"; 92 | form.target = "_blank"; 93 | form.action = "https://codesandbox.io/api/v1/sandboxes/define"; 94 | addHiddenInput(form, "parameters", parameters); 95 | addHiddenInput( 96 | form, 97 | "query", 98 | codeVariant === "TS" ? "file=/demo.tsx" : "file=/demo.js" 99 | ); 100 | document.body.appendChild(form); 101 | form.submit(); 102 | document.body.removeChild(form); 103 | }; 104 | 105 | const handleStackBlitzClick = () => { 106 | const demoConfig = getDemoConfig(demoData, { 107 | indexPath: "index.html", 108 | previewPackage: false, 109 | }); 110 | const form = document.createElement("form"); 111 | form.method = "POST"; 112 | form.target = "_blank"; 113 | form.action = "https://stackblitz.com/run"; 114 | addHiddenInput(form, "project[template]", "create-react-app"); 115 | addHiddenInput(form, "project[title]", demoConfig.title); 116 | addHiddenInput( 117 | form, 118 | "project[description]", 119 | `# ${demoConfig.title}\n${demoConfig.description}` 120 | ); 121 | addHiddenInput( 122 | form, 123 | "project[dependencies]", 124 | JSON.stringify(demoConfig.dependencies) 125 | ); 126 | addHiddenInput( 127 | form, 128 | "project[devDependencies]", 129 | JSON.stringify(demoConfig.devDependencies) 130 | ); 131 | Object.keys(demoConfig.files).forEach((key) => { 132 | const value = demoConfig.files[key]; 133 | addHiddenInput(form, `project[files][${key}]`, value); 134 | }); 135 | document.body.appendChild(form); 136 | form.submit(); 137 | document.body.removeChild(form); 138 | }; 139 | return ( 140 |
141 |

{demoData.title}

142 | 143 | {({ className, style, tokens, getLineProps, getTokenProps }) => ( 144 |
145 |             {tokens.map((line, i) => (
146 |               
147 | {line.map((token, key) => ( 148 | 149 | ))} 150 |
151 | ))} 152 |
153 | )} 154 |
155 |
156 |
157 | 168 | 179 |
180 |
181 | 182 | 183 |
184 |
185 |
186 | ); 187 | } 188 | 189 | export default App; 190 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/challenges/templates.js: -------------------------------------------------------------------------------- 1 | export const joy = { 2 | JS: { 3 | "index.js": `import * as React from 'react'; 4 | import ReactDOM from 'react-dom/client'; 5 | import { StyledEngineProvider, CssVarsProvider } from '@mui/joy/styles'; 6 | import Demo from './demo'; 7 | 8 | ReactDOM.createRoot(document.querySelector("#root")).render( 9 | 10 | 11 | 12 | 13 | 14 | );`, 15 | "demo.js": `import * as React from 'react'; 16 | import Button from '@mui/joy/Button'; 17 | 18 | export default function BasicButtons() { 19 | return ( 20 | 21 | ); 22 | }`, 23 | }, 24 | TS: { 25 | "index.js": `import * as React from 'react'; 26 | import ReactDOM from 'react-dom/client'; 27 | import { StyledEngineProvider, CssVarsProvider } from '@mui/joy/styles'; 28 | import Demo from './demo'; 29 | 30 | ReactDOM.createRoot(document.querySelector("#root")).render( 31 | 32 | 33 | 34 | 35 | 36 | );`, 37 | "demo.js": `import * as React from 'react'; 38 | import Button from '@mui/joy/Button'; 39 | 40 | export default function BasicButtons(): React.ReactElement { 41 | return ( 42 | 43 | ); 44 | }`, 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/deprecated/constants.js: -------------------------------------------------------------------------------- 1 | export const CODE_VARIANTS = { 2 | JS: "JS", 3 | TS: "TS", 4 | }; 5 | -------------------------------------------------------------------------------- /src/deprecated/getDemoConfig.js: -------------------------------------------------------------------------------- 1 | import { CODE_VARIANTS } from "./constants"; 2 | import { getDependencies } from "./helpers"; 3 | 4 | function jsDemo(demoData, options) { 5 | return { 6 | dependencies: getDependencies(demoData.raw, { 7 | codeLanguage: CODE_VARIANTS.JS, 8 | muiCommitRef: 9 | process.env.PULL_REQUEST && options.previewPackage 10 | ? process.env.COMMIT_REF 11 | : undefined, 12 | }), 13 | files: { 14 | "demo.js": demoData.raw, 15 | "index.js": ` 16 | import * as React from 'react'; 17 | import ReactDOM from 'react-dom/client'; 18 | import { StyledEngineProvider } from '@mui/material/styles'; 19 | import Demo from './demo'; 20 | 21 | ReactDOM.createRoot(document.querySelector("#root")).render( 22 | 23 | 24 | 25 | ); 26 | `.trim(), 27 | }, 28 | }; 29 | } 30 | 31 | function tsDemo(demoData, options) { 32 | return { 33 | dependencies: getDependencies(demoData.raw, { 34 | codeLanguage: CODE_VARIANTS.TS, 35 | muiCommitRef: 36 | process.env.PULL_REQUEST && options.previewPackage 37 | ? process.env.COMMIT_REF 38 | : undefined, 39 | }), 40 | files: { 41 | "demo.tsx": demoData.raw, 42 | "index.tsx": ` 43 | import * as React from 'react'; 44 | import ReactDOM from 'react-dom/client'; 45 | import { StyledEngineProvider } from '@mui/material/styles'; 46 | import Demo from './demo'; 47 | 48 | ReactDOM.createRoot(document.querySelector("#root")).render( 49 | 50 | 51 | 52 | ); 53 | `.trim(), 54 | "tsconfig.json": `{ 55 | "compilerOptions": { 56 | "target": "es5", 57 | "lib": [ 58 | "dom", 59 | "dom.iterable", 60 | "esnext" 61 | ], 62 | "allowJs": true, 63 | "skipLibCheck": true, 64 | "esModuleInterop": true, 65 | "allowSyntheticDefaultImports": true, 66 | "strict": true, 67 | "forceConsistentCasingInFileNames": true, 68 | "module": "esnext", 69 | "moduleResolution": "node", 70 | "resolveJsonModule": true, 71 | "isolatedModules": true, 72 | "noEmit": true, 73 | "jsx": "react" 74 | }, 75 | "include": [ 76 | "src" 77 | ] 78 | } 79 | `, 80 | }, 81 | main: "index.tsx", 82 | scripts: { 83 | start: "react-scripts start", 84 | }, 85 | }; 86 | } 87 | 88 | function getLanguageConfig(demoData, options) { 89 | switch (demoData.codeVariant) { 90 | case CODE_VARIANTS.TS: 91 | return tsDemo(demoData, options); 92 | case CODE_VARIANTS.JS: 93 | return jsDemo(demoData, options); 94 | default: 95 | throw new Error(`Unsupported codeVariant: ${demoData.codeVariant}`); 96 | } 97 | } 98 | 99 | export default function getDemoConfig(demoData, options = {}) { 100 | const { indexPath = "public/index.html", previewPackage = true } = options; 101 | const baseConfig = { 102 | title: demoData.title, 103 | description: demoData.githubLocation, 104 | files: { 105 | [indexPath]: ` 106 | 107 | 108 | 109 | ${demoData.title} 110 | 111 | 115 | 116 | 120 | 121 | 122 |
123 | 124 | 125 | `.trim(), 126 | }, 127 | }; 128 | const languageConfig = getLanguageConfig(demoData, { 129 | previewPackage, 130 | }); 131 | 132 | return { 133 | ...baseConfig, 134 | ...languageConfig, 135 | files: { 136 | ...baseConfig.files, 137 | ...languageConfig.files, 138 | }, 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /src/deprecated/helpers.js: -------------------------------------------------------------------------------- 1 | import { CODE_VARIANTS } from "./constants"; 2 | 3 | /** 4 | * Mapping from the date adapter sub-packages to the npm packages they require. 5 | * @example `@mui/lab/AdapterDateFns` has a peer dependency on `date-fns`. 6 | */ 7 | const dateAdapterPackageMapping = { 8 | AdapterDateFns: "date-fns", 9 | AdapterDayjs: "dayjs", 10 | AdapterLuxon: "luxon", 11 | AdapterMoment: "moment", 12 | }; 13 | 14 | /** 15 | * @var 16 | * set of packages that ship their own typings instead of using @types/ namespace 17 | * Array because Set([iterable]) is not supported in IE11 18 | */ 19 | const packagesWithBundledTypes = [ 20 | "date-fns", 21 | "@emotion/react", 22 | "@emotion/styled", 23 | ]; 24 | 25 | /** 26 | * WARNING: Always uses `latest` typings. 27 | * 28 | * Adds dependencies to @types packages only for packages that are not listed 29 | * in packagesWithBundledTypes 30 | * 31 | * @see packagesWithBundledTypes in this module namespace 32 | * @param deps - list of dependency as `name => version` 33 | */ 34 | function addTypeDeps(deps) { 35 | const packagesWithDTPackage = Object.keys(deps) 36 | .filter((name) => packagesWithBundledTypes.indexOf(name) === -1) 37 | // All the MUI packages come with bundled types 38 | .filter((name) => name.indexOf("@mui/") !== 0); 39 | 40 | packagesWithDTPackage.forEach((name) => { 41 | let resolvedName = name; 42 | // scoped package? 43 | if (name.startsWith("@")) { 44 | // https://github.com/DefinitelyTyped/DefinitelyTyped#what-about-scoped-packages 45 | resolvedName = name.slice(1).replace("/", "__"); 46 | } 47 | 48 | deps[`@types/${resolvedName}`] = "latest"; 49 | }); 50 | } 51 | 52 | function includePeerDependencies(deps, versions) { 53 | let newDeps = { 54 | ...deps, 55 | "react-dom": versions["react-dom"], 56 | react: versions.react, 57 | "@emotion/react": versions["@emotion/react"], 58 | "@emotion/styled": versions["@emotion/styled"], 59 | }; 60 | 61 | if (newDeps["@mui/lab"]) { 62 | newDeps["@mui/material"] = versions["@mui/material"]; 63 | } 64 | 65 | if (newDeps["@material-ui/data-grid"]) { 66 | newDeps["@mui/material"] = versions["@mui/material"]; 67 | newDeps["@mui/styles"] = versions["@mui/styles"]; 68 | } 69 | 70 | // TODO: Where is this coming from and why does it need to be injected this way. 71 | if (window.muiDocConfig) { 72 | newDeps = window.muiDocConfig.csbIncludePeerDependencies(newDeps, { 73 | versions, 74 | }); 75 | } 76 | 77 | return newDeps; 78 | } 79 | 80 | /** 81 | * @param packageName - The name of a package living inside this repository. 82 | * @param commitRef 83 | * @return string - A valid version for a dependency entry in a package.json 84 | */ 85 | function getMuiPackageVersion(packageName, commitRef) { 86 | if ( 87 | commitRef === undefined || 88 | process.env.SOURCE_CODE_REPO !== "https://github.com/mui/material-ui" 89 | ) { 90 | // #default-branch-switch 91 | return "latest"; 92 | } 93 | const shortSha = commitRef.slice(0, 8); 94 | return `https://pkg.csb.dev/mui/material-ui/commit/${shortSha}/@mui/${packageName}`; 95 | } 96 | 97 | /** 98 | * @param raw - ES6 source with es module imports 99 | * @param options 100 | * @returns map of packages with their required version 101 | */ 102 | export function getDependencies(raw, options = {}) { 103 | const { codeLanguage, muiCommitRef } = options; 104 | 105 | let deps = {}; 106 | let versions = { 107 | react: "latest", 108 | "react-dom": "latest", 109 | "@emotion/react": "latest", 110 | "@emotion/styled": "latest", 111 | "@mui/material": "5.7.0", 112 | "@mui/icons-material": getMuiPackageVersion("icons-material", muiCommitRef), 113 | "@mui/lab": getMuiPackageVersion("lab", muiCommitRef), 114 | "@mui/styled-engine": getMuiPackageVersion("styled-engine", muiCommitRef), 115 | "@mui/styles": getMuiPackageVersion("styles", muiCommitRef), 116 | "@mui/system": getMuiPackageVersion("system", muiCommitRef), 117 | "@mui/private-theming": getMuiPackageVersion("theming", muiCommitRef), 118 | "@mui/private-classnames": getMuiPackageVersion("classnames", muiCommitRef), 119 | "@mui/base": getMuiPackageVersion("base", muiCommitRef), 120 | "@mui/utils": getMuiPackageVersion("utils", muiCommitRef), 121 | "@mui/material-next": getMuiPackageVersion("material-next", muiCommitRef), 122 | "@mui/joy": getMuiPackageVersion("joy", muiCommitRef), 123 | }; 124 | 125 | // TODO: Where is this coming from and why does it need to be injected this way. 126 | if (window.muiDocConfig) { 127 | versions = window.muiDocConfig.csbGetVersions(versions, { muiCommitRef }); 128 | } 129 | 130 | const re = /^import\s'([^']+)'|import\s[\s\S]*?\sfrom\s+'([^']+)/gm; 131 | let m = null; 132 | // eslint-disable-next-line no-cond-assign 133 | while ((m = re.exec(raw))) { 134 | const fullName = m[2] ?? m[1]; 135 | // handle scope names 136 | const name = 137 | fullName.charAt(0) === "@" 138 | ? fullName.split("/", 2).join("/") 139 | : fullName.split("/", 1)[0]; 140 | 141 | if (!deps[name]) { 142 | deps[name] = versions[name] ? versions[name] : "latest"; 143 | } 144 | 145 | // e.g date-fns 146 | const dateAdapterMatch = fullName.match(/^@mui\/lab\/(Adapter.*)/); 147 | if (dateAdapterMatch !== null) { 148 | const packageName = dateAdapterPackageMapping[dateAdapterMatch[1]]; 149 | if (packageName === undefined) { 150 | throw new TypeError( 151 | `Can't determine required npm package for adapter '${dateAdapterMatch[1]}'` 152 | ); 153 | } 154 | deps[packageName] = "latest"; 155 | } 156 | } 157 | 158 | deps = includePeerDependencies(deps, versions); 159 | 160 | if (codeLanguage === CODE_VARIANTS.TS) { 161 | addTypeDeps(deps); 162 | deps.typescript = "latest"; 163 | } 164 | 165 | if (!deps["@mui/material"]) { 166 | // The `index.js` imports StyledEngineProvider from '@mui/material', so we need to make sure we have it as a dependency 167 | const name = "@mui/material"; 168 | deps[name] = versions[name] ? versions[name] : "latest"; 169 | } 170 | 171 | return deps; 172 | } 173 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | margin: 0; 7 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 8 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 9 | sans-serif; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 16 | monospace; 17 | } 18 | 19 | button { 20 | padding: 0.75rem 1rem; 21 | border: 2px solid #121212; 22 | border-radius: 4px; 23 | outline: none; 24 | font-size: 1rem; 25 | background-color: #f5f5f5; 26 | } 27 | 28 | button:hover { 29 | background-color: antiquewhite; 30 | } 31 | 32 | pre { 33 | padding: 1rem; 34 | border-radius: 4px; 35 | overflow: auto; 36 | } 37 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 3 | theme: { 4 | extend: {}, 5 | }, 6 | plugins: [], 7 | }; 8 | --------------------------------------------------------------------------------