Code Assembly Strategies: Evaluating Template Engines and Shell Scripting

Fragment Assembly Challenges

Constructing code files by combining fragments often leads to ad-hoc scripting that lacks standardization. While build tools like Rsbuild or Vite offer templating capabilities, configuring them for simple concatenation tasks can be cumbersome.

Using EJS as a Template Engine

EJS operates similarly to traditional server-side templating languages, allowing dynamic data injection into markup.

pnpm add -D ejs

Below is a Node.js execution script to process the templates.

generate-build.js

import { readFileSync, writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import ejs from 'ejs';

const currentDir = dirname(fileURLToPath(import.meta.url));

// Load layout and partials
const layoutMarkup = readFileSync(join(currentDir, 'layout.ejs'), 'utf8');
const headerChunk = readFileSync(join(currentDir, 'partials/header.html'), 'utf8');
const logicChunk = readFileSync(join(currentDir, 'partials/app-logic.js'), 'utf8');

// Process the layout
const renderedOutput = ejs.render(layoutMarkup, { headerChunk, logicChunk });

// Save the assembled file
const destination = join(currentDir, 'dist/index.html');
writeFileSync(destination, renderedOutput);
console.log('✅ Build generated successfully');

layout.ejs

<html>
<head>
  <%= headerChunk %>
</head>
<body>
  <script>
    <%= logicChunk %>
  </script>
</body>
</html>

Execute the script to render the final file:

node ./generate-build.js

Revisiting the Approach

Despite using a templating engine, the underlying mechanism remains a custom script responsible for file I/O. Adding a new fragment requires modifications in both the template file and the Node.js script. For straightforward concatenation requirements, a dedicated template engine might introduce unnecessary complexity compared to a direct scripting solution.

Shell Script with Placeholder Substitution

A simpler alternative involves defining custom placeholders within a standard HTML file, which are then replaced using native shell commands.

base-layout.html

<head>%INCLUDE_HEADER%</head>
<body>
  <script>%INCLUDE_SCRIPT%</script>
</body>

assemble-placeholder.sh

#!/bin/bash

# Fetch fragment contents
HEADER_FRAG=$(cat fragments/header.html)
JS_FRAG=$(cat fragments/app-logic.js)

# Load template and swap placeholders
ASSEMBLED=$(cat base-layout.html)
ASSEMBLED=${ASSEMBLED//'%INCLUDE_HEADER%'/$HEADER_FRAG}
ASSEMBLED=${ASSEMBLED//'%INCLUDE_SCRIPT%'/$JS_FRAG}

# Write output
echo "$ASSEMBLED" > dist/index.html
echo "✅ Assembly complete"

This method is highly efficient and allows for arbitrary placeholder definitions. You can even utilize comment syntax as placeholders to prevent IDE validation errors while maintaining valid syntax in the base template.

Shell Script with Heredoc

An even more direct approach leverages bash Heredoc syntax to inject variables directly into the output stream.

assemble-heredoc.sh

#!/bin/bash

# Read fragments into environment variables
header_data=$(cat fragments/header.html)
script_data=$(cat fragments/app-logic.js)

# Construct output using Heredoc
cat <<EOF > dist/index.html
<head>${header_data}</head>
<body>
  <script>${script_data}</script>
</body>
EOF

While this technique is straightforward and readable, it becomes harder to maintain as the structural complexity of the template increases, making the placeholder substitution method more suitable for larger projects.

Tags: template engine EJS bash Code Generation html

Posted on Mon, 29 Jun 2026 17:12:38 +0000 by trazan