Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions packages/transformer/src/__tests__/transformer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
generateSectionBody,
generateMarkdownForSection,
XmlToMarkdownAdapter,
formatSourceCredit,
} from '../index.js';

// --- Minimal USLM XML fixtures ---
Expand Down Expand Up @@ -549,4 +550,60 @@ describe('reformatInlineLists', () => {
expect(result).toContain(' - **(1)** In general The Secretary shall allocate funds.');
expect(result).toContain(' - **(2)** Other For any fiscal year.');
});

describe('formatSourceCredit', () => {
it('formats source credit with Statutes at Large and Public Law links', () => {
const children = [
{ '#text': '(' },
{
ref: [{ '#text': 'July 30, 1947, ch. 388' }],
':@': { href: '/us/act/1947-07-30/ch388' },
},
{ '#text': ',' },
{
ref: [{ '#text': '61 Stat. 633' }],
':@': { href: '/us/stat/61/633' },
},
{ '#text': ';' },
{
ref: [{ '#text': 'Pub. L. 111-148' }],
':@': { href: '/us/pl/111/148' },
},
{ '#text': '.)' },
];

const formatted = formatSourceCredit(children);
expect(formatted).toBe(
'*(July 30, 1947, ch. 388, [61 Stat. 633](https://www.govinfo.gov/link/statute/61/633); [Pub. L. 111-148](https://www.govinfo.gov/link/plaw/111/public/148).)*'
);
});

it('handles empty children gracefully', () => {
expect(formatSourceCredit([])).toBe('');
});

it('integrates source credit into section body', () => {
const sectionNodes = [
{ num: [{ '#text': '101' }] },
{ heading: [{ '#text': 'Definitions' }] },
{ content: [{ '#text': 'In this section, terms apply.' }] },
{
sourceCredit: [
{ '#text': '(' },
{
ref: [{ '#text': '84 Stat. 1236' }],
':@': { '@_href': '/us/stat/84/1236' },
},
{ '#text': '.)' },
],
},
];

const body = generateSectionBody(sectionNodes);
expect(body).toContain('# 101 Definitions');
expect(body).toContain('In this section, terms apply.');
expect(body).toContain('## Source Credit');
expect(body).toContain('*([84 Stat. 1236](https://www.govinfo.gov/link/statute/84/1236).)*');
});
});
});
5 changes: 5 additions & 0 deletions packages/transformer/src/__tests__/uslm-v1-compat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ describe('USLM 1.0 (uscDoc) compatibility', () => {

// Section has body content
expect(firstSection.content).toContain('meaning of any Act of Congress');

// Section extracts Source Credit with GovInfo links
expect(firstSection.content).toContain('## Source Credit');
expect(firstSection.content).toContain('https://www.govinfo.gov/link/statute/61/633');
expect(firstSection.content).toContain('[61 Stat. 633](https://www.govinfo.gov/link/statute/61/633)');
});

it('extracts multiple sections from the excerpt', () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/transformer/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export const USLM_ELEMENTS = {
chapeau: 'chapeau',
/** Note element */
note: 'note',
/** Source credit (enactment and amendment history) */
sourceCredit: 'sourceCredit',
/** Cross-reference */
ref: 'ref',
/** Table element */
Expand Down
1 change: 1 addition & 0 deletions packages/transformer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export {
FrontmatterSchema,
SectionStatusSchema,
detectSectionStatus,
formatSourceCredit,
} from './markdown-generator.js';
export type { Frontmatter, MarkdownFile, SectionStatus } from './markdown-generator.js';
export { USLM_ELEMENTS, INDENT_PER_LEVEL, MAX_NESTING_DEPTH } from './constants.js';
Expand Down
61 changes: 60 additions & 1 deletion packages/transformer/src/markdown-generator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod';
import { USLM_ELEMENTS, INDENT_PER_LEVEL, MAX_NESTING_DEPTH } from './constants.js';
import { extractTextFromNodes, findElements } from './xml-utils.js';
import { extractTextFromNodes, findElements, getAttributes } from './xml-utils.js';

/** Section status — derived from heading text during transformation */
export const SectionStatusSchema = z.enum([
Expand Down Expand Up @@ -271,6 +271,53 @@ function walkListElements(
}
}

/** Format source credit nodes into markdown with GovInfo statutory citations */
export function formatSourceCredit(children: unknown[]): string {
const parts: string[] = [];
function walk(nodes: unknown[]): void {
for (const node of nodes) {
if (!node || typeof node !== 'object') continue;
const obj = node as Record<string, unknown>;
if ('#text' in obj) {
parts.push(String(obj['#text']));
} else if (USLM_ELEMENTS.ref in obj && Array.isArray(obj[USLM_ELEMENTS.ref])) {
const attrs = getAttributes(node);
const href = attrs['@_href'] || attrs['href'] || '';
const refText = extractTextFromNodes(obj[USLM_ELEMENTS.ref] as unknown[]).trim();
const statMatch = /^\/us\/stat\/(\d+)\/(\d+)/.exec(href);
const plMatch = /^\/us\/pl\/(\d+)\/(\d+)/.exec(href);
if (statMatch) {
parts.push(`[${refText}](https://www.govinfo.gov/link/statute/${statMatch[1]}/${statMatch[2]})`);
} else if (plMatch) {
parts.push(`[${refText}](https://www.govinfo.gov/link/plaw/${plMatch[1]}/public/${plMatch[2]})`);
} else {
parts.push(refText);
}
} else {
for (const [k, v] of Object.entries(obj)) {
if (k !== ':@' && Array.isArray(v)) {
walk(v);
}
}
}
}
}
walk(children);

const cleaned = parts
.join(' ')
.replace(/\s+/g, ' ')
.replace(/\(\s+/g, '(')
.replace(/\s+([,.;:)])/g, '$1')
.trim();

if (!cleaned) return '';
if (cleaned.startsWith('*') && cleaned.endsWith('*')) {
return cleaned;
}
return `*${cleaned}*`;
}

/** Generate markdown body for a single section node (preserveOrder children) */
export function generateSectionBody(sectionChildren: unknown[]): string {
const lines: string[] = [];
Expand All @@ -293,6 +340,18 @@ export function generateSectionBody(sectionChildren: unknown[]): string {
// Walk nested list elements
walkListElements(sectionChildren, 0, lines);

// Source credit / legislative history
const sourceCredits = findElements(sectionChildren, USLM_ELEMENTS.sourceCredit);
if (sourceCredits.length > 0) {
lines.push('');
lines.push('## Source Credit');
lines.push('');
for (const sc of sourceCredits) {
const credit = formatSourceCredit(sc.children);
if (credit) lines.push(credit);
}
}

// Notes
const notes = findElements(sectionChildren, USLM_ELEMENTS.note);
if (notes.length > 0) {
Expand Down
Loading