<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sid Vishnoi</title>
    <description>The latest articles on DEV Community by Sid Vishnoi (@sidvishnoi).</description>
    <link>https://dev.to/sidvishnoi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F104422%2F2d6a2470-9b1b-4f7b-bc00-fc786ea118d9.jpg</url>
      <title>DEV Community: Sid Vishnoi</title>
      <link>https://dev.to/sidvishnoi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidvishnoi"/>
    <language>en</language>
    <item>
      <title>Spec Prod: a GitHub Action for validating and deploying W3C specs</title>
      <dc:creator>Sid Vishnoi</dc:creator>
      <pubDate>Thu, 17 Sep 2020 14:17:05 +0000</pubDate>
      <link>https://dev.to/sidvishnoi/spec-prod-a-github-action-for-validating-and-deploying-w3c-specs-459e</link>
      <guid>https://dev.to/sidvishnoi/spec-prod-a-github-action-for-validating-and-deploying-w3c-specs-459e</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;Action name: Spec Prod&lt;/p&gt;

&lt;p&gt;tl;dr: I created a GitHub action that simplifies the compilation, validation and deployment of specifications in the W3C ecosystem.&lt;/p&gt;

&lt;p&gt;This post is solely for the purpose of submission. Read more about the action and its development in my other blog post: &lt;a href="https://dev.to/sidvishnoi/simplifying-w3c-standards-workflow-with-a-github-action-2ch5"&gt;Simplifying W3C standards workflow with a GitHub Action&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;DIY Deployments&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/w3c"&gt;
        w3c
      &lt;/a&gt; / &lt;a href="https://github.com/w3c/spec-prod"&gt;
        spec-prod
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      GitHub Action to build ReSpec/Bikeshed specs, validate output and publish to GitHub pages or W3C
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Spec Prod | &lt;a href="https://w3c.github.io/spec-prod/" rel="nofollow"&gt;Documentation 📘&lt;/a&gt;
&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This GitHub Action lets you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build &lt;a href="https://github.com/w3c/respec"&gt;ReSpec&lt;/a&gt; and &lt;a href="https://github.com/tabatkins/bikeshed"&gt;Bikeshed&lt;/a&gt; specs.&lt;/li&gt;
&lt;li&gt;Validate generated document's markup and check for broken hyperlinks.&lt;/li&gt;
&lt;li&gt;Publish generated spec to GitHub Pages and/or w3.org (using Echidna).&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Basic Usage&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;During a pull request, the action:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;figures out if you're using ReSpec (&lt;code&gt;index.html&lt;/code&gt;) or Bikeshed (&lt;code&gt;index.bs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;converts the ReSpec/Bikeshed source document to regular HTML&lt;/li&gt;
&lt;li&gt;runs broken hyperlink checker, and validate markup using W3C nu validator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, if a commit is pushed to the "main" branch, the action deploys the built specification to /TR/.&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; .github/workflows/auto-publish.yml&lt;/span&gt;
&lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;CI&lt;/span&gt;
&lt;span class="pl-ent"&gt;on&lt;/span&gt;
  &lt;span class="pl-ent"&gt;pull_request&lt;/span&gt;: &lt;span class="pl-s"&gt;{}&lt;/span&gt;
  &lt;span class="pl-ent"&gt;push&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;branches&lt;/span&gt;: &lt;span class="pl-s"&gt;[main]&lt;/span&gt;
&lt;span class="pl-ent"&gt;jobs&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;main&lt;/span&gt;:
    &lt;span class="pl-ent"&gt;name&lt;/span&gt;: &lt;span class="pl-s"&gt;Build, Validate and Deploy&lt;/span&gt;
    &lt;span class="pl-ent"&gt;runs-on&lt;/span&gt;: &lt;span class="pl-s"&gt;ubuntu-20.04&lt;/span&gt;
    &lt;span class="pl-ent"&gt;steps&lt;/span&gt;:
      - &lt;span class="pl-ent"&gt;uses&lt;/span&gt;: &lt;span class="pl-s"&gt;actions/checkout@v4&lt;/span&gt;
      - &lt;span class="pl-ent"&gt;uses&lt;/span&gt;: &lt;span class="pl-s"&gt;w3c/spec-prod@v2&lt;/span&gt;
        &lt;span class="pl-ent"&gt;with&lt;/span&gt;:
          &lt;span class="pl-ent"&gt;W3C_ECHIDNA_TOKEN&lt;/span&gt;: &lt;span class="pl-s"&gt;${{ secrets.ECHIDNA_TOKEN }}&lt;/span&gt;
          &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Replace following with appropriate value. See options.md for details.&lt;/span&gt;
          &lt;span class="pl-ent"&gt;W3C_WG_DECISION_URL&lt;/span&gt;: &lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/w3c/spec-prod"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Projects using this action
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/gamepad/"&gt;https://github.com/w3c/gamepad/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/w3c/web-share/"&gt;https://github.com/w3c/web-share/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/search?q=author%3Asidvishnoi+is%3Apr+created%3A%3E2020-09-14+spec-prod&amp;amp;type=Issues"&gt;and more&lt;/a&gt; (hopefully all W3C specs 🤞) coming soon!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>actionshackathon</category>
    </item>
    <item>
      <title>Simplifying W3C standards workflow with a GitHub Action</title>
      <dc:creator>Sid Vishnoi</dc:creator>
      <pubDate>Mon, 14 Sep 2020 18:22:23 +0000</pubDate>
      <link>https://dev.to/sidvishnoi/simplifying-w3c-standards-workflow-with-a-github-action-2ch5</link>
      <guid>https://dev.to/sidvishnoi/simplifying-w3c-standards-workflow-with-a-github-action-2ch5</guid>
      <description>&lt;p&gt;&lt;strong&gt;What is it about?&lt;/strong&gt; In the W3C world, ReSpec and Bikeshed are two common tools being used to write web standards. &lt;a href="https://github.com/w3c/respec/"&gt;ReSpec&lt;/a&gt; is a JavaScript based tool that runs in browser and enhances a HTML document to generate a static HTML document that is suitable to be published as a standard. On the other hand, &lt;a href="https://github.com/tabatkins/bikeshed"&gt;Bikeshed&lt;/a&gt; is a Python based command line tool which processes a superset of markdown to create similar output. ReSpec also has a CLI. Further, both tools are generic enough to be used (and are being used) outside the W3C. I wrote a bit more about ReSpec in another &lt;a href="https://dev.to/sidvishnoi/mozilla-open-source-support-grant-helping-make-open-source-sustainable-3540"&gt;post about the ReSpec MOSS Grant&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why a compile step is needed?&lt;/strong&gt; ReSpec enhances a HTML document in the browser on runtime, so it is simpler to use (no special tools or dependencies required) and a ReSpec document can be directly hosted on GitHub pages. Bikeshed uses a special text format, so it cannot be hosted directly without a compile step. But as ReSpec does everything at runtime, even with great caching practices, a ReSpec document is slower to load. So, people sometimes use the CLI to compile a ReSpec document and create a static HTML output, as Bikeshed does, and deploy it to GitHub pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why an Action is needed?&lt;/strong&gt; The W3C standards ecosystem is large, and many repositories have setup their own workflows using Travis, GitHub Actions etc. to build, validate and deploy their specs to GitHub Pages or W3C. This leads to fragmentation and it becomes difficult to maintain all these workflows. So, I decided to create a GitHub action, called &lt;strong&gt;Spec Prod&lt;/strong&gt;, that can be used by all the specs with minimal effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the action do?
&lt;/h2&gt;

&lt;p&gt;When setup in a workflow, the action figures out whether the repository uses Bikeshed or ReSpec, and uses appropriate tools to generate a plain HTML document.&lt;/p&gt;

&lt;p&gt;Then it runs various validators, like the W3C markup validator and checks for broken hyperlinks. This serves as a check so we don't accept malformed PRs.&lt;/p&gt;

&lt;p&gt;When a pull request is merged, the action can deploy the generated HTML document to GitHub pages. When appropriate, it can also deploy the specification to the W3C website. You can look at a &lt;a href="https://user-images.githubusercontent.com/8426945/93121389-6376e700-f6e2-11ea-8248-3788d93de5f0.png"&gt;sample run&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Developing the action
&lt;/h2&gt;

&lt;p&gt;In this section, I'll share how I developed the action and I hope you can take away a things or two from my experience!&lt;/p&gt;

&lt;h3&gt;
  
  
  A composite action
&lt;/h3&gt;

&lt;p&gt;GitHub actions recently started to support "composite" actions, so I wanted to try that. In plain words, you can write multiple small programs/scripts and run them one after other as you run "steps" in a workflow. The composite actions have some work to do on GitHub side, like UI improvements and conditional step execution, but it worked well enough for my use case, with some "hacks".&lt;/p&gt;

&lt;p&gt;For the separating steps in the action (not workflow), I decided to use the &lt;code&gt;group&lt;/code&gt; command. So, all the steps in my action are wrapped in a collapsible section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# action.yml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;echo "::group::Step 1"&lt;/span&gt;
    &lt;span class="s"&gt;run-script-for-step-1&lt;/span&gt;
    &lt;span class="s"&gt;echo "::endgroup::"&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;echo "::group::Step 2"&lt;/span&gt;
    &lt;span class="s"&gt;run-script-for-step-1&lt;/span&gt;
    &lt;span class="s"&gt;echo "::endgroup::"&lt;/span&gt;
  &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For conditional execution, the scripts do a check and &lt;code&gt;exit(0)&lt;/code&gt; if that step doesn't need to run. &lt;a href="https://github.com/sidvishnoi/spec-prod/blob/31fbaf287/deploy-gh-pages.js#L11-L14"&gt;See example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's a bit ugly, but it works.&lt;/p&gt;
&lt;h3&gt;
  
  
  Choosing the right language
&lt;/h3&gt;

&lt;p&gt;I love JavaScript. But for this action, I decided to write it in Bash (kinda insane, but I love bash too). Turns out, bash gets out of hands really easily, specially when it comes to string processing and error handling. So, after finishing the "mostly working" action, I decided to re-write it in JavaScript. Some part is still in Bash, perhaps for good ol' memories. Bash is actually great for running sequential commands.&lt;/p&gt;

&lt;p&gt;Apart from the translation, it didn't require much effort in the action. I had to mostly change the calls like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;# action.yml
&lt;span class="gd"&gt;- ${{ github.action_path }}/build.sh
&lt;/span&gt;&lt;span class="gi"&gt;+ node ${{ github.action_path }}/build.sh
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Choosing good default inputs
&lt;/h3&gt;

&lt;p&gt;A good GitHub action (or any tool in general) should have a good default behavior. Even when you do not pass any inputs to the &lt;a href="https://github.com/actions/checkout/"&gt;checkout action&lt;/a&gt;, it does what you expect it to do - it checks out a branch/ref. A good tool should have a good default behavior, and a low learning curve.&lt;/p&gt;

&lt;p&gt;The Spec Prod action, also does not require any input. By default, it builds and validates the document. Want to deploy to the GitHub pages branch also? Specify the branch name. Want to deploy to W3C? Authenticate. Should it deploy on PRs? Not likely, so disable all deployments on PRs, unless asked.&lt;/p&gt;
&lt;h3&gt;
  
  
  Allow overrides
&lt;/h3&gt;

&lt;p&gt;The default behaviour is not always desirable and a software should be flexible enough when needed. So, the Spec Prod action also allows users to change the behavior. Don't want to run a validator? Tell it not to. The input file does not have a generic name? Specify it.&lt;/p&gt;
&lt;h3&gt;
  
  
  Add a pre-process step
&lt;/h3&gt;

&lt;p&gt;As a multi-step action, it helps if you can pre-process all the inputs at once, and pass them to other steps in a "standardized manner". This advice also works in other cases, as long as you don't hit separation of concerns issues.&lt;/p&gt;

&lt;p&gt;In a GitHub action, as all inputs are key-value pairs, and it's not cool to ask users to input a stringified JSON value, I decided to prefix input keys with their categories, and pass validated and structured JSON as inputs to internal steps. So, the plain key-value inputs:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sidvishnoi/spec-prod@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;W3C_ECHIDNA_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SECRET&lt;/span&gt;
    &lt;span class="na"&gt;W3C_MANIFEST_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;URL&lt;/span&gt;
    &lt;span class="na"&gt;W3C_WG_DECISION_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ANOTHER_URL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;become:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;w3c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SECRET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;decisionUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ANOTHER_URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and we just pass the required inputs a step:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node deploy-w3c.js&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;INPUTS_DEPLOY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ toJson(fromJson(steps.prepare.outputs.all).deploy.w3c) }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also, input names should be descriptive enough that users don't always need to read the full documentation to understand what's going on. So, instead of &lt;code&gt;W3C_TOKEN&lt;/code&gt;, I used &lt;code&gt;W3C_ECHIDNA_TOKEN&lt;/code&gt;, as it makes clearer (to the W3C folks) that the token is obtained through &lt;a href="https://github.com/w3c/echidna/"&gt;Echidna&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Keeping it simple - No dependencies
&lt;/h3&gt;

&lt;p&gt;Personally, I don't like build-steps unless required. I like TypeScript, but I didn't write this action in TypeScript because of an seemingly unnecessary (hot take) build step. Instead, I used the &lt;code&gt;// @ts-check&lt;/code&gt; comment and JSDoc annotations to type-check/hint code within the editor. It works pretty well.&lt;/p&gt;

&lt;p&gt;Also, I didn't use the Actions Toolkit, or any other node_modules, and decided to create my own as needed. It's not super productive, but good to have some fun. You don't always need npm; copy-paste works well enough sometimes: compare &lt;a href="https://github.com/sidvishnoi/spec-prod/blob/31fbaf28/utils.js#L143-L151"&gt;this&lt;/a&gt; and &lt;a href="https://unpkg.com/browse/yn@4.0.0/"&gt;this&lt;/a&gt;. Also, in my defense, I &lt;a href="https://github.com/sidvishnoi/spec-prod/tree/d23c9f7ae3bba02cac97229857f4288335b67c88"&gt;originally&lt;/a&gt; wrote this entire action in Bash, where npm isn't a thing.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploying to GitHub pages
&lt;/h3&gt;

&lt;p&gt;If you've used GitHub Actions to deploy to GitHub Pages, you must have come across &lt;a href="https://github.com/peaceiris/actions-gh-pages"&gt;peaceiris/actions-gh-pages&lt;/a&gt;. It's a great action, and does the job.&lt;/p&gt;

&lt;p&gt;But there is a catch. GitHub composite actions do not yet allow you to run another GitHub action as a step. It'll be supported in near future, and that would open a lot of opportunities! Also, it is not always super-safe to trust code by others, specially when you pass it a GitHub access token.&lt;/p&gt;

&lt;p&gt;I needed to support GitHub pages deployment regardless of that support. So, I decided to rewrite it myself. It took me around &lt;a href="https://github.com/sidvishnoi/spec-prod/blob/d23c9f7a/deploy-gh-pages.sh"&gt;60 lines to Bash&lt;/a&gt; to come up with a well working solution. And then later, a near &lt;a href="https://github.com/sidvishnoi/spec-prod/blob/main/deploy-gh-pages.js"&gt;100 line rewrite in JS&lt;/a&gt;, including more validations and logging. Feel free to copy-paste. Feedback welcome!&lt;/p&gt;
&lt;h3&gt;
  
  
  Testing
&lt;/h3&gt;

&lt;p&gt;Testing is presently the weakest part of GitHub actions. Other than simple unit tests, the only way to test the action is to push it, and send PRs/commits to a &lt;a href="https://github.com/sidvishnoi/w3c-deploy-test"&gt;test repository&lt;/a&gt;. That also means a lot of test runs with commits like: &lt;em&gt;"please work, does it work now?, done, looks done now, why don't you work!"&lt;/em&gt; etc.&lt;/p&gt;

&lt;p&gt;For some parts, I created a local environment to run the steps with test inputs, but it's still cumbersome.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I will be sending PRs to the repositories that can make use of this action. I've got plenty of &lt;a href="https://github.com/sidvishnoi/spec-prod/issues/"&gt;open issues&lt;/a&gt; to work when I get free time. Contributions are welcome!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/w3c"&gt;
        w3c
      &lt;/a&gt; / &lt;a href="https://github.com/w3c/spec-prod"&gt;
        spec-prod
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      GitHub Action to build ReSpec/Bikeshed specs, validate output and publish to GitHub pages or W3C
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>github</category>
      <category>actionshackathon</category>
    </item>
    <item>
      <title>Mozilla Open Source Support Grant — Helping Make Open Source Sustainable</title>
      <dc:creator>Sid Vishnoi</dc:creator>
      <pubDate>Fri, 24 Jul 2020 13:08:31 +0000</pubDate>
      <link>https://dev.to/sidvishnoi/mozilla-open-source-support-grant-helping-make-open-source-sustainable-3540</link>
      <guid>https://dev.to/sidvishnoi/mozilla-open-source-support-grant-helping-make-open-source-sustainable-3540</guid>
      <description>&lt;p&gt;I was awarded with a Mozilla Open Source Support (MOSS) grant through which I was funded to work full-time on an open-source project (ReSpec) for over 3 months. In this post, I’ll share my experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is MOSS?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://mozilla.org/moss/"&gt;MOSS&lt;/a&gt; awards program financially supports open source projects that contribute to Mozilla’s work and the health of the Internet. Specifically for the MOSS Foundational Technology track, Mozilla looks for projects that Mozilla products or Mozzilians rely on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ReSpec?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/w3c/respec/"&gt;ReSpec&lt;/a&gt; is an open source project that makes writing international standards easier. For specification authors, ReSpec handles mundane tasks like automatically generating legal and conformance boilerplates, linking citations, generating a bibliography, choosing the appropriate styling for the type of standard, and so on. This allows technical committees to focus on specifying new technical features, without having to concern themselves with the mundane requirements of the standards publication process. Further, it also incorporates utilities like syntax highlighting, &lt;a href="https://heycam.github.io/webidl/"&gt;WebIDL&lt;/a&gt; error reporting, and other features that make technical specifications easier to read, use, test, and implement in browsers, such as Firefox, Chrome, Edge, and Safari.&lt;/p&gt;

&lt;p&gt;The  &lt;a href="https://w3c.github.io/manifest/"&gt;Web App Manifest&lt;/a&gt; specification serves as a typical example of ReSpec-based specification:&lt;br&gt;
&lt;a href="https://dev-to-uploads.s3.amazonaws.com/i/l2wuax4xsk0tediazdst.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl2wuax4xsk0tediazdst.png" alt="Screenshot of a ReSpec-based specification" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ReSpec has been in active development for over 8 years, and is used extensively by Mozillians and the Web Standards Community. There are currently over 70 W3C specifications relying on it, along with an increasing list of standards bodies making use of the project to publish free and open technical standards.&lt;/p&gt;
&lt;h2&gt;
  
  
  Planning and Proposal
&lt;/h2&gt;

&lt;p&gt;Before you get the funding, MOSS requires you to submit an application. The application asks questions like the description of the project, reason for funding request, and the project plan.&lt;/p&gt;

&lt;p&gt;My proposal was to develop new features, fix long standing bugs and improve the documentation, by working full-time for over 3 months.&lt;/p&gt;

&lt;p&gt;This was the first time I made a &lt;em&gt;real&lt;/em&gt; project plan:&lt;br&gt;
&lt;a href="https://dev-to-uploads.s3.amazonaws.com/i/tx4r4yo3oqtrt0x657w7.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftx4r4yo3oqtrt0x657w7.png" alt="A rough scribble of project timeline made in MS Paint" width="800" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a little more professionalism, I created a Gantt Chart, which probably looks nicer:&lt;br&gt;
&lt;a href="https://dev-to-uploads.s3.amazonaws.com/i/b8uuw5svwtysrgg6s1jg.png"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb8uuw5svwtysrgg6s1jg.png" alt="A more professional Gantt Chart for the project timeline" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the pictures above were not required in the application, they helped me better understand the project and its timeline.&lt;/p&gt;

&lt;p&gt;Spoiler: Things often don’t go as planned. Certainly not with a pandemic around.&lt;/p&gt;

&lt;p&gt;When you submit the application, you need someone in Mozilla to champion it to the executive council.&lt;/p&gt;

&lt;p&gt;A few weeks after submitting the application, I received the approval email! Be patient if you’re applying for the grant: the process is very rigorous, but worth the wait! The program committee was very helpful throughout the application process.&lt;/p&gt;

&lt;p&gt;Once the project starts, you also need to submit reports about the project's status from time to time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Work, Work, Work
&lt;/h2&gt;

&lt;p&gt;The project officially started on May 4, 2020, though I had already begun to work as per the previous schedule of March 23. I’ve been contributing to ReSpec in my spare time for over 2 years, but with this grant, I became a full-time maintainer.&lt;/p&gt;

&lt;p&gt;Working on ReSpec means working on multiple repositories - the core tool, its backend APIs and CI solutions. Fortunately, all of them are written in JavaScript so it needs less context switching.&lt;/p&gt;

&lt;p&gt;During the grant period, I developed several new features in ReSpec. A notable one is the generation of an index of terms that a specification defines and references from other specifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://w3c.github.io/manifest/#index"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg7n9wq82l8asuuni2rfy.png" alt="Screenshot of an index of terms generated by ReSpec" width="800" height="937"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a large proportion of the grant period, I worked on documentation improvements. The &lt;a href="https://respec.org/docs/"&gt;revamped documentation&lt;/a&gt; is now searchable and more structured. It includes updated examples and guides, which I hope will add significant value to all users.&lt;/p&gt;

&lt;p&gt;If you’re interested in a summary of the work done from the perspective of ReSpec users, read this &lt;a href="https://lists.w3.org/Archives/Public/spec-prod/2020JulSep/0002.html"&gt;email in W3C mailing list&lt;/a&gt;. There is also a &lt;a href="https://gist.github.com/sidvishnoi/b6aaba4b00ad899f9d71bff95e950e8d"&gt;list of activities&lt;/a&gt; undertaken to achieve project milestones.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the MOSS award, gave me the opportunity to develop the proposed ideas and much more. I set out to let ReSpec empower specification authors by reducing the tooling oriented effort it takes while writing a specification. With the improved syntax support, new configuration options, and more helpful error messages, the community is more empowered to collaborate and standardize the next generation of web technologies. And I hope the comprehensive documentation, full of examples and guides, will be invaluable to all ReSpec users.&lt;/p&gt;

&lt;p&gt;If you’re interested in contributing to an open source project, I would like to welcome you to ReSpec. We’ve some good first issues, as well as more involving issues, and we would be happy to mentor you!&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/w3c"&gt;
        w3c
      &lt;/a&gt; / &lt;a href="https://github.com/w3c/respec"&gt;
        respec
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A tool for creating technical documents and web standards
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Acknowledgements
&lt;/h2&gt;

&lt;p&gt;Thanks a million times to &lt;a href="https://twitter.com/marcosc"&gt;Marcos&lt;/a&gt;, for supporting me through the MOSS process, mentoring, code reviews, reviewing this post and being a friend. This project would not have been possible without you.&lt;/p&gt;

&lt;p&gt;I would like to express my gratitude to Mozilla and the MOSS program committee, for providing funds to help make open source sustainable. Shout out to &lt;a href="https://github.com/saschanaz"&gt;Kagami&lt;/a&gt; for their insightful code reviews.&lt;/p&gt;

&lt;p&gt;Thank you, for reading this far. Stay safe!&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>A Weird Hack using TypeScript</title>
      <dc:creator>Sid Vishnoi</dc:creator>
      <pubDate>Fri, 19 Jul 2019 12:22:38 +0000</pubDate>
      <link>https://dev.to/sidvishnoi/a-weird-hack-using-typescript-35bm</link>
      <guid>https://dev.to/sidvishnoi/a-weird-hack-using-typescript-35bm</guid>
      <description>&lt;p&gt;Hi! In a project in my last company, I ran into a weird issue with the modern JavaScript build system and to work around it, wrote an even weirder hack.&lt;/p&gt;

&lt;p&gt;Sorry if you found the title being click-baity. Alternative title: Using constant assertions in TypeScript to keep constants in sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The project was created using create-react-app (CRA) with TypeScript and it also had an Express server, that too in TypeScript. The project structure was simple (and an even more simplified version is shown here).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./
  src/
    App.tsx
  server/
    app.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then there was a constant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 week&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2 weeks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 year&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The constant &lt;code&gt;options&lt;/code&gt;, was required in client side to show a list of allowed options in a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; and the server needed it for validating the requests coming from client. Now using the DRY principle, I shouldn't be writing that same value of &lt;code&gt;options&lt;/code&gt; in two files - as the values at two places may get out of sync. So, it needed to be shared between &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt; - that's where the problem started.&lt;/p&gt;

&lt;p&gt;Turns out, create-react-app doesn't allow imports outside &lt;code&gt;src&lt;/code&gt; directory (&lt;a href="https://stackoverflow.com/a/44115058"&gt;See this Stack Overflow post&lt;/a&gt;), so I can't import &lt;code&gt;options&lt;/code&gt; from &lt;code&gt;server&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Hey Sid, why don't you do import from &lt;code&gt;src&lt;/code&gt; instead?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server/app.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../src/App.tsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The build process in &lt;code&gt;server&lt;/code&gt; was simple. Take a &lt;code&gt;.ts&lt;/code&gt; file, and run it through &lt;code&gt;tsc&lt;/code&gt; to create a &lt;code&gt;.js&lt;/code&gt; file, keeping the same directory structure.&lt;/p&gt;

&lt;p&gt;So, when I ran &lt;code&gt;tsc&lt;/code&gt; on my &lt;code&gt;server/app.ts&lt;/code&gt;, instead of creating a directory structure like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./server-build/
  app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it ended up creating:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./server-build/
  server/
    app.js
  src/
    app.js 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops. Messing up build directory or build process just to share a constant? Not cool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hack
&lt;/h2&gt;

&lt;p&gt;A prior day, I came across &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions"&gt;constant assertions&lt;/a&gt;, a new TypeScript feature.&lt;/p&gt;

&lt;p&gt;Turns out, in create-react-app, we can't import values outside &lt;code&gt;src&lt;/code&gt; directory, but we can import types.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server/app.ts&lt;/span&gt;
&lt;span class="c1"&gt;// a const assertion&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 week&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2 weeks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 year&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// export the type instead of value&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/App.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../server/app.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 day&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 week&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2 weeks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 month&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1 year&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How does it work? Lets say, I change "2 weeks" in &lt;code&gt;server&lt;/code&gt; to "3 weeks". What happens? Well, the client fails to compile! It fails as it's expecting options to have type &lt;code&gt;Options&lt;/code&gt;, but "3 weeks" doesn't exist in &lt;code&gt;Options&lt;/code&gt; at index 2. It works similarly if I change only in &lt;code&gt;src&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, we've two variables in sync using TypeScript. Not very DRY, but it works™️.&lt;/p&gt;

&lt;p&gt;See for yourself:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/NR6VQ0awdQs"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>react</category>
      <category>node</category>
    </item>
  </channel>
</rss>
