# Ein JavaScript-Modul mit IMPORT() von einer URL laden

> Lade eine JavaScript-Datei von einer Adresse herunter, lege sie im tmp-Ordner ab, binde sie mit require ein und nutze, was sie exportiert; der zweite Aufruf lädt sie nicht erneut herunter.

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

`IMPORT(url, [callback])` lädt eine JavaScript-Datei herunter, speichert sie im Ordner `tmp` der Anwendung und gibt zurück, was die Datei exportiert (`module.exports`), wie es `require()` täte. Mit einem Callback ist es `callback(err, module, response)`; ohne ihn gibt `IMPORT()` ein Promise für das Modul zurück. Die Adresse wird gemerkt: Ein erneuter Aufruf von `IMPORT()` mit derselben Adresse lädt die Datei **nicht** erneut herunter, sondern bindet die in `tmp` ein (und die Datei bleibt zwischen Neustarts dort, das Merken von „schon heruntergeladen“ aber nicht: Nach einem Neustart wird sie erneut heruntergeladen).

**Das führt Code aus dem Netz mit allen Rechten deiner Anwendung aus.** Importiere nur von einer Adresse, die du kontrollierst und die du per `https` erreichst, niemals von einer, die dir ein Benutzer geschickt hat oder die jemand anderes ändern kann. Die Datei ist ein CommonJS-Modul. **Der Status der Antwort wird nicht geprüft:** Eine Adresse, die mit `404` (oder einer beliebigen Fehlerseite) antwortet, wird gespeichert, als wäre sie das Modul, und `require()` wirft dann innerhalb des Downloads einen `SyntaxError`, sodass in `total5` 0.0.19 der Callback **nie aufgerufen** wird und das Promise nie abgeschlossen wird. Gib `IMPORT()` immer ein eigenes Zeitlimit, wie es die letzte Route tut, und prüfe die Adresse, bevor du dich darauf verlässt.

Hier stammt die Datei von einer Route derselben Anwendung, sodass das Beispiel kein Netzwerk braucht. Die Route zählt die Downloads.

```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 });
}
```
