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.
Files changed (61) hide show
  1. package/{types → dist}/index.d.ts +6 -5
  2. package/dist/index.js +29 -14
  3. package/{types → dist}/swpp/FileParser.d.ts +11 -6
  4. package/dist/swpp/FileParser.js +27 -13
  5. package/{types → dist}/swpp/JsonBuilder.d.ts +1 -3
  6. package/dist/swpp/JsonBuilder.js +3 -7
  7. package/dist/swpp/NetworkFileHandler.js +3 -3
  8. package/{types → dist}/swpp/ResourcesScanner.d.ts +18 -14
  9. package/dist/swpp/ResourcesScanner.js +100 -73
  10. package/{types → dist}/swpp/SwCompiler.d.ts +21 -8
  11. package/dist/swpp/SwCompiler.js +51 -15
  12. package/dist/swpp/cli.d.ts +11 -0
  13. package/dist/swpp/cli.js +109 -0
  14. package/{types → dist}/swpp/config/ConfigCluster.d.ts +105 -63
  15. package/dist/swpp/config/ConfigCluster.js +67 -48
  16. package/dist/swpp/config/ConfigLoader.d.ts +79 -0
  17. package/dist/swpp/config/ConfigLoader.js +192 -87
  18. package/dist/swpp/config/SpecialConfig.d.ts +38 -0
  19. package/dist/swpp/config/SpecialConfig.js +70 -0
  20. package/{types → dist}/swpp/database/CompilationEnv.d.ts +16 -14
  21. package/dist/swpp/database/CompilationEnv.js +41 -211
  22. package/dist/swpp/database/CompilationFileParser.d.ts +81 -0
  23. package/dist/swpp/database/CompilationFileParser.js +269 -0
  24. package/{types → dist}/swpp/database/CrossDepCode.d.ts +4 -0
  25. package/dist/swpp/database/CrossDepCode.js +27 -1
  26. package/dist/swpp/database/CrossEnv.js +19 -2
  27. package/{types → dist}/swpp/database/DomCode.d.ts +4 -1
  28. package/dist/swpp/database/DomCode.js +34 -5
  29. package/dist/swpp/database/KeyValueDatabase.d.ts +72 -0
  30. package/dist/swpp/database/KeyValueDatabase.js +120 -27
  31. package/{types → dist}/swpp/database/RuntimeCoreCode.d.ts +2 -1
  32. package/dist/swpp/database/RuntimeCoreCode.js +6 -5
  33. package/{types → dist}/swpp/database/RuntimeDepCode.d.ts +8 -14
  34. package/dist/swpp/database/RuntimeDepCode.js +30 -50
  35. package/{types → dist}/swpp/database/RuntimeEventCode.d.ts +4 -0
  36. package/dist/swpp/database/RuntimeEventCode.js +14 -1
  37. package/{types → dist}/swpp/database/RuntimeKeyValueDatabase.d.ts +1 -1
  38. package/dist/swpp/database/RuntimeKeyValueDatabase.js +2 -2
  39. package/dist/swpp/debug/CallChainRecorder.d.ts +5 -0
  40. package/dist/swpp/debug/CallChainRecorder.js +29 -0
  41. package/{types → dist}/swpp/untils.d.ts +30 -18
  42. package/dist/swpp/untils.js +74 -53
  43. package/dist/test.js +1 -0
  44. package/package.json +8 -5
  45. package/types/DomBuilder.d.ts +0 -6
  46. package/types/FileAnalyzer.d.ts +0 -96
  47. package/types/ServiceWorkerBuilder.d.ts +0 -7
  48. package/types/SwppConfig.d.ts +0 -139
  49. package/types/SwppRules.d.ts +0 -117
  50. package/types/UpdateJsonBuilder.d.ts +0 -44
  51. package/types/Utils.d.ts +0 -43
  52. package/types/Variant.d.ts +0 -58
  53. package/types/VersionAnalyzer.d.ts +0 -27
  54. package/types/browser/ServiceWorkerRuntimeTypes.d.ts +0 -18
  55. package/types/swpp/RuntimeEnv.d.ts +0 -39
  56. package/types/swpp/SwCodeInject.d.ts +0 -17
  57. package/types/swpp/config/ConfigLoader.d.ts +0 -53
  58. package/types/swpp/database/KeyValueDatabase.d.ts +0 -53
  59. package/types/swpp/database/RuntimeEnv.d.ts +0 -5
  60. /package/{types → dist}/swpp/NetworkFileHandler.d.ts +0 -0
  61. /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.compilationEnv.read('FILE_PARSER');
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
- const stream = fs_1.default.createReadStream(file);
52
- const hash = crypto.createHash('md5');
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
- if (matchCacheRule.runOnNode(localUrl)) {
65
+ const isCached = !!matchCacheRule.runOnNode(localUrl);
66
+ if (isCached) {
56
67
  tracker.addUrl(localUrl.href);
57
68
  }
58
- const type = path_1.default.extname(file);
59
- if (register.containsType(type)) {
60
- const set = await register.parserLocalFile(file);
61
- set.forEach(it => urls.add(it));
62
- }
63
- await new Promise((resolve, reject) => {
64
- stream.on('end', () => {
65
- tracker.update(file, hash.digest('hex'));
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.compilationEnv.read('FILE_PARSER');
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
- tracker.update(value.file, value.mark);
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
- this.map.set(uri, value);
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
- return this.map.get(this.normalizeUri(uri).href);
144
- }
145
- /** 设置一个 header */
146
- putHeader(key, value) {
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 domain = this.compilation.compilationEnv.read('DOMAIN_HOST');
158
- return new URL(uri, `https://${domain}`);
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(oldTracker) {
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.map.forEach((value, key) => {
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.tracker);
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 = new URL(jsonInfo.trackerPath, `https://${domain}`);
255
- const fetcher = compilation.compilationEnv.read('FETCH_NETWORK_FILE');
256
- const isNotFound = compilation.compilationEnv.read('IS_NOT_FOUND');
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.utils.printError('SCANNER', e);
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
- readonly insertOrder: (Exclude<keyof RuntimeData, 'insertOrder'> | string)[];
21
+ insertOrder: (Exclude<keyof RuntimeData, 'insertOrder' | 'domConfig'> | string)[];
19
22
  /** 运行时环境变量 */
20
- readonly crossEnv: CrossEnv;
23
+ crossEnv: CrossEnv;
21
24
  /** 运行时工具函数 */
22
- readonly runtimeDep: RuntimeDepCode;
25
+ runtimeDep: RuntimeDepCode;
23
26
  /** 运行时核心功能函数 */
24
- readonly runtimeCore: RuntimeCoreCode;
27
+ runtimeCore: RuntimeCoreCode;
25
28
  /** 运行时事件注册 */
26
- readonly runtimeEvent: RuntimeEventCode;
29
+ runtimeEvent: RuntimeEventCode;
27
30
  /** 运行时/编译时工具函数 */
28
- readonly crossDep: CrossDepCode;
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 interface CompilationData {
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 {
@@ -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.crossDep = new CrossDepCode_1.CrossDepCode();
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>;
@@ -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
+ }