swpp-backends 3.0.0-alpha.4 → 3.0.0-alpha.400
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{types → dist}/index.d.ts +6 -5
- package/dist/index.js +29 -14
- package/{types → dist}/swpp/FileParser.d.ts +11 -6
- package/dist/swpp/FileParser.js +27 -13
- package/{types → dist}/swpp/JsonBuilder.d.ts +1 -3
- package/dist/swpp/JsonBuilder.js +3 -7
- package/dist/swpp/NetworkFileHandler.js +3 -3
- package/{types → dist}/swpp/ResourcesScanner.d.ts +18 -14
- package/dist/swpp/ResourcesScanner.js +100 -73
- package/{types → dist}/swpp/SwCompiler.d.ts +21 -8
- package/dist/swpp/SwCompiler.js +51 -15
- package/dist/swpp/cli.d.ts +11 -0
- package/dist/swpp/cli.js +109 -0
- package/{types → dist}/swpp/config/ConfigCluster.d.ts +105 -63
- package/dist/swpp/config/ConfigCluster.js +67 -48
- package/dist/swpp/config/ConfigLoader.d.ts +79 -0
- package/dist/swpp/config/ConfigLoader.js +192 -87
- package/dist/swpp/config/SpecialConfig.d.ts +38 -0
- package/dist/swpp/config/SpecialConfig.js +70 -0
- package/{types → dist}/swpp/database/CompilationEnv.d.ts +16 -14
- package/dist/swpp/database/CompilationEnv.js +41 -211
- package/dist/swpp/database/CompilationFileParser.d.ts +81 -0
- package/dist/swpp/database/CompilationFileParser.js +269 -0
- package/{types → dist}/swpp/database/CrossDepCode.d.ts +4 -0
- package/dist/swpp/database/CrossDepCode.js +27 -1
- package/dist/swpp/database/CrossEnv.js +19 -2
- package/{types → dist}/swpp/database/DomCode.d.ts +4 -1
- package/dist/swpp/database/DomCode.js +34 -5
- package/dist/swpp/database/KeyValueDatabase.d.ts +72 -0
- package/dist/swpp/database/KeyValueDatabase.js +120 -27
- package/{types → dist}/swpp/database/RuntimeCoreCode.d.ts +2 -1
- package/dist/swpp/database/RuntimeCoreCode.js +6 -5
- package/{types → dist}/swpp/database/RuntimeDepCode.d.ts +8 -14
- package/dist/swpp/database/RuntimeDepCode.js +30 -50
- package/{types → dist}/swpp/database/RuntimeEventCode.d.ts +4 -0
- package/dist/swpp/database/RuntimeEventCode.js +14 -1
- package/{types → dist}/swpp/database/RuntimeKeyValueDatabase.d.ts +1 -1
- package/dist/swpp/database/RuntimeKeyValueDatabase.js +2 -2
- package/dist/swpp/debug/CallChainRecorder.d.ts +5 -0
- package/dist/swpp/debug/CallChainRecorder.js +29 -0
- package/{types → dist}/swpp/untils.d.ts +30 -18
- package/dist/swpp/untils.js +74 -53
- package/dist/test.js +1 -0
- package/package.json +8 -5
- package/types/DomBuilder.d.ts +0 -6
- package/types/FileAnalyzer.d.ts +0 -96
- package/types/ServiceWorkerBuilder.d.ts +0 -7
- package/types/SwppConfig.d.ts +0 -139
- package/types/SwppRules.d.ts +0 -117
- package/types/UpdateJsonBuilder.d.ts +0 -44
- package/types/Utils.d.ts +0 -43
- package/types/Variant.d.ts +0 -58
- package/types/VersionAnalyzer.d.ts +0 -27
- package/types/browser/ServiceWorkerRuntimeTypes.d.ts +0 -18
- package/types/swpp/RuntimeEnv.d.ts +0 -39
- package/types/swpp/SwCodeInject.d.ts +0 -17
- package/types/swpp/config/ConfigLoader.d.ts +0 -53
- package/types/swpp/database/KeyValueDatabase.d.ts +0 -53
- package/types/swpp/database/RuntimeEnv.d.ts +0 -5
- /package/{types → dist}/swpp/NetworkFileHandler.d.ts +0 -0
- /package/{types → dist}/swpp/database/CrossEnv.d.ts +0 -0
|
@@ -27,6 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
exports.FileUpdateTracker = exports.ResourcesScanner = void 0;
|
|
30
|
+
exports.traverseDirectory = traverseDirectory;
|
|
30
31
|
const fs_1 = __importDefault(require("fs"));
|
|
31
32
|
const crypto = __importStar(require("node:crypto"));
|
|
32
33
|
const path_1 = __importDefault(require("path"));
|
|
@@ -37,36 +38,42 @@ const untils_1 = require("./untils");
|
|
|
37
38
|
* 资源文件扫描器
|
|
38
39
|
*/
|
|
39
40
|
class ResourcesScanner {
|
|
40
|
-
constructor(compilation) {
|
|
41
|
+
constructor(compilation, oldTracker) {
|
|
41
42
|
this.compilation = compilation;
|
|
43
|
+
this.oldTracker = oldTracker;
|
|
42
44
|
}
|
|
43
45
|
// noinspection JSUnusedGlobalSymbols
|
|
44
46
|
/** 扫描指定目录下的所有文件 */
|
|
45
47
|
async scanLocalFile(path) {
|
|
46
48
|
const matchCacheRule = this.compilation.crossDep.read('matchCacheRule');
|
|
47
|
-
const register = this.compilation.
|
|
49
|
+
const register = this.compilation.fileParser;
|
|
50
|
+
const jsonInfo = this.compilation.compilationEnv.read('SWPP_JSON_FILE');
|
|
51
|
+
const excludes = [
|
|
52
|
+
path_1.default.posix.join(path, jsonInfo.swppPath, jsonInfo.versionPath),
|
|
53
|
+
path_1.default.posix.join(path, jsonInfo.swppPath, jsonInfo.trackerPath),
|
|
54
|
+
path_1.default.posix.join(path, this.compilation.compilationEnv.read('SERVICE_WORKER') + '.js')
|
|
55
|
+
];
|
|
56
|
+
if (!this.oldTracker) {
|
|
57
|
+
this.oldTracker = await jsonInfo.fetchTrackerFile(this.compilation);
|
|
58
|
+
}
|
|
48
59
|
const urls = new Set();
|
|
49
|
-
const tracker = new FileUpdateTracker(this.compilation);
|
|
60
|
+
const tracker = new FileUpdateTracker(this.compilation, this.oldTracker);
|
|
50
61
|
await traverseDirectory(path, async (file) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
stream.on('data', data => hash.update(data));
|
|
62
|
+
if (excludes.includes(file))
|
|
63
|
+
return;
|
|
54
64
|
const localUrl = tracker.normalizeUri(file.substring(path.length));
|
|
55
|
-
|
|
65
|
+
const isCached = !!matchCacheRule.runOnNode(localUrl);
|
|
66
|
+
if (isCached) {
|
|
56
67
|
tracker.addUrl(localUrl.href);
|
|
57
68
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
resolve();
|
|
67
|
-
});
|
|
68
|
-
stream.on('error', err => reject(err));
|
|
69
|
-
});
|
|
69
|
+
const set = await register.parserLocalFile(file, content => {
|
|
70
|
+
if (isCached) {
|
|
71
|
+
const hash = crypto.createHash('md5');
|
|
72
|
+
hash.update(content);
|
|
73
|
+
tracker.update(localUrl.pathname, hash.digest('hex'));
|
|
74
|
+
}
|
|
75
|
+
}, isCached);
|
|
76
|
+
set.forEach(it => urls.add(it));
|
|
70
77
|
});
|
|
71
78
|
await this.scanNetworkFile(tracker, urls);
|
|
72
79
|
return tracker;
|
|
@@ -74,7 +81,8 @@ class ResourcesScanner {
|
|
|
74
81
|
/** 扫描网络文件 */
|
|
75
82
|
async scanNetworkFile(tracker, urls, record = new Set()) {
|
|
76
83
|
const matchCacheRule = this.compilation.crossDep.read('matchCacheRule');
|
|
77
|
-
const registry = this.compilation.
|
|
84
|
+
const registry = this.compilation.fileParser;
|
|
85
|
+
const isStable = this.compilation.compilationEnv.read('isStable');
|
|
78
86
|
const appendedUrls = new Set();
|
|
79
87
|
const taskList = new Array(urls.size);
|
|
80
88
|
let i = 0;
|
|
@@ -87,9 +95,19 @@ class ResourcesScanner {
|
|
|
87
95
|
if (isCached) {
|
|
88
96
|
tracker.addUrl(normalizeUri.href);
|
|
89
97
|
}
|
|
98
|
+
if (isStable(normalizeUri)) {
|
|
99
|
+
const oldValue = this.oldTracker?.get?.(normalizeUri.href);
|
|
100
|
+
if (Array.isArray(oldValue)) {
|
|
101
|
+
const list = tracker.syncStable(normalizeUri, oldValue, this.oldTracker);
|
|
102
|
+
list.forEach(it => appendedUrls.add(it));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
90
106
|
taskList[i++] = registry.parserUrlFile(normalizeUri.href, !!isCached)
|
|
91
107
|
.then(value => {
|
|
92
|
-
|
|
108
|
+
if (isCached) {
|
|
109
|
+
tracker.update(value.file, value.mark);
|
|
110
|
+
}
|
|
93
111
|
value.urls.forEach(it => appendedUrls.add(it));
|
|
94
112
|
}).catch(err => untils_1.utils.printError('SCAN NETWORK FILE', err));
|
|
95
113
|
}
|
|
@@ -112,7 +130,7 @@ async function traverseDirectory(dir, callback) {
|
|
|
112
130
|
if (err)
|
|
113
131
|
reject(err);
|
|
114
132
|
else {
|
|
115
|
-
Promise.all(files.map(it => traverseDirectory(path_1.default.join(dir, it), callback))).then(() => resolve());
|
|
133
|
+
Promise.all(files.map(it => traverseDirectory(path_1.default.posix.join(dir, it), callback))).then(() => resolve());
|
|
116
134
|
}
|
|
117
135
|
});
|
|
118
136
|
});
|
|
@@ -125,10 +143,9 @@ async function traverseDirectory(dir, callback) {
|
|
|
125
143
|
* 文件更新监听器
|
|
126
144
|
*/
|
|
127
145
|
class FileUpdateTracker {
|
|
128
|
-
constructor(compilation) {
|
|
146
|
+
constructor(compilation, oldTracker) {
|
|
129
147
|
this.compilation = compilation;
|
|
130
|
-
|
|
131
|
-
this.headers = new Map();
|
|
148
|
+
this.oldTracker = oldTracker;
|
|
132
149
|
/** 存储列表,key 为文件路径,value 为文件的唯一标识符 */
|
|
133
150
|
this.map = new Map();
|
|
134
151
|
/** 存储所有存在的 URL */
|
|
@@ -136,26 +153,58 @@ class FileUpdateTracker {
|
|
|
136
153
|
}
|
|
137
154
|
/** 更新一个文件的标识符 */
|
|
138
155
|
update(uri, value) {
|
|
139
|
-
|
|
156
|
+
if (typeof value == 'string') {
|
|
157
|
+
if (value.startsWith('[')) {
|
|
158
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.invalidValue, `插入数据("${value}")时,不应当以方括号开头`);
|
|
159
|
+
}
|
|
160
|
+
this.map.set(uri, value);
|
|
161
|
+
}
|
|
162
|
+
else if (Array.isArray(value)) {
|
|
163
|
+
this.map.set(uri, JSON.stringify(value));
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.map.set(uri, JSON.stringify(Array.from(value)));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 同步指定的稳定资源(同步时会连同同步其连接的稳定资源)
|
|
171
|
+
* @return 直接或间接连接的一些需要扫描的资源
|
|
172
|
+
*/
|
|
173
|
+
syncStable(uri, value, oldTracker) {
|
|
174
|
+
const isStable = this.compilation.compilationEnv.read('isStable');
|
|
175
|
+
this.update(uri.href, value);
|
|
176
|
+
this.addUrl(uri.href);
|
|
177
|
+
const result = [];
|
|
178
|
+
for (let item of value) {
|
|
179
|
+
this.addUrl(item);
|
|
180
|
+
const itemUrl = new URL(item);
|
|
181
|
+
if (isStable(itemUrl)) {
|
|
182
|
+
const oldValue = oldTracker.get(item);
|
|
183
|
+
if (Array.isArray(oldValue)) {
|
|
184
|
+
const son = this.syncStable(itemUrl, oldValue, oldTracker);
|
|
185
|
+
result.push(...son);
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
result.push(item);
|
|
190
|
+
}
|
|
191
|
+
return result;
|
|
140
192
|
}
|
|
141
193
|
/** 读取一个文件的标识符 */
|
|
142
194
|
get(uri) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
this.headers.set(key, value);
|
|
148
|
-
}
|
|
149
|
-
/** 读取一个 header */
|
|
150
|
-
getHeader(key) {
|
|
151
|
-
return this.headers.get(key);
|
|
195
|
+
const value = this.map.get(this.normalizeUri(uri).href);
|
|
196
|
+
if (!value)
|
|
197
|
+
return;
|
|
198
|
+
return value.startsWith('[') ? JSON.parse(value) : value;
|
|
152
199
|
}
|
|
153
200
|
/** 归一化 uri */
|
|
154
201
|
normalizeUri(uri) {
|
|
155
202
|
if (uri.startsWith('http:'))
|
|
156
203
|
uri = `https:${uri.substring(5)}`;
|
|
157
|
-
const
|
|
158
|
-
|
|
204
|
+
const baseUrl = this.compilation.compilationEnv.read('DOMAIN_HOST');
|
|
205
|
+
const url = new URL(uri, baseUrl);
|
|
206
|
+
const normalizer = this.compilation.crossDep.read('normalizeUrl');
|
|
207
|
+
return new URL(normalizer.runOnNode(url.href));
|
|
159
208
|
}
|
|
160
209
|
/** 添加一个 URL */
|
|
161
210
|
addUrl(url) {
|
|
@@ -170,23 +219,20 @@ class FileUpdateTracker {
|
|
|
170
219
|
* + 在新旧 tracker 中都存在且唯一标识符发生变化
|
|
171
220
|
* + 在新 tracker 中不存在且在旧 tracker 中存在
|
|
172
221
|
*/
|
|
173
|
-
diff(
|
|
222
|
+
async diff() {
|
|
223
|
+
const baseUrl = this.compilation.compilationEnv.read('DOMAIN_HOST');
|
|
174
224
|
const diff = new JsonBuilder_1.JsonBuilder(this.compilation, this.allUrl);
|
|
175
|
-
oldTracker
|
|
225
|
+
const oldTracker = this.oldTracker ??
|
|
226
|
+
await this.compilation.compilationEnv.read('SWPP_JSON_FILE').fetchTrackerFile(this.compilation);
|
|
227
|
+
oldTracker?.map?.forEach?.((value, key) => {
|
|
176
228
|
if (this.map.has(key)) {
|
|
177
229
|
if (this.get(key) !== value)
|
|
178
|
-
diff.update(key, value);
|
|
230
|
+
diff.update(untils_1.utils.splicingUrl(baseUrl, key).href, value);
|
|
179
231
|
}
|
|
180
232
|
else {
|
|
181
|
-
diff.update(key, value);
|
|
233
|
+
diff.update(untils_1.utils.splicingUrl(baseUrl, key).href, value);
|
|
182
234
|
}
|
|
183
235
|
});
|
|
184
|
-
this.headers.forEach((value, key) => {
|
|
185
|
-
diff.putHeader(key, {
|
|
186
|
-
oldValue: oldTracker.getHeader(key),
|
|
187
|
-
newValue: value
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
236
|
return diff;
|
|
191
237
|
}
|
|
192
238
|
// noinspection JSUnusedGlobalSymbols
|
|
@@ -198,9 +244,6 @@ class FileUpdateTracker {
|
|
|
198
244
|
* ```json
|
|
199
245
|
* {
|
|
200
246
|
* "version": 4,
|
|
201
|
-
* "headers": {
|
|
202
|
-
* [key: string]: any
|
|
203
|
-
* },
|
|
204
247
|
* "tracker" {
|
|
205
248
|
* [uri: string]: string
|
|
206
249
|
* }
|
|
@@ -215,7 +258,7 @@ class FileUpdateTracker {
|
|
|
215
258
|
this.map.forEach((value, key) => {
|
|
216
259
|
result.tracker[key] = value;
|
|
217
260
|
});
|
|
218
|
-
return JSON.stringify(result
|
|
261
|
+
return JSON.stringify(result);
|
|
219
262
|
}
|
|
220
263
|
/** 解序列化数据 */
|
|
221
264
|
static unJson(compilation, jsonStr) {
|
|
@@ -223,26 +266,17 @@ class FileUpdateTracker {
|
|
|
223
266
|
const json = JSON.parse(jsonStr);
|
|
224
267
|
switch (json.version) {
|
|
225
268
|
case 4:
|
|
226
|
-
for (let key in json.headers) {
|
|
227
|
-
tracker.headers.set(key, json.headers[key]);
|
|
228
|
-
}
|
|
229
269
|
for (let key in json.tracker) {
|
|
230
270
|
tracker.map.set(key, json.tracker[key]);
|
|
231
271
|
}
|
|
232
272
|
break;
|
|
233
273
|
case 3:
|
|
234
|
-
for (let key in json['external']) {
|
|
235
|
-
tracker.headers.set(key, json['external'][key]);
|
|
236
|
-
}
|
|
237
274
|
for (let key in json.list) {
|
|
238
275
|
const value = json.list[key];
|
|
239
276
|
tracker.map.set(key, value.length === 32 ? value : '');
|
|
240
277
|
}
|
|
241
278
|
break;
|
|
242
|
-
default: throw {
|
|
243
|
-
code: untils_1.exceptionNames.unsupportedVersion,
|
|
244
|
-
message: `不支持 ${json.version}`,
|
|
245
|
-
};
|
|
279
|
+
default: throw new untils_1.RuntimeException(untils_1.exceptionNames.unsupportedVersion, `不支持 ${json.version}`);
|
|
246
280
|
}
|
|
247
281
|
return tracker;
|
|
248
282
|
}
|
|
@@ -251,9 +285,9 @@ class FileUpdateTracker {
|
|
|
251
285
|
static async parserJsonFromNetwork(compilation) {
|
|
252
286
|
const domain = compilation.compilationEnv.read('DOMAIN_HOST');
|
|
253
287
|
const jsonInfo = compilation.compilationEnv.read('SWPP_JSON_FILE');
|
|
254
|
-
const url =
|
|
255
|
-
const fetcher = compilation.compilationEnv.read('
|
|
256
|
-
const isNotFound = compilation.compilationEnv.read('
|
|
288
|
+
const url = untils_1.utils.splicingUrl(domain, jsonInfo.swppPath, jsonInfo.trackerPath);
|
|
289
|
+
const fetcher = compilation.compilationEnv.read('NETWORK_FILE_FETCHER');
|
|
290
|
+
const isNotFound = compilation.compilationEnv.read('isNotFound');
|
|
257
291
|
const notFoundLevel = compilation.compilationEnv.read('ALLOW_NOT_FOUND');
|
|
258
292
|
let error;
|
|
259
293
|
const result = await (async () => {
|
|
@@ -261,10 +295,7 @@ class FileUpdateTracker {
|
|
|
261
295
|
const response = await fetcher.fetch(url);
|
|
262
296
|
if (isNotFound.response(response)) {
|
|
263
297
|
if (notFoundLevel == CompilationEnv_1.AllowNotFoundEnum.REJECT_ALL) {
|
|
264
|
-
error = {
|
|
265
|
-
code: untils_1.exceptionNames.notFound,
|
|
266
|
-
message: `拉取 ${url} 时出现 404 错误`
|
|
267
|
-
};
|
|
298
|
+
error = new untils_1.RuntimeException(untils_1.exceptionNames.notFound, `拉取 ${url} 时出现 404 错误`);
|
|
268
299
|
return;
|
|
269
300
|
}
|
|
270
301
|
untils_1.utils.printWarning('SCANNER', '拉取 tracker 时服务器返回了 404,如果是第一次携带 swpp v3 构建网站请忽视这条信息');
|
|
@@ -278,11 +309,7 @@ class FileUpdateTracker {
|
|
|
278
309
|
untils_1.utils.printWarning('SCANNER', '拉取 tracker 时 DNS 解析失败,如果是第一次携带 swpp v3 构建网站且网站暂时无法解析请忽视这条信息');
|
|
279
310
|
return new FileUpdateTracker(compilation);
|
|
280
311
|
}
|
|
281
|
-
untils_1.
|
|
282
|
-
throw {
|
|
283
|
-
code: untils_1.exceptionNames.error,
|
|
284
|
-
message: `拉取或解析历史 Tracker 时出现错误`
|
|
285
|
-
};
|
|
312
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.error, `拉取或解析历史 Tracker 时出现错误`, { cause: e });
|
|
286
313
|
}
|
|
287
314
|
})();
|
|
288
315
|
if (result)
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { CompilationEnv } from './database/CompilationEnv';
|
|
2
|
+
import { CompilationFileParser } from './database/CompilationFileParser';
|
|
2
3
|
import { CrossDepCode } from './database/CrossDepCode';
|
|
4
|
+
import { DomCode } from './database/DomCode';
|
|
3
5
|
import { RuntimeCoreCode } from './database/RuntimeCoreCode';
|
|
4
6
|
import { RuntimeDepCode } from './database/RuntimeDepCode';
|
|
5
7
|
import { CrossEnv } from './database/CrossEnv';
|
|
6
8
|
import { RuntimeEventCode } from './database/RuntimeEventCode';
|
|
7
9
|
import { RuntimeKeyValueDatabase } from './database/RuntimeKeyValueDatabase';
|
|
10
|
+
import { CallChainRecorder } from './debug/CallChainRecorder';
|
|
8
11
|
export declare class SwCompiler {
|
|
9
12
|
private swCode;
|
|
10
13
|
/**
|
|
@@ -15,24 +18,34 @@ export declare class SwCompiler {
|
|
|
15
18
|
/** 运行时数据 */
|
|
16
19
|
export declare class RuntimeData {
|
|
17
20
|
/** 控制插入顺序 */
|
|
18
|
-
|
|
21
|
+
insertOrder: (Exclude<keyof RuntimeData, 'insertOrder' | 'domConfig'> | string)[];
|
|
19
22
|
/** 运行时环境变量 */
|
|
20
|
-
|
|
23
|
+
crossEnv: CrossEnv;
|
|
21
24
|
/** 运行时工具函数 */
|
|
22
|
-
|
|
25
|
+
runtimeDep: RuntimeDepCode;
|
|
23
26
|
/** 运行时核心功能函数 */
|
|
24
|
-
|
|
27
|
+
runtimeCore: RuntimeCoreCode;
|
|
25
28
|
/** 运行时事件注册 */
|
|
26
|
-
|
|
29
|
+
runtimeEvent: RuntimeEventCode;
|
|
27
30
|
/** 运行时/编译时工具函数 */
|
|
28
|
-
|
|
31
|
+
crossDep: CrossDepCode;
|
|
32
|
+
/** DOM 相关设置 */
|
|
33
|
+
domConfig: DomCode;
|
|
34
|
+
/** 追踪调用链 */
|
|
35
|
+
debugCallChain: CallChainRecorder;
|
|
36
|
+
constructor(compilationData: CompilationData);
|
|
29
37
|
getDatabase(key: string): RuntimeKeyValueDatabase<any, {}>;
|
|
38
|
+
initCompilation(compilation: CompilationData): void;
|
|
39
|
+
freezeAll(): void;
|
|
30
40
|
}
|
|
31
41
|
/** 编译时数据 */
|
|
32
|
-
export
|
|
42
|
+
export declare class CompilationData {
|
|
33
43
|
compilationEnv: CompilationEnv;
|
|
34
|
-
crossEnv: CrossEnv;
|
|
35
44
|
crossDep: CrossDepCode;
|
|
45
|
+
crossEnv: CrossEnv;
|
|
46
|
+
fileParser: CompilationFileParser;
|
|
47
|
+
initRuntime(runtime: RuntimeData): void;
|
|
48
|
+
freezeAll(): void;
|
|
36
49
|
}
|
|
37
50
|
/** 版本号信息 */
|
|
38
51
|
export interface BrowserVersion {
|
package/dist/swpp/SwCompiler.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RuntimeData = exports.SwCompiler = void 0;
|
|
3
|
+
exports.CompilationData = exports.RuntimeData = exports.SwCompiler = void 0;
|
|
4
|
+
const CompilationEnv_1 = require("./database/CompilationEnv");
|
|
5
|
+
const CompilationFileParser_1 = require("./database/CompilationFileParser");
|
|
4
6
|
const CrossDepCode_1 = require("./database/CrossDepCode");
|
|
7
|
+
const DomCode_1 = require("./database/DomCode");
|
|
8
|
+
const KeyValueDatabase_1 = require("./database/KeyValueDatabase");
|
|
5
9
|
const RuntimeCoreCode_1 = require("./database/RuntimeCoreCode");
|
|
6
10
|
const RuntimeDepCode_1 = require("./database/RuntimeDepCode");
|
|
7
11
|
const CrossEnv_1 = require("./database/CrossEnv");
|
|
8
12
|
const RuntimeEventCode_1 = require("./database/RuntimeEventCode");
|
|
13
|
+
const CallChainRecorder_1 = require("./debug/CallChainRecorder");
|
|
9
14
|
const untils_1 = require("./untils");
|
|
10
15
|
class SwCompiler {
|
|
11
16
|
constructor() {
|
|
@@ -18,7 +23,6 @@ class SwCompiler {
|
|
|
18
23
|
buildSwCode(runtime) {
|
|
19
24
|
if (this.swCode)
|
|
20
25
|
return this.swCode;
|
|
21
|
-
runtime.runtimeDep.fixDepFunction();
|
|
22
26
|
this.swCode = '(() => {' + runtime.insertOrder
|
|
23
27
|
.map(it => runtime.getDatabase(it).buildJsSource())
|
|
24
28
|
.join(';\n')
|
|
@@ -29,35 +33,67 @@ class SwCompiler {
|
|
|
29
33
|
exports.SwCompiler = SwCompiler;
|
|
30
34
|
/** 运行时数据 */
|
|
31
35
|
class RuntimeData {
|
|
32
|
-
constructor() {
|
|
36
|
+
constructor(compilationData) {
|
|
33
37
|
/** 控制插入顺序 */
|
|
34
38
|
this.insertOrder = [
|
|
35
39
|
'crossEnv', 'crossDep', 'runtimeDep', 'runtimeCore', 'runtimeEvent'
|
|
36
40
|
];
|
|
37
|
-
/** 运行时环境变量 */
|
|
38
|
-
this.crossEnv = new CrossEnv_1.CrossEnv();
|
|
39
41
|
/** 运行时工具函数 */
|
|
40
42
|
this.runtimeDep = new RuntimeDepCode_1.RuntimeDepCode();
|
|
41
43
|
/** 运行时核心功能函数 */
|
|
42
44
|
this.runtimeCore = new RuntimeCoreCode_1.RuntimeCoreCode();
|
|
43
45
|
/** 运行时事件注册 */
|
|
44
46
|
this.runtimeEvent = new RuntimeEventCode_1.RuntimeEventCode();
|
|
45
|
-
/**
|
|
46
|
-
this.
|
|
47
|
+
/** DOM 相关设置 */
|
|
48
|
+
this.domConfig = new DomCode_1.DomCode();
|
|
49
|
+
/** 追踪调用链 */
|
|
50
|
+
this.debugCallChain = new CallChainRecorder_1.CallChainRecorder();
|
|
51
|
+
this.crossDep = compilationData.crossDep;
|
|
52
|
+
this.crossEnv = compilationData.crossEnv;
|
|
47
53
|
}
|
|
48
54
|
getDatabase(key) {
|
|
49
55
|
if (!(key in this))
|
|
50
|
-
throw {
|
|
51
|
-
code: untils_1.exceptionNames.invalidKey,
|
|
52
|
-
message: `传入的 key [${key}] 不在当前对象中存在`
|
|
53
|
-
};
|
|
56
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.invalidKey, `传入的 key [${key}] 不在当前对象中存在`);
|
|
54
57
|
if (key == 'insertOrder')
|
|
55
|
-
throw {
|
|
56
|
-
code: untils_1.exceptionNames.invalidKey,
|
|
57
|
-
message: `传入的 key [${key}] 非法`
|
|
58
|
-
};
|
|
58
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.invalidKey, `传入的 key [${key}] 非法`);
|
|
59
59
|
// @ts-ignore
|
|
60
60
|
return this[key];
|
|
61
61
|
}
|
|
62
|
+
initCompilation(compilation) {
|
|
63
|
+
for (let key of this.insertOrder) {
|
|
64
|
+
this.getDatabase(key).initRuntimeAndCompilation(this, compilation);
|
|
65
|
+
}
|
|
66
|
+
this.domConfig.initRuntimeAndCompilation(this, compilation);
|
|
67
|
+
}
|
|
68
|
+
freezeAll() {
|
|
69
|
+
this.insertOrder.forEach(it => this.getDatabase(it).freeze());
|
|
70
|
+
this.domConfig.freeze();
|
|
71
|
+
}
|
|
62
72
|
}
|
|
63
73
|
exports.RuntimeData = RuntimeData;
|
|
74
|
+
/** 编译时数据 */
|
|
75
|
+
class CompilationData {
|
|
76
|
+
constructor() {
|
|
77
|
+
this.compilationEnv = new CompilationEnv_1.CompilationEnv();
|
|
78
|
+
this.crossDep = new CrossDepCode_1.CrossDepCode();
|
|
79
|
+
this.crossEnv = new CrossEnv_1.CrossEnv();
|
|
80
|
+
this.fileParser = new CompilationFileParser_1.CompilationFileParser();
|
|
81
|
+
}
|
|
82
|
+
initRuntime(runtime) {
|
|
83
|
+
for (let key in this) {
|
|
84
|
+
const value = this[key];
|
|
85
|
+
if (value instanceof KeyValueDatabase_1.KeyValueDatabase) {
|
|
86
|
+
value.initRuntimeAndCompilation(runtime, this);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
freezeAll() {
|
|
91
|
+
for (let key in this) {
|
|
92
|
+
const value = this[key];
|
|
93
|
+
if (value instanceof KeyValueDatabase_1.KeyValueDatabase) {
|
|
94
|
+
value.freeze();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.CompilationData = CompilationData;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SwppCliConfig {
|
|
2
|
+
/** 网站根目录 */
|
|
3
|
+
webRoot: string;
|
|
4
|
+
/** 配置文件所在的相对路径(越靠前优先级越高) */
|
|
5
|
+
configFiles: string[];
|
|
6
|
+
/** dom js 的相对路径(以 `/` 开头 `.js` 结尾) */
|
|
7
|
+
domJsPath?: string;
|
|
8
|
+
/** 需要被排除的 html 文件名,正则表达式,区分大小写 */
|
|
9
|
+
excludes?: string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function initCommand(): Promise<void>;
|
package/dist/swpp/cli.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.initCommand = initCommand;
|
|
30
|
+
const commander_1 = require("commander");
|
|
31
|
+
const fs_1 = __importDefault(require("fs"));
|
|
32
|
+
const path_1 = __importDefault(require("path"));
|
|
33
|
+
const index_1 = require("../index");
|
|
34
|
+
const ConfigLoader_1 = require("./config/ConfigLoader");
|
|
35
|
+
const ResourcesScanner_1 = require("./ResourcesScanner");
|
|
36
|
+
const SwCompiler_1 = require("./SwCompiler");
|
|
37
|
+
const untils_1 = require("./untils");
|
|
38
|
+
const HTMLParser = __importStar(require("node-html-parser"));
|
|
39
|
+
async function initCommand() {
|
|
40
|
+
commander_1.program.version(index_1.swppVersion, '-v, --version', '查看当前 swpp backends 的版本号');
|
|
41
|
+
commander_1.program.addHelpText('after', ' 每行显示一条指令信息,指令后跟方括号表示可选参数,尖括号表示必填参数');
|
|
42
|
+
commander_1.program.option('-b, --build [config: string]', '构建网站的 sw 与版本文件');
|
|
43
|
+
commander_1.program.parse();
|
|
44
|
+
if (commander_1.program.opts().build) {
|
|
45
|
+
const build = commander_1.program.opts().build;
|
|
46
|
+
await runBuild(typeof build === 'string' ? build : undefined);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function runBuild(cliJsonPath = './swpp.cli.json') {
|
|
50
|
+
if (!cliJsonPath.endsWith('.json')) {
|
|
51
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.unsupportedFileType, 'CLI 配置文件仅支持 JSON 格式', { yourPath: cliJsonPath });
|
|
52
|
+
}
|
|
53
|
+
const cliConfig = JSON.parse(await untils_1.utils.readFileUtf8(cliJsonPath));
|
|
54
|
+
if (!cliConfig.webRoot || !fs_1.default.existsSync(cliConfig.webRoot) || !fs_1.default.statSync(cliConfig.webRoot).isDirectory()) {
|
|
55
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.error, 'CLI 配置文件中缺少 webRoot 配置项或传入了一个非文件夹路径', { webRoot: cliConfig.webRoot });
|
|
56
|
+
}
|
|
57
|
+
if (cliConfig.domJsPath && (!cliConfig.domJsPath.startsWith('/') || !cliConfig.domJsPath.endsWith('.js'))) {
|
|
58
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.invalidVarName, 'CLI 配置文件中的 domJsPath 应当传入一个 `/` 开头 `.js` 结尾的字符串');
|
|
59
|
+
}
|
|
60
|
+
if (!cliConfig.configFiles || cliConfig.configFiles.length === 0) {
|
|
61
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.nullPoint, 'CLI 配置文件中缺少 configFiles 配置项或数组长度为 0', { configFiles: cliConfig.configFiles });
|
|
62
|
+
}
|
|
63
|
+
cliConfig.configFiles.forEach(path => {
|
|
64
|
+
if (!fs_1.default.existsSync(path)) {
|
|
65
|
+
throw new untils_1.RuntimeException(untils_1.exceptionNames.notFound, 'CLI 配置文件的 configFiles 配置项中某项目录不存在', { path });
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
// 加载配置项
|
|
69
|
+
const loader = new ConfigLoader_1.ConfigLoader();
|
|
70
|
+
for (let item of cliConfig.configFiles) {
|
|
71
|
+
const path = path_1.default.isAbsolute(item) ? item : path_1.default.resolve(item);
|
|
72
|
+
await loader.load(path);
|
|
73
|
+
}
|
|
74
|
+
const { runtime, compilation } = loader.generate();
|
|
75
|
+
// 扫描目录
|
|
76
|
+
const jsonInfo = compilation.compilationEnv.read('SWPP_JSON_FILE');
|
|
77
|
+
const scanner = new ResourcesScanner_1.ResourcesScanner(compilation);
|
|
78
|
+
const newTracker = await scanner.scanLocalFile(cliConfig.webRoot);
|
|
79
|
+
const updateJsonBuilder = await newTracker.diff();
|
|
80
|
+
const updateJson = await updateJsonBuilder.buildJson();
|
|
81
|
+
fs_1.default.mkdirSync(path_1.default.join(cliConfig.webRoot, jsonInfo.swppPath), { recursive: true });
|
|
82
|
+
// 生成各项文件
|
|
83
|
+
await Promise.all([
|
|
84
|
+
// 生成 json
|
|
85
|
+
untils_1.utils.writeFile(path_1.default.join(cliConfig.webRoot, jsonInfo.swppPath, jsonInfo.trackerPath), newTracker.json()),
|
|
86
|
+
untils_1.utils.writeFile(path_1.default.join(cliConfig.webRoot, jsonInfo.swppPath, jsonInfo.versionPath), JSON.stringify(updateJson)),
|
|
87
|
+
// 生成 sw js
|
|
88
|
+
untils_1.utils.writeFile(path_1.default.join(cliConfig.webRoot, compilation.compilationEnv.read('SERVICE_WORKER') + '.js'), new SwCompiler_1.SwCompiler().buildSwCode(runtime)),
|
|
89
|
+
// 生成 dom js
|
|
90
|
+
untils_1.utils.writeFile(path_1.default.join(cliConfig.webRoot, cliConfig.domJsPath ?? '/sw-dom.js'), runtime.domConfig.buildJsSource())
|
|
91
|
+
]);
|
|
92
|
+
const regexes = cliConfig.excludes?.map?.(it => new RegExp(it)) ?? [];
|
|
93
|
+
const swRegistry = `<script>(${runtime.domConfig.read('registry')})()</script>`;
|
|
94
|
+
const domJsScript = `<script defer src="${cliConfig.domJsPath ?? '/sw-dom.js'}"></script>`;
|
|
95
|
+
// 修改 html
|
|
96
|
+
await (0, ResourcesScanner_1.traverseDirectory)(cliConfig.webRoot, async (file) => {
|
|
97
|
+
if (!file.endsWith('.html') || regexes.some(regex => regex.test(file)))
|
|
98
|
+
return;
|
|
99
|
+
const html = await readHtml(compilation, file);
|
|
100
|
+
const head = html.querySelector('head');
|
|
101
|
+
head.insertAdjacentHTML('afterbegin', swRegistry);
|
|
102
|
+
head.insertAdjacentHTML('beforeend', domJsScript);
|
|
103
|
+
await untils_1.utils.writeFile(file, html.outerHTML);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async function readHtml(compilation, path) {
|
|
107
|
+
const content = await compilation.compilationEnv.read('readLocalFile')(path);
|
|
108
|
+
return HTMLParser.parse(content);
|
|
109
|
+
}
|