Repository navigation
Expand file tree
/
Copy pathindex.js
More file actions
executable file
·170 lines (144 loc) · 6.37 KB
/
Copy pathindex.js
File metadata and controls
executable file
·170 lines (144 loc) · 6.37 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env node
'use strict';
const path = require('path');
const fs = require('fs');
const minimist = require('minimist');
const chalk = require('chalk');
const htmlToJsonMl = require('html-to-jsonml');
const jsonMlToHtml = require('./libs/jsonml-to-html');
const webstrates = require('./libs/webstrates-server');
const FileManager = require('./libs/file-manager');
const assetUploader = require('./libs/asset-uploader');
const assetDownloader = require('./libs/asset-downloader');
const normalizeJson = require('./libs/normalize-json');
const resourceManager = require('./libs/resource-manager');
const argv = minimist(process.argv.slice(2));
const args = argv._;
const WEBSTRATE_ID = argv.webstrateId || argv.id;
const HOST = argv.host || argv.h || 'web:strate@localhost:7007';
const SECURE = !(argv.insecure || argv.i);
const TOKEN = argv.token || argv.t;
const TOKEN_QUERY = (TOKEN ? '?token=' + TOKEN : '');
const WEB_HOST = (SECURE ? 'https://' : 'http://') + HOST + '/' + WEBSTRATE_ID + '/';
const SOCKET_HOST = (SECURE ? 'wss://' : 'ws://') + HOST + '/' + WEBSTRATE_ID + '/' + TOKEN_QUERY;
// Whether to terminate immediately after compiling and submitting the index.html.
const ONE_SHOT = argv.oneshot || argv.n || false;
global.DOWNLOAD_ASSETS = argv['download-assets'] || false;
if (!WEBSTRATE_ID) {
console.error(chalk.red(chalk.bold('!')), 'Missing --webstrateId parameter.');
process.exit(1);
}
webstrates.checkAccess(WEB_HOST + TOKEN_QUERY);
const MOUNT_PATH = path.resolve(args[0] || WEBSTRATE_ID);
const fileManager = FileManager(MOUNT_PATH);
// When connecting to a webstrate with an existing index.html, should we keep our index.html and
// overwrite the one on the server or vice versa. If no index.html exists, we have nothing to keep,
// so we keep theirs.
const indexFileExists = fs.existsSync(path.resolve(MOUNT_PATH, 'index.html'));
const KEEP_OURS = !(argv.theirs || argv.t) && indexFileExists;
// Holds current state of document.
let jsonml;
// Holds all resources (script and style tags) that have been extracted into separate files.
let resources = new Map();
let hasSubmittedOurs = false;
// Listen for changes to the file system. fileName is the file name, hash is an md5 hash of the file
// if the file is an asset. readFile is a function that returns the contents of the file.
fileManager.onChange(async (type, activePath, readFile) => {
const fileName = path.basename(activePath);
if (type === 'primary' && fileName === 'index.html') {
let html = readFile();
if (!html) return;
jsonml = normalizeJson(htmlToJsonMl(html));
// We extract any resources by (side effect) that might have been added manually to index.html.
let extractedResources = [];
jsonml = resourceManager.extract(jsonml, extractedResources);
extractedResources.forEach(([fileName, resource]) => {
fileManager.writeFile('resources/' + fileName, resource);
resources.set(fileName, resource);
});
// If we have extracted something, we need to update index.html, so the inline resource
// will get removed.
if (extractedResources.length > 0) {
html = jsonMlToHtml(jsonml);
fileManager.writeFile('index.html', html);
}
jsonml = resourceManager.insert(jsonml, resources);
await webstrates.save(jsonml);
if (ONE_SHOT) {
process.exit(0);
}
hasSubmittedOurs = true;
}
else if (type === 'asset') {
const assetName = path.basename(activePath);
if (fileManager.ignoreList.has(assetName)) {
// If the file is in the ignore list, it's because the file is already being uploaded or it has
// just been downloaded, so we don't want to reupload it.
return;
}
// Add file to the fileManager's ignore list, so it doesn't trigger an upload.
fileManager.ignoreList.add(assetName);
await assetUploader.upload(WEB_HOST + TOKEN_QUERY, activePath);
// Remove from the ignore list. We delay it, to ensure the fileManager has picked up on the write
// (and ignored it).
setTimeout(() => fileManager.ignoreList.delete(assetName), 5 * 1000);
}
else if (type === 'resource') {
const resource = readFile();
// Don't update jsonml if the file hasn't changed.
if (resources.get(fileName) === resource) return;
resources.set(fileName, resource);
// The HTML file might not have been read yet, in which case jsonml hasn't been defined. Because
// of this, we can't insert the the resource into it.
if (jsonml) {
jsonml = resourceManager.insert(jsonml, resources);
await webstrates.save(jsonml);
}
}
});
fileManager.watch();
// Listen for changes on the Webstrates server.
webstrates.onChange((jsonml) => {
// If we're preferring our version over the version on the server (theirs), we don't want to have
// our local version overwritten whatever comes from the server, so we ignore all updates coming
// from the server, until we've submitted our own.
if (KEEP_OURS && !hasSubmittedOurs) return;
jsonml = normalizeJson(jsonml);
// Extract resources by side effects.
let extractedResources = [];
jsonml = resourceManager.extract(jsonml, extractedResources);
extractedResources.forEach(([fileName, resource]) => {
// Don't rewrite the file if it hasn't changed.
if (resources.get(fileName) === resource) return;
fileManager.writeFile('resources/' + fileName, resource);
resources.set(fileName, resource);
});
const html = jsonMlToHtml(jsonml);
fileManager.writeFile('index.html', html);
});
webstrates.onClose((event) => {
console.log('Reconnecting');
setTimeout(() => webstrates.connect(SOCKET_HOST, WEBSTRATE_ID), 1000);
});
// Download assets to disk if --download-assets flag is set.
if (global.DOWNLOAD_ASSETS) {
webstrates.onAsset(async (assets) => {
const assetNames = [...new Set(assets.map(asset =>
asset.fileName))];
for (let i=0; i < assetNames.length; i++) {
const assetName = assetNames[i];
if (fileManager.ignoreList.has(assetName)) {
// If the file is in the ignore list, it's because it has just been uploaded, so we don't want to
// redownload it.
return;
}
// Add file to the fileManager's ignore list, so it doesn't trigger an upload.
fileManager.ignoreList.add(assetName);
await assetDownloader.download(WEB_HOST, TOKEN_QUERY, WEBSTRATE_ID, assetName, fileManager.assetPath(), );
// Remove from the ignore list. We delay it, to ensure the fileManager has picked up on the write
// (and ignored it).
setTimeout(() => fileManager.ignoreList.delete(assetName), 5 * 1000);
}
});
}
webstrates.connect(SOCKET_HOST, WEBSTRATE_ID);