Skip to content

Conversation

DemoMacro
Copy link

@DemoMacro DemoMacro commented Sep 21, 2025

Add heading configuration options to document options with support for:

  • Custom font family, font size, and formatting for headings 1-6
  • Configurable paragraph spacing before and after headings
  • Keep lines and keep next paragraph options
  • Outline level configuration for document structure
  • Merge user heading config with default settings

I created issues #115

@nicolasiscoding
Copy link
Member

Thanks @DemoMacro ! Someone will QA this soon

@nicolasiscoding nicolasiscoding changed the base branch from main to develop September 22, 2025 23:35
@nicolasiscoding
Copy link
Member

PR Review: feat: add customizable heading styles configuration

Overview

This PR adds comprehensive customizable heading styles configuration to the html-to-docx library. The changes allow users to configure font family, font size, formatting, spacing, and outline levels for headings 1-6.

✅ Strengths

  1. Comprehensive Feature: Covers all heading levels (H1-H6) with detailed configuration options
  2. Good Default Values: Provides sensible defaults that follow typical document hierarchy
  3. Backward Compatibility: Changes are additive and don't break existing functionality
  4. Documentation: README.md is updated with clear usage examples
  5. Clean Architecture: Configuration is properly separated and merged with defaults

🔍 Issues & Suggestions

Critical Issues

  1. Code Duplication: The default heading configuration is duplicated between src/constants.js:76-133 and src/schemas/styles.js:11-68. This violates DRY principle and creates maintenance issues.

  2. Inconsistent Font Size Handling: In the default config, heading5.fontSize is set to null, but the XML generation doesn't handle this properly - it will render empty <w:sz w:val="" /> tags.

Code Quality Issues

  1. Template Literal Formatting: The XML generation uses complex template literals with conditional rendering that's hard to read and maintain.

  2. Missing Input Validation: No validation for heading configuration values (e.g., fontSize should be positive, outlineLevel should be 0-5).

  3. Inconsistent Parameter Naming: The function parameter is headingConfig but the property is heading.

Minor Issues

  1. Comments: Inline comments like // use default font are redundant when the value is null.

  2. Spacing Logic: The spacing after logic config.heading1.spacing.after ? \w:after="${config.heading1.spacing.after}"` : ''will fail ifafteris0` (falsy but valid).

🛠️ Recommended Fixes

  1. Remove duplication: Import the default config from constants.js into styles.js
  2. Fix font size handling: Add proper null checks for fontSize after implementing DRY
  3. Fix spacing logic: Use !== undefined instead of truthy checks
  4. Add validation: Validate configuration values
  5. Refactor XML generation: Extract heading style generation into separate functions

📝 Code Suggestions

// Fix spacing logic (line 112+)
<w:spacing w:before="${config.heading1.spacing.before}" ${
  config.heading1.spacing.after !== undefined ? `w:after="${config.heading1.spacing.after}"` : ''
} />

// Fix font size handling
${config.heading1.fontSize !== null ? `<w:sz w:val="${config.heading1.fontSize}" />` : ''}

🎯 Overall Assessment

Status: Needs work before merge

The feature is well-designed and useful, but has several implementation issues that should be addressed:

  • Critical: Remove code duplication
  • Critical: Fix null fontSize handling
  • Important: Improve spacing logic
  • Nice-to-have: Add input validation

The PR adds valuable functionality but needs refinement to meet production quality standards.

@nicolasiscoding
Copy link
Member

@DemoMacro - Claude did the above review. The null thing looks wrong to me, but, after doing DRY make sure we handle this properly.

@DemoMacro
Copy link
Author

@nicolasiscoding Thanks for the thorough review! I've made adjustments in my latest commit.

Copy link
Collaborator

@K-Kumar-01 K-Kumar-01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice PR @DemoMacro.
Requested one change

Comment on lines 92 to 97
${generateHeadingStyleXML('Heading1', config.heading1)}
${generateHeadingStyleXML('Heading2', config.heading2)}
${generateHeadingStyleXML('Heading3', config.heading3)}
${generateHeadingStyleXML('Heading4', config.heading4)}
${generateHeadingStyleXML('Heading5', config.heading5)}
${generateHeadingStyleXML('Heading6', config.heading6)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should iterate over it using a map instead of writing like this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your feedback, I have changed the content to the mapping method.

@nicolasiscoding
Copy link
Member

Code Review - Technical Issues Found

Found several technical issues that should be addressed:

🚨 Runtime Error Risk - Missing Null Safety

// src/schemas/styles.js:22-23 - Will crash if heading.spacing is undefined
const spacingAfterXml = heading.spacing.after !== undefined ? `w:after="${heading.spacing.after}"` : '';
const spacingXml = `<w:spacing w:before="${heading.spacing.before}" ${spacingAfterXml} />`;

Fix: Add null/undefined checks:

const spacingXml = heading.spacing 
  ? `<w:spacing w:before="${heading.spacing.before || 0}" ${heading.spacing.after !== undefined ? `w:after="${heading.spacing.after}"` : ''} />`
  : '<w:spacing w:before="0" />';

🔒 XML Injection Vulnerability

// User font names inserted directly into XML without escaping
`<w:rFonts w:ascii="${heading.font}" w:eastAsiaTheme="minorHAnsi" w:hAnsiTheme="minorHAnsi" w:cstheme="minorBidi" />`

Fix: Escape XML special characters in user inputs

⚠️ Invalid fontSize Handling

// This condition fails for fontSize: 0 (valid value)
heading.fontSize && heading.fontSize !== defaultFontSize

Fix:

heading.fontSize !== undefined && heading.fontSize !== defaultFontSize

📝 Type Safety Issues

  • TypeScript allows font?: string | null but JavaScript doesn't handle null properly
  • Missing outline level validation (should be 0-5 range)
  • Consider updating TypeScript types: outlineLevel?: 0 | 1 | 2 | 3 | 4 | 5

Please address the null safety and fontSize issues as they could cause runtime errors.

@nicolasiscoding
Copy link
Member

Code Review - Technical Issues Found

Found several technical issues that should be addressed:

🚨 Runtime Error Risk - Missing Null Safety

// src/schemas/styles.js:22-23 - Will crash if heading.spacing is undefined
const spacingAfterXml = heading.spacing.after \!== undefined ? `w:after="${heading.spacing.after}"` : '';
const spacingXml = `<w:spacing w:before="${heading.spacing.before}" ${spacingAfterXml} />`;

Fix: Add null/undefined checks for heading.spacing before accessing properties

🔒 XML Injection Vulnerability

// User font names inserted directly into XML without escaping
`<w:rFonts w:ascii="${heading.font}" ...`

Fix: Escape XML special characters in user inputs

⚠️ Invalid fontSize Handling

// This condition fails for fontSize: 0 (valid value)
heading.fontSize && heading.fontSize \!== defaultFontSize

Fix: Use heading.fontSize \!== undefined && heading.fontSize \!== defaultFontSize

📝 Type Safety Issues

  • TypeScript allows font?: string | null but JavaScript doesn't handle null properly
  • Missing outline level validation (should be 0-5 range)
  • Consider updating TypeScript types: outlineLevel?: 0 | 1 | 2 | 3 | 4 | 5

Please address the null safety and fontSize issues as they could cause runtime errors.

@nicolasiscoding
Copy link
Member

@DemoMacro , kindly rebase against develop and address the final pass of claude code and we should be good to merge and release this. Thank you for your contribution!

Copy link
Member

@nicolasiscoding nicolasiscoding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See AI Code review feedback, nice work!

@nicolasiscoding
Copy link
Member

I will test, and if good, we will merge this, then after merging will fix the feedback around:

// src/schemas/styles.js:22-23 - Will crash if heading.spacing is undefined
const spacingAfterXml = heading.spacing.after \!== undefined ? `w:after="${heading.spacing.after}"` : '';
const spacingXml = `<w:spacing w:before="${heading.spacing.before}" ${spacingAfterXml} />`;

@K-Kumar-01
Copy link
Collaborator

Hey @DemoMacro
Apologies as your PR got closed by me. I was trying to push some changes but looks like it caused a PR closure. Can you create a new PR handling the below scenario.

I will test, and if good, we will merge this, then after merging will fix the feedback around:

// src/schemas/styles.js:22-23 - Will crash if heading.spacing is undefined
const spacingAfterXml = heading.spacing.after \!== undefined ? `w:after="${heading.spacing.after}"` : '';
const spacingXml = `<w:spacing w:before="${heading.spacing.before}" ${spacingAfterXml} />`;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants