diff --git a/data/resources.json b/data/resources.json new file mode 100644 index 000000000..9da497fee --- /dev/null +++ b/data/resources.json @@ -0,0 +1,2 @@ +[{"title":"JavaScript schema library from the Future 🧬","description":"ReScript Schema - The fastest parser in the entire JavaScript ecosystem with a focus on small bundle... Tagged with schema, typescript, rescript, opensource.","image":"https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn102ksd9w1xo5ysgxbur.png","url":"https://dev.to/dzakh/javascript-schema-library-from-the-future-5420"},{"title":"ReScript: A Better Typed JavaScript? (with Gabriel Nordeborn)","description":"ReScript is a strongly-typed programming language that compiles to JavaScript, and that puts it squarely in competition with TypeScript. So why would a JavaS...","image":"https://i.ytimg.com/vi/yKl2fSdnw7w/maxresdefault.jpg","url":"https://www.youtube.com/watch?v=yKl2fSdnw7w"},{"title":"GitHub - rescript-lang/awesome-rescript: A collection of materials about the ReScript programming language and toolchain.","description":"A collection of materials about the ReScript programming language and toolchain. - rescript-lang/awesome-rescript","image":"https://repository-images.githubusercontent.com/395642331/9d6aaef3-c81c-4156-8f65-45c3dbd011ac","url":"https://github.com/rescript-lang/awesome-rescript"},{"title":"An early look at @rescript/webapi","description":"Here's an early look at the new ReScript Web API bindings I've been working on.For more information, visit https://rescript-lang.github.io/experimental-rescr...","image":"https://i.ytimg.com/vi/MC-dbM-GEuw/maxresdefault.jpg","url":"https://www.youtube.com/watch?v=MC-dbM-GEuw"},{"title":"ReScript has come a long way, maybe it's time to switch from TypeScript?","description":"ReScript, the \"Fast, Simple, Fully Typed JavaScript from the Future\", has been around for awhile now.... Tagged with rescript, javascript, typescript, webdev.","image":"https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jrqcbteob11bc7nvwz9.jpg","url":"https://dev.to/jderochervlk/rescript-has-come-a-long-way-maybe-its-time-to-switch-from-typescript-29he"},{"title":"Create a Snake Game in ReScript","description":"Let's create a simple snake game in ReScript without using any framework.Full code here https://github.com/Exegetech/snake-rescript00:00 Getting started03:20...","image":"https://i.ytimg.com/vi/f0gDMjuaCZo/maxresdefault.jpg","url":"https://www.youtube.com/watch?v=f0gDMjuaCZo"},{"title":"ReScript and EdgeDB | Gel Blog","description":"Learn how together ReScript and EdgeDB achieve full type safety with less busy work. This post shows you the benefits and how you can get started with this pairing today!","image":"https://www.geldata.com/_images/_blog/ab9848d310c94bb25e21f219ee74b7f24ca16baa.jpg","url":"https://www.geldata.com/blog/rescript-and-edgedb"},{"title":"Building and consuming REST API in ReScript with rescript-rest and Fastify","description":"In the video, I show how you can use my ReScript Rest library to create an HTTP server with Fastify, generate OpenAPI for it, and then consume it on the clie...","image":"https://i.ytimg.com/vi/37FY6a-zY20/maxresdefault.jpg?sqp=-oaymwEmCIAKENAF8quKqQMa8AEB-AH-CYAC0AWKAgwIABABGEsgXShlMA8=&rs=AOn4CLBrz-ZJf8pcr_1_YZCfiMUwFKqj6A","url":"https://www.youtube.com/watch?v=37FY6a-zY20"},{"title":"When and Where to use ReScript? The ReScript happy path","description":"Are you ever hesitant about adopting ReScript, or have you tried it and been frustrated? I will give you a realistic guide for adopting ReScript in the project. (Hint: Review your project architecture first). Tagged with rescript, typescript, architecture.","image":"https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fool4835vdaqw8vueb9b5.png","url":"https://dev.to/cometkim/when-and-where-to-use-rescript-the-rescript-happy-path-47ni"},{"title":"From TypeScript To ReScript | Serhii Potapov (greyblake)","description":"A blog about software development.","image":"https://www.greyblake.com/greyblake.jpeg","url":"https://www.greyblake.com/blog/from-typescript-to-rescript/"},{"title":"Getting rid of your dead code in ReScript","description":"Exploring ReScript's tools for eliminating dead code, keeping your repository clean and without... Tagged with rescript.","image":"https://media2.dev.to/dynamic/image/width=1000,height=500,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12lsasc06v1a355i6rfk.jpeg","url":"https://dev.to/zth/getting-rid-of-your-dead-code-in-rescript-3mba"},{"title":"Rescript React Error boundary usage","description":"Hi I was trying to capture the react errors. I had to write the bindings for the ErrorBoundary and... Tagged with rescript, react.","image":"https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3otvb2z646ytpt1hl2rv.jpg","url":"https://dev.to/srikanthkyatham/rescript-react-error-boundary-usage-3b05"},{"title":"Full-stack ReScript. Architecture Overview","description":"Can ReScript be used to create a full-featured back-end? In this article, I’d try to prove it can and does it with success.\n","image":"","url":"https://fullsteak.dev/posts/fullstack-rescript-architecture-overview"},{"title":"ReScript for React Development","description":"Looking for ReScript for React Development information? In this article, I highlight the development & business advantages of ReScript.","image":"https://scalac.io/wp-content/uploads/2021/08/ReScript-for-React-Development-FB.png","url":"https://scalac.io/blog/rescript-for-react-development/"},{"title":"Rewriting a Project in ReScript","description":"My experience reimplementing a small project in ReScript","image":"","url":"https://yangdanny97.github.io/blog/2021/07/09/Migrating-to-Rescript"},{"title":"Responsive Images and Cumulative Layout Shift","description":"Solving cumulative layout shift issue caused by responsive images in layouts.","image":"https://d20bjcorj7xdk.cloudfront.net/eyJidWNrZXQiOiJpbWFnZXMuYWxleGZlZG9zZWV2LmNvbSIsImtleSI6Im1ldGEtYmxvZy5wbmciLCJlZGl0cyI6eyJyZXNpemUiOnsid2lkdGgiOjEyMDAsImhlaWdodCI6NjMwLCJmaXQiOiJjb3ZlciJ9LCJqcGVnIjp7InF1YWxpdHkiOjkwfX19?signature=d8e6c0ac1ff03d0f5ee04d128b96a7701b998952a38ba96e9a16e4414cd05ed0&version=58cfd6f8abdefeca2195a6a1f1108596","url":"https://alexfedoseev.com/blog/post/responsive-images-and-cumulative-layout-shift"},{"title":"ReScript records, NextJS, undefined and getStaticProps","description":"NextJS, a pretty solid framework for building React based websites and web-applications, offers a nic... Tagged with rescript, javascript, nextjs.","image":"https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3otvb2z646ytpt1hl2rv.jpg","url":"https://dev.to/ryyppy/rescript-records-nextjs-undefined-and-getstaticprops-4890"}] + \ No newline at end of file diff --git a/data/sidebar_community.json b/data/sidebar_community.json index 9f40e27f2..83d693cfd 100644 --- a/data/sidebar_community.json +++ b/data/sidebar_community.json @@ -1,8 +1,9 @@ { "Resources": [ "overview", + "content", "roadmap", "code-of-conduct", "translations" ] -} +} \ No newline at end of file diff --git a/package.json b/package.json index ef6303763..d6b9a9fdf 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ "test": "node scripts/test-examples.mjs && node scripts/test-hrefs.mjs", "reanalyze": "reanalyze -all-cmt .", "update-index": "npm run generate-llms && node scripts/extract-indices.mjs && node scripts/extract-tocs.mjs && node scripts/extract-syntax.mjs && node scripts/generate_feed.mjs > public/blog/feed.xml", - "generate-llms": "node scripts/generate_llms.mjs" + "generate-llms": "node scripts/generate_llms.mjs", + "generate-resources": "node scripts/generate_resources.mjs" }, "devDependencies": { "@mdx-js/react": "^2.3.0", @@ -78,4 +79,4 @@ "simple-functional-loader": "^1.2.1", "tailwindcss": "^3.3.3" } -} +} \ No newline at end of file diff --git a/pages/community/content.mdx b/pages/community/content.mdx new file mode 100644 index 000000000..84c68f3f6 --- /dev/null +++ b/pages/community/content.mdx @@ -0,0 +1,10 @@ +--- +title: "Content" +description: "Community Content" +canonical: "/community/content" +--- + + +import CommunityContent from 'src/CommunityContent.mjs'; + + diff --git a/pages/community/overview.mdx b/pages/community/overview.mdx index debfbe2c7..cfdd6cf03 100644 --- a/pages/community/overview.mdx +++ b/pages/community/overview.mdx @@ -4,7 +4,7 @@ description: "Community Resources Overview" canonical: "/community/overview" --- -# Community +# Community Overview ## Official Channels @@ -14,19 +14,11 @@ canonical: "/community/overview" - [Forum](https://forum.rescript-lang.org) - [ReScript GitHub Org](https://github.com/rescript-lang/) - [ReScript YouTube Channel](https://www.youtube.com/@rescriptlang) -- [ReScript Online Meetup on Guild](https://guild.host/rescript-online-meetup) -- [ReScript Online Meetup YouTube Channel](https://www.youtube.com/@ReScriptOnlineMeetup) News are broadcasted on this site's blog, on Bluesky and X. Some extra, less important news are also posted on the forum's [Announcements category](https://forum.rescript-lang.org/c/announcements/). **We don't use any other channel to communicate officially**. Any announcement made by users on Reddit, Discord, Medium and others don't necessarily represent our intent. -## Articles - -- [Getting rid of your dead code in ReScript](https://dev.to/zth/getting-rid-of-your-dead-code-in-rescript-3mba) -- [Speeding up ReScript compilation using interface files](https://dev.to/zth/speeding-up-rescript-compilation-using-interface-files-4fgn) -- Articles in [awesome-rescript](https://github.com/fhammerschmidt/awesome-rescript#readme) - ## Questions Your questions can go on: diff --git a/pages/docs/manual/v11.0.0/faq.mdx b/pages/docs/manual/v11.0.0/faq.mdx index 5400024fc..a2c6ec366 100644 --- a/pages/docs/manual/v11.0.0/faq.mdx +++ b/pages/docs/manual/v11.0.0/faq.mdx @@ -42,7 +42,11 @@ Our focus is a solid JS story right now. In the future, if there’s strong dema **What’s the current state of ReScript?** -Currently, we're actively working on the editor support. +We're working on the v12.0 release (see [v12 milestone](https://github.com/rescript-lang/rescript/milestone/16)). + +- Move the [Rescript Core](https://github.com/rescript-lang/rescript-core) standard library into the compiler / remove the OCaml standard library +- A new build system tailored to ReScript's needs ([rewatch](https://github.com/teamwalnut/rewatch)) for better monorepo support and even faster compilation speed +- Make it easier to create libraries for consumption from TypeScript with GenType **When will we get the `async/await` keywords?** diff --git a/pages/docs/manual/v12.0.0/faq.mdx b/pages/docs/manual/v12.0.0/faq.mdx index 60f7f39c6..d7efd476e 100644 --- a/pages/docs/manual/v12.0.0/faq.mdx +++ b/pages/docs/manual/v12.0.0/faq.mdx @@ -42,7 +42,11 @@ Our focus is a solid JS story right now. In the future, if there’s strong dema **What’s the current state of ReScript?** -Currently, we're actively working on the editor support. +We're working on the v12.0 release (see [v12 milestone](https://github.com/rescript-lang/rescript/milestone/16)). + +- Move the [Rescript Core](https://github.com/rescript-lang/rescript-core) standard library into the compiler / remove the OCaml standard library +- A new build system tailored to ReScript's needs ([rewatch](https://github.com/teamwalnut/rewatch)) for better monorepo support and even faster compilation speed +- Make it easier to create libraries for consumption from TypeScript with GenType **When will we get the `async/await` keywords?** diff --git a/scripts/generate_resources.res b/scripts/generate_resources.res new file mode 100644 index 000000000..d504c14dd --- /dev/null +++ b/scripts/generate_resources.res @@ -0,0 +1,50 @@ +/** This is the list of community content we want to generate. */ +/** If you have content you would like to add, please open up a PR adding the link to this list and then run `npm run generate-resources` */ +let urls = [ + // 2025 + "https://dev.to/dzakh/javascript-schema-library-from-the-future-5420", + "https://www.youtube.com/watch?v=yKl2fSdnw7w", + "https://github.com/rescript-lang/awesome-rescript", // regardless of age this seems like it should always be near the top + // 2024 + "https://www.youtube.com/watch?v=MC-dbM-GEuw", + "https://dev.to/jderochervlk/rescript-has-come-a-long-way-maybe-its-time-to-switch-from-typescript-29he", + "https://www.youtube.com/watch?v=f0gDMjuaCZo", + "https://www.geldata.com/blog/rescript-and-edgedb", + "https://www.youtube.com/watch?v=37FY6a-zY20", + // 2023 + "https://dev.to/cometkim/when-and-where-to-use-rescript-the-rescript-happy-path-47ni", + // 2022 + "https://www.greyblake.com/blog/from-typescript-to-rescript/", + "https://dev.to/zth/getting-rid-of-your-dead-code-in-rescript-3mba", + "https://www.youtube.com/watch?v=KDL-kRgilkQ", + "https://dev.to/srikanthkyatham/rescript-react-error-boundary-usage-3b05", + // "https://www.daggala.com/belt_vs_js_array_in_rescript/" I think we should exclude this one since it's related to API we are deprecating + // 2021 + "https://fullsteak.dev/posts/fullstack-rescript-architecture-overview", + "https://scalac.io/blog/rescript-for-react-development/", + "https://yangdanny97.github.io/blog/2021/07/09/Migrating-to-Rescript", + "https://alexfedoseev.com/blog/post/responsive-images-and-cumulative-layout-shift", + "https://dev.to/ryyppy/rescript-records-nextjs-undefined-and-getstaticprops-4890", +] + +let generate = async () => { + let filePath = "data/resources.json" + let metaData = await MetaTagsApi.getMetaTags(urls) + + let fileContent = `${metaData + ->Array.map(i => + { + "title": `${i.title->Option.getOr("")}`, + "description": `${i.description->Option.getOr("")}`, + "image": `${i.image->Option.getOr("")}`, + "url": `${i.url}`, + } + ) + ->JSON.stringifyAny + ->Option.getOr("[]")} + ` + + Node.Fs.writeFileSync(filePath, fileContent) +} + +await generate() diff --git a/src/CommunityContent.res b/src/CommunityContent.res new file mode 100644 index 000000000..0f9ff49ac --- /dev/null +++ b/src/CommunityContent.res @@ -0,0 +1,68 @@ +type link = { + url: string, + title: string, + description: string, + image: string, +} + +@module("../data/resources.json") +external resources: array = "default" + +let simplifyUrl = url => + url + ->String.replace("https://", "") + ->String.replace("http://", "") + ->String.split("/") + ->Array.at(0) + +module LinkCard = { + @react.component + let make = (~link) => { +
+ + +
+

{React.string(link.title)}

+

{React.string(link.description)}

+
+

+ {React.string(link.url->simplifyUrl->Option.getOr(""))} +

+
+
+ } +} + +module LinkCards = { + @react.component + let make = () => { +
+ {resources + ->Array.map(link => + switch link.image { + | "" => {...link, image: "/static/Art-3-rescript-launch.jpg"} + | _ => link + } + ) + ->Array.map(link => ) + ->React.array} +
+ } +} + +@react.component +let make = () => { +
+

{"Community Content"->React.string}

+

+ {React.string( + "These articles, videos, and resources are created by the amazing ReScript community.", + )} +
+ {React.string("If you have a resource you'd like to share, please feel free to submit a PR!")} +

+ +
+} + +let default = make diff --git a/src/bindings/Jsdom.res b/src/bindings/Jsdom.res new file mode 100644 index 000000000..5021bab42 --- /dev/null +++ b/src/bindings/Jsdom.res @@ -0,0 +1,5 @@ +type window = {document: Dom.document} +type t = {window: window} + +@module("jsdom") @new +external make: string => t = "JSDOM" diff --git a/src/bindings/Webapi.res b/src/bindings/Webapi.res index 0cbc09f93..ab53ceb41 100644 --- a/src/bindings/Webapi.res +++ b/src/bindings/Webapi.res @@ -2,6 +2,11 @@ module Document = { @val external document: Dom.element = "document" @scope("document") @val external createElement: string => Dom.element = "createElement" @scope("document") @val external createTextNode: string => Dom.element = "createTextNode" + @send + external querySelector: (Dom.document, string) => Nullable.t = "querySelector" + @send + external querySelectorAll: (Dom.document, string) => Js.Array2.array_like = + "querySelectorAll" } module ClassList = { @@ -17,6 +22,7 @@ module Element = { @get external classList: Dom.element => ClassList.t = "classList" @send external getBoundingClientRect: Dom.element => {..} = "getBoundingClientRect" @send external addEventListener: (Dom.element, string, unit => unit) => unit = "addEventListener" + @send external getAttribute: (Dom.element, string) => Nullable.t = "getAttribute" @send external getElementById: (Dom.element, string) => Nullable.t = "getElementById" diff --git a/src/common/MetaTagsApi.res b/src/common/MetaTagsApi.res new file mode 100644 index 000000000..01a3dbc90 --- /dev/null +++ b/src/common/MetaTagsApi.res @@ -0,0 +1,93 @@ +type t = { + title: option, + description: option, + image: option, +} + +/** + This function uses JSDOM to fetch a webpage and extract the meta tags from it. + JSDOM is required since this runs on Node. + */ +let extractMetaTags = async (url: string) => { + open Webapi + try { + let response = await Fetch.fetch(url) + + let html = await response->Fetch.Response.text + let dom = Jsdom.make(html) + let document = dom.window.document + + let metaTags = + document + ->Document.querySelectorAll("meta") + ->Array.fromArrayLike + ->Array.reduce(Dict.fromArray([]), (tags, meta) => { + let name = meta->Element.getAttribute("name")->Nullable.toOption + let property = meta->Element.getAttribute("property")->Nullable.toOption + let itemprop = meta->Element.getAttribute("itemprop")->Nullable.toOption + + let name = switch (name, property, itemprop) { + | (Some(name), _, _) => Some(name) + | (_, Some(property), _) => Some(property) + | (_, _, Some(itemprop)) => Some(itemprop) + | _ => None + } + + let content = meta->Element.getAttribute("content")->Nullable.toOption + + switch (name, content) { + | (Some(name), Some(content)) => tags->Dict.set(name, content) + | _ => () + } + + tags + }) + + let title = metaTags->Dict.get("og:title") + let description = metaTags->Dict.get("og:description") + let image = metaTags->Dict.get("og:image") + + Some({ + title, + description, + image, + }) + } catch { + | _ => { + Console.error(`Error fetching Open Graph details for ${url}`) + None + } + } +} + +type tags = { + ...t, + url: string, +} + +/* + Pass an array of URLs and get back an array of meta tags for each URL. + */ +let getMetaTags = async (urls: array) => { + let metaTags: array = [] + for i in 0 to Array.length(urls) - 1 { + let url = urls[i] + switch url { + | Some(url) => { + let tags = await extractMetaTags(url) + switch tags { + | Some(tags) => + metaTags->Array.push({ + title: tags.title, + description: tags.description, + image: tags.image, + url, + }) + | None => () + } + } + | None => () + } + } + metaTags +} diff --git a/src/components/Button.res b/src/components/Button.res index 06110fe09..6f034a1a1 100644 --- a/src/components/Button.res +++ b/src/components/Button.res @@ -3,7 +3,12 @@ type size = Small | Large /* type theme = Light | Dark */ @react.component -let make = (~kind: kind=PrimaryRed, ~size: size=Large, ~children) => { +let make = ( + ~kind: kind=PrimaryRed, + ~size: size=Large, + ~children, + ~onClick: option unit>=?, +) => { let bgColor = switch kind { | PrimaryRed => "bg-fire hover:bg-fire-70 text-white" | PrimaryBlue => "bg-sky hover:bg-sky-70 text-white" @@ -16,6 +21,7 @@ let make = (~kind: kind=PrimaryRed, ~size: size=Large, ~children) => { }