node.js - Analizando XML en TypeScript
Usando Typescript 2.0 (versión de tsc 2.0.3).
He intentado sin éxito usar la biblioteca xml2js de Node dentro de una aplicación angular2/typescript (ver https://www.npmjs.com/package/xml2js). Parece claro que no estoy lo suficientemente familiarizado con la forma en que SystemJS configura la biblioteca para su uso.
Para instalar la biblioteca xml2js hice lo siguiente:
npm install --save xml2js
npm install --save @types/xml2js
Aquí están los archivos relevantes:
tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false,
"outDir" : "build"
}
}
systemjs.config.js
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'build',
// angular bundles
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
'@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js',
'@angular/http': 'npm:@angular/http/bundles/http.umd.js',
'@angular/router': 'npm:@angular/router/bundles/router.umd.js',
'@angular/forms': 'npm:@angular/forms/bundles/forms.umd.js',
'@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.js',
// other libraries
'rxjs': 'npm:rxjs',
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
'lodash' : 'npm:lodash',
'xml2js' : 'npm:xml2js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
},
lodash: {
main : 'index.js',
defaultExtension: 'js'
},
xml2js: {
defaultExtension: 'js'
}
}
});
})(this);
El archivo de consumo:
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/Rx';
import * as _ from 'lodash';
import * as xml2js from 'xml2js';
@Injectable()
export class CatalogService {
constructor (private http:Http) {
}
getCatalog() {
//return Promise.resolve(['This', 'That']);
var xml = "<root>Hello xml2js!</root>"
xml2js.parseString(xml, function (err, result) {
alert(result);
});
alert(_.indexOf([1, 2, 1, 2], 2));
}
}
archivo index.html
<html>
<head>
<title>Angular QuickStart</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- 1. Load libraries -->
<!-- Polyfill for older browsers -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/reflect-metadata/Reflect.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('app').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<dsa-app>Loading DSA App...</dsa-app>
</body>
</html>
Con esa configuración, obtengo un error como el siguiente:
zone.js:1382 GET http://localhost:3000/node_modules/xml2js/ 404 (No encontrado)
La biblioteca lodash se carga bien, así que estoy bastante seguro de que el problema aquí tiene que ver más con la forma en que se define la biblioteca xml2js que con mi configuración.
La solución más cercana que he visto para esto está un poco desactualizada para Angular 2.0, pero la pongo aquí como referencia: Usar la biblioteca JS xml2js con Angular 2
Aunque la pregunta específicamente aborda el uso de esa biblioteca, cualquier otra idea sobre cómo analizar/convertir XML en algo manejable dentro de TypeScript también sería útil.
Actualización:
Como se sugirió, empecé a agregar definiciones "main" a mi configuración de systemjs. Sin embargo, pensé que parte de esto era que las dependencias se cargarían/descubrirían a partir del contenido de node.
Por lo tanto, modificando el system.config.js para tener:
xml2js: {
main: 'lib/xml2js.js',
defaultExtension: 'js'
},
Mueve el problema a donde ahora obtengo entradas 404 para sax y xmlbuilder.
Así que, también añadí lo siguiente:
sax: {
main: 'lib/sax.js',
defaultExtension: 'js'
},
xmlbuilder: {
main: 'lib/index.js',
defaultExtension: 'js'
}
Que aborda el sax y xmlbuilder, pero luego aparecen más errores.
Pensé que toda la idea de usar SystemJS y el tsconfig sería que esto se resolviera a partir de los módulos de Node, agregar todas las dependencias del árbol en los paquetes parece derrotar el propósito.
Respuesta #1
Creo que el problema aquí está en tu configuración de systemjs: no has definido main para xml2js. Debido a eso, systemjs no sabe qué archivo cargar, así que intenta cargar directamente el directorio de NPM por alguna razón (cargando /node_modules/xml2js como un archivo, lo cual claramente no va a funcionar). Ten en cuenta que tu configuración de lodash sí especifica 'index.js', presumiblemente llevando a que se cargue /node_modules/lodash/index.js cuando importas lodash, por lo que eso funciona.
Si especificas un 'main' para xml2js en tu configuración de systemjs que apunte a lib/xml2js.js, entonces systemjs debería poder encontrarlo correctamente.
Respuesta #2
Como señaló @tim-perry, el problema aquí era la forma en que se definía el paquete. Otro problema en mi trabajo es que estaba haciendo suposiciones incorrectas sobre lo que SystemJS haría por mí. Necesito investigar una herramienta diferente (comenzando con JSPM) que realice el recorrido del árbol al cargar el módulo base. En mi ejemplo, lodash funcionaba porque está auto contenido. Sin embargo, xml2js tenía muchas dependencias, que a su vez tenían muchas dependencias, que a su vez tenían muchas dependencias... por lo tanto, aplicar la solución de agregar la definición del paquete para cada dependencia resolvía el problema, aunque de una manera muy engorrosa.
De nuevo, gracias a @tim-perry y @artem que ayudaron con esto.