swpp-backends 3.0.0-alpha.3 → 3.0.0-alpha.310

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 (58) hide show
  1. package/{types → dist}/index.d.ts +5 -4
  2. package/dist/index.js +25 -17
  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 +11 -14
  9. package/dist/swpp/ResourcesScanner.js +94 -70
  10. package/{types → dist}/swpp/SwCompiler.d.ts +21 -8
  11. package/dist/swpp/SwCompiler.js +49 -15
  12. package/{types → dist}/swpp/config/ConfigCluster.d.ts +98 -63
  13. package/dist/swpp/config/ConfigCluster.js +60 -48
  14. package/dist/swpp/config/ConfigLoader.d.ts +78 -0
  15. package/dist/swpp/config/ConfigLoader.js +180 -89
  16. package/dist/swpp/config/SpecialConfig.d.ts +29 -0
  17. package/dist/swpp/config/SpecialConfig.js +53 -0
  18. package/{types → dist}/swpp/database/CompilationEnv.d.ts +19 -16
  19. package/dist/swpp/database/CompilationEnv.js +41 -197
  20. package/dist/swpp/database/CompilationFileParser.d.ts +81 -0
  21. package/dist/swpp/database/CompilationFileParser.js +267 -0
  22. package/{types → dist}/swpp/database/CrossDepCode.d.ts +4 -0
  23. package/dist/swpp/database/CrossDepCode.js +27 -1
  24. package/dist/swpp/database/CrossEnv.js +19 -2
  25. package/{types → dist}/swpp/database/DomCode.d.ts +4 -1
  26. package/dist/swpp/database/DomCode.js +34 -5
  27. package/dist/swpp/database/KeyValueDatabase.d.ts +72 -0
  28. package/dist/swpp/database/KeyValueDatabase.js +122 -27
  29. package/{types → dist}/swpp/database/RuntimeCoreCode.d.ts +2 -1
  30. package/dist/swpp/database/RuntimeCoreCode.js +6 -5
  31. package/{types → dist}/swpp/database/RuntimeDepCode.d.ts +8 -14
  32. package/dist/swpp/database/RuntimeDepCode.js +30 -50
  33. package/{types → dist}/swpp/database/RuntimeEventCode.d.ts +4 -0
  34. package/dist/swpp/database/RuntimeEventCode.js +8 -1
  35. package/{types → dist}/swpp/database/RuntimeKeyValueDatabase.d.ts +1 -1
  36. package/dist/swpp/database/RuntimeKeyValueDatabase.js +2 -2
  37. package/dist/swpp/debug/CallChainRecorder.d.ts +5 -0
  38. package/dist/swpp/debug/CallChainRecorder.js +29 -0
  39. package/{types → dist}/swpp/untils.d.ts +25 -18
  40. package/dist/swpp/untils.js +47 -53
  41. package/package.json +2 -2
  42. package/types/DomBuilder.d.ts +0 -6
  43. package/types/FileAnalyzer.d.ts +0 -96
  44. package/types/ServiceWorkerBuilder.d.ts +0 -7
  45. package/types/SwppConfig.d.ts +0 -139
  46. package/types/SwppRules.d.ts +0 -117
  47. package/types/UpdateJsonBuilder.d.ts +0 -44
  48. package/types/Utils.d.ts +0 -43
  49. package/types/Variant.d.ts +0 -58
  50. package/types/VersionAnalyzer.d.ts +0 -27
  51. package/types/browser/ServiceWorkerRuntimeTypes.d.ts +0 -18
  52. package/types/swpp/RuntimeEnv.d.ts +0 -39
  53. package/types/swpp/SwCodeInject.d.ts +0 -17
  54. package/types/swpp/config/ConfigLoader.d.ts +0 -53
  55. package/types/swpp/database/KeyValueDatabase.d.ts +0 -53
  56. package/types/swpp/database/RuntimeEnv.d.ts +0 -5
  57. /package/{types → dist}/swpp/NetworkFileHandler.d.ts +0 -0
  58. /package/{types → dist}/swpp/database/CrossEnv.d.ts +0 -0
@@ -1,45 +1,21 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.AllowNotFoundEnum = exports.CompilationEnv = void 0;
30
7
  const fs_1 = __importDefault(require("fs"));
31
- const FileParser_1 = require("../FileParser");
32
8
  const NetworkFileHandler_1 = require("../NetworkFileHandler");
9
+ const ResourcesScanner_1 = require("../ResourcesScanner");
33
10
  const untils_1 = require("../untils");
34
11
  const KeyValueDatabase_1 = require("./KeyValueDatabase");
35
- const HTMLParser = __importStar(require("fast-html-parser"));
36
12
  /**
37
13
  * 仅在编译期生效的配置项
38
14
  */
39
15
  class CompilationEnv extends KeyValueDatabase_1.KeyValueDatabase {
40
- constructor(crossEnv, crossCode) {
41
- super();
42
- this.lazyInit(buildCommon(this, crossEnv, crossCode));
16
+ constructor() {
17
+ super('CompilationEnv');
18
+ this.lazyInit(buildCommon(this));
43
19
  }
44
20
  }
45
21
  exports.CompilationEnv = CompilationEnv;
@@ -53,32 +29,36 @@ var AllowNotFoundEnum;
53
29
  /** 拒绝任意形式的 404 */
54
30
  AllowNotFoundEnum[AllowNotFoundEnum["REJECT_ALL"] = 2] = "REJECT_ALL";
55
31
  })(AllowNotFoundEnum || (exports.AllowNotFoundEnum = AllowNotFoundEnum = {}));
56
- function buildCommon(_env, crossEnv, crossCode) {
32
+ function buildCommon(_env) {
57
33
  const env = _env;
58
34
  return {
59
35
  DOMAIN_HOST: (0, KeyValueDatabase_1.buildEnv)({
60
- default: 'www.example.com',
36
+ default: new URL("https://www.example.com"),
61
37
  checker(value) {
62
- if (value === 'www.example.com')
63
- return {
64
- value, message: '应当手动设置一个域名而非使用默认域名'
65
- };
66
- if (value.includes('/'))
38
+ if (value.host === 'www.example.com')
67
39
  return {
68
- value, message: '传入的域名不应当包含“/”字符'
40
+ value, message: 'DOMAIN_HOST 必须手动设置而非使用默认值'
69
41
  };
70
- if (value.includes('#') || value.includes('?'))
42
+ if (value.hash || value.search)
71
43
  return {
72
44
  value, message: '传入的域名不应当包含查询参数和片段标识符'
73
45
  };
74
- if (!value.includes('.') || !/^[a-zA-Z0-9.-]$/.test(value))
46
+ if (value.protocol !== 'https:' && value.host !== '127.0.0.1' && value.host !== 'localhost')
75
47
  return {
76
- value, message: '传入的域名不是一个合法的域名'
48
+ value, message: '传入的 URL 必须使用 https 协议'
77
49
  };
78
50
  return false;
79
51
  }
80
52
  }),
81
- /** HTML 数量限制 */
53
+ SERVICE_WORKER: (0, KeyValueDatabase_1.buildEnv)({
54
+ default: 'sw',
55
+ checker(value) {
56
+ return value.endsWith('.js') ? {
57
+ value, message: 'SW 文件名不需要包含拓展名'
58
+ } : false;
59
+ }
60
+ }),
61
+ /** HTML 数量限制,设置为 <= 0 表示不限制 */
82
62
  JSON_HTML_LIMIT: (0, KeyValueDatabase_1.buildEnv)({
83
63
  default: 0
84
64
  }),
@@ -96,16 +76,20 @@ function buildCommon(_env, crossEnv, crossCode) {
96
76
  return false;
97
77
  }
98
78
  }),
99
- /** 获取已经上线的版本文件 */
100
- VERSION_FILE: (0, KeyValueDatabase_1.buildEnv)({
79
+ /** swpp 的 JSON 文件的基本信息 */
80
+ SWPP_JSON_FILE: (0, KeyValueDatabase_1.buildEnv)({
101
81
  default: {
102
- path: 'update.json',
103
- async fetcher() {
104
- const host = env.read('DOMAIN_HOST');
105
- const fetcher = env.read('FETCH_NETWORK_FILE');
106
- const isNotFound = env.read('IS_NOT_FOUND');
82
+ swppPath: 'swpp',
83
+ trackerPath: 'tracker.json',
84
+ versionPath: 'update.json',
85
+ async fetchVersionFile() {
86
+ const baseUrl = env.read('DOMAIN_HOST');
87
+ const fetcher = env.read('NETWORK_FILE_FETCHER');
88
+ const isNotFound = env.read('isNotFound');
107
89
  try {
108
- const response = await fetcher.fetch(`https://${host}/${this.path}`);
90
+ const swppPath = (0, KeyValueDatabase_1.readThisValue)(this, 'swppPath');
91
+ const versionPath = (0, KeyValueDatabase_1.readThisValue)(this, 'versionPath');
92
+ const response = await fetcher.fetch(untils_1.utils.splicingUrl(baseUrl, swppPath, versionPath));
109
93
  if (!isNotFound.response(response)) {
110
94
  const json = await response.json();
111
95
  return json;
@@ -116,11 +100,14 @@ function buildCommon(_env, crossEnv, crossCode) {
116
100
  throw e;
117
101
  }
118
102
  return { global: 0, info: [] };
103
+ },
104
+ async fetchTrackerFile(compilation) {
105
+ return await ResourcesScanner_1.FileUpdateTracker.parserJsonFromNetwork(compilation);
119
106
  }
120
107
  }
121
108
  }),
122
109
  /** 读取一个本地文件 */
123
- LOCAL_FILE_READER: (0, KeyValueDatabase_1.buildEnv)({
110
+ readLocalFile: (0, KeyValueDatabase_1.buildEnv)({
124
111
  default: (path) => {
125
112
  return new Promise((resolve, reject) => {
126
113
  fs_1.default.readFile(path, 'utf8', (err, data) => {
@@ -133,11 +120,11 @@ function buildCommon(_env, crossEnv, crossCode) {
133
120
  }
134
121
  }),
135
122
  /** 拉取网络文件 */
136
- FETCH_NETWORK_FILE: (0, KeyValueDatabase_1.buildEnv)({
123
+ NETWORK_FILE_FETCHER: (0, KeyValueDatabase_1.buildEnv)({
137
124
  default: new NetworkFileHandler_1.FiniteConcurrencyFetcher()
138
125
  }),
139
126
  /** 判断文件是否是 404 */
140
- IS_NOT_FOUND: (0, KeyValueDatabase_1.buildEnv)({
127
+ isNotFound: (0, KeyValueDatabase_1.buildEnv)({
141
128
  default: {
142
129
  response: (response) => response.status == 404,
143
130
  error: (err) => err?.cause?.code === 'ENOTFOUND'
@@ -157,152 +144,9 @@ function buildCommon(_env, crossEnv, crossCode) {
157
144
  }
158
145
  }
159
146
  }),
160
- /** 文件解析器 */
161
- FILE_PARSER: (0, KeyValueDatabase_1.buildEnv)({
162
- default: createRegister(env, crossEnv, crossCode)
147
+ /** 检查一个链接是否是稳定的(也就是 URL 不变其返回的结果永远不变) */
148
+ isStable: (0, KeyValueDatabase_1.buildEnv)({
149
+ default: (_url) => false
163
150
  })
164
151
  };
165
152
  }
166
- function createRegister(env, crossEnv, crossCode) {
167
- const register = new FileParser_1.FileParserRegistry({
168
- compilationEnv: env,
169
- crossEnv,
170
- crossDep: crossCode
171
- });
172
- register.registry('html', (0, FileParser_1.buildFileParser)({
173
- readFromLocal(compilation, path) {
174
- return compilation.compilationEnv.read('LOCAL_FILE_READER')(path);
175
- },
176
- readFromNetwork(_, response) {
177
- return response.text();
178
- },
179
- async extractUrls(compilation, content) {
180
- const host = compilation.compilationEnv.read("DOMAIN_HOST");
181
- const html = HTMLParser.parse(content, {
182
- script: true, style: true
183
- });
184
- const queue = [html];
185
- const result = new Set();
186
- async function handleItem(item) {
187
- queue.push(...item.childNodes);
188
- switch (item.tagName.toLowerCase()) {
189
- case 'script': {
190
- if (!register.containsType('script'))
191
- break;
192
- const src = item.attributes.src;
193
- if (src) {
194
- const son = await register.parserContent('script', item.rawText);
195
- son.forEach(it => result.add(it));
196
- }
197
- else if (!untils_1.utils.isSameHost(src, host)) {
198
- result.add(src);
199
- }
200
- break;
201
- }
202
- case 'link': {
203
- if (item.attributes.rel !== 'preconnect') {
204
- const href = item.attributes.href;
205
- if (!untils_1.utils.isSameHost(href, host))
206
- result.add(href);
207
- }
208
- break;
209
- }
210
- case 'img':
211
- case 'source':
212
- case 'iframe':
213
- case 'embed': {
214
- const src = item.attributes.src;
215
- if (src && !untils_1.utils.isSameHost(src, host)) {
216
- result.add(src);
217
- }
218
- break;
219
- }
220
- case 'object': {
221
- const data = item.attributes.data;
222
- if (data && !untils_1.utils.isSameHost(data, host)) {
223
- result.add(data);
224
- }
225
- break;
226
- }
227
- case 'style': {
228
- const son = await register.parserContent('css', item.rawText);
229
- son.forEach(it => result.add(it));
230
- break;
231
- }
232
- }
233
- }
234
- do {
235
- const item = queue.pop();
236
- try {
237
- await handleItem(item);
238
- }
239
- catch (e) {
240
- untils_1.utils.printError('PARSER HTML', e);
241
- }
242
- } while (queue.length > 0);
243
- return result;
244
- }
245
- }));
246
- register.registry('css', (0, FileParser_1.buildFileParser)({
247
- readFromLocal(compilation, path) {
248
- return compilation.compilationEnv.read('LOCAL_FILE_READER')(path);
249
- },
250
- readFromNetwork(_, response) {
251
- return response.text();
252
- },
253
- async extractUrls(compilation, content) {
254
- const host = compilation.compilationEnv.read('DOMAIN_HOST');
255
- const urls = new Set();
256
- /** 从指定位置开始查询注释 */
257
- const findComment = (tag, start) => {
258
- for (let i = start; i < content.length;) {
259
- const item = content[i];
260
- switch (item) {
261
- case tag[0]:
262
- if (content[i + 1] === tag[1])
263
- return i;
264
- ++i;
265
- break;
266
- case '"':
267
- case '\'':
268
- while (true) {
269
- const index = content.indexOf(item, i + 1);
270
- if (index < 0)
271
- return -1;
272
- i = index + 1;
273
- if (content[index - 1] !== '\\')
274
- break;
275
- }
276
- break;
277
- default:
278
- ++i;
279
- break;
280
- }
281
- }
282
- return -1;
283
- };
284
- for (let i = 0; i < content.length;) {
285
- const left = findComment('/*', i);
286
- let sub;
287
- if (left === -1) {
288
- sub = content.substring(i);
289
- i = Number.MAX_VALUE;
290
- }
291
- else {
292
- sub = content.substring(i, left);
293
- const right = findComment('*/', left + 2);
294
- if (right === -1)
295
- i = Number.MAX_VALUE;
296
- else
297
- i = right + 2;
298
- }
299
- sub.match(/(url\(.*?\))|(@import\s+['"].*?['"])|((https?:)?\/\/[^\s/$.?#].\S*)/g)
300
- ?.map(it => it.replace(/(^url\(\s*(['"]?))|((['"]?\s*)\)$)|(^@import\s+['"])|(['"]$)/g, ''))
301
- ?.filter(it => !untils_1.utils.isSameHost(it, host))
302
- ?.forEach(it => urls.add(it));
303
- }
304
- return urls;
305
- }
306
- }));
307
- return register;
308
- }
@@ -0,0 +1,81 @@
1
+ import * as crypto from 'node:crypto';
2
+ import { CompilationData } from '../SwCompiler';
3
+ import { KeyValueDatabase } from './KeyValueDatabase';
4
+ export type COMMON_TYPE_COMP_FP = ReturnType<typeof buildCommon>;
5
+ export declare class CompilationFileParser extends KeyValueDatabase<FileParser<crypto.BinaryLike>, COMMON_TYPE_COMP_FP> {
6
+ constructor();
7
+ /** 解析本地文件 */
8
+ parserLocalFile(path: string, cb?: (content: crypto.BinaryLike) => void, force?: boolean): Promise<Set<string>>;
9
+ /** 解析网络文件 */
10
+ parserNetworkFile(response: Response, callback?: (content: crypto.BinaryLike) => Promise<any> | any): Promise<Set<string>>;
11
+ /**
12
+ * 解析指定的 URL
13
+ * @param url 链接
14
+ * @param isCached 该链接指向的资源是否需要缓存
15
+ */
16
+ parserUrlFile(url: string, isCached: boolean): Promise<FileMark>;
17
+ /** 解析指定类型的文件内容 */
18
+ parserContent(type: string, content: string): Promise<Set<string>>;
19
+ }
20
+ declare function buildCommon($this: any): {
21
+ readonly html: {
22
+ readonly default: {
23
+ readonly readFromLocal: (compilation: CompilationData, path: string) => Promise<string>;
24
+ readonly readFromNetwork: (_: CompilationData, response: Response) => Promise<string>;
25
+ readonly extractUrls: (compilation: CompilationData, content: string) => Promise<Set<string>>;
26
+ };
27
+ };
28
+ readonly css: {
29
+ readonly default: {
30
+ readonly readFromLocal: (compilation: CompilationData, path: string) => Promise<string>;
31
+ readonly readFromNetwork: (_: CompilationData, response: Response) => Promise<string>;
32
+ readonly extractUrls: (compilation: CompilationData, content: string) => Promise<Set<string>>;
33
+ };
34
+ };
35
+ };
36
+ /**
37
+ * 文件处理器
38
+ *
39
+ * 用于处理指定类型的文件,从中提取文件的 mark 和外部链接列表
40
+ */
41
+ export interface FileParser<T extends crypto.BinaryLike> {
42
+ /**
43
+ * 从本地读取一个文件
44
+ * @param compilation 编译期依赖
45
+ * @param path 文件路径
46
+ */
47
+ readFromLocal(compilation: CompilationData, path: string): Promise<T>;
48
+ /**
49
+ * 从网络读取一个文件
50
+ * @param compilation 编译期依赖
51
+ * @param response 拉取的结果
52
+ */
53
+ readFromNetwork(compilation: CompilationData, response: Response): Promise<T>;
54
+ /**
55
+ * 从文件内容中提取 URL
56
+ * @param compilation 编译期依赖
57
+ * @param content 文件内容
58
+ */
59
+ extractUrls(compilation: CompilationData, content: T): Promise<Set<string>>;
60
+ /**
61
+ * 计算一个链接对应的资源的标识符及其内部资源
62
+ * @return 返回 undefined/null 表示使用缺省逻辑
63
+ */
64
+ calcUrl?(url: string): Promise<Omit<FileMark, 'file'> | undefined | null>;
65
+ }
66
+ /**
67
+ * 存储文件标识信息
68
+ */
69
+ export interface FileMark {
70
+ /** URL */
71
+ file: string;
72
+ /**
73
+ * 文件标识符或子文件列表
74
+ *
75
+ * 如果链接为稳定链接,则为子文件列表,否则为文件标识符
76
+ */
77
+ mark: string | Set<string>;
78
+ /** URL 列表 */
79
+ urls: Set<string>;
80
+ }
81
+ export {};
@@ -0,0 +1,267 @@
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.CompilationFileParser = void 0;
30
+ const HTMLParser = __importStar(require("fast-html-parser"));
31
+ const path_1 = __importDefault(require("path"));
32
+ const untils_1 = require("../untils");
33
+ const KeyValueDatabase_1 = require("./KeyValueDatabase");
34
+ class CompilationFileParser extends KeyValueDatabase_1.KeyValueDatabase {
35
+ constructor() {
36
+ super('CompilationFileParser');
37
+ this.lazyInit(buildCommon(this));
38
+ }
39
+ /** 解析本地文件 */
40
+ async parserLocalFile(path, cb, force) {
41
+ const extname = path_1.default.extname(path).substring(1);
42
+ if (this.hasKey(extname)) {
43
+ const parser = this.read(extname);
44
+ const content = await parser.readFromLocal(this.compilation, path);
45
+ cb?.(content);
46
+ return await parser.extractUrls(this.compilation, content);
47
+ }
48
+ else {
49
+ if (force && cb) {
50
+ const reader = this.compilation.compilationEnv.read('readLocalFile');
51
+ const content = await reader(path);
52
+ cb(content);
53
+ }
54
+ return new Set();
55
+ }
56
+ }
57
+ /** 解析网络文件 */
58
+ async parserNetworkFile(response, callback) {
59
+ const fileHandler = this.compilation.compilationEnv.read('NETWORK_FILE_FETCHER');
60
+ const contentType = fileHandler.getUrlContentType(response.url, response);
61
+ if (this.hasKey(contentType)) {
62
+ const parser = this.read(contentType);
63
+ const content = await parser.readFromNetwork(this.compilation, response);
64
+ if (callback)
65
+ await callback(content);
66
+ return await parser.extractUrls(this.compilation, content);
67
+ }
68
+ else {
69
+ if (callback) {
70
+ const blob = await response.blob();
71
+ const array = await blob.stream().getReader().read();
72
+ callback(array.value);
73
+ }
74
+ return new Set();
75
+ }
76
+ }
77
+ /**
78
+ * 解析指定的 URL
79
+ * @param url 链接
80
+ * @param isCached 该链接指向的资源是否需要缓存
81
+ */
82
+ async parserUrlFile(url, isCached) {
83
+ const fileHandler = this.compilation.compilationEnv.read('NETWORK_FILE_FETCHER');
84
+ const contentType = fileHandler.getUrlContentType(url);
85
+ if (!contentType && !isCached)
86
+ return { file: url, mark: '', urls: new Set() };
87
+ const parser = this.hasKey(contentType) ? this.read(contentType) : undefined;
88
+ if (!parser && !isCached)
89
+ return { file: url, mark: '', urls: new Set() };
90
+ if (parser?.calcUrl) {
91
+ const result = await parser.calcUrl(url);
92
+ if (result)
93
+ return {
94
+ file: url,
95
+ ...result
96
+ };
97
+ }
98
+ const fetcher = this.compilation.compilationEnv.read('NETWORK_FILE_FETCHER');
99
+ const urls = new Set();
100
+ let mark = '';
101
+ await fetcher.fetch(url)
102
+ .then(response => this.parserNetworkFile(response, isCached ? content => {
103
+ mark = untils_1.utils.calcHash(content);
104
+ } : undefined))
105
+ .then(urls => urls.forEach(it => urls.add(it)));
106
+ return { file: url, mark, urls };
107
+ }
108
+ /** 解析指定类型的文件内容 */
109
+ async parserContent(type, content) {
110
+ if (!this.hasKey(type))
111
+ return new Set();
112
+ const parser = this.read(type);
113
+ return await parser.extractUrls(this.compilation, content);
114
+ }
115
+ }
116
+ exports.CompilationFileParser = CompilationFileParser;
117
+ function buildCommon($this) {
118
+ const registry = $this;
119
+ return {
120
+ html: {
121
+ default: {
122
+ readFromLocal(compilation, path) {
123
+ return compilation.compilationEnv.read('readLocalFile')(path);
124
+ },
125
+ readFromNetwork(_, response) {
126
+ return response.text();
127
+ },
128
+ async extractUrls(compilation, content) {
129
+ const baseUrl = compilation.compilationEnv.read("DOMAIN_HOST");
130
+ const html = HTMLParser.parse(content, {
131
+ script: true, style: true
132
+ });
133
+ const queue = [html];
134
+ const result = new Set();
135
+ async function handleItem(item) {
136
+ queue.push(...(item.childNodes ?? []));
137
+ if (!item.tagName)
138
+ return;
139
+ switch (item.tagName.toLowerCase()) {
140
+ case 'script': {
141
+ const src = item.attributes.src;
142
+ if (src) {
143
+ if (!untils_1.utils.isSameHost(src, baseUrl)) {
144
+ result.add(src);
145
+ }
146
+ }
147
+ else {
148
+ const son = await registry.parserContent('js', item.rawText);
149
+ son.forEach(it => result.add(it));
150
+ }
151
+ break;
152
+ }
153
+ case 'link': {
154
+ if (item.attributes.rel !== 'preconnect') {
155
+ const href = item.attributes.href;
156
+ if (!href) {
157
+ const son = await registry.parserContent('css', item.rawText);
158
+ son.forEach(it => result.add(it));
159
+ }
160
+ else if (!untils_1.utils.isSameHost(href, baseUrl)) {
161
+ result.add(href);
162
+ }
163
+ }
164
+ break;
165
+ }
166
+ case 'img':
167
+ case 'source':
168
+ case 'iframe':
169
+ case 'embed': {
170
+ const src = item.attributes.src;
171
+ if (src && !untils_1.utils.isSameHost(src, baseUrl)) {
172
+ result.add(src);
173
+ }
174
+ break;
175
+ }
176
+ case 'object': {
177
+ const data = item.attributes.data;
178
+ if (data && !untils_1.utils.isSameHost(data, baseUrl)) {
179
+ result.add(data);
180
+ }
181
+ break;
182
+ }
183
+ case 'style': {
184
+ const son = await registry.parserContent('css', item.rawText);
185
+ son.forEach(it => result.add(it));
186
+ break;
187
+ }
188
+ }
189
+ }
190
+ try {
191
+ do {
192
+ const item = queue.pop();
193
+ await handleItem(item);
194
+ } while (queue.length > 0);
195
+ }
196
+ catch (e) {
197
+ throw new untils_1.RuntimeException(untils_1.exceptionNames.error, '解析 HTML 时出现错误', { cause: e });
198
+ }
199
+ return result;
200
+ }
201
+ }
202
+ },
203
+ css: {
204
+ default: {
205
+ readFromLocal(compilation, path) {
206
+ return compilation.compilationEnv.read('readLocalFile')(path);
207
+ },
208
+ readFromNetwork(_, response) {
209
+ return response.text();
210
+ },
211
+ async extractUrls(compilation, content) {
212
+ const baseUrl = compilation.compilationEnv.read('DOMAIN_HOST');
213
+ const urls = new Set();
214
+ /** 从指定位置开始查询注释 */
215
+ const findComment = (tag, start) => {
216
+ for (let i = start; i < content.length;) {
217
+ const item = content[i];
218
+ switch (item) {
219
+ case tag[0]:
220
+ if (content[i + 1] === tag[1])
221
+ return i;
222
+ ++i;
223
+ break;
224
+ case '"':
225
+ case '\'':
226
+ while (true) {
227
+ const index = content.indexOf(item, i + 1);
228
+ if (index < 0)
229
+ return -1;
230
+ i = index + 1;
231
+ if (content[index - 1] !== '\\')
232
+ break;
233
+ }
234
+ break;
235
+ default:
236
+ ++i;
237
+ break;
238
+ }
239
+ }
240
+ return -1;
241
+ };
242
+ for (let i = 0; i < content.length;) {
243
+ const left = findComment('/*', i);
244
+ let sub;
245
+ if (left === -1) {
246
+ sub = content.substring(i);
247
+ i = Number.MAX_VALUE;
248
+ }
249
+ else {
250
+ sub = content.substring(i, left);
251
+ const right = findComment('*/', left + 2);
252
+ if (right === -1)
253
+ i = Number.MAX_VALUE;
254
+ else
255
+ i = right + 2;
256
+ }
257
+ sub.match(/(url\(.*?\))|(@import\s+['"].*?['"])|((https?:)?\/\/[^\s/$.?#].\S*)/g)
258
+ ?.map(it => it.replace(/(^url\(\s*(['"]?))|((['"]?\s*)\)$)|(^@import\s+['"])|(['"]$)/g, ''))
259
+ ?.filter(it => !untils_1.utils.isSameHost(it, baseUrl))
260
+ ?.forEach(it => urls.add(it));
261
+ }
262
+ return urls;
263
+ }
264
+ }
265
+ }
266
+ };
267
+ }
@@ -22,6 +22,10 @@ declare function buildCommon(): {
22
22
  readonly matchCacheRule: {
23
23
  readonly default: FunctionInBrowserAndNode<[_url: URL], number | false | null | undefined>;
24
24
  };
25
+ /** 归一化 URL */
26
+ readonly normalizeUrl: {
27
+ readonly default: FunctionInBrowserAndNode<[url: string], string>;
28
+ };
25
29
  /** 匹配缓存更新规则 */
26
30
  readonly matchUpdateRule: {
27
31
  readonly default: FunctionInBrowserAndNode<[exp: UpdateChangeExp], (url: string) => boolean | undefined | null>;