{"id":15633678,"url":"https://github.com/jaydenseric/ruck","last_synced_at":"2025-10-09T23:34:12.072Z","repository":{"id":47798777,"uuid":"468654896","full_name":"jaydenseric/ruck","owner":"jaydenseric","description":"Ruck is an open source buildless React web application framework for Deno.","archived":false,"fork":false,"pushed_at":"2025-05-26T11:55:28.000Z","size":240,"stargazers_count":251,"open_issues_count":2,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-02T02:29:26.564Z","etag":null,"topics":["deno","maintained","react","typescript"],"latest_commit_sha":null,"homepage":"https://ruck.tech","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jaydenseric.png","metadata":{"files":{"readme":"readme.md","changelog":"changelog.md","contributing":null,"funding":".github/funding.yml","license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"jaydenseric"}},"created_at":"2022-03-11T07:41:45.000Z","updated_at":"2025-08-27T13:20:27.000Z","dependencies_parsed_at":"2025-03-13T01:10:15.710Z","dependency_job_id":"95509e87-1df9-46bc-a7a0-d9795b80a37d","html_url":"https://github.com/jaydenseric/ruck","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/jaydenseric/ruck","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaydenseric%2Fruck","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaydenseric%2Fruck/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaydenseric%2Fruck/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaydenseric%2Fruck/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaydenseric","download_url":"https://codeload.github.com/jaydenseric/ruck/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaydenseric%2Fruck/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002312,"owners_count":26083340,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deno","maintained","react","typescript"],"created_at":"2024-10-03T10:49:57.679Z","updated_at":"2025-10-09T23:34:12.057Z","avatar_url":"https://github.com/jaydenseric.png","language":"JavaScript","readme":"![Ruck logo](./ruck-logo.svg)\n\n# Ruck\n\n[Ruck](https://ruck.tech) is an open source buildless\n[React](https://reactjs.org) web application framework for\n[Deno](https://deno.land). It can be used to create basic sites or powerful\napps.\n\n- [Features](#features)\n- [Installation](#installation)\n- [Examples](#examples)\n- [Requirements](#requirements)\n- [Contributing](#contributing)\n\n## Features\n\nWork with cutting edge standard technologies such as\n[ESM](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules),\n[dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#dynamic_module_loading),\n[HTTP imports](https://deno.land/manual/linking_to_external_code), and\n[import maps](https://github.com/WICG/import-maps) to avoid build steps like\ntranspilation or bundling. Deno and browsers directly run the source code. Ruck\nis _extremely_ lean with few dependencies. Modules are focused with default\nexports that are only deep imported when needed, for\n[optimal JavaScript module design](https://jaydenseric.com/blog/optimal-javascript-module-design).\n\nSome things that are complicated or impossible with traditional frameworks are\neasy with Ruck, for example…\n\n- Matching dynamic routes with RegEx or custom logic. Ideally an invalid slug in\n  a route URL results in a error page without loading the route’s components or\n  data.\n- Components can use the [`TransferContext`](./TransferContext.mjs) React\n  context during SSR to read the page HTTP request and modify the response. This\n  is surprisingly difficult with [Next.js](https://nextjs.org), see\n  [`next-server-context`](https://github.com/jaydenseric/next-server-context).\n- Proper React rendering of head tags specified by the\n  [`useHead`](./useHead.mjs) React hook. React components with a normal\n  lifecycle can be used to render head tags that can be grouped, ordered, and\n  prioritized for overrides. Frameworks like [Next.js](https://nextjs.org)\n  provide a React component that accepts basic head tags as children that it\n  manually iterates and syncs in the document head DOM.\n- SSR with component level data fetching. This is quite tricky with frameworks\n  like [Next.js](https://nextjs.org) and [Remix](https://remix.run) that support\n  data fetching at the route level, see\n  [`next-graphql-react`](https://github.com/jaydenseric/next-graphql-react).\n- [GraphQL](https://graphql.org) ready to use via React hooks from\n  [`graphql-react`](https://github.com/jaydenseric/graphql-react).\n- Declarative system for auto loading and unloading component CSS file\n  dependencies with absolute or relative URLs.\n- Baked-in type safety and IntelliSense via\n  [TypeScript JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html).\n\n## Installation\n\nA Ruck project contains:\n\n- A\n  [Deno config file](https://deno.land/manual/getting_started/configuration_file)\n  called `deno.json` or `deno.jsonc`, containing:\n\n  ```json\n  {\n    \"compilerOptions\": {\n      \"lib\": [\n        \"dom\",\n        \"dom.iterable\",\n        \"dom.asynciterable\",\n        \"deno.ns\",\n        \"deno.unstable\"\n      ]\n    }\n  }\n  ```\n\n  This enables Deno and DOM types for project and imported dependency modules.\n- [Import map](https://github.com/WICG/import-maps) JSON files that tell your\n  IDE, Deno, and browsers where to import dependencies from.\n\n  Ideally use separate development and production import maps for the server and\n  client. This way a version of React that has more detailed error messages can\n  be used during local development, and server specific dependencies can be\n  excluded from the browser import map for a faster page load.\n\n  Recommended import map file names and starter contents:\n\n  - `importMap.server.dev.json`\n\n    ```json\n    {\n      \"imports\": {\n        \"@std/http/\": \"jsr:/@std/http@^1.0.12/\",\n        \"@std/media-types/\": \"jsr:/@std/media-types@^1.1.0/\",\n        \"@std/path/\": \"jsr:/@std/path@^1.0.8/\",\n        \"graphql-react/\": \"https://unpkg.com/graphql-react@20.0.0/\",\n        \"react\": \"https://esm.sh/react@18.3.1?target=deno\u0026dev\",\n        \"react-dom/server\": \"https://esm.sh/react-dom@18.3.1/server?target=deno\u0026dev\",\n        \"react-waterfall-render/\": \"https://unpkg.com/react-waterfall-render@5.0.0/\",\n        \"ruck/\": \"https://deno.land/x/ruck@v9.0.0/\"\n      }\n    }\n    ```\n  - `importMap.server.json`\n\n    ```json\n    {\n      \"imports\": {\n        \"@std/http/\": \"jsr:/@std/http@^1.0.12/\",\n        \"@std/media-types/\": \"jsr:/@std/media-types@^1.1.0/\",\n        \"@std/path/\": \"jsr:/@std/path@^1.0.8/\",\n        \"graphql-react/\": \"https://unpkg.com/graphql-react@20.0.0/\",\n        \"react\": \"https://esm.sh/react@18.3.1?target=deno\",\n        \"react-dom/server\": \"https://esm.sh/react-dom@18.3.1/server?target=deno\",\n        \"react-waterfall-render/\": \"https://unpkg.com/react-waterfall-render@5.0.0/\",\n        \"ruck/\": \"https://deno.land/x/ruck@v9.0.0/\"\n      }\n    }\n    ```\n  - `importMap.client.dev.json`\n\n    ```json\n    {\n      \"imports\": {\n        \"graphql-react/\": \"https://unpkg.com/graphql-react@20.0.0/\",\n        \"react\": \"https://esm.sh/react@18.3.1/es2021/react.development.mjs\",\n        \"react-dom/client\": \"https://esm.sh/react-dom@18.3.1/es2021/client.development.mjs\",\n        \"react-waterfall-render/\": \"https://unpkg.com/react-waterfall-render@5.0.0/\",\n        \"ruck/\": \"https://deno.land/x/ruck@v9.0.0/\"\n      }\n    }\n    ```\n\n    The [esm.sh](https://esm.sh) URLs should be regularly updated to the latest\n    build URLs these modules re-export:\n\n    - `react`:\n      [https://esm.sh/react@18.3.1?target=es2021\u0026dev](https://esm.sh/react@18.3.1?target=es2021\u0026dev)\n    - `react-dom/client`:\n      [https://esm.sh/react-dom@18.3.1/client?target=es2021\u0026dev](https://esm.sh/react-dom@18.3.1/client?target=es2021\u0026dev)\n  - `importMap.client.json`\n\n    ```json\n    {\n      \"imports\": {\n        \"graphql-react/\": \"https://unpkg.com/graphql-react@20.0.0/\",\n        \"react\": \"https://esm.sh/react@18.3.1/es2021/react.mjs\",\n        \"react-dom/client\": \"https://esm.sh/react-dom@18.3.1/es2021/client.mjs\",\n        \"react-waterfall-render/\": \"https://unpkg.com/react-waterfall-render@5.0.0/\",\n        \"ruck/\": \"https://deno.land/x/ruck@v9.0.0/\"\n      }\n    }\n    ```\n\n    The [esm.sh](https://esm.sh) URLs should be regularly updated to the latest\n    build URLs these modules re-export:\n\n    - `react`:\n      [https://esm.sh/react@18.3.1?target=es2021](https://esm.sh/react@18.3.1?target=es2021)\n    - `react-dom/client`:\n      [https://esm.sh/react-dom@18.3.1/client?target=es2021](https://esm.sh/react-dom@18.3.1/client?target=es2021)\n\n  A DRY approach is to Git ignore the import map files and generate them with a\n  script that’s a single source of truth.\n- A module that imports and uses Ruck’s [`serve`](./serve.mjs) function to start\n  the Ruck app server, typically called `scripts/serve.mjs`. Here’s an example:\n\n  ```js\n  // @ts-check\n\n  import serve from \"ruck/serve.mjs\";\n\n  const ruckAppHttpServer = await serve({\n    clientImportMap: new URL(\n      Deno.env.get(\"RUCK_DEV\") === \"true\"\n        ? \"../importMap.client.dev.json\"\n        : \"../importMap.client.json\",\n      import.meta.url,\n    ),\n    port: Number(Deno.env.get(\"RUCK_PORT\")),\n  });\n\n  console.info(\n    `Ruck app HTTP server listening on http://localhost:${ruckAppHttpServer.addr.port}`,\n  );\n  ```\n\n  The\n  [Deno CLI](https://deno.land/manual/getting_started/command_line_interface) is\n  used to run this script; Ruck doesn’t have a CLI.\n\n  You may choose to create a `scripts/serve.sh` shell script that serves the\n  Ruck app:\n\n  ```sh\n  #!/bin/sh\n  # Serves the Ruck app.\n\n  # Asserts an environment variable is set.\n  # Argument 1: Name.\n  # Argument 2: Value.\n  assertEnvVar() {\n    if [ -z \"$2\" ]\n    then\n      echo \"Missing environment variable \\`$1\\`.\" \u003e\u00262\n      exit 1\n    fi\n  }\n\n  # Assert environment variables are set.\n  assertEnvVar RUCK_DEV $RUCK_DEV\n  assertEnvVar RUCK_PORT $RUCK_PORT\n\n  # Serve the Ruck app.\n  if [ \"$RUCK_DEV\" = \"true\" ]\n  then\n    deno run \\\n      --allow-env \\\n      --allow-import \\\n      --allow-net \\\n      --allow-read \\\n      --import-map=importMap.server.dev.json \\\n      --watch=. \\\n      scripts/serve.mjs\n  else\n    deno run \\\n      --allow-env \\\n      --allow-import \\\n      --allow-net \\\n      --allow-read \\\n      --import-map=importMap.server.json \\\n      --no-check \\\n      scripts/serve.mjs\n  fi\n  ```\n\n  First, ensure it’s executable:\n\n  ```sh\n  chmod +x ./scripts/serve.sh\n  ```\n\n  Then, run it like this:\n\n  ```sh\n  RUCK_DEV=\"true\" RUCK_PORT=\"3000\" ./scripts/serve.sh\n  ```\n\n  You may choose to store environment variables in a Git ignored\n  `scripts/.env.sh` file:\n\n  ```sh\n  export RUCK_DEV=\"true\"\n  export RUCK_PORT=\"3000\"\n  ```\n\n  Then, you could create a `scripts/dev.sh` shell script (also ensure it’s\n  executable):\n\n  ```sh\n  #!/bin/sh\n  # Loads the environment variables and serves the Ruck app.\n\n  # Load the environment variables.\n  . scripts/.env.sh \u0026\u0026\n\n  # Serve the Ruck app.\n  ./scripts/serve.sh\n  ```\n\n  This way you only need to run this when developing your Ruck app:\n\n  ```sh\n  ./scripts/dev.sh\n  ```\n\n  There isn’t a universally “correct” way to use environment variables or start\n  serving the Ruck app; create an optimal workflow for your particular\n  development and production environments.\n- A public directory containing files that Ruck serves directly to browsers, by\n  default called `public`. For example, `public/favicon.ico` could be accessed\n  in a browser at the URL path `/favicon.ico`.\n- A `router.mjs` module in the public directory that default exports a function\n  that Ruck calls on both the server and client with details such as the route\n  URL to determine what the route content should be. It should have this JSDoc\n  type:\n\n  ```js\n  /** @import { Router } from \"ruck/serve.mjs\" */\n  ```\n\n  Ruck provides an (optional) declarative system for automatic loading and\n  unloading of component CSS file dependencies served by Ruck via the public\n  directory or CDN. Ruck’s\n  [`routePlanForContentWithCss`](./routePlanForContentWithCss.mjs) function can\n  be imported and used to create route plan for content with CSS file\n  dependencies.\n\n  Here is an example for a website that has a home page, a `/blog` page that\n  lists blog posts, and a `/blog/post-id-slug-here` page for individual blog\n  posts:\n\n  ```js\n  // @ts-check\n\n  /**\n   * @import { RouteContentWithCss } from \"ruck/routePlanForContentWithCss.mjs\"\n   * @import { Router } from \"ruck/serve.mjs\"\n   */\n\n  import { createElement as h } from \"react\";\n  import routePlanForContentWithCss from \"ruck/routePlanForContentWithCss.mjs\";\n\n  // The component used to display a route loading error (e.g. due to an\n  // internet dropout) should be imported up front instead of dynamically\n  // importing it when needed, as it would likely also fail to load.\n  import PageError, {\n    // A `Set` instance containing CSS URLs.\n    css as cssPageError,\n  } from \"./components/PageError.mjs\";\n\n  /**\n   * Gets the Ruck app route plan for a URL.\n   * @type {Router}\n   */\n  export default function router(url, headManager, isInitialRoute) {\n    if (url.pathname === \"/\") {\n      return routePlanForContentWithCss(\n        // Dynamically import route components so they only load when needed.\n        import(\"./components/PageHome.mjs\").then(\n          ({ default: PageHome, css }) =\u003e ({\n            content: h(PageHome),\n            css,\n          }),\n          // It’s important to handle dynamic import loading errors.\n          catchImportContentWithCss,\n        ),\n        headManager,\n        isInitialRoute,\n      );\n    }\n\n    if (url.pathname === \"/blog\") {\n      return routePlanForContentWithCss(\n        import(\"./components/PageBlog.mjs\").then(\n          ({ default: PageBlog, css }) =\u003e ({\n            content: h(PageBlog),\n            css,\n          }),\n          catchImportContentWithCss,\n        ),\n        headManager,\n        isInitialRoute,\n      );\n    }\n\n    // For routes with URL slugs, use RegEx that only matches valid slugs,\n    // instead of simply extracting the whole slug. This way an invalid URL slug\n    // naturally results in an immediate 404 error and avoids loading the route\n    // component or loading data with the invalid slug.\n    const matchPagePost = url.pathname.match(/^\\/blog\\/(?\u003cpostId\u003e[\\w-]+)$/u);\n\n    if (matchPagePost?.groups) {\n      const { postId } = matchPagePost.groups;\n\n      return routePlanForContentWithCss(\n        import(\"./components/PagePost.mjs\").then(\n          ({ default: PagePost, css }) =\u003e ({\n            content: h(PagePost, { postId }),\n            css,\n          }),\n        ),\n        headManager,\n        isInitialRoute,\n      );\n    }\n\n    // Fallback to a 404 error page.\n    return routePlanForContentWithCss(\n      // If you have a component specifically for a 404 error page, it would be\n      // ok to dynamically import it here. In this particular example the\n      // component was already imported for the loading error page.\n      {\n        content: h(PageError, {\n          status: 404,\n          title: \"Error 404\",\n          description: \"Something is missing.\",\n        }),\n        css: cssPageError,\n      },\n      headManager,\n      isInitialRoute,\n    );\n  }\n\n  /**\n   * Catches a dynamic import error for route content with CSS.\n   * @param {Error} cause Import error.\n   * @returns {RouteContentWithCss}\n   */\n  function catchImportContentWithCss(cause) {\n    console.error(new Error(\"Import rejection for route with CSS.\", { cause }));\n\n    return {\n      content: h(PageError, {\n        status: 500,\n        title: \"Error loading\",\n        description: \"Unable to load.\",\n      }),\n      css: cssPageError,\n    };\n  }\n  ```\n\n  For the previous example, here’s the `public/components/PageError.mjs` module:\n\n  ```js\n  // @ts-check\n\n  import { createElement as h, useContext } from \"react\";\n  import TransferContext from \"ruck/TransferContext.mjs\";\n\n  import Heading, { css as cssHeading } from \"./Heading.mjs\";\n  import Para, { css as cssPara } from \"./Para.mjs\";\n\n  // Export CSS URLs for the component and its dependencies.\n  export const css = new Set([\n    ...cssHeading,\n    ...cssPara,\n    \"/components/PageError.css\",\n  ]);\n\n  /**\n   * React component for an error page.\n   * @param {object} props Props.\n   * @param {number} props.status HTTP status code.\n   * @param {number} props.title Error title.\n   * @param {string} props.description Error description.\n   */\n  export default function PageError({ status, title, description }) {\n    // Ruck’s transfer (request/response) context; only populated on the server.\n    const ruckTransfer = useContext(TransferContext);\n\n    // If server side rendering, modify the HTTP status code for the Ruck app\n    // page response.\n    if (ruckTransfer) ruckTransfer.responseInit.status = status;\n\n    return h(\n      \"section\",\n      { className: \"PageError__section\" },\n      h(Heading, null, title),\n      h(Para, null, description),\n    );\n  }\n  ```\n- A `components/App.mjs` module in the public directory that default exports a\n  React component that renders the entire app. It should have this JSDoc type:\n\n  ```js\n  /** @import { AppComponent } from \"ruck/serve.mjs\" */\n  ```\n\n  It typically imports and uses several React hooks from Ruck:\n  - [`useCss`](./useCss.mjs) to declare CSS files that apply to the entire app.\n  - [`useHead`](./useHead.mjs) to establish head tags that apply to the entire\n    app such as `meta[name=\"viewport\"]` and `link[rel=\"manifest\"]`.\n  - [`useRoute`](./useRoute.mjs) to get the current route URL and content, and\n    render it in a persistent layout containing global content such as a header\n    and footer.\n\n  Here’s an example `public/components/App.mjs` module for a website with home\n  and blog pages:\n\n  ```js\n  // @ts-check\n\n  /** @import { AppComponent } from \"ruck/serve.mjs\" */\n\n  import { createElement as h, Fragment, useMemo } from \"react\";\n  import useCss from \"ruck/useCss.mjs\";\n  import useHead from \"ruck/useHead.mjs\";\n  import useRoute from \"ruck/useRoute.mjs\";\n\n  import NavLink, { css as cssNavLink } from \"./NavLink.mjs\";\n\n  const css = new Set([\n    ...cssNavLink,\n    \"/components/App.css\",\n  ]);\n\n  /**\n   * React component for the Ruck app.\n   * @type {AppComponent}\n   */\n  export default function App() {\n    const route = useRoute();\n\n    useHead(\n      // Head tag fragments render in the document head in key order. A good\n      // convention is to use group and subgroup numbers, followed by a\n      // descriptive name.\n      \"1-1-meta\",\n      // Must be memoized. If it’s dynamic use the `useMemo` React hook,\n      // otherwise define it outside the component function scope.\n      useMemo(() =\u003e\n        h(\n          Fragment,\n          null,\n          h(\"meta\", {\n            name: \"viewport\",\n            content: \"width=device-width, initial-scale=1\",\n          }),\n          h(\"meta\", {\n            name: \"og:image\",\n            content:\n              // Sometimes an absolute URL is necessary.\n              `${route.url.origin}/social-preview.png`,\n          }),\n          h(\"link\", { rel: \"manifest\", href: \"/manifest.webmanifest\" }),\n          // More head tags here…\n        ), [route.url.origin]),\n    );\n\n    // This loop doesn’t break React hook rules as the list never changes.\n    for (const href of css) useCss(href);\n\n    return h(\n      Fragment,\n      null,\n      // Global nav…\n      h(\n        \"nav\",\n        { className: \"App__nav\" },\n        h(NavLink, { href: \"/\" }, \"Home\"),\n        h(NavLink, { href: \"/blog\" }, \"Blog\"),\n      ),\n      // Route content…\n      route.content,\n      // Global footer…\n      h(\"footer\", { className: \"App__footer\" }, \"Global footer content.\"),\n    );\n  }\n  ```\n\n  Ruck app route navigation links make use of these React hooks from Ruck:\n\n  - [`useRoute`](./useRoute.mjs) to get the current route URL path for\n    comparison with the link’s URL path to determine active state.\n  - [`useOnClickRouteLink`](./useOnClickRouteLink.mjs) to replace the default\n    browser navigation that happens when a link is clicked with a Ruck client\n    side route navigation.\n\n  For the previous example, here’s the `public/components/NavLink.mjs` module:\n\n  ```js\n  // @ts-check\n\n  /** @import { ReactNode } from \"react\" */\n\n  import { createElement as h } from \"react\";\n  import useOnClickRouteLink from \"ruck/useOnClickRouteLink.mjs\";\n  import useRoute from \"ruck/useRoute.mjs\";\n\n  export const css = new Set([\n    \"/components/NavLink.css\",\n  ]);\n\n  /**\n   * React component for a navigation link.\n   * @param {object} props Props.\n   * @param {string} props.href Link URL.\n   * @param {ReactNode} [props.children] Children.\n   */\n  export default function NavLink({ href, children }) {\n    const route = useRoute();\n    const onClick = useOnClickRouteLink();\n\n    let className = \"NavLink__a\";\n    if (href === route.url.pathname) className += \" NavLink__a--active\";\n\n    return h(\"a\", { className, href, onClick }, children);\n  }\n  ```\n\n## Examples\n\n- [Ruck website repo](https://github.com/jaydenseric/ruck-website).\n- [`graphql-react` examples repo](https://github.com/jaydenseric/graphql-react-examples).\n- [`device-agnostic-ui` website repo](https://github.com/jaydenseric/device-agnostic-ui-website).\n- [SVG symbol viewer repo](https://github.com/jaydenseric/svg-symbol-viewer).\n- [Jayden Seric’s website](https://jaydenseric.com).\n\n## Requirements\n\n- [Deno CLI](https://deno.land/#installation) v2+.\n\n## Contributing\n\n### Scripts\n\nThese CLI scripts are used for development and\n[GitHub Actions CI](./.github/workflows/ci.yml) checks.\n\n#### Test\n\nTo run the tests:\n\n```sh\n./scripts/test.sh\n```\n\n#### Serve\n\nTo serve the Ruck project files for testing in other local projects (argument 1\nis the localhost port for the HTTP server to listen on):\n\n```sh\n./scripts/serve.sh 3001\n```\n\n#### Find min compatible Deno version\n\nTo find Ruck’s minimum compatible Deno version:\n\n```sh\n./scripts/findMinCompatibleDenoVersion.mjs\n```\n\n#### Type check\n\nTo type check every JavaScript module in the project:\n\n```sh\n./scripts/type-check.sh\n```\n\n#### Format\n\nTo format the project:\n\n```sh\ndeno fmt\n```\n\n#### Lint\n\nTo lint the project:\n\n```sh\ndeno lint\n```\n","funding_links":["https://github.com/sponsors/jaydenseric"],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaydenseric%2Fruck","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaydenseric%2Fruck","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaydenseric%2Fruck/lists"}