๐Ÿ“ commands/release-notes/ ยท 05_response_formatting.md

Chapter 5: Response Formatting

๐Ÿ“„ commands/release-notes/05_response_formatting.md

Chapter 5: Response Formatting

Welcome to the final chapter of the release-notes tutorial!

In the previous chapter, Optimistic Fetching Strategy, we successfully retrieved our data. We raced the network against a timer and grabbed the release notes (either from the live internet or our local cache).

However, we have a new problem. The data currently looks like "computer code." It is full of brackets, quotes, and commas. If we show this to a user, they will be confused.

In this chapter, we will learn about Response Formatting. This is the art of taking raw data and "plating" it so it looks beautiful for the user.

The Motivation: Plating the Meal

Let's return to our restaurant analogy one last time.

  1. Registration: The menu exists.
  2. Lazy Loading: The chef is called to the kitchen.
  3. Async Handler: The chef cooks the food.
  4. Optimistic Strategy: The food is ready fast.

Now, imagine the chef takes the steak, the potatoes, and the sauce, and throws them all into a blender, then pours the result into a bowl. Technically, the food is there, but it looks terrible.

Response Formatting is the step where the chef carefully arranges the steak on the plate, places the potatoes on the side, and drizzles the sauce on top.

Key Concepts

To format our data, we need to transform a list of computer objects into a single, long text string that contains Newlines and Bullet Points.

The Input (Raw Ingredients)

Our data currently looks like a list of "Tuples" (pairs).

// This is hard for a human to read quickly
[
  ["1.0.0", ["Fixed login bug", "Added dark mode"]],
  ["0.9.0", ["Initial release"]]
]

The Output (The Plated Meal)

We want to transform that into this:

Version 1.0.0:
ยท Fixed login bug
ยท Added dark mode

Version 0.9.0:
ยท Initial release

Implementing the Formatter

We will write a helper function called formatReleaseNotes. We will use two powerful JavaScript tools: .map() (transform) and .join() (glue).

Step 1: The Helper Function

Let's define our function in release-notes.ts. It takes the raw list as input and returns a single string.

// --- File: release-notes.ts ---

// Input: A list of pairs (Version string, Array of notes)
// Output: A single big string
function formatReleaseNotes(notes: Array<[string, string[]]>): string {
  
  // We will transform each pair into a nice block of text
  const formattedBlocks = notes.map(([version, changes]) => {
    
    // Logic continues below...
    return '' // Placeholder
  })

  // Glue all blocks together with a double newline (gap)
  return formattedBlocks.join('\n\n')
}

Explanation:

Step 2: Formatting the Header

Inside our loop, we first want to create the header (the version number).

// --- File: release-notes.ts (Inside the .map function) ---

    // 1. Create a nice header line
    // Example result: "Version 1.0.0:"
    const header = `Version ${version}:`

Explanation:

Step 3: Formatting the Bullet Points

Next, we need to handle the list of changes. We want a dot (ยท) before each one.

// --- File: release-notes.ts (Inside the .map function) ---

    // 2. Add a dot before every note in the list
    const lines = changes.map(note => `ยท ${note}`)

    // 3. Glue these lines together with a single newline
    // Example result:
    // ยท Fixed login bug
    // ยท Added dark mode
    const bulletPoints = lines.join('\n')

Explanation:

Step 4: Assembling the Block

Finally, we combine the Header and the Bullet Points for this specific version.

// --- File: release-notes.ts (Inside the .map function) ---

    // 4. Combine header and body
    return `${header}\n${bulletPoints}`
  
  // End of .map function

Integrating into the Command

Now we simply call this function inside our main call() handler (which we built in Chapter 3).

// --- File: release-notes.ts ---

// Inside call() function...

// If we have data (fresh or cached), format it!
if (freshNotes.length > 0) {
  
  // 1. Convert raw data to pretty string
  const prettyString = formatReleaseNotes(freshNotes)

  // 2. Return the result object
  return { 
    type: 'text', 
    value: prettyString 
  }
}

Explanation:

Under the Hood

How does this string actually get to the screen?

Sequence Diagram

Here is the flow of data from the raw arrays to the user's eyes.

sequenceDiagram participant Handler as Command Handler participant FMT as Formatter Logic participant CLI as CLI Core participant Screen as User Terminal Note over Handler: Has raw data: ['1.0', ['Fix']] Handler->>FMT: Call formatReleaseNotes(data) FMT->>FMT: Add "Version" prefix FMT->>FMT: Add "ยท" bullets FMT->>FMT: Join with \n (Newlines) FMT-->>Handler: Returns "Version 1.0:\nยท Fix" Handler-->>CLI: Returns { type: 'text', value: string } CLI->>Screen: console.log(value) Note over Screen: The \n turns into<br/>actual line breaks

Internal Implementation Details

The CLI Core handles the final output. It receives your LocalCommandResult.

// --- File: core-runner.ts (Simplified) ---

// The CLI receives your result object
const result = await command.call()

if (result.type === 'text') {
  // console.log interprets the special character '\n'
  // as a command to move the cursor to the next line.
  console.log(result.value)
}

Explanation:

Conclusion

Congratulations! You have completed the release-notes project tutorial.

In this chapter, we learned that Response Formatting is the "Presentation Layer" of our command. We turned raw, ugly data arrays into a clean, readable list using string manipulation (map, join, and \n).

Let's recap what you have built:

  1. Command Registration: You created a menu entry so the CLI knows your command exists.
  2. Lazy Module Loading: You ensured your code is only loaded when the user asks for it, keeping the app fast.
  3. Asynchronous Command Handler: You wrote the logic to process the command without freezing the computer.
  4. Optimistic Fetching Strategy: You implemented a race condition to ensure the user never waits too long.
  5. Response Formatting: You styled the output to be human-readable.

You now have a fully functional, high-performance CLI command structure. Happy coding!


Generated by Code IQ