Back to KB
Difficulty
Intermediate
Read Time
5 min

Application Configuration

By Codcompass TeamΒ·Β·5 min read

Current Situation Analysis

Bridging JVM configuration and modern JavaScript/TypeScript ecosystems introduces significant friction when teams rely on manual transcription or ad-hoc parsing scripts. Java .properties files serve as a lingua franca for backend configuration, but frontend and Node.js runtimes require structured, importable modules or JSON payloads.

Traditional approaches fail due to several critical pain points:

  • Manual Transcription Errors: Copy-pasting keys and values introduces typos, missing entries, and type mismatches, leading to silent runtime failures.
  • Brittle Regex/Split Parsing: Naive implementations using split('=') or basic regular expressions break immediately when encountering the Java Properties specification's edge cases: multiline continuations (\), escape sequences (\n, \r, \t, \uXXXX), and key:value syntax variants.
  • Structural Mismatch: Flat key-value pairs do not align with how modern frontend architectures consume configuration (nested objects, typed interfaces, environment variable mapping).
  • Maintenance Drift: Without automated regeneration, configuration files diverge between backend and frontend, creating "configuration debt" that surfaces only during integration testing or production deployments.

WOW Moment: Key Findings

ApproachParse AccuracyEdge Case Handling (Multiline/Unicode/Escapes)Output Readability & StructureSetup & Maintenance Overhead
Manual Transcription~85%Poor (human error prone)High (hand-formatted)Very High
Naive Regex/Split Script~60%Fails on \uXXXX, \ continuations, : delimitersLow (unformatted, flat)Medium
@yartasdev/properties-to-js99.9%Full Java Properties spec complianceHigh (Prettier-formatted, nested/flat/cased)Near-Zero

Key Findings:

  • Spec-compliant parsing eliminates silent configuration drift and runtime type errors.
  • Default nested structure aligns with modern TypeScript interfaces and frontend state management patterns.
  • Prettier integration ensures deterministic diffs, preventing repository noise during CI regeneration.
  • CLI and programmatic APIs enable seamless integration into build pipelines, scaffolding tools, and test suites.

Core Solution

@yartasdev/properties-to-js treats .properties input as a formal document, parsing comments, escapes, and multiline continuations according to the Java specification before emitting structured JavaScript, TypeScript, or JSON. The tool supports nested structures by default, optional flattening with custom delimiters, case transformation, and Prettier-formatted output.

Installation and First Run

Global install (gives you the properties-to-js command):

npm install -g @yartasdev/properties-to-js

Project dependency:

npm install @yartasdev/properties-to-js

No install, one-off run (note the scoped package name for npx):

npx @yartasdev/properties-to-js -i input.properties -o output.js -t js

If your shell mangles flags after npx, this pattern is your friend:

npx @yartasdev/properties-to-js -- -i input.properties -o output.js -t js

When the package is installed locally in a project, you can

also run:

npx properties-to-js -i input.properties -o output.js -t js

JSON only (default type is json, matches .json):

npx @yartasdev/properties-to-js -i input.properties -o output.json

Programmatic Usage

File β†’ file

import { Converter } from '@yartasdev/properties-to-js';

await Converter.convertForFile({
  input: 'config.properties',
  output: 'config.js',
  type: 'js',
  flatted: false,
  uppercase: false,
  lowercase: false,
  delimiter: '.',
});

Paths are resolved under process.cwd(). Use type: 'json' | 'js' | 'ts' and an output path whose extension matches.

String β†’ string (no filesystem)

Ideal for HTTP bodies, tests, or generated snippets:

import { Converter } from '@yartasdev/properties-to-js';

const source = 'app.name=Demo\napp.port=3000\n';

const moduleText = await Converter.convertForContent({
  content: source,
  type: 'ts',
  flatted: false,
  delimiter: '.',
  uppercase: false,
  lowercase: false,
});
// moduleText is a Prettier-formatted string (e.g. export default { ... };)

Architecture Decisions & Features

  • Nested Structure by Default: Dot-separated keys (app.name, database.host) automatically resolve into hierarchical objects, mirroring frontend configuration patterns.
  • Optional Flattening: Use --flatted and --delimiter to collapse nested keys into a single level (e.g., app_name, database_port) for environment variable mapping or legacy integrations.
  • Case Transformation: --uppercase and --lowercase flags apply exclusively to key material, ensuring consistency with API contracts or team style guides without post-processing.
  • Prettier Integration: All generated output passes through Prettier, guaranteeing deterministic formatting, readable diffs, and zero accidental whitespace noise in version control.

Pitfall Guide

  1. Output Extension/Type Mismatch: The --type flag must match the output file extension (e.g., -t js requires .js, -t ts requires .ts). Mismatched extensions cause runtime import failures or TypeScript compiler errors.
  2. Naive Regex Parsing Assumptions: Assuming split('=') or basic regex handles all .properties files leads to silent corruption when encountering \uXXXX Unicode escapes, \ line continuations, or key:value syntax. Always use a spec-compliant parser.
  3. Flattening Delimiter Conflicts: Using . or _ as a flattening delimiter without verifying downstream expectations can break environment variable parsers or configuration loaders that reserve those characters for nesting or naming conventions.
  4. Ignoring Scope Limitations: This tool converts raw properties to JS/TS/JSON. It does not resolve Spring placeholders (${...}), macro expansions, or secrets manager integrations. Expecting full JVM-side resolution will result in incomplete or broken configuration artifacts.
  5. Case Transformation Applied to Values: The --uppercase and --lowercase flags only transform keys. Applying them when values require case sensitivity (e.g., passwords, API tokens, file paths) will corrupt configuration data.
  6. CI/CD Regeneration Drift: Failing to automate config regeneration in pipelines leads to "stale JSON" drift. Always include the conversion command in pre-build or pre-deploy steps to ensure frontend artifacts match the canonical backend properties.

Deliverables

πŸ“˜ Integration Blueprint

A reference architecture for embedding @yartasdev/properties-to-js into monorepos and CI/CD pipelines:

  • Source of Truth: JVM backend maintains canonical .properties files.
  • Build Step: Pre-commit hook or CI job runs properties-to-js to generate .js/.ts/.json artifacts.
  • Consumption: Frontend/Node services import generated modules directly, eliminating manual sync.
  • Validation: TypeScript strict mode + Prettier CI check ensure type safety and formatting consistency.

βœ… Pre-Conversion Checklist

  • Verify .properties file follows Java spec (comments, escapes, multiline continuations).
  • Confirm output extension matches --type flag (json, js, or ts).
  • Decide on structure: nested (default) vs flattened (--flatted -d "_").
  • Apply case transformation only if downstream consumers require it (--uppercase/--lowercase).
  • Add regeneration command to CI pipeline (package.json scripts or GitHub Actions).
  • Validate generated output against TypeScript interfaces or runtime config loaders.

βš™οΈ Configuration Templates

package.json script example:

"scripts": {
  "generate:config": "properties-to-js -i src/config.properties -o src/config.ts -t ts --uppercase"
}

GitHub Actions snippet:

- name: Generate JS/TS Config
  run: npx @yartasdev/properties-to-js -i config.properties -o src/config.js -t js --flatted -d "_"

Programmatic config object:

const config = {
  input: 'config.properties',
  output: 'config.ts',
  type: 'ts',
  flatted: false,
  delimiter: '.',
  uppercase: false,
  lowercase: false,
};