diff --git a/.github/linters/.eslintrc.yml b/.github/linters/.eslintrc.yml new file mode 100644 index 00000000..c1e1a953 --- /dev/null +++ b/.github/linters/.eslintrc.yml @@ -0,0 +1,100 @@ +--- +env: + browser: true + es6: true + jest: true + node: true + +extends: + - "eslint:recommended" + +globals: + $WPT_ACCESSIBILITY_TREE: readonly + $WPT_BODIES: readonly + $WPT_COOKIES: readonly + $WPT_DNS: readonly + $WPT_REQUESTS: readonly + $WPT_TEST_URL: readonly + httparchive_enable_observations: writable + __REACT_DEVTOOLS_GLOBAL_HOOK__: writable + CSSUnparsedValue: readonly + LaunchParams: readonly + +ignorePatterns: + - "!.*" + - "**/node_modules/.*" + - "/dist/third-parties.js" + +plugins: + - n + - prettier + +rules: + no-inner-declarations: off + +overrides: + # JSON files + - files: + - "*.json" + extends: + - plugin:jsonc/recommended-with-json + parser: jsonc-eslint-parser + parserOptions: + jsonSyntax: JSON + + # JSONC files + - files: + - "*.jsonc" + extends: + - plugin:jsonc/recommended-with-jsonc + parser: jsonc-eslint-parser + parserOptions: + jsonSyntax: JSONC + + # JSON5 files + - files: + - "*.json5" + extends: + - plugin:jsonc/recommended-with-json5 + parser: jsonc-eslint-parser + parserOptions: + jsonSyntax: JSON5 + + # Javascript files + - files: + - "**/*.js" + extends: + - "plugin:react/recommended" + parserOptions: + ecmaVersion: latest + + - files: + - "**/*.mjs" + - "**/*.cjs" + - "**/*.jsx" + extends: + - "plugin:react/recommended" + parserOptions: + sourceType: module + ecmaVersion: latest + ecmaFeatures: + jsx: true + modules: true + + # TypeScript files + - files: + - "**/*.ts" + - "**/*.cts" + - "**/*.mts" + - "**/*.tsx" + extends: + - "plugin:@typescript-eslint/recommended" + - plugin:n/recommended + - plugin:react/recommended + - prettier + parser: "@typescript-eslint/parser" + plugins: + - "@typescript-eslint" + parserOptions: + ecmaVersion: latest + sourceType: module diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index bf1e2e03..22fca377 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -15,6 +15,9 @@ # # +ignore: + - ".github/pull_request_template.md" + ############### # Rules by id # ############### diff --git a/.markdownlintignore b/.markdownlintignore index 0c993e2e..c2435efc 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -1,2 +1,4 @@ # ignore github markdown files (like pull request templates) -**/.github/**/*.md" +.github/*.md +.github/**/*.md + diff --git a/bin/create-fugu-apis.js b/bin/create-fugu-apis.js index 6adfd6b8..1c6f09c0 100644 --- a/bin/create-fugu-apis.js +++ b/bin/create-fugu-apis.js @@ -16,7 +16,7 @@ const checkURLConditions = (where, url, mimeType, responseBody) => { // (https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#textjavascript). if ( where === "JavaScript" && - /\.m?js/.test(url) && + /\\.m?js/.test(url) && mimeType.toLowerCase().endsWith("script") ) { return true; diff --git a/dist/Images.js b/dist/Images.js index 9eb860b1..51597540 100644 --- a/dist/Images.js +++ b/dist/Images.js @@ -33,7 +33,9 @@ var wptImages = function (win) { if (im && im.length) { images = images.concat(im); } - } catch (e) {} + } catch (e) { + // continue regardless of error + } } if (images.length > 10000) break; } diff --git a/dist/a11y.js b/dist/a11y.js index d488ca4b..3594e7d6 100644 --- a/dist/a11y.js +++ b/dist/a11y.js @@ -83,7 +83,7 @@ return JSON.stringify({ // HEIF 'heif', 'heic', ]; - const extension_regex = new RegExp(`\.(${image_file_extensions.join('|')})$`, 'i'); + const extension_regex = new RegExp(`\\.(${image_file_extensions.join('|')})$`, 'i'); let total_elements_with_non_empty_alt = 0; let total_with_file_extension = 0; diff --git a/dist/ads.js b/dist/ads.js index ebf05913..d647d32a 100644 --- a/dist/ads.js +++ b/dist/ads.js @@ -1,6 +1,5 @@ //[ads] -const ACCOUNT_TYPES = ['direct', 'reseller']; const SELLER_TYPES = ['publisher', 'intermediary', 'both']; const isPresent = (response, endings) => response.ok && endings.some(ending => response.url.endsWith(ending)); @@ -86,7 +85,7 @@ const parseAdsTxt = async (response) => { } result.account_count += 1; } - }; + } // Count unique and remove domain Sets for now for (let accountType of Object.values(result.account_types)) { @@ -173,7 +172,7 @@ const parseSellersJSON = async (response) => { seller_type.domain_count = seller_type.domains.size; seller_type.domains = Array.from(seller_type.domains); // delete seller_type.domains; } - }; + } return result; } diff --git a/dist/almanac.js b/dist/almanac.js index f12f2fa9..4bf0bf6f 100644 --- a/dist/almanac.js +++ b/dist/almanac.js @@ -268,12 +268,13 @@ return JSON.stringify({ // json+ld try { var content = JSON.parse(node.textContent); - var contentLoop = []; if (content instanceof Object || content instanceof Array) { // nested lookup nestedLookup(content, 0); } - } catch (e) {} + } catch (e) { + // continue regardless of error + } } } } @@ -313,7 +314,9 @@ return JSON.stringify({ } else { navigateHash++; } - } catch (e) {} + } catch (e) { + // continue regardless of error + } } } else if (document.location.hostname !== link.hostname) { external++; @@ -373,7 +376,6 @@ return JSON.stringify({ wordsCount = 0; wordElements = 0; var n, - nodes = [], walk = document.createTreeWalker( body, NodeFilter.SHOW_ALL, @@ -471,7 +473,7 @@ return JSON.stringify({ }; })(), '09.27': (() => { - // Returns a JSON array of nodes with a tabindex and their key/value attributes. + // Returns a JSON array of nodes with a tabindex and their key/value attributes. // We acknowledge that attribute selectors are expensive to query. var nodes = document.querySelectorAll('body [tabindex]'); return parseNodes(nodes, { @@ -569,10 +571,6 @@ return JSON.stringify({ max_prop_length: 255, }; - const parsed_pictures = parseNodes(pictures, filter_options); - const parsed_imgs = parseNodes(imgs, filter_options); - const parsed_sources = parseNodes(sources, filter_options); - return { pictures: parseNodes(pictures, filter_options), imgs: parseNodes(imgs, filter_options), diff --git a/dist/avg_dom_depth.js b/dist/avg_dom_depth.js index 2d4ff3d3..69f1d57f 100644 --- a/dist/avg_dom_depth.js +++ b/dist/avg_dom_depth.js @@ -12,7 +12,7 @@ function avgDomDepth() { function numParents(elem) { var n = 0; if ( elem.parentNode ) { - while ( elem = elem.parentNode) { + while ((elem = elem.parentNode)) { n++; } } diff --git a/dist/cms.js b/dist/cms.js index f708bc0e..0f62beb6 100644 --- a/dist/cms.js +++ b/dist/cms.js @@ -32,7 +32,9 @@ function getWordPressTheme() { theme.child_theme = childTheme.replace( 'wp-child-theme-', '' ); } - } catch ( e ) {} + } catch ( e ) { + // continue regardless of error + } return theme; } @@ -179,7 +181,9 @@ function getWordPressContentType() { } } } - } catch ( e ) {} + } catch ( e ) { + // continue regardless of error + } return content; } diff --git a/dist/crawl_links.js b/dist/crawl_links.js index a4692981..6bc410d1 100644 --- a/dist/crawl_links.js +++ b/dist/crawl_links.js @@ -15,6 +15,7 @@ const sameOrigin = function(uri1, uri2){ uri2 = new URL(uri2); return uri1.origin == uri2.origin; } catch(e) { + // continue regardless of error } return false; } @@ -56,6 +57,7 @@ const getLinks = function(visibleOnly){ } } } catch (e) { + // continue regardless of error } } } diff --git a/dist/css-variables.js b/dist/css-variables.js index bfaede92..4643efb1 100644 --- a/dist/css-variables.js +++ b/dist/css-variables.js @@ -214,7 +214,9 @@ function analyzeVariables() { try { var rules = stylesheet.cssRules; } - catch (e) { } + catch (e) { + // continue regardless of error + } if (rules) { for (let rule of rules) { @@ -286,7 +288,7 @@ function analyzeVariables() { return { summary, computed }; -}; +} function serialize(data, separator) { return JSON.stringify(data, (key, value) => { diff --git a/dist/css.js b/dist/css.js index 87a3a977..61ae04df 100644 --- a/dist/css.js +++ b/dist/css.js @@ -2,7 +2,7 @@ // Uncomment the previous line for testing on webpagetest.org const PREFERS_COLOR_SCHEME_REGEXP = - /(?:@media\s*\(\s*prefers-color-scheme\s*:\s*(?:dark|light)\s*\)\s*\{[^\}]*\}|matchMedia\s*\(\s*['"]\s*\(\s*prefers-color-scheme\s*:\s*(?:dark|light)\s*\)\s*['"]\s*\))/gms; + /(?:@media\s*\(\s*prefers-color-scheme\s*:\s*(?:dark|light)\s*\)\s*\{[^}]*\}|matchMedia\s*\(\s*['"]\s*\(\s*prefers-color-scheme\s*:\s*(?:dark|light)\s*\)\s*['"]\s*\))/gms; const bodies = $WPT_BODIES; @@ -46,7 +46,7 @@ return JSON.stringify({ }; const usedLibraries = []; - for (l in CssInJsMap) { + for (let l in CssInJsMap) { if (CssInJsMap[l]) { usedLibraries.push(l); } diff --git a/dist/doctype.js b/dist/doctype.js index 9c9bbc11..eed5e807 100644 --- a/dist/doctype.js +++ b/dist/doctype.js @@ -12,4 +12,5 @@ if ( dt ) { sDoctype += " " + dt.internalSubset; } } +/* eslint-disable-next-line no-control-regex */ return sDoctype.replace(/[\x00-\x1F\x80-\xFF]/g, "").toString(); diff --git a/dist/event-names.js b/dist/event-names.js index 707284fd..2f2f01c2 100644 --- a/dist/event-names.js +++ b/dist/event-names.js @@ -1,5 +1,5 @@ const response_bodies = $WPT_BODIES; -const eventNamePattern = /addEventListener\([\'"`](\w+)/g; +const eventNamePattern = /addEventListener\(['"`](\w+)/g; return Object.fromEntries(response_bodies.filter(har => { return eventNamePattern.test(har.response_body); diff --git a/dist/fugu-apis.js b/dist/fugu-apis.js index c79eab6a..006c6076 100644 --- a/dist/fugu-apis.js +++ b/dist/fugu-apis.js @@ -448,7 +448,7 @@ const patterns = { chromeStatusID: 5739224579964928, }, 'Pointer Lock (unadjustedMovement)': { - regEx: /unadjustedMovement\s*\:\s*/g, + regEx: /unadjustedMovement\s*:\s*/g, where: 'JavaScript', supported: (async () => 'HTMLParagraphElement' in self @@ -486,7 +486,7 @@ const patterns = { ((await navigator.serviceWorker?.ready) || self.registration))(), featureDetection: `(async () => 'serviceWorker' in navigator && 'pushManager' in (await navigator.serviceWorker?.ready || self.registration))()`, documentation: 'https://developer.mozilla.org/en-US/docs/Web/API/Push_API', - blinkFeatureID: 769, + blinkFeatureID: 371, chromeStatusID: 5416033485586432, }, 'Relative Orientation Sensor': { @@ -631,7 +631,7 @@ const patterns = { chromeStatusID: 5662315307335680, }, 'Web Share Target (Files)': { - regEx: /"enctype"\s*\:\s*"multipart\/form\-data"/g, + regEx: /"enctype"\s*:\s*"multipart\/form-data"/g, where: 'Web App Manifest', supported: (async () => undefined)(), featureDetection: `(async () => undefined)()`, @@ -695,7 +695,7 @@ const patterns = { chromeStatusID: 6261030015467520, }, 'WebOTP': { - regEx: /transport\s*\:\s*\[["']sms["']\]/g, + regEx: /transport\s*:\s*\[["']sms["']\]/g, where: 'JavaScript', supported: (async () => 'OTPCredential' in self)(), featureDetection: `(async () => 'OTPCredential' in self)()`, @@ -740,7 +740,7 @@ const patterns = { chromeStatusID: 5651917954875392, }, 'Window Controls Overlay': { - regEx: /"window\-controls\-overlay"/g, + regEx: /"window-controls-overlay"/g, where: 'Web App Manifest', supported: (async () => 'windowControlsOverlay' in navigator)(), featureDetection: `(async () => 'windowControlsOverlay' in navigator)()`, diff --git a/dist/javascript.js b/dist/javascript.js index e18656ee..9ee7d032 100644 --- a/dist/javascript.js +++ b/dist/javascript.js @@ -11,8 +11,6 @@ function fetchWithTimeout(url) { return fetch(url, { signal: controller.signal }); } -const requests = $WPT_REQUESTS; - let getSourceMaps = (async () => { const $$ = (s) => [...document.querySelectorAll(s)]; const sourcemapRegex = /\/\/[#@] sourceMappingURL=(.+?)$/; diff --git a/dist/localstorage_size.js b/dist/localstorage_size.js index 7b475113..1c65ef76 100644 --- a/dist/localstorage_size.js +++ b/dist/localstorage_size.js @@ -14,10 +14,10 @@ function numKeys(storage) { return storage.length; } else { - len = 0; - for ( var key in storage ) { + let len = 0; + Object.keys(storage).forEach(() => { len++; - } + }); return len; } } diff --git a/dist/markup.js b/dist/markup.js index 672761f4..f12aff37 100644 --- a/dist/markup.js +++ b/dist/markup.js @@ -189,7 +189,7 @@ try { // whole process is placed in a try/catch so we can log uncaught errors }); return target; - }; + } let dir = document.querySelector('html[dir]')?.getAttribute("dir"); @@ -373,7 +373,7 @@ try { // whole process is placed in a try/catch so we can log uncaught errors var pictureNodes = document.querySelectorAll('picture'); - pictureNodes.forEach(node => { + pictureNodes.forEach(() => { result.picture.total++; }); diff --git a/dist/media.js b/dist/media.js index b880ce0c..a6414b6d 100644 --- a/dist/media.js +++ b/dist/media.js @@ -1,51 +1,6 @@ //[media] // Uncomment the previous line for testing on webpagetest.org -// Sanitize the `attributes` property. -function getNodeAttributes(node) { - // Inspired by dequelabs/axe-core. - if (node.attributes instanceof NamedNodeMap) { - return node.attributes; - } - return node.cloneNode(false).attributes; -} - -// Map nodes to their attributes, -function parseNodes(nodes) { - var parsedNodes = []; - if (nodes) { - for (var i = 0, len = nodes.length; i < len; i++) { - var node = nodes[i]; - var attributes = Object.values(getNodeAttributes(node)); - var el = {}; - - el.tagName = node.tagName.toLowerCase(); // for reference - for (var n = 0, len2 = attributes.length; n < len2; n++) { - var attribute = attributes[n]; - el[attribute.name.toLowerCase()] = attribute.value; - } - - parsedNodes.push(el); - } - } - return parsedNodes; -} - -// Return the set of attributes for nodes, -function getNodesAttributes(nodes) { - if (!nodes) { - return []; - } - var uniqueAttributes = new Set(); - for (var node of nodes) { - var attributes = Object.values(getNodeAttributes(node)); - for (var attribute of attributes) { - uniqueAttributes.add(attribute.name.toLowerCase()); - } - } - return Array.from(uniqueAttributes); -} - return JSON.stringify({ // Counts the number of picture tags containing an img tag 'num_picture_img': document.querySelectorAll('picture img').length, diff --git a/dist/meta_viewport.js b/dist/meta_viewport.js index 9c5aef3a..e96b90ab 100644 --- a/dist/meta_viewport.js +++ b/dist/meta_viewport.js @@ -6,4 +6,5 @@ for (var i = 0; i < metaTags.length; i++) { break; } } +/* eslint-disable-next-line no-control-regex */ return viewport.replace(/[\x00-\x1F\x80-\xFF]/g, ""); diff --git a/dist/num_scripts_async.js b/dist/num_scripts_async.js index 96a1698f..3e4b2d5a 100644 --- a/dist/num_scripts_async.js +++ b/dist/num_scripts_async.js @@ -1,13 +1,10 @@ var aElems = document.getElementsByTagName("script"); -var nAsync = 0, nSync = 0; +var nAsync = 0; for ( var i = 0, len = aElems.length; i < len; i++ ) { var e = aElems[i]; if ( e.src ) { if ( e.async ) { - nAsync++; - } - else { - nSync++; + nAsync++; } } } diff --git a/dist/num_scripts_sync.js b/dist/num_scripts_sync.js index 4bf1b613..aa1f0b7f 100644 --- a/dist/num_scripts_sync.js +++ b/dist/num_scripts_sync.js @@ -1,13 +1,10 @@ var aElems = document.getElementsByTagName("script"); -var nAsync = 0, nSync = 0; +var nSync = 0; for ( var i = 0, len = aElems.length; i < len; i++ ) { var e = aElems[i]; if ( e.src ) { - if ( e.async ) { - nAsync++; - } - else { - nSync++; + if ( !e.async ) { + nSync++; } } } diff --git a/dist/origin-trials.js b/dist/origin-trials.js index 5ccb3f9c..5fe35ef7 100644 --- a/dist/origin-trials.js +++ b/dist/origin-trials.js @@ -15,8 +15,7 @@ * @returns {object} origin_trial_metadata */ function validate(tokenElem) { - let validityElem, - versionElem, + let versionElem, originElem, subdomainElem, thirdpartyElem, diff --git a/dist/parsed_css.js b/dist/parsed_css.js index b90a08cf..60d41506 100644 --- a/dist/parsed_css.js +++ b/dist/parsed_css.js @@ -1,5 +1,6 @@ //[parsed_css] +/* eslint-disable no-cond-assign */ try { const MAX_STYLESHEET_BYTES = 500 * 1024; // 500 KB const MAX_AST_BYTES = 4 * 1024 * 1024; // 4 MB @@ -8,7 +9,7 @@ try { const block = Array.from(document.querySelectorAll('style')).map(i => ({url: 'block', body: i.innerHTML})); const inline = Array.from(document.querySelectorAll('[style]')).map(i => ({url: 'inline', body: i.getAttribute('style')})); - const parsed_css = stylesheets.concat(block, inline).filter(({url, body}) => { + const parsed_css = stylesheets.concat(block, inline).filter(({body}) => { return body.length <= MAX_STYLESHEET_BYTES; }).map(({url, body}) => { return { @@ -18,7 +19,7 @@ try { inline: url == 'inline' }) }; - }).filter(({url, ast}) => { + }).filter(({ast}) => { return JSON.stringify(ast).length <= MAX_AST_BYTES; }); @@ -35,6 +36,7 @@ try { // http://www.w3.org/TR/CSS21/grammar.html // https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027 +/* eslint-disable-next-line no-unreachable */ var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g function parse(css, options){ @@ -64,7 +66,7 @@ function parse(css, options){ */ function position() { - var start = { line: lineno, column: column }; + //var start = { line: lineno, column: column }; return function(node){ //node.position = new Position(start); whitespace(); @@ -252,7 +254,7 @@ function parse(css, options){ var pos = position(); // prop - var prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); + var prop = match(/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/); if (!prop) return; prop = trim(prop[0]); @@ -260,7 +262,7 @@ function parse(css, options){ if (!match(/^:\s*/)) return error("property missing ':'"); // val - var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/); + var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/); var ret = pos({ type: 'declaration', @@ -332,7 +334,7 @@ function parse(css, options){ var vendor = m[1]; // identifier - var m = match(/^([-\w]+)\s*/); + m = match(/^([-\w]+)\s*/); if (!m) return error("@keyframes missing name"); var name = m[1]; diff --git a/dist/performance.js b/dist/performance.js index 29355793..a79f3118 100644 --- a/dist/performance.js +++ b/dist/performance.js @@ -9,7 +9,7 @@ function getRawHtmlDocument() { rawHtml = response_bodies[0].response_body; } - rawHtmlDocument = document.implementation.createHTMLDocument("New Document"); + let rawHtmlDocument = document.implementation.createHTMLDocument("New Document"); rawHtmlDocument.documentElement.innerHTML = rawHtml; return rawHtmlDocument; @@ -247,7 +247,7 @@ function parseLinkHeader(link) { if (!paramPattern.test(p)) { return; } - const [_, key, value] = p.match(paramPattern); + const [, key, value] = p.match(paramPattern); return {key, value}; }); return [src, params]; diff --git a/dist/privacy.js b/dist/privacy.js index 54c17407..cdf3c96c 100644 --- a/dist/privacy.js +++ b/dist/privacy.js @@ -81,9 +81,9 @@ const parseDSRdelete = async (response) => { } } catch (error) { Object.assign(result, result.present ? { error: error.message } : {}); - } finally { - return Promise.resolve(result); } + + return Promise.resolve(result); } let sync_metrics = { @@ -150,6 +150,7 @@ let sync_metrics = { zh: "数据使用政策|隐私政策|数据保护政策|隐私保护政策|數據使用政策|隱私政策|數據保護政策|隱私保護政策" } const websiteLanguage = document.documentElement.lang.slice(0, 2).toLowerCase(); + let keywords; if (websiteLanguage == 'en') { keywords = languageKeywords[websiteLanguage] } else if (!(websiteLanguage in languageKeywords)) { @@ -203,9 +204,11 @@ let sync_metrics = { } }); } - } finally { - return consentData; + } catch { + // continue regardless of error } + + return consentData; })(), /** @@ -238,9 +241,12 @@ let sync_metrics = { } }); } - } finally { - return tcData; + } catch { + // continue regardless of error } + + return tcData; + })(), /** @@ -259,9 +265,11 @@ let sync_metrics = { } }); } - } finally { - return gppData; + } catch { + // continue regardless of error } + + return gppData; })(), /** @@ -280,9 +288,11 @@ let sync_metrics = { } }); } - } finally { - return uspData; + } catch { + // continue regardless of error } + + return uspData; })(), /** @@ -487,16 +497,18 @@ let sync_metrics = { for (const request of $WPT_REQUESTS) { // Add try/catch in case "new URL" throws an exception try { - request_hostname = (new URL(request.url)).hostname; + let request_hostname = (new URL(request.url)).hostname; for (const [origin, dns_info] of Object.entries($WPT_DNS)) { - dns_hostname = (new URL(origin)).hostname; + let dns_hostname = (new URL(origin)).hostname; if (request_hostname == dns_hostname && request_hostname !== dns_info.results.canonical_names[0]) { results[dns_hostname] = dns_info.results.canonical_names; } } - } catch { } + } catch { + // continue regardless of error + } } return results; diff --git a/dist/pwa.js b/dist/pwa.js index 2333df5b..e0dbe98b 100644 --- a/dist/pwa.js +++ b/dist/pwa.js @@ -85,13 +85,13 @@ function getInfoForPattern(regexPattern, extractMatchingGroupOnly) { const serviceWorkerLaxRegistrationPattern = /navigator\.serviceWorker\.register\(([^)]*)\)/g; const serviceWorkerRegistrationInfo = getInfoForPattern(serviceWorkerLaxRegistrationPattern, true); -const workboxPattern = /(?:workbox:[a-z\-]+:[\d.]+|workbox\.[a-zA-Z]+\.?[a-zA-Z]*)/g; +const workboxPattern = /(?:workbox:[a-z-]+:[\d.]+|workbox\.[a-zA-Z]+\.?[a-zA-Z]*)/g; const workboxInfo = getInfoForPattern(workboxPattern); const importScriptsPattern = /importScripts\(([^)]*)\)/g; const importScriptsInfo = getInfoForPattern(importScriptsPattern, true); -const swEventListenersPattern = /addEventListener\(\s*[\'"](install|activate|push|fetch|notificationclick|notificationclose|sync|canmakepayment|paymentrequest|periodicsync|backgroundfetchsuccess|backgroundfetchfailure|backgroundfetchabort|backgroundfetchclick)[\'"]/g; +const swEventListenersPattern = /addEventListener\(\s*['"](install|activate|push|fetch|notificationclick|notificationclose|sync|canmakepayment|paymentrequest|periodicsync|backgroundfetchsuccess|backgroundfetchfailure|backgroundfetchabort|backgroundfetchclick)['"]/g; const swEventListenersInfo = getInfoForPattern(swEventListenersPattern, true); const swPropertiesPattern = /\.on(install|activate|push|notificationclick|notificationclose|sync|canmakepayment|paymentrequest|periodicsync|backgroundfetchsuccess|backgroundfetchfailure|backgroundfetchabort|backgroundfetchclick)\s*=/g; @@ -106,7 +106,7 @@ const swObjectsInfo = getInfoForPattern(swObjectsPattern); const swRegistrationPropertiesPattern = /navigationPreload\.(enable|disable|setHeaderValue|getState)|pushManager\.(getSubscription|permissionState|subscribe)|sync\.(register|getTags)/g; const swRegistrationPropertiesInfo = getInfoForPattern(swRegistrationPropertiesPattern); -const windowEventListenersPattern = /addEventListener\(\s*[\'"](appinstalled|beforeinstallprompt)[\'"]/g; +const windowEventListenersPattern = /addEventListener\(\s*['"](appinstalled|beforeinstallprompt)['"]/g; const windowEventListenersInfo = getInfoForPattern(windowEventListenersPattern, true); const windowPropertiesPattern = /\.on(appinstalled|beforeinstallprompt)\s*=/g; diff --git a/dist/responsive_images.js b/dist/responsive_images.js index 179b8e47..8a677303 100644 --- a/dist/responsive_images.js +++ b/dist/responsive_images.js @@ -100,8 +100,7 @@ function winningSrcsetAndSizes( img ) { if ( img.parentNode.tagName === "PICTURE" ) { - const picture = img.parentNode, - sources = pictureSources( img ); + const sources = pictureSources( img ); for ( const source of sources ) { @@ -155,8 +154,7 @@ function totalNumberOfCandidates( img ) { } if ( img.parentNode.tagName === "PICTURE" ) { - const picture = img.parentNode, - sources = pictureSources( img ); + const sources = pictureSources( img ); sources.forEach( source => { if ( source.hasAttribute( 'srcset' ) ) { const parsedSrcset = parseSrcset( source.getAttribute( 'srcset' ) ).candidates; @@ -210,10 +208,10 @@ function pictureFeatures( img ) { function srcsetFeatures( parsedSrcset ) { const resourcesWithWDescriptors = parsedSrcset.filter( - o => o.hasOwnProperty( 'w' ) + o => Object.prototype.hasOwnProperty.call(o, 'w' ) ); const resourcesWithXDescriptors = parsedSrcset.filter( - o => o.hasOwnProperty( 'd' ) + o => Object.prototype.hasOwnProperty.call(o, 'd' ) ); return { @@ -349,7 +347,7 @@ function getImgData( img ) { parsedSrcset = parseSrcset( srcset ); srcsetCandidates = parsedSrcset.candidates; imgData.srcsetParseError = parsedSrcset.parseError; - srcsetFeatures_ = srcsetFeatures( parsedSrcset.candidates ); + let srcsetFeatures_ = srcsetFeatures( parsedSrcset.candidates ); imgData.srcsetHasXDescriptors = srcsetFeatures_.xDescriptors; imgData.srcsetHasWDescriptors = srcsetFeatures_.wDescriptors; @@ -401,9 +399,9 @@ function getImgData( img ) { // note: modifies srcsetCandidates in place if ( srcsetCandidates ) { srcsetCandidates.forEach( i => { - if ( i.hasOwnProperty( 'd' ) ) { + if ( Object.prototype.hasOwnProperty.call(i, 'd') ) { i.density = i.d; - } else if ( i.hasOwnProperty( 'w' ) && imgData.sizesWidth && imgData.sizesWidth > 0 ) { + } else if ( Object.prototype.hasOwnProperty.call(i, 'w') && imgData.sizesWidth && imgData.sizesWidth > 0 ) { i.density = i.w / imgData.sizesWidth; } else { i.density = 1; @@ -569,13 +567,13 @@ function getImgData( img ) { // using ideal (rather than actual) sizes value // deep copy - idealSizesSrcsetCandidates = JSON.parse( JSON.stringify( srcsetCandidates ) ); + let idealSizesSrcsetCandidates = JSON.parse( JSON.stringify( srcsetCandidates ) ); // modify in place (just like before) TODO: turn this into a function, use in both places... // overwriting densities that we copied idealSizesSrcsetCandidates.forEach( i => { - if ( i.hasOwnProperty( 'd' ) ) { + if ( Object.prototype.hasOwnProperty.call(i, 'd') ) { i.density = i.d; - } else if ( i.hasOwnProperty( 'w' ) && imgData.clientWidth > 0 ) { + } else if ( Object.prototype.hasOwnProperty.call(i, 'w') && imgData.clientWidth > 0 ) { i.density = i.w / imgData.clientWidth; } else { i.density = 1; @@ -590,7 +588,7 @@ function getImgData( img ) { // how does this resource differ from the actual selected resource? determine and log. if ( idealSizesSelectedResource && - idealSizesSelectedResource.hasOwnProperty( 'w' ) && + Object.prototype.hasOwnProperty.call(idealSizesSelectedResource, 'w') && imgData.approximateResourceWidth > 0 && imgData.approximateResourceHeight > 0 && imgData.currentSrcWDescriptor && @@ -655,7 +653,7 @@ function parseSizes(strValue) { // (This is a quick and lenient test. Because of optional unlimited-depth internal // grouping parens and strict spacing rules, this could get very complicated.) - regexCssCalc = /^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i, + regexCssCalc = /^calc\((?:[0-9a-z .+\-*/()]+)\)$/i, i, unparsedSizesList, unparsedSizesListLength, unparsedSize, lastComponentValue, size; // UTILITY FUNCTIONS @@ -699,55 +697,56 @@ function parseSizes(strValue) { } // (Loop forwards from the beginning of the string.) + /* eslint-disable-next-line no-constant-condition */ while (true) { chrctr = str.charAt(pos); - if (chrctr === "") { // ( End of string reached.) - pushComponent(); - pushComponentArray(); - return listArray; - } else if (inComment) { - if ( (chrctr === "*") && (str.charAt(pos + 1) === "/") ) { // (At end of a comment.) - inComment = false; - pos += 2; + if (chrctr === "") { // ( End of string reached.) pushComponent(); - continue; - } else { - pos += 1; // (Skip all characters inside comments.) - continue; - } - } else if (isSpace(chrctr)) { - // (If previous character in loop was also a space, or if - // at the beginning of the string, do not add space char to - // component.) - if ((str.charAt(pos - 1) && isSpace(str.charAt(pos - 1) ) ) || (!component)) { + pushComponentArray(); + return listArray; + } else if (inComment) { + if ( (chrctr === "*") && (str.charAt(pos + 1) === "/") ) { // (At end of a comment.) + inComment = false; + pos += 2; + pushComponent(); + continue; + } else { + pos += 1; // (Skip all characters inside comments.) + continue; + } + } else if (isSpace(chrctr)) { + // (If previous character in loop was also a space, or if + // at the beginning of the string, do not add space char to + // component.) + if ((str.charAt(pos - 1) && isSpace(str.charAt(pos - 1) ) ) || (!component)) { + pos += 1; + continue; + } else if (parenDepth === 0) { + pushComponent(); + pos += 1; + continue; + } else { + // (Replace any space character with a plain space for legibility.) + chrctr = " "; + } + } else if (chrctr === "(") { + parenDepth += 1; + } else if (chrctr === ")") { + parenDepth -= 1; + } else if (chrctr === ",") { + pushComponent() + pushComponentArray(); pos += 1; continue; - } else if (parenDepth === 0) { - pushComponent(); - pos += 1; + } else if ( (chrctr === "/") && (str.charAt( pos + 1 ) === "*") ) { + inComment = true; + pos += 2; continue; - } else { - // (Replace any space character with a plain space for legibility.) - chrctr = " "; } - } else if (chrctr === "(") { - parenDepth += 1; - } else if (chrctr === ")") { - parenDepth -= 1; - } else if (chrctr === ",") { - pushComponent() - pushComponentArray(); - pos += 1; - continue; - } else if ( (chrctr === "/") && (str.charAt( pos + 1 ) === "*") ) { - inComment = true; - pos += 2; - continue; - } - component = component + chrctr; - pos += 1; + component = component + chrctr; + pos += 1; } } @@ -880,8 +879,11 @@ function parseSrcset(input) { var inputLength = input.length, // (Don't use \s, to avoid matching non-breaking space) + /* eslint-disable-next-line no-control-regex */ regexLeadingSpaces = /^[ \t\n\r\u000c]+/, + /* eslint-disable-next-line no-control-regex */ regexLeadingCommasOrSpaces = /^[, \t\n\r\u000c]+/, + /* eslint-disable-next-line no-control-regex */ regexLeadingNotSpaces = /^[^ \t\n\r\u000c]+/, regexTrailingCommas = /[,]+$/, regexNonNegativeInteger = /^\d+$/, @@ -911,6 +913,7 @@ function parseSrcset(input) { // 4. Splitting loop: Collect a sequence of characters that are space // characters or U+002C COMMA characters. If any U+002C COMMA characters // were collected, that is a parse error. + /* eslint-disable-next-line no-constant-condition */ while (true) { collectCharacters(regexLeadingCommasOrSpaces); @@ -957,6 +960,7 @@ function parseSrcset(input) { // 8.3. Let state be in descriptor. state = "in descriptor"; + /* eslint-disable-next-line no-constant-condition */ while (true) { // 8.4. Let c be the character at position. diff --git a/dist/robots_meta.js b/dist/robots_meta.js index 90314fdd..3c2c4801 100644 --- a/dist/robots_meta.js +++ b/dist/robots_meta.js @@ -93,8 +93,8 @@ try { let headers = body[2]; let robots_bodies = processRobotsHTML(doc); - for (var bot in robots_bodies) { - if (!result_bodies.hasOwnProperty(bot)) { + for (let bot in robots_bodies) { + if (!Object.prototype.hasOwnProperty.call(result_bodies, bot)) { result_bodies[bot] = Object.assign({}, base); } [...Object.entries(robots_bodies[bot])].forEach(item=>{ @@ -104,8 +104,8 @@ try { } let robots_headers = processRobotsHeaders(headers); - for (var bot in robots_headers) { - if (!result_headers.hasOwnProperty(bot)) { + for (let bot in robots_headers) { + if (!Object.prototype.hasOwnProperty.call(result_headers, bot)) { result_headers[bot] = Object.assign({}, base); } [...Object.entries(robots_headers[bot])].forEach(item=>{ diff --git a/dist/robots_txt.js b/dist/robots_txt.js index 6e774a0e..5a89778a 100644 --- a/dist/robots_txt.js +++ b/dist/robots_txt.js @@ -113,7 +113,7 @@ return fetchWithTimeout('/robots.txt') } // Record counts by user-agent - counts_by_useragent = {}; + let counts_by_useragent = {}; var applies_to_useragent = []; var last = null; @@ -137,7 +137,7 @@ return fetchWithTimeout('/robots.txt') } } else if (record.record_type in BY_USERAGENT_TYPES) { - for (ua of applies_to_useragent) { + for (let ua of applies_to_useragent) { counts_by_useragent[ua][BY_USERAGENT_TYPES[record.record_type]] += 1; } diff --git a/dist/sessionstorage_size.js b/dist/sessionstorage_size.js index 2ccf0232..799a3851 100644 --- a/dist/sessionstorage_size.js +++ b/dist/sessionstorage_size.js @@ -14,10 +14,10 @@ function numKeys(storage) { return storage.length; } else { - len = 0; - for ( var key in storage ) { + let len = 0; + Object.keys(storage).forEach(() => { len++; - } + }); return len; } } diff --git a/dist/test_result.js b/dist/test_result.js index 2d411224..d42bb6e9 100644 --- a/dist/test_result.js +++ b/dist/test_result.js @@ -5,6 +5,7 @@ const sameOrigin = function(uri1, uri2){ uri2 = new URL(uri2); return uri1.origin == uri2.origin; } catch(e) { + // continue regardless of error } return false; } diff --git a/dist/valid-head.js b/dist/valid-head.js index 992d88c0..93ea4105 100644 --- a/dist/valid-head.js +++ b/dist/valid-head.js @@ -25,7 +25,7 @@ try { if (!valid_elements.includes(tagname)) { data.invalidElements.push(tagname); data.invalidHead = true; - }; + } } return data; } catch (err) { diff --git a/dist/wpt_bodies.js b/dist/wpt_bodies.js index 46d02569..587f28d0 100644 --- a/dist/wpt_bodies.js +++ b/dist/wpt_bodies.js @@ -467,7 +467,7 @@ try { // whole process is placed in a try/catch so we can log uncaught errors }); } return target; - }; + } let result = {}; @@ -697,7 +697,7 @@ try { // whole process is placed in a try/catch so we can log uncaught errors let words = 0; // if don't have a primary heading yet, search for one. - var hs = nodes.map(n => { + nodes.map(n => { let h = processHeading(d, target, n, l); characters += h.characters; words += h.words; diff --git a/package-lock.json b/package-lock.json index 9e192e25..94295b14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "Apache-2.0", "devDependencies": { - "fugu-api-data": "^1.19.1", + "fugu-api-data": "^1.20.0", "jest": "^29.7.0", "webpagetest": "github:HTTPArchive/WebPageTest.api-nodejs" } @@ -1850,9 +1850,9 @@ } }, "node_modules/fugu-api-data": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fugu-api-data/-/fugu-api-data-1.19.1.tgz", - "integrity": "sha512-kyFxVDUsU/Pwekmv/yDNyTbwZVwc9HhzjswGFfJCVHdI0Y/mtxse06xVvrVPhDqVqr8mxaOsTx4GdyCcUsyD3Q==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/fugu-api-data/-/fugu-api-data-1.20.0.tgz", + "integrity": "sha512-1pLCOhfjl7w/VbKy24WSK/vj97mn+ct92XqyHyBcTamLBfK4A10R3yZJt7g7uAiexF/8B5ftaFqBX0ixp9CzSg==", "dev": true, "license": "Apache-2.0" }, diff --git a/package.json b/package.json index 5b20c2a8..b0ab8d28 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "homepage": "https://github.com/HTTPArchive/custom-metrics#readme", "devDependencies": { - "fugu-api-data": "^1.19.1", + "fugu-api-data": "^1.20.0", "jest": "^29.7.0", "webpagetest": "github:HTTPArchive/WebPageTest.api-nodejs" }, diff --git a/tests/wpt.js b/tests/wpt.js index f490b033..46cc3eeb 100644 --- a/tests/wpt.js +++ b/tests/wpt.js @@ -182,7 +182,7 @@ Cannot display changed custom metrics due to comment size limits, \ * @returns {string[]} Test websites */ getTestWebsites() { - const urlPattern = /\b((http|https):\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}(\/[a-zA-Z0-9_.-]*)*(\?[a-zA-Z0-9_.-]+=[a-zA-Z0-9%_.-]*)?(\#?[a-zA-Z0-9%_.=-]*)?)\b/; + const urlPattern = /\b((http|https):\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}(\/[a-zA-Z0-9_.-]*)*(\?[a-zA-Z0-9_.-]+=[a-zA-Z0-9%_.-]*)?(#?[a-zA-Z0-9%_.=-]*)?)\b/; return this.prBody.split(/\r?\n/).reduce((urls, line, index, lines) => { if (line.includes('**Test websites**:')) { for (let i = index + 1; i < lines.length; i++) {