# Carga un módulo JavaScript desde una URL con IMPORT()

> Descarga un archivo con JavaScript desde una dirección, guárdalo en la carpeta tmp, haz require y usa lo que exporta; la segunda llamada no lo descarga otra vez.

Framework: total5
Product: Total.js 5 (total5@0.0.19)
Language: es
Content-SHA256: aa39933174bcd5b9a166f05f99b818cd0a05588803f7540fd499dbeaa634c934
Snapshot: c0700e321579725c5cf4f5ace1ad7d2866d1b4f93c24a63c0b41b6b7998e976c
Sources: total5-installed@total5@0.0.19
Review: not-recorded

`IMPORT(url, [callback])` descarga un archivo JavaScript, lo guarda en la carpeta `tmp` de la aplicación y devuelve lo que exporta el archivo (`module.exports`), como haría `require()`. Con un callback es `callback(err, module, response)`; sin él, `IMPORT()` devuelve una promesa del módulo. La dirección se recuerda: llamar a `IMPORT()` de nuevo con la misma dirección **no** descarga el archivo otra vez, hace require del que está en `tmp` (y el archivo permanece allí entre reinicios, pero la memoria de «ya descargado» no: tras un reinicio se descarga de nuevo).

**Esto ejecuta código que viene de la red con todos los derechos de tu aplicación.** Importa solo desde una dirección que controles y a la que llegues con `https`, nunca una que te haya enviado un usuario o que cualquier otra persona pueda cambiar. El archivo es un módulo CommonJS. **No se comprueba el estado de la respuesta:** una dirección que responde `404` (o cualquier página de error) se guarda como si fuera el módulo, y `require()` lanza entonces un `SyntaxError` dentro de la descarga, de modo que en `total5` 0.0.19 el callback **no se llama nunca** y la promesa nunca se resuelve. Da siempre a `IMPORT()` un límite de tiempo propio, como hace la última ruta, y comprueba la dirección antes de depender de ella.

Aquí el archivo viene de una ruta de la misma aplicación, de modo que el ejemplo no necesita red. La ruta cuenta las descargas.

```js controllers/import.js
exports.install = function() {
	ROUTE('GET /lib/tax/', lib);
	ROUTE('POST /use/', use);
	ROUTE('POST /use-twice/', use_twice);
	ROUTE('POST /missing/', missing);
	ROUTE('GET /downloads/', downloads);
};

let count = 0;

function lib($) {
	count++;
	$.plain('module.exports = { vat: function(price) { return Math.round(price * 121) / 100; } };');
}

function downloads($) {
	$.json({ downloads: count });
}

function base() {
	return 'http://127.0.0.1:' + CONF.$port;
}

async function use($) {
	const tax = await IMPORT(base() + '/lib/tax/');
	$.json({ price: tax.vat(100) });
}

function use_twice($) {
	IMPORT(base() + '/lib/tax/', function(err, tax) {
		$.json({ err: err ? err.toString() : null, price: tax ? tax.vat(200) : null });
	});
}

async function missing($) {
	// the address does not exist: the answer is the 404 page of the application
	const winner = await Promise.race([
		IMPORT(base() + '/lib/missing/').then(() => 'answered', () => 'answered'),
		new Promise(resolve => setTimeout(() => resolve('no answer'), 1500))
	]);
	$.json({ result: winner });
}
```
