forked from GoogleChrome/lighthouse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstacks.js
128 lines (110 loc) · 4.5 KB
/
stacks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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
/**
* @license Copyright 2019 The Lighthouse Authors. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
'use strict';
/**
* @fileoverview Gathers a list of detected JS libraries and their versions.
*/
/* global window */
/* global d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests */
const fs = require('fs');
const log = require('lighthouse-logger');
const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
const libDetectorSource = fs.readFileSync(
require.resolve('js-library-detector/library/libraries.js'), 'utf8');
/** @typedef {false | {version: string|number|null}} JSLibraryDetectorTestResult */
/**
* @typedef JSLibraryDetectorTest
* @property {string} id
* @property {string} icon
* @property {string} url
* @property {string|null} npm npm module name, if applicable to library.
* @property {function(Window): JSLibraryDetectorTestResult | Promise<JSLibraryDetectorTestResult>} test Returns false if library is not present, otherwise returns an object that contains the library version (set to null if the version is not detected).
*/
/**
* @typedef JSLibrary
* @property {string} id
* @property {string} name
* @property {string|number|null} version
* @property {string|null} npm
*/
/**
* Obtains a list of detected JS libraries and their versions.
*/
/* c8 ignore start */
async function detectLibraries() {
/** @type {JSLibrary[]} */
const libraries = [];
// d41d8cd98f00b204e9800998ecf8427e_ is a consistent prefix used by the detect libraries
// see https://github.com/HTTPArchive/httparchive/issues/77#issuecomment-291320900
/** @type {Record<string, JSLibraryDetectorTest>} */
// @ts-expect-error - injected libDetectorSource var
const libraryDetectorTests = d41d8cd98f00b204e9800998ecf8427e_LibraryDetectorTests; // eslint-disable-line
for (const [name, lib] of Object.entries(libraryDetectorTests)) {
try {
/** @type {NodeJS.Timeout|undefined} */
let timeout;
// Some library detections are async that can never return.
// Guard ourselves from PROTOCL_TIMEOUT by limiting each detection to a max of 1s.
// See https://github.com/GoogleChrome/lighthouse/issues/11124.
const timeoutPromise = new Promise(r => timeout = setTimeout(() => r(false), 1000));
const result = await Promise.race([lib.test(window), timeoutPromise]);
if (timeout) clearTimeout(timeout);
if (result) {
libraries.push({
id: lib.id,
name: name,
version: result.version,
npm: lib.npm,
});
}
} catch (e) {}
}
return libraries;
}
/* c8 ignore stop */
/** @implements {LH.Gatherer.FRGathererInstance} */
class Stacks extends FRGatherer {
constructor() {
super();
// Because this file uses `fs.readFile` it gets parsed by a different branch of the browserify internals
// that cannot handle the latest ECMAScript features.
// See https://github.com/GoogleChrome/lighthouse/issues/12134
/** @type {LH.Gatherer.GathererMeta} */
this.meta = {
supportedModes: ['snapshot', 'navigation'],
};
}
/**
* @param {LH.Gatherer.FRTransitionalDriver['executionContext']} executionContext
* @return {Promise<LH.Artifacts['Stacks']>}
*/
static async collectStacks(executionContext) {
const status = {msg: 'Collect stacks', id: 'lh:gather:collectStacks'};
log.time(status);
const jsLibraries = await executionContext.evaluate(detectLibraries, {
args: [],
deps: [libDetectorSource],
});
/** @type {LH.Artifacts['Stacks']} */
const stacks = jsLibraries.map(lib => ({
detector: 'js',
id: lib.id,
name: lib.name,
version: typeof lib.version === 'number' ? String(lib.version) : (lib.version || undefined),
npm: lib.npm || undefined,
}));
log.timeEnd(status);
return stacks;
}
/**
* @param {LH.Gatherer.FRTransitionalContext} context
* @return {Promise<LH.Artifacts['Stacks']>}
*/
async snapshot(context) {
return Stacks.collectStacks(context.driver.executionContext);
}
}
module.exports = Stacks;