-
Notifications
You must be signed in to change notification settings - Fork 1
/
manifest.js
93 lines (89 loc) · 2.82 KB
/
manifest.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { EventEmitter } from 'node:events';
import { join } from 'node:path';
import { readFileSync } from 'node:fs';
import mime from 'mime-types';
import { CID } from 'multiformats/cid';
import Watcher from './watcher.js';
import { fromDataWithData } from './cid.js';
// Manifest:
// {
// name
// description
// icons
// resources: {
// '/': {} // this one is a *copy* of the one for index.html
// '/path/to/something': {
// src: 'cid',
// mediaType: 'text/html',
// },
// },
// }
export default class Manifest extends EventEmitter {
constructor (path, meta = {}) {
super();
this.path = path; // should be absolute
this.watcher = new Watcher(path);
this.resources = {};
this.meta = meta; // name, description, icons (though those must be resources too)
}
async map2manifest (map) {
Object.entries(map).forEach(([cid, path]) => this.addToResourceMap(path, cid));
}
addToResourceMap (path, cid) {
const fullPath = join(this.path, path);
const mediaType = mime.lookup(fullPath);
if (!/^\//.test(path)) path = `/${path}`;
if (path === '/manifest.json') {
this.meta = JSON.parse(readFileSync(fullPath));
return;
}
this.resources[path] = { src: cid, mediaType };
// if the file is index.html at the root, we add a second / entry for that
if (path === '/index.html') this.resources['/'] = Object.assign({}, this.resources[path]);
}
async generate () {
this.map2manifest(await this.watcher.run());
this.watcher.stop();
return this.manifest();
}
async watch () {
// console.warn(`[📃] Calling awaited watcher.run`);
this.map2manifest(await this.watcher.run());
// console.warn(`[📃] After resolution of watcher.run, adding event listener`);
this.watcher.on('update', (map, type, cid, path) => {
// console.warn(`[📃] ••• update! ${path}`);
if (type === 'add' || type === 'change') this.addToResourceMap(path, cid);
else if (type === 'delete') {
if (!/^\//.test(path)) path = `/${path}`;
delete this.resources[path];
}
else console.error(`Unknown change type ${type}`);
this.emit('update', this.manifest());
});
}
async stop () {
await this.watcher?.stop();
}
manifest () {
const m = {
name: this.meta?.name || 'Unnamed Tile',
description: this.meta?.description || null,
// XXX to support icons, need to check that they are resources
icons: this.meta?.icons,
};
m.resources = Object.assign({}, this.resources);
Object.values(m.resources).forEach(r => {
if (typeof r.src === 'string') r.src = CID.parse(r.src);
});
return m;
}
async tile () {
const m = this.manifest();
const [cid, tile] = await fromDataWithData(m);
return {
cid,
tile,
url: `web+tile://${cid}/`,
};
}
}