diff --git a/commands/reshowcase b/commands/reshowcase index 331af47..22f9e10 100755 --- a/commands/reshowcase +++ b/commands/reshowcase @@ -33,13 +33,48 @@ const getRequiredPathValue = (name) => { const task = process.argv[2]; -if (task !== "build" && task !== "start") { +if (task !== "build" && task !== "start" && task !== "generate-demo") { console.error( "You need to pass 'start' or 'build' command and path to the entry point.\nFor example: reshowcase start --entry=./example/Demo.bs.js" ); process.exit(1); } +if (task == "generate-demo") { + const prefix = "--examples-path="; + const examplesPathValue = process.argv.find((item) => item.startsWith(prefix))?.replace(prefix, ""); + const examplesPath = toAbsolutePath(examplesPathValue); + + function readJSFilesRecursively(dir, code = "", level) { + const files = fs.readdirSync(dir); + + return files.map(file => { + const filePath = path.join(dir, file); + const fileName = path.basename(filePath); + const stat = fs.statSync(filePath); + + if (stat.isDirectory() && !fileName.startsWith(".")) { + const categoryString = `addCategory("${fileName}", ({addDemo, addCategory: _}) => { + ${readJSFilesRecursively(filePath, code, level + 1)} + })`; + return level === 0 ? `demo(({addDemo: _, addCategory}) => ${categoryString} );` : categoryString; + } else if (filePath.endsWith('.re')) { + return `addDemo("${fileName.replace(/\.re$/, "")}", _ => React.null);` + } + }).join("\n") + }; + + const code = readJSFilesRecursively(examplesPath, "", 0) + + + console.log(`// generated from ${examplesPath} +open Reshowcase.Entry; +${code} +start();`) + + process.exit(0); +} + // Servers can handle paths to HTML files differently. // Allow using full path to HTML template in "src" attribute of iframe in case of possible issues. const useFullframeUrl = (() => { @@ -107,10 +142,32 @@ const watchPlugin = { }; // entryPoint passed to htmlPlugin must be relative to the current working directory -const entryPathRelativeToCwd = path.relative(process.cwd(), entryPath); +const demoEntry = path.relative(process.cwd(), "/Users/rusty/code/tools/reshowcase/_build/default/example/example/example/Demo__.js"); + +function readJSFilesRecursively(dir, fileList = []) { + const files = fs.readdirSync(dir); + + files.forEach(file => { + const filePath = path.join(dir, file); + const fileName = path.basename(filePath); + const stat = fs.statSync(filePath); + + if (stat.isDirectory() && !fileName.startsWith(".")) { + readJSFilesRecursively(filePath, fileList); + } else if (filePath.endsWith('.js') && path.normalize(demoEntry) != path.normalize(filePath)) { + fileList.push(filePath); + } + }); + + return fileList; +}; + +const startingDir = path.dirname(demoEntry); + +const storiesEntries = readJSFilesRecursively(startingDir); const defaultConfig = { - entryPoints: [entryPath], + entryPoints: [demoEntry, ...storiesEntries], entryNames: "[name]-[hash]", assetNames: "[name]-[hash]", chunkNames: "[name]-[hash]", @@ -148,25 +205,35 @@ const defaultConfig = { files: [ { filename: "index.html", - entryPoints: [entryPathRelativeToCwd], + entryPoints: [demoEntry], htmlTemplate: path.join(__dirname, "./ui-template.html"), scriptLoading: "module", }, - { - filename: "./demo/index.html", - entryPoints: [entryPathRelativeToCwd], - htmlTemplate: process.argv.find((item) => - item.startsWith("--template=") - ) - ? path.join( - process.cwd(), - process.argv - .find((item) => item.startsWith("--template=")) - .replace(/--template=/, "") - ) - : path.join(__dirname, "./demo-template.html"), - scriptLoading: "module", - }, + ...(storiesEntries.map(storyEntry => { + const relativePath = path.relative(path.dirname(demoEntry), storyEntry).replace(/\.js$/, ""); + console.log(relativePath); + return { + filename: `${relativePath}/index.html`, + entryPoints: [storyEntry], + htmlTemplate: path.join(__dirname, "./ui-template.html"), + scriptLoading: "module", + } + })) + // { + // filename: "./demo/index.html", + // entryPoints: [entryPathRelativeToCwd], + // htmlTemplate: process.argv.find((item) => + // item.startsWith("--template=") + // ) + // ? path.join( + // process.cwd(), + // process.argv + // .find((item) => item.startsWith("--template=")) + // .replace(/--template=/, "") + // ) + // : path.join(__dirname, "./demo-template.html"), + // scriptLoading: "module", + // }, ], }), ...(isBuild ? [] : [watchPlugin]), diff --git a/example/Buttons/Huge.re b/example/Buttons/Huge.re new file mode 100644 index 0000000..fbc0d92 --- /dev/null +++ b/example/Buttons/Huge.re @@ -0,0 +1,29 @@ +module Component = { + [@react.component] + let make = () => { + ; + }; +}; + +switch (ReactDOM.querySelector("body")) { +| Some(node) => + let root = ReactDOM.Client.createRoot(node); + ReactDOM.Client.render(root, ); +| None => + Js.Console.error( + "RR.renderToElementWithId : no element of id '" + ++ "body" + ++ "' found in the HTML.", + ) +}; diff --git a/example/Buttons/Normal.re b/example/Buttons/Normal.re new file mode 100644 index 0000000..06a143e --- /dev/null +++ b/example/Buttons/Normal.re @@ -0,0 +1,29 @@ +module Component = { + [@react.component] + let make = () => { + ; + }; +}; + +switch (ReactDOM.querySelector("body")) { +| Some(node) => + let root = ReactDOM.Client.createRoot(node); + ReactDOM.Client.render(root, ); +| None => + Js.Console.error( + "RR.renderToElementWithId : no element of id '" + ++ "body" + ++ "' found in the HTML.", + ) +}; diff --git a/example/Demo.re b/example/Demo.re deleted file mode 100644 index 877d3da..0000000 --- a/example/Demo.re +++ /dev/null @@ -1,180 +0,0 @@ -open Reshowcase.Entry; - -module Cn = { - let ifTrue = (cn, x) => x ? cn : ""; -}; - -let spaceConcat = (x1, x2) => - switch (x1, x2) { - | ("", x) - | (x, "") => x - | (x1, x2) => x1 ++ " " ++ x2 - }; - -let (+++) = spaceConcat; - -module Css = { - let button = [%cx - {| - color: #fff; - border: none; - padding: 10px; - border-radius: 10px; - font-family: inherit; - font-size: inherit; - |} - ]; - - let buttonHuge = [%cx {| - padding: 20px; - font-size: 30px; - |}]; - - let buttonDisabled = [%cx {| - cursor: default; - opacity: 0.5; - |}]; - - let buttonColor = color => { - let color = `hex(color); - [%cx {| - background-color: $(color); - |}]; - }; - - let h1Size = size => { - let fontSize = `px(size); - [%cx {| - font-size: $(fontSize); - |}]; - }; - - let code = [%cx - {| - white-space: pre; - padding: 0; - background-color: #f5f6f6; - |} - ]; -}; - -demo(({addDemo: _, addCategory}) => - addCategory("Buttons", ({addDemo, addCategory: _}) => { - addDemo("Normal", ({string, bool, _}) => { - let disabled = bool("Disabled", false); - let color = - string( - "Color", - ~options=[| - ("Red", "E02020"), - ("Green", "6DD400"), - ("Blue", "0091FF"), - |], - "0091FF", - ); - ; - }); - addDemo("Huge", ({string, bool, _}) => { - let disabled = bool("Disabled", false); - let color = - string( - "Color", - ~options=[| - ("Red", "E02020"), - ("Green", "6DD400"), - ("Blue", "0091FF"), - |], - "0091FF", - ); - ; - }); - }) -); - -demo(({addDemo: _, addCategory}) => - addCategory("Typography", ({addDemo: _, addCategory}) => { - addCategory("Headings", ({addDemo, addCategory: _}) => { - addDemo("H1", ({string, int, _}) => { - let size = - int("Font size", {min: 0, max: 100, initial: 30, step: 1}); - -

- {string("Text", "hello")->React.string} -

; - }); - addDemo("H2", ({string, _}) => -

{string("Text", "hello")->React.string}

- ); - }); - addCategory("Text", ({addDemo, addCategory: _}) => { - addDemo("Paragraph", ({string, _}) => -

{string("Text", "hello")->React.string}

- ); - addDemo("Italic", ({string, _}) => - {string("Text", "hello")->React.string} - ); - }); - }) -); - -demo(({addDemo, addCategory: _}) => - addDemo("Code example", _propsApi => - - {js|open Reshowcase.Entry; - -demo(({addDemo: _, addCategory}) => - addCategory("Typography", ({addDemo: _, addCategory}) => { - addCategory("Headings", ({addDemo, addCategory: _}) => { - addDemo("H1", ({string, int, _}) => { - let size = - int("Font size", {min: 0, max: 100, initial: 30, step: 1}); - -

- {string("Text", "hello")->React.string} -

; - }); - addDemo("H2", ({string, _}) => -

{string("Text", "hello")->React.string}

- ); - }); - addCategory("Text", ({addDemo, addCategory: _}) => { - addDemo("Paragraph", ({string, _}) => -

{string("Text", "hello")->React.string}

- ); - addDemo("Italic", ({string, _}) => - {string("Text", "hello")->React.string} - ); - }); - }) -);|js} - ->React.string -
- ) -); - -demo(({addDemo: _, addCategory}) => - addCategory("Test search", ({addDemo, addCategory: _}) => { - addDemo("OneTwoThreeFour", _ => React.null); - addDemo("OneTwoThreeFive", _ => React.null); - addDemo("OneTwoFourSeven", _ => React.null); - }) -); - -start(); diff --git a/example/Typography/H1.re b/example/Typography/H1.re new file mode 100644 index 0000000..ee71660 --- /dev/null +++ b/example/Typography/H1.re @@ -0,0 +1,29 @@ +module Component = { + [@react.component] + let make = () => { +

+ "Hello"->React.string +

; + }; +}; + +switch (ReactDOM.querySelector("body")) { +| Some(node) => + let root = ReactDOM.Client.createRoot(node); + ReactDOM.Client.render(root, ); +| None => + Js.Console.error( + "RR.renderToElementWithId : no element of id '" + ++ "body" + ++ "' found in the HTML.", + ) +}; diff --git a/example/dune b/example/dune index a2c3fa7..77fdb13 100644 --- a/example/dune +++ b/example/dune @@ -1,7 +1,22 @@ +(include_subdirs unqualified) + (melange.emit (target example) + (compile_flags -w -27) (alias example) (libraries reshowcase reason-react styled-ppx.melange) (module_systems es6) (preprocess (pps melange.ppx reason-react-ppx styled-ppx))) + +(rule + (target Demo__.re) + (deps + (glob_files_rec *[!D][!e][!m][!o][!_][!_].re)) + (action + (with-stdout-to + %{target} + (run + /Users/rusty/code/tools/reshowcase/commands/reshowcase + generate-demo + --examples-path=/Users/rusty/code/tools/reshowcase/example)))) diff --git a/src/ReshowcaseUi.re b/src/ReshowcaseUi.re index f4fb10d..1e57f08 100644 --- a/src/ReshowcaseUi.re +++ b/src/ReshowcaseUi.re @@ -925,19 +925,27 @@ module DemoUnitFrame = { [@react.component] let make = - (~queryString: string, ~responsiveMode, ~onLoad: Js.t('a) => unit) => { - let iframePath = if (useFullframeUrl) {"demo/index.html"} else {"demo"}; + ( + ~demoName: string, + ~categories, + ~responsiveMode, + ~onLoad: Js.t('a) => unit, + ) => { + let _iframePath = if (useFullframeUrl) {"demo/index.html"} else {"demo"}; + let iframePath = + "/" ++ (categories |> String.concat("/")) ++ "/" ++ demoName;