@tradik/xslt-processor 1.0.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/LICENSE +29 -0
- package/LICENSE.md +28 -0
- package/README.md +634 -0
- package/bin/xslt.js +208 -0
- package/dist/xslt-processor.browser.js +3302 -0
- package/dist/xslt-processor.browser.js.map +7 -0
- package/dist/xslt-processor.browser.min.js +6 -0
- package/dist/xslt-processor.browser.min.js.map +7 -0
- package/dist/xslt-processor.cjs +3311 -0
- package/dist/xslt-processor.cjs.map +7 -0
- package/dist/xslt-processor.d.ts +211 -0
- package/dist/xslt-processor.js +3271 -0
- package/dist/xslt-processor.js.map +7 -0
- package/package.json +68 -0
- package/src/XSLTProcessor.js +368 -0
- package/src/XSLTProcessor.test.js +930 -0
- package/src/index.js +66 -0
- package/src/xpath/evaluator.js +1012 -0
- package/src/xpath/evaluator.test.js +1852 -0
- package/src/xpath/index.js +67 -0
- package/src/xpath/parser.js +595 -0
- package/src/xpath/tokenizer.js +383 -0
- package/src/xpath/tokenizer.test.js +224 -0
- package/src/xslt/engine.js +1812 -0
- package/src/xslt/engine.test.js +3130 -0
- package/src/xslt/index.js +6 -0
package/bin/xslt.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* XSLT Processor CLI
|
|
5
|
+
*
|
|
6
|
+
* Command-line interface for transforming XML using XSLT stylesheets.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
12
|
+
import { parseArgs } from 'node:util';
|
|
13
|
+
import { JSDOM } from 'jsdom';
|
|
14
|
+
import { XSLTProcessor } from '../src/XSLTProcessor.js';
|
|
15
|
+
|
|
16
|
+
const VERSION = '1.0.5';
|
|
17
|
+
|
|
18
|
+
function printHelp() {
|
|
19
|
+
console.log(`
|
|
20
|
+
xslt-processor - Transform XML documents using XSLT stylesheets
|
|
21
|
+
|
|
22
|
+
USAGE:
|
|
23
|
+
xslt <xml-file> <xslt-file> [options]
|
|
24
|
+
|
|
25
|
+
ARGUMENTS:
|
|
26
|
+
<xml-file> Path to XML source document
|
|
27
|
+
<xslt-file> Path to XSLT stylesheet
|
|
28
|
+
|
|
29
|
+
OPTIONS:
|
|
30
|
+
-o, --output <file> Write output to file instead of stdout
|
|
31
|
+
-p, --param <n>=<v> Set XSLT parameter (can be used multiple times)
|
|
32
|
+
-f, --format Format output with indentation
|
|
33
|
+
-h, --help Show this help message
|
|
34
|
+
-v, --version Show version number
|
|
35
|
+
|
|
36
|
+
EXAMPLES:
|
|
37
|
+
# Basic transformation
|
|
38
|
+
xslt data.xml transform.xsl
|
|
39
|
+
|
|
40
|
+
# Save output to file
|
|
41
|
+
xslt data.xml transform.xsl -o result.html
|
|
42
|
+
|
|
43
|
+
# With parameters
|
|
44
|
+
xslt data.xml transform.xsl -p title="My Page" -p count=10
|
|
45
|
+
|
|
46
|
+
# Multiple parameters with formatted output
|
|
47
|
+
xslt data.xml transform.xsl -p lang=en -p debug=true -f -o output.html
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function printVersion() {
|
|
52
|
+
console.log(`xslt-processor v${VERSION}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseParameters(params) {
|
|
56
|
+
const result = {};
|
|
57
|
+
|
|
58
|
+
if (!params || !Array.isArray(params)) {
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const param of params) {
|
|
63
|
+
const equalIndex = param.indexOf('=');
|
|
64
|
+
if (equalIndex === -1) {
|
|
65
|
+
console.error(`Warning: Invalid parameter format "${param}". Expected name=value`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const name = param.substring(0, equalIndex);
|
|
70
|
+
const value = param.substring(equalIndex + 1);
|
|
71
|
+
result[name] = value;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatXml(xml) {
|
|
78
|
+
let formatted = '';
|
|
79
|
+
let indent = 0;
|
|
80
|
+
const lines = xml.replace(/>\s*</g, '>\n<').split('\n');
|
|
81
|
+
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed) continue;
|
|
85
|
+
|
|
86
|
+
if (trimmed.startsWith('</')) {
|
|
87
|
+
indent = Math.max(0, indent - 1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
formatted += ' '.repeat(indent) + trimmed + '\n';
|
|
91
|
+
|
|
92
|
+
if (trimmed.startsWith('<') && !trimmed.startsWith('</') &&
|
|
93
|
+
!trimmed.startsWith('<?') && !trimmed.startsWith('<!') &&
|
|
94
|
+
!trimmed.endsWith('/>') && !trimmed.includes('</')) {
|
|
95
|
+
indent++;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return formatted;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function main() {
|
|
103
|
+
const options = {
|
|
104
|
+
output: { type: 'string', short: 'o' },
|
|
105
|
+
param: { type: 'string', short: 'p', multiple: true },
|
|
106
|
+
format: { type: 'boolean', short: 'f', default: false },
|
|
107
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
108
|
+
version: { type: 'boolean', short: 'v', default: false }
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let args;
|
|
112
|
+
try {
|
|
113
|
+
args = parseArgs({ options, allowPositionals: true });
|
|
114
|
+
} catch (err) {
|
|
115
|
+
console.error(`Error: ${err.message}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (args.values.help) {
|
|
120
|
+
printHelp();
|
|
121
|
+
process.exit(0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (args.values.version) {
|
|
125
|
+
printVersion();
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const [xmlPath, xsltPath] = args.positionals;
|
|
130
|
+
|
|
131
|
+
if (!xmlPath || !xsltPath) {
|
|
132
|
+
console.error('Error: Both XML and XSLT file paths are required');
|
|
133
|
+
console.error('Run "xslt --help" for usage information');
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Setup JSDOM for DOM parsing
|
|
138
|
+
const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
|
|
139
|
+
contentType: 'text/html'
|
|
140
|
+
});
|
|
141
|
+
const { DOMParser, XMLSerializer } = dom.window;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
// Read input files
|
|
145
|
+
const [xmlContent, xsltContent] = await Promise.all([
|
|
146
|
+
readFile(xmlPath, 'utf-8'),
|
|
147
|
+
readFile(xsltPath, 'utf-8')
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
// Parse documents
|
|
151
|
+
const parser = new DOMParser();
|
|
152
|
+
const xmlDoc = parser.parseFromString(xmlContent, 'application/xml');
|
|
153
|
+
const xsltDoc = parser.parseFromString(xsltContent, 'application/xml');
|
|
154
|
+
|
|
155
|
+
// Check for parsing errors
|
|
156
|
+
const xmlError = xmlDoc.querySelector('parsererror');
|
|
157
|
+
if (xmlError) {
|
|
158
|
+
console.error(`Error parsing XML: ${xmlError.textContent}`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const xsltError = xsltDoc.querySelector('parsererror');
|
|
163
|
+
if (xsltError) {
|
|
164
|
+
console.error(`Error parsing XSLT: ${xsltError.textContent}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Create processor
|
|
169
|
+
const processor = new XSLTProcessor();
|
|
170
|
+
processor.importStylesheet(xsltDoc);
|
|
171
|
+
|
|
172
|
+
// Set parameters
|
|
173
|
+
const params = parseParameters(args.values.param);
|
|
174
|
+
for (const [name, value] of Object.entries(params)) {
|
|
175
|
+
processor.setParameter(null, name, value);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Transform
|
|
179
|
+
const fragment = processor.transformToFragment(xmlDoc, dom.window.document);
|
|
180
|
+
|
|
181
|
+
// Serialize result
|
|
182
|
+
const serializer = new XMLSerializer();
|
|
183
|
+
let output = serializer.serializeToString(fragment);
|
|
184
|
+
|
|
185
|
+
// Format if requested
|
|
186
|
+
if (args.values.format) {
|
|
187
|
+
output = formatXml(output);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Output result
|
|
191
|
+
if (args.values.output) {
|
|
192
|
+
await writeFile(args.values.output, output, 'utf-8');
|
|
193
|
+
console.error(`Output written to ${args.values.output}`);
|
|
194
|
+
} else {
|
|
195
|
+
console.log(output);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
} catch (err) {
|
|
199
|
+
if (err.code === 'ENOENT') {
|
|
200
|
+
console.error(`Error: File not found: ${err.path}`);
|
|
201
|
+
} else {
|
|
202
|
+
console.error(`Error: ${err.message}`);
|
|
203
|
+
}
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
main();
|