<?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: Roberto B. Stanziale</title>
    <description>The latest articles on DEV Community by Roberto B. Stanziale (@rstanziale).</description>
    <link>https://dev.to/rstanziale</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%2F1242514%2F4728978b-595e-4f4d-9a74-81f1d13f1d81.jpg</url>
      <title>DEV Community: Roberto B. Stanziale</title>
      <link>https://dev.to/rstanziale</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rstanziale"/>
    <language>en</language>
    <item>
      <title>ez-api – API development toolkit powered by TypeSpec</title>
      <dc:creator>Roberto B. Stanziale</dc:creator>
      <pubDate>Sat, 29 Mar 2025 13:28:06 +0000</pubDate>
      <link>https://dev.to/rstanziale/ez-api-api-development-toolkit-powered-by-typespec-474</link>
      <guid>https://dev.to/rstanziale/ez-api-api-development-toolkit-powered-by-typespec-474</guid>
      <description>&lt;p&gt;I first came across &lt;a href="https://typespec.io/" rel="noopener noreferrer"&gt;TypeSpec&lt;/a&gt; through an article on LinkedIn where someone shared a positive experience using it. This sparked my curiosity, as I was looking for ways to improve API documentation in my current project. The API documentation for our product was relatively weak, making it difficult to ensure consistency and clarity. I decided to experiment with TypeSpec by applying it to a real-world scenario, aiming to simplify API definition and improve collaboration.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;strong&gt;Disclaimer&lt;/strong&gt;:&lt;br&gt;
This article was written with the assistance of AI to refine structure and clarity. However, all technical content, insights, and project details are based on my personal experience and research.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;The official documentation states that TypeSpec is a language and toolset developed by Microsoft for defining data models and service APIs. It provides a structured way to describe the shape and behavior of data and services, ensuring consistency and reducing errors in API development. By leveraging TypeSpec, developers can generate code, documentation, and other artifacts directly from their API definitions, making it easier to maintain and evolve their services.&lt;/p&gt;

&lt;p&gt;Microsoft itself uses TypeSpec to define APIs for various products, including Azure. The language focuses on specifying the interface of an API—such as operations, request and response models, and error-handling mechanisms—while the actual logic remains in the backend service that processes requests and interacts with the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing ez-api
&lt;/h2&gt;

&lt;p&gt;As I explored TypeSpec, I saw an opportunity to build a development toolkit that would make API creation more straightforward. My goal with ez-api was to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define multiple APIs without excessive complexity, similar to a monorepo approach&lt;/li&gt;
&lt;li&gt;Leverage Git to facilitate collaboration among team members&lt;/li&gt;
&lt;li&gt;Introduce useful scripts to streamline workflows, particularly in continuous integration scenarios&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By addressing these challenges, ez-api provides a structured yet flexible way to manage API development efficiently. In the following sections, I will delve deeper into how it works and what I learned from the experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ez-api works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Project setup
&lt;/h3&gt;

&lt;p&gt;To develop &lt;strong&gt;ez-api&lt;/strong&gt;, I used the following tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Visual Studio Code&lt;/em&gt; – with the official TypeSpec extension for syntax highlighting and validation&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;TypeSpec CLI&lt;/em&gt; – to compile TypeSpec definitions and generate API artifacts&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Node.js&lt;/em&gt; (&amp;gt;= 20) – required for running scripts and dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project structure
&lt;/h3&gt;

&lt;p&gt;The ez-api project follows a structured layout to keep API definitions organized and maintainable. Below is an overview of its directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ez-api/
├── dist/
│   └── my-awesome-api/
│       ├── api-1.0.0-beta.1.yaml
│       └── api-1.0.0-beta.1.json
├── doc/
│   └── my-awesome-api/
│       ├── api-x.y.z.yaml
│       └── api-x.y.z.json
├── ext/
│   └── &amp;lt;ext-name&amp;gt;.tsp
├── projects/
│   └── my-awesome-api/
│       ├── model/
│       ├── routes/
│       ├── config.json
│       ├── main.tsp
│       ├── tspconfig-json.yaml
│       └── tspconfig-yaml.yaml
├── tools/
│   ├── api-new.ts
│   └── postbuild.ts
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Where the main directories are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;projects/&amp;lt;api-name&amp;gt;&lt;/code&gt; – where the TypeSpec, configurations, routes and model files of the API being modeled reside&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doc/&amp;lt;api-name&amp;gt;&lt;/code&gt; – the &lt;em&gt;development&lt;/em&gt; version of the API in JSON and YAML format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dist/&amp;lt;api-name&amp;gt;&lt;/code&gt; – the &lt;em&gt;official&lt;/em&gt; OpenAPI version of the API in JSON and YAML format&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tools&lt;/code&gt; – utility scripts of this toolkit to create and manipulate the API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ext&lt;/code&gt; – directory where to define useful OpenAPI extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Available commands
&lt;/h3&gt;

&lt;p&gt;The ez-api toolkit provides a set of commands to facilitate API development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;api:new &amp;lt;api-name&amp;gt;&lt;/code&gt; – Creates a new API project with the specified name, setting up the necessary directory structure and configuration files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;compile-yaml:&amp;lt;api-name&amp;gt;&lt;/code&gt;/&lt;code&gt;compile-json:&amp;lt;api-name&amp;gt;&lt;/code&gt; – Generates developer preview YAML &lt;strong&gt;or&lt;/strong&gt; JSON files in the &lt;code&gt;doc/&amp;lt;api-name&amp;gt;&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;compile:&amp;lt;api-name&amp;gt;&lt;/code&gt; – Generates production ready OpenAPI-compliant YAML 
&lt;strong&gt;and&lt;/strong&gt; JSON files in the &lt;code&gt;dist/&amp;lt;api-name&amp;gt;&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These commands simplify the workflow, making it easier to bootstrap new API projects and generate the required artifacts with minimal effort.&lt;/p&gt;
&lt;h3&gt;
  
  
  Real-World scenario: i18n-resources API
&lt;/h3&gt;

&lt;p&gt;One of the practical applications of ez-api was to define APIs for handling i18n bundles in an Angular application. The goal was to provide endpoints that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retrieve UI labels based on the selected language&lt;/li&gt;
&lt;li&gt;Offer CRUD operations for administrative users to customize translations via a third-party tool&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, the &lt;code&gt;GET&lt;/code&gt; route &lt;code&gt;/i18n/bundle&lt;/code&gt; definition to retrieve i18n resource by a &lt;strong&gt;selector&lt;/strong&gt; and &lt;strong&gt;langTag&lt;/strong&gt; is shown below:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@typespec/http";
import "@typespec/openapi";
import "../model/i18n-resource.model.tsp";

namespace I18nResourceNamespace;

using TypeSpec.Http;
using TypeSpec.OpenAPI;

@route("/i18n")
namespace I18nRoute {
  @route("/bundle")
  @summary("Consume bundle resource")
  @doc("""
      Retrieve a bundle resource according **selector** and **langTag** taken as input.

      It's important to note that if a custom bundle is present, it will override the default entries.
    """)
  @get
  op consumeBundle(@query selector: string, @query langTag: string): {
    @statusCode _: 200;
    @body result: BaseResponse&amp;lt;I18nResource&amp;gt;;
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Through the use of some decorators, a Markdown (or HTML) syntax, it is quite easy to define a route.&lt;br&gt;
The real convenience I have identified is the definition of the model, useful to define the route. For example, let see how I defined &lt;code&gt;I18nResource&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "@typespec/openapi3";

using TypeSpec.OpenAPI;

@example(i18nResourceExample)
model I18nResource {
  /**
   * Resource bundle identifier
   */
  selector: string;

  /**
   * Language identifier according application locales
   */
  langTag: string;

  /**
   * Json object with i18n entries
   */
  bundle: Record&amp;lt;unknown&amp;gt;;

  /**
   * True if resource is provided by application, false if it's created by customer
   */
  default: boolean;
}

const i18nResourceExample: I18nResource = #{
  selector: "reportingApp",
  langTag: "it-IT",
  bundle: #{ key1: #{ key2: "Label per key 2" }, key3: "Label per key 3" },
  default: true,
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As you can note, for a &lt;strong&gt;Typescript&lt;/strong&gt; developer it's very easy define a model in TypeSpec.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The ez-api project showcases how TypeSpec can be leveraged to define and manage APIs in a structured and maintainable way. By using TypeSpec along with this toolkit, I was able to streamline API development while ensuring consistency across multiple services.&lt;/p&gt;

&lt;p&gt;If you're interested in exploring the full implementation of &lt;strong&gt;ez-api&lt;/strong&gt;, including the complete definition of the &lt;strong&gt;i18n-resources&lt;/strong&gt; API, you can find the repository here:&lt;/p&gt;

&lt;p&gt;➡️ GitHub Repository: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rstanziale" rel="noopener noreferrer"&gt;
        rstanziale
      &lt;/a&gt; / &lt;a href="https://github.com/rstanziale/ez-api" rel="noopener noreferrer"&gt;
        ez-api
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      API development toolkit powered by TypeSpec
    &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;ez-api&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A robust API development toolkit powered by &lt;a href="https://typespec.io/" rel="nofollow noopener noreferrer"&gt;TypeSpec&lt;/a&gt;, enabling seamless API design and generation.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;ez-api leverages Microsoft's TypeSpec to define and maintain consistent APIs across projects. This toolkit streamlines the API development workflow with powerful developer experience (DX) features.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeSpec Integration&lt;/strong&gt;: First-class support for Microsoft's TypeSpec specification language&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Generation&lt;/strong&gt;: Quick scaffolding with predefined archetypes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import OpenAPI Specs&lt;/strong&gt;: Import existing OpenAPI specifications into TypeSpec projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAPI Compliance&lt;/strong&gt;: Automatic generation of OpenAPI 3.0 specifications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer Tools&lt;/strong&gt;: Enhanced DX with built-in utilities&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Prerequisites&lt;/h3&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Node.js (v20 or higher)&lt;/li&gt;
&lt;li&gt;TypeSpec CLI (&lt;code&gt;npm install -g @typespec/compiler&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Visual Studio Code as editor&lt;/li&gt;
&lt;li&gt;Official TypeSpec VSCode extension&lt;/li&gt;
&lt;li&gt;Prettier extension for VSCode (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Installation&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pnpm install&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Quick Start&lt;/h3&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Create your first API project:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pnpm run api:new my-awesome-api&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;or import an existing OpenAPI specification:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;pnpm run api:import my-awesome-api path/to/openapi.yaml&lt;/pre&gt;

&lt;/div&gt;

&lt;ol start="2"&gt;
&lt;li&gt;Customize project settings in &lt;code&gt;config.json&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight highlight-source-json notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&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/rstanziale/ez-api" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Future improvements
&lt;/h3&gt;

&lt;p&gt;While ez-api already simplifies API development, I have a few improvements in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenAPI Import&lt;/strong&gt; – The ability to import an existing OpenAPI specification and automatically generate the corresponding ez-api structure. This would help teams transition smoothly from OpenAPI-based workflows to TypeSpec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Tooling&lt;/strong&gt; – Additional scripts and automation to further integrate with CI/CD pipelines and improve developer experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TypeSpec is a powerful tool, and I believe it has the potential to become a key component in modern API development workflows. If you have any feedback or ideas for improvement, feel free to leave a comment or open a question issue in the GitHub repository! 🚀&lt;/p&gt;

</description>
      <category>typespec</category>
      <category>typescript</category>
      <category>api</category>
      <category>tooling</category>
    </item>
    <item>
      <title>ez-flow – Typescript library for a workflow engine</title>
      <dc:creator>Roberto B. Stanziale</dc:creator>
      <pubDate>Sun, 25 Feb 2024 14:42:44 +0000</pubDate>
      <link>https://dev.to/rstanziale/ez-flow-typescript-library-for-a-workflow-engine-l67</link>
      <guid>https://dev.to/rstanziale/ez-flow-typescript-library-for-a-workflow-engine-l67</guid>
      <description>&lt;p&gt;This library was heavily inspired to &lt;a href="https://github.com/j-easy/easy-flows" rel="noopener noreferrer"&gt;j-easy/easy-flows&lt;/a&gt; in the Java world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Definitions
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Workflow&lt;/strong&gt; is a collection of &lt;strong&gt;Work Units&lt;/strong&gt; must be run tu achieve a given purpose.&lt;/p&gt;

&lt;p&gt;Each &lt;em&gt;Work&lt;/em&gt; unit implements a behaviour and interacts with outer environment and the other units by the means of a &lt;strong&gt;Context&lt;/strong&gt; exchanged between them. You can place each unit in a flow applying some logic to control it.&lt;/p&gt;

&lt;p&gt;That is you can place unit in a sequential flow, in a conditional one, in a parallel one and so on.&lt;/p&gt;

&lt;p&gt;At the end of this "chain" we obtain the final result.&lt;/p&gt;

&lt;p&gt;In this library are actually available the following flow constructs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequential Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conditional Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iterative Flow&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parallel Flow&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10gdh01zjligtse1hevd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10gdh01zjligtse1hevd.png" alt="Flows definitions" width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to apply a flow?
&lt;/h3&gt;

&lt;p&gt;Suppose you have defined a work unit called &lt;strong&gt;PrintMessageWork&lt;/strong&gt; that implements &lt;em&gt;Work&lt;/em&gt;. It takes a &lt;em&gt;message&lt;/em&gt; and its &lt;em&gt;call&lt;/em&gt; method prints it:&lt;br&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Work&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WorkContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WorkReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DefaultWorkReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WorkStatus&lt;/span&gt;&lt;span class="p"&gt;,&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;@rs-box/ez-flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrintMessageWork&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;Work&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;print message&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="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WorkContext&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WorkReport&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefaultWorkReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WorkStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;workContext&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;h2&gt;
  
  
  Use case
&lt;/h2&gt;

&lt;p&gt;To test the library, a use case scenario was built in which, given a list of Italian cities, I wish to perform certain operations on each city and produce results.&lt;/p&gt;

&lt;p&gt;To achieve this, it was important to first define the available inputs and what the workflow wanted to obtain, in this case the cumulative data on the regions to which the cities belong.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @rs-box/ez-flow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Workflows definition
&lt;/h2&gt;

&lt;p&gt;Specifically, three workflows were built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt; workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop over cities&lt;/strong&gt; workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Main
&lt;/h3&gt;

&lt;p&gt;This is the main workflow, a &lt;em&gt;Sequential Flow&lt;/em&gt; that performs two operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the context given as input (&lt;em&gt;ValidateContext&lt;/em&gt; unit).&lt;/li&gt;
&lt;li&gt;Calling the &lt;em&gt;Loop over cities&lt;/em&gt; workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxs16bedflfuqodx9n51p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxs16bedflfuqodx9n51p.png" alt="Main workflow" width="746" height="254"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Loop over cities
&lt;/h3&gt;

&lt;p&gt;This is a &lt;em&gt;Repeat Flow&lt;/em&gt;, it will run until the &lt;em&gt;IsNotLastCity&lt;/em&gt; predicate returns a negative value&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8itum2kxdjbbi093xxh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw8itum2kxdjbbi093xxh.png" alt="Loop over cities" width="746" height="254"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Operations city
&lt;/h3&gt;

&lt;p&gt;This is a &lt;em&gt;Parallel Flow&lt;/em&gt; that executes the list of units passed as input, in the specific case of this test it will perform the following actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Print the city name&lt;/li&gt;
&lt;li&gt;Update the cumulative region data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk4fba6w6fgp0m1115l5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnk4fba6w6fgp0m1115l5.png" alt="Operations city" width="746" height="427"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Complete
&lt;/h3&gt;

&lt;p&gt;This is the complete workflow resulting from the combination of those shown above.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcex0cirkq1hwlsrbzf9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcex0cirkq1hwlsrbzf9x.png" alt="Complete workflow" width="766" height="1017"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Finally, the workflow described above is translated as follows:&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;workflow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SequentialFlow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Main workflow&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="nf"&gt;addWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ValidateContextWork&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;RepeatFlow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loop over cities&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="nf"&gt;withWork&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;ParallelFlow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Operations city&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="nf"&gt;withWorks&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrintCityWork&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegionCity&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&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="nf"&gt;until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IsNotLastCityPredicate&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&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="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Source code
&lt;/h3&gt;

&lt;p&gt;You can find the full source code for this scenario here:&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://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rstanziale" rel="noopener noreferrer"&gt;
        rstanziale
      &lt;/a&gt; / &lt;a href="https://github.com/rstanziale/ez-flow-test" rel="noopener noreferrer"&gt;
        ez-flow-test
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Application for testing ez-flow
    &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;ez-flow-test&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This application test aims to demonstrate &lt;a href="https://github.com/rstanziale/ez-flow" rel="noopener noreferrer"&gt;@rs-box/ez-flow&lt;/a&gt; functions.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Use case&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;To test the library, a use case scenario was built in which, given a list of Italian cities, I wish to perform certain operations on each city and produce results.&lt;/p&gt;

&lt;p&gt;To achieve this, it was important to first define the available inputs and what the workflow wanted to obtain, in this case the cumulative data on the regions to which the cities belong.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Workflows definition&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;Specifically, three workflows were built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Main&lt;/strong&gt; workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loop over cities&lt;/strong&gt; workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations&lt;/strong&gt; workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Main&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;This is the main workflow, a &lt;em&gt;Sequential Flow&lt;/em&gt; that performs two operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate the context given as input (&lt;em&gt;ValidateContext&lt;/em&gt; unit).&lt;/li&gt;
&lt;li&gt;Calling the &lt;em&gt;Loop over cities&lt;/em&gt; workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rstanziale/ez-flow-test./doc/img/ez-flow-test_main_workflow.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frstanziale%2Fez-flow-test.%2Fdoc%2Fimg%2Fez-flow-test_main_workflow.png" alt="Main workflow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Loop over cities&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;This is a &lt;em&gt;Repeat Flow&lt;/em&gt;, it will run until the &lt;em&gt;IsNotLastCity&lt;/em&gt; predicate returns a negative value&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/rstanziale/ez-flow-test./doc/img/ez-flow-test_loop-on-cities.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Frstanziale%2Fez-flow-test.%2Fdoc%2Fimg%2Fez-flow-test_loop-on-cities.png" alt="Loop over cities"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Operations city&lt;/h3&gt;

&lt;/div&gt;

&lt;p&gt;This is a &lt;em&gt;Parallel Flow&lt;/em&gt; that executes the list of units…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rstanziale/ez-flow-test" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>typescript</category>
      <category>programming</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>ez-bundle – Javascript tool to research JSON keys in a target directory</title>
      <dc:creator>Roberto B. Stanziale</dc:creator>
      <pubDate>Sat, 20 Jan 2024 18:15:16 +0000</pubDate>
      <link>https://dev.to/rstanziale/ez-bundle-javascript-tool-to-research-json-keys-in-a-target-directory-2o8d</link>
      <guid>https://dev.to/rstanziale/ez-bundle-javascript-tool-to-research-json-keys-in-a-target-directory-2o8d</guid>
      <description>&lt;p&gt;As a web developer, the task of internationalizing a web application can often lead to the accumulation of large and unwieldy language bundles over time. These bundles, containing key-value pairs representing translated strings, can become outdated or unused as the application evolves.&lt;/p&gt;

&lt;p&gt;Have you ever wondered: &lt;em&gt;Are all JSON keys still being used by my application?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;tool&lt;/strong&gt; addresses this issue by providing a simple yet effective means to verify whether a specified directory contains all the keys defined in a JSON file provided as input.&lt;/p&gt;

&lt;p&gt;It helps ensure that your language bundles are accurate, up-to-date, and efficiently utilized, minimizing the risk of translation errors and inconsistencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;In order to be executed, it needs three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A JSON file containing your bundle of keys to verify&lt;/li&gt;
&lt;li&gt;A target directory where you want to look for the keys in the bundle&lt;/li&gt;
&lt;li&gt;A list of comma-separated file extensions to search for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the execution, this utility will create an &lt;code&gt;entries-not-found.txt&lt;/code&gt; file containing the unused keys as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;widget.caseDetail.details
widget.caseDetail.identifier
widget.caseDetail.priority
widget.caseDetail.progressBar
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  How to execute it
&lt;/h2&gt;

&lt;p&gt;The following command is necessary:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js bundle.json src/ js,ts,html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Where &lt;em&gt;bundle.json&lt;/em&gt; and &lt;em&gt;src/&lt;/em&gt; are examples of arguments passed as input to the script, and &lt;em&gt;js,ts,html&lt;/em&gt; is an optional argument, otherwise, the default is &lt;strong&gt;ts,html&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;For large bundles, it may take a while, for example:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[2023-09-14T10:34:22.663Z] [INFO] Number of keys: 1422
[2023-09-14T10:34:22.666Z] [INFO] Starting process...
[2023-09-14T10:51:52.683Z] [INFO] End process!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In an &lt;em&gt;Angular&lt;/em&gt; application using the &lt;em&gt;ngx-translate&lt;/em&gt; library, with 1422 keys and more than 950 files to scan, it takes about 17 minutes to complete.&lt;/p&gt;

&lt;p&gt;Of course, the script could always be improved from a performance point of view. It should be okay as an initial version because I don't expect it to be executed that often 😉&lt;/p&gt;
&lt;h2&gt;
  
  
  Constraints
&lt;/h2&gt;

&lt;p&gt;Currently, this tool only works on &lt;strong&gt;Linux&lt;/strong&gt; shells because it uses the &lt;strong&gt;grep&lt;/strong&gt; command internally. For Windows, you can run it on shells like &lt;a href="https://git-scm.com/download/win" rel="noopener noreferrer"&gt;git bash&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;Here you can find the script, download it, try it or use it for yourself:&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://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rstanziale" rel="noopener noreferrer"&gt;
        rstanziale
      &lt;/a&gt; / &lt;a href="https://github.com/rstanziale/ez-bundle" rel="noopener noreferrer"&gt;
        ez-bundle
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Given a json file, it performs a search for its keys in a target directory taken as input.
    &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;ez-bundle&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;As a web developer I found myself in the situation of internationalizing a web application, and clearly after a few years the language bundles can grow out of proportion. It is also possible that the application has been modified, refactored, etc...&lt;/p&gt;

&lt;p&gt;Now, are all the keys in the language bundles still being used?&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;tool&lt;/em&gt; allows you to answer that question.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;To be executed, it needs three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A JSON file containing your bundle of keys to verify&lt;/li&gt;
&lt;li&gt;A target directory where you want to look for the keys in the bundle&lt;/li&gt;
&lt;li&gt;A list of file extensions comma separated to search for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After execution, this utility will create a &lt;code&gt;entries-not-found.txt&lt;/code&gt; file containing the unused keys as shown below:&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;widget.caseDetail.details
widget.caseDetail.identifier
widget.caseDetail.priority
widget.caseDetail.progressBar
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How to execute it&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;It is necessary to run the command:&lt;/p&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;node index.js bundle.json src/ js,ts,html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where &lt;em&gt;bundle.json&lt;/em&gt; and &lt;em&gt;src/&lt;/em&gt; are examples of…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/rstanziale/ez-bundle" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>tooling</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
