legal-markdown-js 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/core/processors/reference-processor.d.ts +40 -86
- package/dist/core/processors/reference-processor.d.ts.map +1 -1
- package/dist/core/processors/reference-processor.js +172 -215
- package/dist/core/processors/reference-processor.js.map +1 -1
- package/dist/legal-markdown.umd.min.js +1 -1
- package/dist/legal-markdown.umd.min.js.map +1 -1
- package/dist/styles/default.css +407 -83
- package/dist/web/legal-markdown.umd.min.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
> feature parity.
|
|
6
6
|
|
|
7
7
|
Process markdown with YAML front matter, conditional clauses
|
|
8
|
-
`[text]{condition}`, cross-references `|
|
|
9
|
-
generate professional PDFs ready to be shared.
|
|
8
|
+
`[text]{condition}`, cross-references `|reference|`, mixins `{{variable}}`,
|
|
9
|
+
imports `@import`, and generate professional PDFs ready to be shared.
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
@@ -84,8 +84,7 @@ All original Legal Markdown features are fully implemented:
|
|
|
84
84
|
`lll.`)
|
|
85
85
|
- **Optional Clauses**: Boolean, equality, and logical operations
|
|
86
86
|
(`[text]{condition}`)
|
|
87
|
-
- **Cross-References**:
|
|
88
|
-
(`|reference|`)
|
|
87
|
+
- **Cross-References**: Internal section references using (`|reference|`) syntax
|
|
89
88
|
- **Partial Imports**: File inclusion with path resolution (`@import`)
|
|
90
89
|
- **Metadata Export**: YAML and JSON export with custom paths
|
|
91
90
|
|
|
@@ -93,7 +92,8 @@ All original Legal Markdown features are fully implemented:
|
|
|
93
92
|
|
|
94
93
|
Additional features available only in the Node.js version:
|
|
95
94
|
|
|
96
|
-
- **Mixins System**: Template substitution with `{{variable}}`
|
|
95
|
+
- **Mixins System**: Template substitution and helpers with `{{variable}}`
|
|
96
|
+
syntax
|
|
97
97
|
- **PDF Generation**: Professional PDF output with styling and field
|
|
98
98
|
highlighting
|
|
99
99
|
- **HTML Generation**: Custom HTML output with CSS support
|
|
@@ -1,115 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Cross-Reference Processing Module for Legal Markdown Documents
|
|
3
3
|
*
|
|
4
|
-
* This module provides functionality to process cross-references in Legal Markdown
|
|
5
|
-
* documents,
|
|
6
|
-
*
|
|
7
|
-
* formatting, and nested metadata access through dot notation.
|
|
4
|
+
* This module provides functionality to process internal cross-references in Legal Markdown
|
|
5
|
+
* documents, allowing sections to reference other sections by their keys.
|
|
6
|
+
* Based on the original Ruby Legal Markdown specification.
|
|
8
7
|
*
|
|
9
8
|
* Features:
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* - Fallback to original reference if value not found
|
|
16
|
-
* - Integration with date-processor for date handling
|
|
9
|
+
* - Internal cross-reference syntax: |reference_key|
|
|
10
|
+
* - Automatic section numbering and reference resolution
|
|
11
|
+
* - Reference capture from headers with |key| syntax
|
|
12
|
+
* - Section reference replacement throughout the document
|
|
13
|
+
* - Compatible with legal document numbering (l., ll., lll.)
|
|
17
14
|
*
|
|
18
15
|
* @example
|
|
19
16
|
* ```typescript
|
|
20
17
|
* import { processCrossReferences } from './reference-processor';
|
|
21
18
|
*
|
|
22
19
|
* const content = `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `;
|
|
20
|
+
* l. **Definitions** |definitions|
|
|
21
|
+
*
|
|
22
|
+
* Terms defined in |definitions| apply throughout this agreement.
|
|
27
23
|
*
|
|
28
|
-
*
|
|
29
|
-
* client: { name: "Acme Corp" },
|
|
30
|
-
* provider: { name: "Service Ltd" },
|
|
31
|
-
* contract: {
|
|
32
|
-
* amount: 25000,
|
|
33
|
-
* due_date: new Date("2024-12-31")
|
|
34
|
-
* },
|
|
35
|
-
* payment_currency: "USD"
|
|
36
|
-
* };
|
|
24
|
+
* l. **Payment Terms** |payment|
|
|
37
25
|
*
|
|
38
|
-
*
|
|
26
|
+
* As outlined in |payment|, payment is due within 30 days.
|
|
27
|
+
* Reference to |definitions| for term meanings.
|
|
28
|
+
* `;
|
|
29
|
+
*
|
|
30
|
+
* const processed = processCrossReferences(content, {});
|
|
39
31
|
* console.log(processed);
|
|
40
32
|
* // Output:
|
|
41
|
-
* //
|
|
42
|
-
* //
|
|
43
|
-
* //
|
|
33
|
+
* // Article 1. **Definitions**
|
|
34
|
+
* //
|
|
35
|
+
* // Terms defined in Article 1 apply throughout this agreement.
|
|
36
|
+
* //
|
|
37
|
+
* // Article 2. **Payment Terms**
|
|
38
|
+
* //
|
|
39
|
+
* // As outlined in Article 2, payment is due within 30 days.
|
|
40
|
+
* // Reference to Article 1 for term meanings.
|
|
44
41
|
* ```
|
|
45
42
|
*/
|
|
46
43
|
/**
|
|
47
|
-
* Processes cross-references in a
|
|
44
|
+
* Processes internal cross-references in a Legal Markdown document
|
|
48
45
|
*
|
|
49
|
-
* This
|
|
50
|
-
*
|
|
51
|
-
*
|
|
46
|
+
* This function implements a hybrid approach:
|
|
47
|
+
* 1. First tries to resolve |key| as internal section references (Ruby spec)
|
|
48
|
+
* 2. Falls back to metadata values for backward compatibility
|
|
52
49
|
*
|
|
53
50
|
* @param {string} content - The document content containing cross-references
|
|
54
|
-
* @param {Record<string, any>} metadata - Document metadata
|
|
55
|
-
* @returns {string} Processed content with references
|
|
51
|
+
* @param {Record<string, any>} metadata - Document metadata (used for level formatting and fallback)
|
|
52
|
+
* @returns {string} Processed content with internal references resolved
|
|
56
53
|
* @example
|
|
57
54
|
* ```typescript
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* const metadata = {
|
|
61
|
-
* payment: {
|
|
62
|
-
* amount: 1500,
|
|
63
|
-
* date: new Date("2024-03-15")
|
|
64
|
-
* },
|
|
65
|
-
* payment_currency: "EUR"
|
|
66
|
-
* };
|
|
67
|
-
*
|
|
68
|
-
* const result = processCrossReferences(content, metadata);
|
|
69
|
-
* // Output: "Payment due: €1,500.00 on 2024-03-15"
|
|
55
|
+
* const content = `
|
|
56
|
+
* l. **Contract Terms** |terms|
|
|
70
57
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* client: {
|
|
75
|
-
* contact: {
|
|
76
|
-
* email: "info@client.com",
|
|
77
|
-
* phone: "+1-555-0123"
|
|
78
|
-
* }
|
|
79
|
-
* }
|
|
80
|
-
* };
|
|
58
|
+
* As specified in |terms|, this agreement is binding.
|
|
59
|
+
* Reference to |client_name| from metadata.
|
|
60
|
+
* `;
|
|
81
61
|
*
|
|
82
|
-
* const
|
|
83
|
-
*
|
|
62
|
+
* const metadata = { client_name: "ACME Corp" };
|
|
63
|
+
* const result = processCrossReferences(content, metadata);
|
|
64
|
+
* // |terms| -> "Article 1." (internal reference)
|
|
65
|
+
* // |client_name| -> "ACME Corp" (metadata fallback)
|
|
84
66
|
* ```
|
|
85
67
|
*/
|
|
86
68
|
export declare function processCrossReferences(content: string, metadata: Record<string, any>): string;
|
|
87
|
-
/**
|
|
88
|
-
* Processes special reference formats like dates and currency amounts
|
|
89
|
-
*
|
|
90
|
-
* Applies appropriate formatting to reference values based on their type and
|
|
91
|
-
* optional format specifiers. Handles dates, currency amounts, and falls back
|
|
92
|
-
* to string conversion for other types.
|
|
93
|
-
*
|
|
94
|
-
* @param {any} value - The reference value to format
|
|
95
|
-
* @param {string} [format] - Optional format specifier (e.g., "currency:USD:en-US")
|
|
96
|
-
* @returns {string} Formatted value as string
|
|
97
|
-
* @example
|
|
98
|
-
* ```typescript
|
|
99
|
-
* // Date formatting
|
|
100
|
-
* const date = new Date("2024-03-15");
|
|
101
|
-
* console.log(formatReferenceValue(date)); // "2024-03-15"
|
|
102
|
-
* console.log(formatReferenceValue(date, "long")); // "March 15, 2024"
|
|
103
|
-
* console.log(formatReferenceValue(date, "short")); // "3/15/2024"
|
|
104
|
-
*
|
|
105
|
-
* // Currency formatting
|
|
106
|
-
* console.log(formatReferenceValue(1500, "currency:USD")); // "$1,500.00"
|
|
107
|
-
* console.log(formatReferenceValue(1500, "currency:EUR:de-DE")); // "1.500,00 €"
|
|
108
|
-
*
|
|
109
|
-
* // Default string conversion
|
|
110
|
-
* console.log(formatReferenceValue("Hello World")); // "Hello World"
|
|
111
|
-
* console.log(formatReferenceValue(12345)); // "12345"
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
|
-
export declare function formatReferenceValue(value: any, format?: string): string;
|
|
115
69
|
//# sourceMappingURL=reference-processor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-processor.d.ts","sourceRoot":"","sources":["../../../src/core/processors/reference-processor.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"reference-processor.d.ts","sourceRoot":"","sources":["../../../src/core/processors/reference-processor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAWH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAM7F"}
|
|
@@ -2,144 +2,194 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @fileoverview Cross-Reference Processing Module for Legal Markdown Documents
|
|
4
4
|
*
|
|
5
|
-
* This module provides functionality to process cross-references in Legal Markdown
|
|
6
|
-
* documents,
|
|
7
|
-
*
|
|
8
|
-
* formatting, and nested metadata access through dot notation.
|
|
5
|
+
* This module provides functionality to process internal cross-references in Legal Markdown
|
|
6
|
+
* documents, allowing sections to reference other sections by their keys.
|
|
7
|
+
* Based on the original Ruby Legal Markdown specification.
|
|
9
8
|
*
|
|
10
9
|
* Features:
|
|
11
|
-
* -
|
|
12
|
-
* -
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* - Fallback to original reference if value not found
|
|
17
|
-
* - Integration with date-processor for date handling
|
|
10
|
+
* - Internal cross-reference syntax: |reference_key|
|
|
11
|
+
* - Automatic section numbering and reference resolution
|
|
12
|
+
* - Reference capture from headers with |key| syntax
|
|
13
|
+
* - Section reference replacement throughout the document
|
|
14
|
+
* - Compatible with legal document numbering (l., ll., lll.)
|
|
18
15
|
*
|
|
19
16
|
* @example
|
|
20
17
|
* ```typescript
|
|
21
18
|
* import { processCrossReferences } from './reference-processor';
|
|
22
19
|
*
|
|
23
20
|
* const content = `
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* `;
|
|
21
|
+
* l. **Definitions** |definitions|
|
|
22
|
+
*
|
|
23
|
+
* Terms defined in |definitions| apply throughout this agreement.
|
|
28
24
|
*
|
|
29
|
-
*
|
|
30
|
-
* client: { name: "Acme Corp" },
|
|
31
|
-
* provider: { name: "Service Ltd" },
|
|
32
|
-
* contract: {
|
|
33
|
-
* amount: 25000,
|
|
34
|
-
* due_date: new Date("2024-12-31")
|
|
35
|
-
* },
|
|
36
|
-
* payment_currency: "USD"
|
|
37
|
-
* };
|
|
25
|
+
* l. **Payment Terms** |payment|
|
|
38
26
|
*
|
|
39
|
-
*
|
|
27
|
+
* As outlined in |payment|, payment is due within 30 days.
|
|
28
|
+
* Reference to |definitions| for term meanings.
|
|
29
|
+
* `;
|
|
30
|
+
*
|
|
31
|
+
* const processed = processCrossReferences(content, {});
|
|
40
32
|
* console.log(processed);
|
|
41
33
|
* // Output:
|
|
42
|
-
* //
|
|
43
|
-
* //
|
|
44
|
-
* //
|
|
34
|
+
* // Article 1. **Definitions**
|
|
35
|
+
* //
|
|
36
|
+
* // Terms defined in Article 1 apply throughout this agreement.
|
|
37
|
+
* //
|
|
38
|
+
* // Article 2. **Payment Terms**
|
|
39
|
+
* //
|
|
40
|
+
* // As outlined in Article 2, payment is due within 30 days.
|
|
41
|
+
* // Reference to Article 1 for term meanings.
|
|
45
42
|
* ```
|
|
46
43
|
*/
|
|
47
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
45
|
exports.processCrossReferences = processCrossReferences;
|
|
49
|
-
exports.formatReferenceValue = formatReferenceValue;
|
|
50
|
-
const date_processor_1 = require("./date-processor");
|
|
51
46
|
/**
|
|
52
|
-
* Processes cross-references in a
|
|
47
|
+
* Processes internal cross-references in a Legal Markdown document
|
|
53
48
|
*
|
|
54
|
-
* This
|
|
55
|
-
*
|
|
56
|
-
*
|
|
49
|
+
* This function implements a hybrid approach:
|
|
50
|
+
* 1. First tries to resolve |key| as internal section references (Ruby spec)
|
|
51
|
+
* 2. Falls back to metadata values for backward compatibility
|
|
57
52
|
*
|
|
58
53
|
* @param {string} content - The document content containing cross-references
|
|
59
|
-
* @param {Record<string, any>} metadata - Document metadata
|
|
60
|
-
* @returns {string} Processed content with references
|
|
54
|
+
* @param {Record<string, any>} metadata - Document metadata (used for level formatting and fallback)
|
|
55
|
+
* @returns {string} Processed content with internal references resolved
|
|
61
56
|
* @example
|
|
62
57
|
* ```typescript
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* const metadata = {
|
|
66
|
-
* payment: {
|
|
67
|
-
* amount: 1500,
|
|
68
|
-
* date: new Date("2024-03-15")
|
|
69
|
-
* },
|
|
70
|
-
* payment_currency: "EUR"
|
|
71
|
-
* };
|
|
72
|
-
*
|
|
73
|
-
* const result = processCrossReferences(content, metadata);
|
|
74
|
-
* // Output: "Payment due: €1,500.00 on 2024-03-15"
|
|
58
|
+
* const content = `
|
|
59
|
+
* l. **Contract Terms** |terms|
|
|
75
60
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* client: {
|
|
80
|
-
* contact: {
|
|
81
|
-
* email: "info@client.com",
|
|
82
|
-
* phone: "+1-555-0123"
|
|
83
|
-
* }
|
|
84
|
-
* }
|
|
85
|
-
* };
|
|
61
|
+
* As specified in |terms|, this agreement is binding.
|
|
62
|
+
* Reference to |client_name| from metadata.
|
|
63
|
+
* `;
|
|
86
64
|
*
|
|
87
|
-
* const
|
|
88
|
-
*
|
|
65
|
+
* const metadata = { client_name: "ACME Corp" };
|
|
66
|
+
* const result = processCrossReferences(content, metadata);
|
|
67
|
+
* // |terms| -> "Article 1." (internal reference)
|
|
68
|
+
* // |client_name| -> "ACME Corp" (metadata fallback)
|
|
89
69
|
* ```
|
|
90
70
|
*/
|
|
91
71
|
function processCrossReferences(content, metadata) {
|
|
92
|
-
// First
|
|
93
|
-
const
|
|
94
|
-
// Then
|
|
95
|
-
|
|
96
|
-
const referencePattern = /\|(.*?)\|/g;
|
|
97
|
-
return processedContent.replace(referencePattern, (match, key) => {
|
|
98
|
-
// Get the reference value from metadata
|
|
99
|
-
const value = getNestedValue(metadata, key.trim());
|
|
100
|
-
if (value === undefined) {
|
|
101
|
-
return match;
|
|
102
|
-
}
|
|
103
|
-
// Format value based on type
|
|
104
|
-
if (value instanceof Date) {
|
|
105
|
-
return formatDate(value);
|
|
106
|
-
}
|
|
107
|
-
else if (typeof value === 'number' && key.includes('amount')) {
|
|
108
|
-
// Format currency if key contains 'amount'
|
|
109
|
-
const currency = metadata.payment_currency || 'USD';
|
|
110
|
-
return formatCurrency(value, `currency:${currency}`);
|
|
111
|
-
}
|
|
112
|
-
// Default string conversion
|
|
113
|
-
return String(value);
|
|
114
|
-
});
|
|
72
|
+
// First, extract all cross-reference definitions from headers
|
|
73
|
+
const crossReferences = extractCrossReferences(content, metadata);
|
|
74
|
+
// Then replace all cross-reference usage with section numbers or metadata
|
|
75
|
+
return replaceCrossReferences(content, crossReferences, metadata);
|
|
115
76
|
}
|
|
116
77
|
/**
|
|
117
|
-
*
|
|
78
|
+
* Extracts cross-reference definitions from headers in the document
|
|
118
79
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
80
|
+
* Scans the document for headers containing |key| syntax and captures
|
|
81
|
+
* their section numbers after header processing. This creates a mapping
|
|
82
|
+
* of reference keys to their section identifiers.
|
|
122
83
|
*
|
|
123
84
|
* @private
|
|
124
|
-
* @param {
|
|
125
|
-
* @param {string}
|
|
126
|
-
* @returns {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
85
|
+
* @param {string} content - Document content to scan for cross-references
|
|
86
|
+
* @param {Record<string, any>} metadata - Metadata for level formatting
|
|
87
|
+
* @returns {CrossReference[]} Array of cross-reference mappings
|
|
88
|
+
*/
|
|
89
|
+
function extractCrossReferences(content, metadata) {
|
|
90
|
+
const crossReferences = [];
|
|
91
|
+
const lines = content.split('\n');
|
|
92
|
+
// Track section counters for each level
|
|
93
|
+
const sectionCounters = { level1: 0, level2: 0, level3: 0 };
|
|
94
|
+
// Get level formats from metadata
|
|
95
|
+
const levelFormats = {
|
|
96
|
+
level1: metadata['level-one'] || 'Article %n.',
|
|
97
|
+
level2: metadata['level-two'] || 'Section %n.',
|
|
98
|
+
level3: metadata['level-three'] || '(%n)',
|
|
99
|
+
};
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const trimmedLine = line.trim();
|
|
102
|
+
// Check for header lines with cross-reference keys
|
|
103
|
+
const headerMatch = trimmedLine.match(/^(l{1,3})\.\s+(.+?)\s+\|(\w+)\|$/);
|
|
104
|
+
if (headerMatch) {
|
|
105
|
+
const [, levelMarker, headerText, key] = headerMatch;
|
|
106
|
+
const level = levelMarker.length;
|
|
107
|
+
// Update section counters
|
|
108
|
+
if (level === 1) {
|
|
109
|
+
sectionCounters.level1++;
|
|
110
|
+
sectionCounters.level2 = 0; // Reset level 2 counter
|
|
111
|
+
sectionCounters.level3 = 0; // Reset level 3 counter
|
|
112
|
+
}
|
|
113
|
+
else if (level === 2) {
|
|
114
|
+
sectionCounters.level2++;
|
|
115
|
+
sectionCounters.level3 = 0; // Reset level 3 counter
|
|
116
|
+
}
|
|
117
|
+
else if (level === 3) {
|
|
118
|
+
sectionCounters.level3++;
|
|
119
|
+
}
|
|
120
|
+
// Generate section number based on level
|
|
121
|
+
let sectionNumber;
|
|
122
|
+
let sectionText;
|
|
123
|
+
if (level === 1) {
|
|
124
|
+
sectionNumber = levelFormats.level1.replace(/%n/g, sectionCounters.level1.toString());
|
|
125
|
+
sectionText = `${sectionNumber} ${headerText}`;
|
|
126
|
+
}
|
|
127
|
+
else if (level === 2) {
|
|
128
|
+
sectionNumber = levelFormats.level2.replace(/%n/g, sectionCounters.level2.toString());
|
|
129
|
+
sectionText = `${sectionNumber} ${headerText}`;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
sectionNumber = levelFormats.level3.replace(/%n/g, sectionCounters.level3.toString());
|
|
133
|
+
sectionText = `${sectionNumber} ${headerText}`;
|
|
134
|
+
}
|
|
135
|
+
crossReferences.push({
|
|
136
|
+
key,
|
|
137
|
+
sectionNumber: sectionNumber.trim(),
|
|
138
|
+
sectionText: sectionText.trim(),
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return crossReferences;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Replaces cross-reference usage throughout the document
|
|
138
146
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
147
|
+
* Scans the document for |key| references and replaces them with:
|
|
148
|
+
* 1. Section numbers from internal cross-references (priority)
|
|
149
|
+
* 2. Metadata values as fallback for backward compatibility
|
|
150
|
+
* 3. Original reference if neither is found
|
|
151
|
+
*
|
|
152
|
+
* @private
|
|
153
|
+
* @param {string} content - Document content to process
|
|
154
|
+
* @param {CrossReference[]} crossReferences - Map of keys to section numbers
|
|
155
|
+
* @param {Record<string, any>} metadata - Metadata for fallback resolution
|
|
156
|
+
* @returns {string} Document with cross-references replaced
|
|
157
|
+
*/
|
|
158
|
+
function replaceCrossReferences(content, crossReferences, metadata) {
|
|
159
|
+
// Create a map for quick lookups
|
|
160
|
+
const referenceMap = new Map();
|
|
161
|
+
for (const ref of crossReferences) {
|
|
162
|
+
referenceMap.set(ref.key, ref.sectionNumber);
|
|
163
|
+
}
|
|
164
|
+
// Replace all |key| references (except those in headers which define them)
|
|
165
|
+
const lines = content.split('\n');
|
|
166
|
+
const processedLines = lines.map(line => {
|
|
167
|
+
const trimmedLine = line.trim();
|
|
168
|
+
// Skip lines that define cross-references (headers with |key| syntax)
|
|
169
|
+
if (trimmedLine.match(/^(l{1,3})\.\s+(.+?)\s+\|(\w+)\|$/)) {
|
|
170
|
+
return line;
|
|
171
|
+
}
|
|
172
|
+
// Process cross-reference usage in content lines
|
|
173
|
+
return line.replace(/\|([^|]+)\|/g, (match, key) => {
|
|
174
|
+
const trimmedKey = key.trim();
|
|
175
|
+
// First try internal section reference
|
|
176
|
+
const sectionNumber = referenceMap.get(trimmedKey);
|
|
177
|
+
if (sectionNumber) {
|
|
178
|
+
return sectionNumber;
|
|
179
|
+
}
|
|
180
|
+
// Fallback to metadata value
|
|
181
|
+
const metadataValue = getNestedValue(metadata, trimmedKey);
|
|
182
|
+
if (metadataValue !== undefined) {
|
|
183
|
+
return formatMetadataValue(metadataValue, trimmedKey, metadata);
|
|
184
|
+
}
|
|
185
|
+
// Return original if no reference found
|
|
186
|
+
return match;
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
return processedLines.join('\n');
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Gets a potentially nested value from an object using dot notation
|
|
143
193
|
*/
|
|
144
194
|
function getNestedValue(obj, path) {
|
|
145
195
|
const keys = path.split('.');
|
|
@@ -153,121 +203,28 @@ function getNestedValue(obj, path) {
|
|
|
153
203
|
return value;
|
|
154
204
|
}
|
|
155
205
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
* Applies appropriate formatting to reference values based on their type and
|
|
159
|
-
* optional format specifiers. Handles dates, currency amounts, and falls back
|
|
160
|
-
* to string conversion for other types.
|
|
161
|
-
*
|
|
162
|
-
* @param {any} value - The reference value to format
|
|
163
|
-
* @param {string} [format] - Optional format specifier (e.g., "currency:USD:en-US")
|
|
164
|
-
* @returns {string} Formatted value as string
|
|
165
|
-
* @example
|
|
166
|
-
* ```typescript
|
|
167
|
-
* // Date formatting
|
|
168
|
-
* const date = new Date("2024-03-15");
|
|
169
|
-
* console.log(formatReferenceValue(date)); // "2024-03-15"
|
|
170
|
-
* console.log(formatReferenceValue(date, "long")); // "March 15, 2024"
|
|
171
|
-
* console.log(formatReferenceValue(date, "short")); // "3/15/2024"
|
|
172
|
-
*
|
|
173
|
-
* // Currency formatting
|
|
174
|
-
* console.log(formatReferenceValue(1500, "currency:USD")); // "$1,500.00"
|
|
175
|
-
* console.log(formatReferenceValue(1500, "currency:EUR:de-DE")); // "1.500,00 €"
|
|
176
|
-
*
|
|
177
|
-
* // Default string conversion
|
|
178
|
-
* console.log(formatReferenceValue("Hello World")); // "Hello World"
|
|
179
|
-
* console.log(formatReferenceValue(12345)); // "12345"
|
|
180
|
-
* ```
|
|
206
|
+
* Formats metadata values based on type and context
|
|
181
207
|
*/
|
|
182
|
-
function
|
|
183
|
-
if (value === undefined
|
|
208
|
+
function formatMetadataValue(value, key, metadata) {
|
|
209
|
+
if (value === undefined) {
|
|
184
210
|
return '';
|
|
185
211
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return formatDate(value, format);
|
|
212
|
+
if (value === null) {
|
|
213
|
+
return 'null';
|
|
189
214
|
}
|
|
190
|
-
|
|
191
|
-
|
|
215
|
+
// Handle Date objects
|
|
216
|
+
if (value instanceof Date) {
|
|
217
|
+
return value.toISOString().split('T')[0]; // ISO date format
|
|
192
218
|
}
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
* Supports ISO format (default), short format, and long format for different
|
|
201
|
-
* document presentation needs.
|
|
202
|
-
*
|
|
203
|
-
* @private
|
|
204
|
-
* @param {Date} date - Date to format
|
|
205
|
-
* @param {string} [format] - Date format specification ("short", "long", or default ISO)
|
|
206
|
-
* @returns {string} Formatted date string
|
|
207
|
-
* @example
|
|
208
|
-
* ```typescript
|
|
209
|
-
* const date = new Date("2024-03-15T10:30:00Z");
|
|
210
|
-
*
|
|
211
|
-
* console.log(formatDate(date)); // "2024-03-15" (ISO format)
|
|
212
|
-
* console.log(formatDate(date, "short")); // "3/15/2024" (locale-specific)
|
|
213
|
-
* console.log(formatDate(date, "long")); // "March 15, 2024" (long format)
|
|
214
|
-
* ```
|
|
215
|
-
*/
|
|
216
|
-
function formatDate(date, format) {
|
|
217
|
-
if (!format) {
|
|
218
|
-
// Default format: YYYY-MM-DD
|
|
219
|
-
return date.toISOString().split('T')[0];
|
|
219
|
+
// Handle currency amounts
|
|
220
|
+
if (typeof value === 'number' && key.includes('amount')) {
|
|
221
|
+
const currency = metadata.payment_currency || 'USD';
|
|
222
|
+
return new Intl.NumberFormat('en-US', {
|
|
223
|
+
style: 'currency',
|
|
224
|
+
currency,
|
|
225
|
+
}).format(value);
|
|
220
226
|
}
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
return date.toLocaleDateString();
|
|
224
|
-
}
|
|
225
|
-
else if (format === 'long') {
|
|
226
|
-
return date.toLocaleDateString(undefined, {
|
|
227
|
-
year: 'numeric',
|
|
228
|
-
month: 'long',
|
|
229
|
-
day: 'numeric',
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
return date.toISOString().split('T')[0]; // Default to ISO format
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Formats a number as currency
|
|
236
|
-
*
|
|
237
|
-
* Converts a numeric amount to a formatted currency string using the
|
|
238
|
-
* Intl.NumberFormat API. Supports different currencies and locales
|
|
239
|
-
* based on the format specification.
|
|
240
|
-
*
|
|
241
|
-
* @private
|
|
242
|
-
* @param {number} amount - Amount to format
|
|
243
|
-
* @param {string} [format] - Currency format specification ("currency:CODE:locale")
|
|
244
|
-
* @returns {string} Formatted currency string
|
|
245
|
-
* @example
|
|
246
|
-
* ```typescript
|
|
247
|
-
* console.log(formatCurrency(1500)); // "$1,500.00" (default USD)
|
|
248
|
-
* console.log(formatCurrency(1500, "currency:EUR")); // "€1,500.00"
|
|
249
|
-
* console.log(formatCurrency(1500, "currency:GBP:en-GB")); // "£1,500.00"
|
|
250
|
-
* console.log(formatCurrency(1500, "currency:JPY:ja-JP")); // "¥1,500"
|
|
251
|
-
* console.log(formatCurrency(1500, "currency:EUR:de-DE")); // "1.500,00 €"
|
|
252
|
-
* ```
|
|
253
|
-
*/
|
|
254
|
-
function formatCurrency(amount, format) {
|
|
255
|
-
let currency = 'USD';
|
|
256
|
-
let locale = 'en-US';
|
|
257
|
-
// Parse format specification
|
|
258
|
-
if (format) {
|
|
259
|
-
const parts = format.split(':');
|
|
260
|
-
if (parts.length > 1) {
|
|
261
|
-
currency = parts[1].trim().toUpperCase();
|
|
262
|
-
}
|
|
263
|
-
if (parts.length > 2) {
|
|
264
|
-
locale = parts[2].trim();
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// Format using Intl.NumberFormat
|
|
268
|
-
return new Intl.NumberFormat(locale, {
|
|
269
|
-
style: 'currency',
|
|
270
|
-
currency,
|
|
271
|
-
}).format(amount);
|
|
227
|
+
// Default string conversion
|
|
228
|
+
return String(value);
|
|
272
229
|
}
|
|
273
230
|
//# sourceMappingURL=reference-processor.js.map
|