reffy 16.1.1 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,10 +1,19 @@
1
- module.exports = {
2
- parseIdl: require("./src/cli/parse-webidl").parse,
3
- crawlSpecs: require("./src/lib/specs-crawler").crawlSpecs,
4
- expandCrawlResult: require("./src/lib/util").expandCrawlResult,
5
- mergeCrawlResults: require("./src/lib/util").mergeCrawlResults,
6
- isLatestLevelThatPasses: require("./src/lib/util").isLatestLevelThatPasses,
7
- getInterfaceTreeInfo: require("./src/lib/util").getInterfaceTreeInfo,
8
- getSchemaValidationFunction: require("./src/lib/util").getSchemaValidationFunction,
9
- postProcessor: require("./src/lib/post-processor")
10
- };
1
+ import { parseIdl } from "./src/cli/parse-webidl.js";
2
+ import { crawlSpecs } from "./src/lib/specs-crawler.js";
3
+ import { expandCrawlResult } from "./src/lib/util.js";
4
+ import { mergeCrawlResults } from "./src/lib/util.js";
5
+ import { isLatestLevelThatPasses } from "./src/lib/util.js";
6
+ import { getInterfaceTreeInfo } from "./src/lib/util.js";
7
+ import { getSchemaValidationFunction } from "./src/lib/util.js";
8
+ import postProcessor from "./src/lib/post-processor.js";
9
+
10
+ export {
11
+ parseIdl,
12
+ crawlSpecs,
13
+ expandCrawlResult,
14
+ mergeCrawlResults,
15
+ isLatestLevelThatPasses,
16
+ getInterfaceTreeInfo,
17
+ getSchemaValidationFunction,
18
+ postProcessor
19
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reffy",
3
- "version": "16.1.1",
3
+ "version": "17.0.0",
4
4
  "description": "W3C/WHATWG spec dependencies exploration companion. Features a short set of tools to study spec references as well as WebIDL term definitions and references found in W3C specifications.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,23 +29,24 @@
29
29
  "engines": {
30
30
  "node": ">=20.12.1"
31
31
  },
32
+ "type": "module",
32
33
  "main": "index.js",
33
34
  "bin": "./reffy.js",
34
35
  "dependencies": {
35
- "ajv": "8.16.0",
36
+ "ajv": "8.17.1",
36
37
  "ajv-formats": "3.0.1",
37
38
  "commander": "12.1.0",
38
39
  "fetch-filecache-for-crawling": "5.1.1",
39
- "puppeteer": "22.13.0",
40
+ "puppeteer": "22.13.1",
40
41
  "semver": "^7.3.5",
41
42
  "web-specs": "3.13.1",
42
43
  "webidl2": "24.4.1"
43
44
  },
44
45
  "devDependencies": {
45
- "mocha": "10.6.0",
46
+ "mocha": "10.7.0",
46
47
  "respec": "35.1.1",
47
48
  "respec-hljs": "2.1.1",
48
- "rollup": "4.18.1",
49
+ "rollup": "4.19.0",
49
50
  "undici": "^6.1.0"
50
51
  },
51
52
  "overrides": {
package/reffy.js CHANGED
@@ -21,16 +21,17 @@
21
21
  * @module crawler
22
22
  */
23
23
 
24
- const commander = require('commander');
25
- const satisfies = require('semver/functions/satisfies');
26
- const specs = require('web-specs');
27
- const { version, engines } = require('./package.json');
28
- const { requireFromWorkingDirectory } = require('./src/lib/util');
29
- const { crawlSpecs } = require('./src/lib/specs-crawler');
30
- const { modules } = require('./src/lib/post-processor');
24
+ import { Command } from 'commander';
25
+ import satisfies from 'semver/functions/satisfies.js';
26
+ import specs from 'web-specs' with { type: 'json' };
27
+ import packageConfig from './package.json' with { type: 'json' };
28
+ import { crawlSpecs } from './src/lib/specs-crawler.js';
29
+ import { modules } from './src/lib/post-processor.js';
30
+ import { loadJSON } from './src/lib/util.js';
31
31
 
32
32
  // Warn if version of Node.js does not satisfy requirements
33
- if (engines && engines.node && !satisfies(process.version, engines.node)) {
33
+ if (packageConfig.engines && packageConfig.engines.node &&
34
+ !satisfies(process.version, packageConfig.engines.node)) {
34
35
  console.warn(`
35
36
  [WARNING] Node.js ${process.version} detected but Reffy needs Node.js ${engines.node}.
36
37
  Please consider upgrading Node.js if the program crashes!`);
@@ -54,14 +55,14 @@ function parseModuleOption(input) {
54
55
  }
55
56
  }
56
57
 
57
- function parseSpecOption(input) {
58
+ async function parseSpecOption(input) {
58
59
  if (input === 'all') {
59
60
  return specs
60
61
  .filter(s => s.standing !== 'discontinued')
61
62
  .map(s => s.shortname)
62
63
  }
63
64
  else {
64
- const list = requireFromWorkingDirectory(input);
65
+ const list = await loadJSON(input);
65
66
  return list ?? input;
66
67
  }
67
68
  }
@@ -76,9 +77,9 @@ function parsePostOption(input) {
76
77
  }
77
78
 
78
79
 
79
- const program = new commander.Command();
80
+ const program = new Command();
80
81
  program
81
- .version(version)
82
+ .version(packageConfig.version)
82
83
  .usage('[options]')
83
84
  .description('Crawls and processes a list of Web specifications')
84
85
  .option('-d, --debug', 'debug mode, crawl one spec at a time')
@@ -91,7 +92,7 @@ program
91
92
  .option('-s, --spec <specs...>', 'specs to crawl')
92
93
  .option('-t, --terse', 'output crawl results without metadata')
93
94
  .option('-u, --use-crawl <folder>', 'use given crawl result folder as input for post-processing')
94
- .action(options => {
95
+ .action(async options => {
95
96
  if (!(options.output || options.module || options.spec || options.useCrawl)) {
96
97
  console.error(`
97
98
  At least one of the --output, --module, --spec or --use-crawl options needs to be
@@ -118,10 +119,10 @@ will dump ~100MB of data to the console:
118
119
  crawlOptions.modules = options.module.map(parseModuleOption);
119
120
  }
120
121
  if (options.spec) {
121
- crawlOptions.specs = options.spec.map(parseSpecOption).flat();
122
+ crawlOptions.specs = (await Promise.all(options.spec.map(parseSpecOption))).flat();
122
123
  }
123
124
  else {
124
- crawlOptions.specs = parseSpecOption('all');
125
+ crawlOptions.specs = await parseSpecOption('all');
125
126
  }
126
127
  if (options.post) {
127
128
  crawlOptions.post = options.post.map(parsePostOption).flat();
@@ -404,14 +404,17 @@ function getDefinedNameIn(el) {
404
404
  */
405
405
  function findIntroParagraph(algo) {
406
406
  let paragraph;
407
- let container = algo.root.closest('.algorithm');
407
+ let container = algo.root.closest('li,.algorithm');
408
408
  while (container) {
409
409
  const dfn = container.querySelector('dfn');
410
- if (dfn) {
411
- paragraph = dfn.closest('p,div');
410
+ if (dfn && !algo.root.contains(dfn)) {
411
+ paragraph = dfn.closest('p,div,li');
412
+ break;
413
+ }
414
+ if (container.nodeName === 'LI') {
412
415
  break;
413
416
  }
414
- container = container.parentElement.closest('.algorithm');
417
+ container = container.parentElement.closest('li,.algorithm');
415
418
  }
416
419
 
417
420
  if (!paragraph) {
@@ -441,9 +444,10 @@ function getAlgorithmInfo(algo, context) {
441
444
  // Note some specs add the "algorithm" class to the `<ol>` and to the
442
445
  // wrapping container, and define the name in the wrapping container.
443
446
  let info = {};
447
+
444
448
  let container = algo.root.closest('.algorithm');
445
- while (container) {
446
- if (container && !context?.nested) {
449
+ if (!context?.nested) {
450
+ while (container) {
447
451
  if (container.getAttribute('data-algorithm')) {
448
452
  info.name = normalize(container.getAttribute('data-algorithm'));
449
453
  if (container.getAttribute('data-algorithm-for')) {
@@ -462,13 +466,15 @@ function getAlgorithmInfo(algo, context) {
462
466
  info.href = dfn.href;
463
467
  }
464
468
  }
465
- break;
466
469
  }
467
470
  else {
468
471
  info = getDefinedNameIn(container);
472
+ if (info.name || info.href) {
473
+ break;
474
+ }
469
475
  }
476
+ container = container.parentElement.closest('.algorithm');
470
477
  }
471
- container = container.parentElement.closest('.algorithm');
472
478
  }
473
479
 
474
480
  // Get the introductory prose from the previous paragraph
@@ -22,7 +22,10 @@
22
22
  * @module checker
23
23
  */
24
24
 
25
- const path = require('path');
25
+ import path from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ import process from 'node:process';
28
+ import { loadJSON } from '../lib/util.js';
26
29
 
27
30
  /**
28
31
  * List of spec shortnames that, so far, don't follow the dfns data model
@@ -358,19 +361,19 @@ function matchIdlDfn(expected, actual,
358
361
  * an array of missing CSS or IDL definitions. The function returns null when
359
362
  * there are no missing definitions.
360
363
  */
361
- function checkSpecDefinitions(spec, options = {}) {
364
+ async function checkSpecDefinitions(spec, options = {}) {
362
365
  if (!options.includeObsolete && specsWithObsoleteDfnsModel.includes(spec.shortname)) {
363
366
  return { obsoleteDfnsModel: true };
364
367
  }
365
368
 
366
369
  const dfns = (typeof spec.dfns === "string") ?
367
- require(path.resolve(options.rootFolder, spec.dfns)).dfns :
370
+ (await loadJSON(path.resolve(options.rootFolder, spec.dfns))).dfns :
368
371
  (spec.dfns || []);
369
372
  const css = (typeof spec.css === "string") ?
370
- require(path.resolve(options.rootFolder, spec.css)) :
373
+ (await loadJSON(path.resolve(options.rootFolder, spec.css))) :
371
374
  (spec.css || {});
372
375
  const idl = (typeof spec.idlparsed === "string") ?
373
- require(path.resolve(options.rootFolder, spec.idlparsed)).idlparsed :
376
+ (await loadJSON(path.resolve(options.rootFolder, spec.idlparsed))).idlparsed :
374
377
  spec.idlparsed;
375
378
 
376
379
  // Make sure that all expected CSS definitions exist in the dfns extract
@@ -466,9 +469,9 @@ function checkSpecDefinitions(spec, options = {}) {
466
469
  * identify the specification, and a `missing` property that is an object that
467
470
  * may have `css` and `idl` properties which list missing CSS/IDL definitions.
468
471
  */
469
- function checkDefinitions(pathToReport, options = {}) {
472
+ async function checkDefinitions(pathToReport, options = {}) {
470
473
  const rootFolder = path.resolve(process.cwd(), pathToReport);
471
- const index = require(path.resolve(rootFolder, 'index.json')).results;
474
+ const index = (await loadJSON(path.resolve(rootFolder, 'index.json'))).results;
472
475
 
473
476
  // Check all dfns against CSS and IDL extracts
474
477
  const checkOptions = {
@@ -477,7 +480,7 @@ function checkDefinitions(pathToReport, options = {}) {
477
480
  };
478
481
  const missing = index
479
482
  .filter(spec => !options.shortname || spec.shortname === options.shortname)
480
- .map(spec => {
483
+ .map(async spec => {
481
484
  const res = {
482
485
  url: spec.url,
483
486
  crawled: spec.crawled,
@@ -486,11 +489,10 @@ function checkDefinitions(pathToReport, options = {}) {
486
489
  if (!spec.dfns) {
487
490
  return res;
488
491
  }
489
- res.missing = checkSpecDefinitions(spec, checkOptions);
492
+ res.missing = await checkSpecDefinitions(spec, checkOptions);
490
493
  return res;
491
494
  });
492
-
493
- return missing;
495
+ return Promise.all(missing);
494
496
  }
495
497
 
496
498
 
@@ -516,26 +518,28 @@ function reportMissing(missing) {
516
518
  /**************************************************
517
519
  Export methods for use as module
518
520
  **************************************************/
519
- module.exports.checkSpecDefinitions = checkSpecDefinitions;
520
- module.exports.checkDefinitions = checkDefinitions;
521
+ export {
522
+ checkSpecDefinitions,
523
+ checkDefinitions,
521
524
 
522
- // "Inner" functions that the IDL names generator uses to link IDL terms with
523
- // their definition (see generate-idlnames.js)
524
- module.exports.getExpectedDfnFromIdlDesc = getExpectedDfnFromIdlDesc;
525
- module.exports.matchIdlDfn = matchIdlDfn;
525
+ // "Inner" functions that the IDL names generator uses to link IDL terms with
526
+ // their definition (see generate-idlnames.js)
527
+ getExpectedDfnFromIdlDesc,
528
+ matchIdlDfn
529
+ };
526
530
 
527
531
 
528
532
 
529
533
  /**************************************************
530
534
  Code run if the code is run as a stand-alone module
531
535
  **************************************************/
532
- if (require.main === module) {
536
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
533
537
  const pathToReport = process.argv[2];
534
538
  const shortname = process.argv[3] || 'all';
535
539
  const format = process.argv[4] || 'markdown';
536
540
 
537
541
  const options = (shortname === 'all') ? undefined : { shortname };
538
- let res = checkDefinitions(pathToReport, options);
542
+ let res = await checkDefinitions(pathToReport, options);
539
543
  if (shortname === 'all') {
540
544
  res = res
541
545
  .filter(result => result.missing &&
@@ -15,8 +15,10 @@
15
15
  * @module merger
16
16
  */
17
17
 
18
- const fs = require('fs');
19
- const requireFromWorkingDirectory = require('../lib/util').requireFromWorkingDirectory;
18
+ import fs from 'node:fs';
19
+ import { fileURLToPath } from 'node:url';
20
+ import process from 'node:process';
21
+ import { loadJSON } from '../lib/util.js';
20
22
 
21
23
 
22
24
  /**
@@ -81,11 +83,11 @@ function mergeCrawlResults(newCrawl, refCrawl, options) {
81
83
  * @param {Object} options Merge options. Only "matchTitle" is supported for now
82
84
  * @return {Promise} The promise to have merged the two JSON files into one
83
85
  */
84
- function mergeCrawlFiles(newCrawlPath, refCrawlPath, resPath, options) {
86
+ async function mergeCrawlFiles(newCrawlPath, refCrawlPath, resPath, options) {
85
87
  options = options || {};
86
88
 
87
- let newCrawl = requireFromWorkingDirectory(newCrawlPath);
88
- let refCrawl = requireFromWorkingDirectory(refCrawlPath);
89
+ let newCrawl = await loadJSON(newCrawlPath);
90
+ let refCrawl = await loadJSON(refCrawlPath);
89
91
  return mergeCrawlResults(newCrawl, refCrawl, options)
90
92
  .then(filedata => new Promise((resolve, reject) =>
91
93
  fs.writeFile(resPath, JSON.stringify(filedata, null, 2),
@@ -96,14 +98,16 @@ function mergeCrawlFiles(newCrawlPath, refCrawlPath, resPath, options) {
96
98
  /**************************************************
97
99
  Export the methods for use as module
98
100
  **************************************************/
99
- module.exports.mergeCrawlResults = mergeCrawlResults;
100
- module.exports.mergeCrawlFiles = mergeCrawlFiles;
101
+ export {
102
+ mergeCrawlResults,
103
+ mergeCrawlFiles
104
+ };
101
105
 
102
106
 
103
107
  /**************************************************
104
108
  Code run if the code is run as a stand-alone module
105
109
  **************************************************/
106
- if (require.main === module) {
110
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
107
111
  let newCrawlPath = process.argv[2];
108
112
  let refCrawlPath = process.argv[3];
109
113
  let resPath = process.argv[4];
@@ -18,7 +18,9 @@
18
18
  * @module webidlParser
19
19
  */
20
20
 
21
- const WebIDL2 = require("webidl2");
21
+ import * as WebIDL2 from 'webidl2';
22
+ import { fileURLToPath } from 'node:url';
23
+ import process from 'node:process';
22
24
 
23
25
 
24
26
  /**
@@ -410,15 +412,17 @@ function addDependency(name, idlNames, externalDependencies) {
410
412
  /**************************************************
411
413
  Export the parse method for use as module
412
414
  **************************************************/
413
- module.exports.parse = parse;
414
- module.exports.hasObsoleteIdl = hasObsoleteIdl;
415
+ export {
416
+ parse,
417
+ hasObsoleteIdl
418
+ };
415
419
 
416
420
 
417
421
  /**************************************************
418
422
  Code run if the code is run as a stand-alone module
419
423
  **************************************************/
420
- if (require.main === module) {
421
- const fs = require("fs");
424
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
425
+ const fs = await import('node:fs');
422
426
  const idlFile = process.argv[2];
423
427
  if (!idlFile) {
424
428
  console.error("No IDL file to parse");
@@ -437,4 +437,4 @@ const parsePropDefValue = (value) => {
437
437
  return res.length === 1 ? res[0] : res;
438
438
  };
439
439
 
440
- module.exports.parsePropDefValue = parsePropDefValue;
440
+ export { parsePropDefValue };
package/src/lib/fetch.js CHANGED
@@ -5,16 +5,14 @@
5
5
  * @module finder
6
6
  */
7
7
 
8
- const os = require('os');
9
- const path = require('path');
10
- const baseFetch = require('fetch-filecache-for-crawling');
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import baseFetch from 'fetch-filecache-for-crawling';
11
+ import { loadJSON } from './util.js';
11
12
 
12
13
  // Read configuration parameters from `config.json` file
13
- let config = null;
14
- try {
15
- config = require(path.resolve('config.json'));
16
- }
17
- catch (err) {
14
+ let config = await loadJSON('config.json');
15
+ if (!config) {
18
16
  config = {};
19
17
  }
20
18
 
@@ -32,7 +30,7 @@ catch (err) {
32
30
  * options for fetch-filecache-for-crawling)
33
31
  * @return {Promise(Response)} Promise to get an HTTP response
34
32
  */
35
- async function fetch(url, options) {
33
+ export default async function fetch(url, options) {
36
34
  options = Object.assign({headers: {}}, options);
37
35
  ['cacheFolder', 'resetCache', 'cacheRefresh', 'logToConsole'].forEach(param => {
38
36
  let fetchParam = (param === 'cacheRefresh') ? 'refresh' : param;
@@ -51,6 +49,3 @@ async function fetch(url, options) {
51
49
 
52
50
  return baseFetch(url, options);
53
51
  }
54
-
55
-
56
- module.exports = fetch;
@@ -5,9 +5,12 @@
5
5
  * @module mock-server
6
6
  */
7
7
 
8
- const { MockAgent, setGlobalDispatcher } = require('undici');
9
- const path = require("path");
10
- const { existsSync, readFileSync } = require('fs');
8
+ import { MockAgent, setGlobalDispatcher } from 'undici';
9
+ import path from 'node:path';
10
+ import { existsSync, readFileSync } from 'node:fs';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const scriptPath = path.dirname(fileURLToPath(import.meta.url));
11
14
 
12
15
  /**
13
16
  * Determine the path to the "node_modules" folder. The path depends on whether
@@ -17,7 +20,7 @@ const { existsSync, readFileSync } = require('fs');
17
20
  * @return {String} Path to the node_modules folder.
18
21
  */
19
22
  function getModulesFolder() {
20
- const rootFolder = path.resolve(__dirname, '../..');
23
+ const rootFolder = path.resolve(scriptPath, '../..');
21
24
  let folder = path.resolve(rootFolder, 'node_modules');
22
25
  if (existsSync(folder)) {
23
26
  return folder;
@@ -185,4 +188,4 @@ nock.emitter.on('no match', function(req, options, requestBody) {
185
188
  }
186
189
  });*/
187
190
 
188
- module.exports = mockAgent;
191
+ export default mockAgent;
@@ -47,24 +47,69 @@
47
47
  * @module
48
48
  */
49
49
 
50
- const fs = require('fs');
51
- const path = require('path');
52
- const { createFolderIfNeeded, requireFromWorkingDirectory } = require('./util');
50
+ import fs from 'node:fs';
51
+ import path from 'node:path';
52
+ import { pathToFileURL } from 'node:url';
53
+ import { createFolderIfNeeded } from './util.js';
54
+ import csscomplete from '../postprocessing/csscomplete.js';
55
+ import events from '../postprocessing/events.js';
56
+ import idlnames from '../postprocessing/idlnames.js';
57
+ import idlparsed from '../postprocessing/idlparsed.js';
58
+ import annotatelinks from '../postprocessing/annotate-links.js';
59
+ import patchdfns from '../postprocessing/patch-dfns.js';
53
60
 
54
61
 
55
62
  /**
56
63
  * Core post-processing modules
57
64
  */
58
65
  const modules = {
59
- csscomplete: require('../postprocessing/csscomplete'),
60
- events: require('../postprocessing/events'),
61
- idlnames: require('../postprocessing/idlnames'),
62
- idlparsed: require('../postprocessing/idlparsed'),
63
- annotatelinks: require('../postprocessing/annotate-links'),
64
- patchdfns: require('../postprocessing/patch-dfns')
66
+ csscomplete,
67
+ events,
68
+ idlnames,
69
+ idlparsed,
70
+ annotatelinks,
71
+ patchdfns
65
72
  };
66
73
 
67
74
 
75
+ /**
76
+ * Custom post-processing modules
77
+ */
78
+ const customModules = {};
79
+
80
+
81
+ /**
82
+ * Loads post-processing modules once and for all
83
+ */
84
+ async function loadModules(mods) {
85
+ for (const mod of mods) {
86
+ if (typeof mod === 'string') {
87
+ if (modules[mod]) {
88
+ // Core post-processing module, already loaded
89
+ continue;
90
+ }
91
+ else {
92
+ try {
93
+ customModules[mod] = await import(pathToFileURL(mod));
94
+ }
95
+ catch (err) {
96
+ throw new Error(`Unknown post-processing module "${mod}"`);
97
+ }
98
+ if (!isModuleValid(customModules[mod])) {
99
+ throw new Error(`"${mod}" is not a valid post-processing module`);
100
+ }
101
+ }
102
+ }
103
+ else {
104
+ // Given post-processing moduel should already be a good one
105
+ if (!isModuleValid(customModules[mod])) {
106
+ throw new Error(`Post-processing module given as parameter does not have a "run" function`);
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+
68
113
  /**
69
114
  * Returns the post-processing module that match the requested name, or the
70
115
  * given parameter if it is a post-processing module already
@@ -79,20 +124,13 @@ function getModule(mod) {
79
124
  if (modules[mod]) {
80
125
  return Object.assign({ name: mod }, modules[mod]);
81
126
  }
127
+ else if (customModules[mod]) {
128
+ return Object.assign({ name: mod }, customModules[mod]);
129
+ }
82
130
  else {
83
- const fmod = requireFromWorkingDirectory(mod);
84
- if (!fmod) {
85
- throw new Error(`Unknown post-processing module "${mod}"`);
86
- }
87
- if (!isModuleValid(fmod)) {
88
- throw new Error(`"${mod}" is not a valid post-processing module`);
89
- }
90
- return Object.assign({ name: mod }, fmod);
131
+ throw new Error(`Unknown post-processing module "${mod}"`);
91
132
  }
92
133
  }
93
- else if (!isModuleValid(mod)) {
94
- throw new Error(`Post-processing module given as parameter does not have a "run" function`);
95
- }
96
134
  return mod;
97
135
  }
98
136
 
@@ -270,11 +308,13 @@ function appliesAtLevel(mod, level) {
270
308
  /**************************************************
271
309
  Export post-processing functions
272
310
  **************************************************/
273
- module.exports = {
274
- modules: Object.keys(modules),
311
+ const moduleNames = Object.keys(modules);
312
+ export {
313
+ moduleNames as modules,
314
+ loadModules,
275
315
  run, save,
276
316
  extractsPerSeries,
277
317
  dependsOn,
278
318
  getProperty,
279
319
  appliesAtLevel
280
- };
320
+ };
@@ -10,27 +10,27 @@
10
10
  * @module crawler
11
11
  */
12
12
 
13
- const fs = require('fs');
14
- const path = require('path');
15
- const specs = require('web-specs');
16
- const inspect = require('util').inspect;
17
- const cssDfnParser = require('./css-grammar-parser');
18
- const postProcessor = require('./post-processor');
19
- const ThrottledQueue = require('./throttled-queue');
20
- const {
13
+ import fs from 'node:fs';
14
+ import path from 'node:path';
15
+ import { inspect } from 'node:util';
16
+ import specs from 'web-specs' with { type: 'json' };
17
+ import * as postProcessor from './post-processor.js';
18
+ import ThrottledQueue from './throttled-queue.js';
19
+ import {
21
20
  completeWithAlternativeUrls,
22
21
  expandBrowserModules,
23
22
  expandCrawlResult,
24
23
  expandSpecResult,
25
24
  isLatestLevelThatPasses,
26
25
  processSpecification,
27
- requireFromWorkingDirectory,
28
26
  setupBrowser,
29
27
  teardownBrowser,
30
- createFolderIfNeeded
31
- } = require('./util');
28
+ createFolderIfNeeded,
29
+ loadJSON
30
+ } from './util.js';
32
31
 
33
- const {version: reffyVersion} = require('../../package.json');
32
+ import packageConfig from '../../package.json' with { type: 'json' };
33
+ const reffyVersion = packageConfig.version;
34
34
 
35
35
 
36
36
  /**
@@ -342,6 +342,9 @@ async function crawlList(speclist, crawlOptions) {
342
342
  list = list.filter(spec => !!spec.release);
343
343
  }
344
344
 
345
+ // Load post-processing modules as needed
346
+ await postProcessor.loadModules(crawlOptions.post ?? []);
347
+
345
348
  const nbStr = '' + list.length;
346
349
  async function processSpec(spec, idx) {
347
350
  const logCounter = ('' + (idx + 1)).padStart(nbStr.length, ' ') + '/' + nbStr;
@@ -465,7 +468,7 @@ async function saveResults(contents, settings) {
465
468
  * See CLI help (node reffy.js --help) for details.
466
469
  * @return {Promise<void>} The promise that the crawl will have been made
467
470
  */
468
- function crawlSpecs(options) {
471
+ async function crawlSpecs(options) {
469
472
  function prepareListOfSpecs(list) {
470
473
  return list.map(spec => {
471
474
  if (typeof spec !== 'string') {
@@ -506,7 +509,7 @@ function crawlSpecs(options) {
506
509
  }
507
510
 
508
511
  const crawlIndex = options?.useCrawl ?
509
- requireFromWorkingDirectory(options.useCrawl) :
512
+ await loadJSON(path.join(options.useCrawl, 'index.json')) :
510
513
  null;
511
514
 
512
515
  const requestedList = crawlIndex ? crawlIndex.results :
@@ -606,6 +609,10 @@ Export methods for use as module
606
609
  // - "crawlSpecs" takes options as input, runs all steps and saves results
607
610
  // to files (or outputs the results to the console). It does not return
608
611
  // anything.
609
- module.exports.crawlSpecs = (...args) => Array.isArray(args[0]) ?
610
- crawlList.apply(this, args) :
611
- crawlSpecs.apply(this, args);
612
+ function crawl(...args) {
613
+ return Array.isArray(args[0]) ?
614
+ crawlList.apply(this, args) :
615
+ crawlSpecs.apply(this, args);
616
+ }
617
+
618
+ export { crawl as crawlSpecs };
@@ -43,7 +43,7 @@ function getOrigin(url) {
43
43
  * while guaranteeing that only one request will be sent to a given origin
44
44
  * server at a time.
45
45
  */
46
- module.exports = class ThrottledQueue {
46
+ export default class ThrottledQueue {
47
47
  originQueue = {};
48
48
  maxParallel = 4;
49
49
  sleepInterval = 2000;
package/src/lib/util.js CHANGED
@@ -2,26 +2,27 @@
2
2
  * A bunch of utility functions common to multiple scripts
3
3
  */
4
4
 
5
- const fs = require('fs').promises;
6
- const { existsSync, readdirSync } = require('fs');
7
- const path = require('path');
8
- const puppeteer = require('puppeteer');
9
- const crypto = require('crypto');
10
- const { Buffer } = require('buffer');
11
- const Ajv = require('ajv');
12
- const addFormats = require('ajv-formats');
13
- const commonSchema = require('../../schemas/common.json');
14
- const fetch = require('./fetch');
15
- const specEquivalents = require('../specs/spec-equivalents.json');
16
-
17
-
18
- const reffyModules = require('../browserlib/reffy.json');
5
+ import fs from 'node:fs/promises';
6
+ import path from 'node:path';
7
+ import crypto from 'node:crypto';
8
+ import { Buffer } from 'node:buffer';
9
+ import { fileURLToPath } from 'node:url';
10
+ import puppeteer from 'puppeteer';
11
+ import Ajv from 'ajv';
12
+ import addFormats from 'ajv-formats';
13
+ import fetch from './fetch.js';
14
+
15
+ import commonSchema from '../../schemas/common.json' with { type: 'json' };
16
+ import specEquivalents from '../specs/spec-equivalents.json' with { type: 'json' };
17
+ import reffyModules from '../browserlib/reffy.json' with { type: 'json' };
18
+
19
+ const scriptPath = path.dirname(fileURLToPath(import.meta.url));
19
20
 
20
21
  /**
21
22
  * Maximum depth difference supported between Reffy's install path and custom
22
23
  * modules that may be provided on the command-line
23
24
  *
24
- * TODO: Find a way to get right of that, there should be no limit
25
+ * TODO: Find a way to get rid of that, there should be no limit
25
26
  */
26
27
  const maxPathDepth = 20;
27
28
 
@@ -38,39 +39,30 @@ const prop = p => x => x[p];
38
39
 
39
40
 
40
41
  /**
41
- * Wrapper around the "require" function to require files relative to the
42
- * current working directory (CWD), instead of relative to the current JS
43
- * file.
44
- *
45
- * This is typically needed to be able to use "require" to load JSON config
46
- * files provided as command-line arguments.
42
+ * Load a JSON file as JS object.
47
43
  *
48
44
  * @function
49
45
  * @param {String} filename The path to the file to require
50
- * @return {Object} The result of requiring the file relative to the current
51
- * working directory.
46
+ * @return {Object} The result of loading and parsing the file relative to the
47
+ * current working directory.
52
48
  */
53
- function requireFromWorkingDirectory(filename) {
54
- try {
55
- return require(path.resolve(filename));
56
- }
57
- catch (err) {
58
- return null;
59
- }
60
- }
49
+ async function loadJSON(filename) {
50
+ try {
51
+ const json = await fs.readFile(filename, 'utf8');
52
+ return JSON.parse(json);
53
+ }
54
+ catch (err) {
55
+ return null;
56
+ }
57
+ };
61
58
 
62
59
 
63
60
  /**
64
61
  * Path to the "webidl2" folder to resolve relative links in the ES6 browser
65
62
  * lib modules. The path depends on whether Reffy is run directly, or installed
66
63
  * as a library.
67
- *
68
- * Code relies on the "require.resolve" function, but note that, when given a
69
- * simple module name, that function returns the path to the file targeted by
70
- * the "main" property in "package.json" which, in the case of the webidl2
71
- * module, is "dist/webidl2.js".
72
64
  */
73
- const webidl2Folder = path.resolve(path.dirname(require.resolve('webidl2')), '..');
65
+ const webidl2Folder = path.dirname(fileURLToPath(import.meta.resolve('webidl2')));
74
66
 
75
67
 
76
68
  /**
@@ -128,7 +120,7 @@ function expandBrowserModules(modules) {
128
120
  return name;
129
121
  }
130
122
 
131
- const browserlibPath = path.resolve(__dirname, '..', 'browserlib');
123
+ const browserlibPath = path.resolve(scriptPath, '..', 'browserlib');
132
124
  if (!modules) {
133
125
  return reffyModules.map(mod => Object.assign({
134
126
  name: getCamelCaseName(mod.href),
@@ -391,7 +383,7 @@ async function processSpecification(spec, processFunction, args, options) {
391
383
  const requestPath = request.url.substring(request.url.indexOf(reffyPath) + reffyPath.length);
392
384
  let depth = requestPath.lastIndexOf('__/') / 3;
393
385
  const filename = requestPath.substring(requestPath.lastIndexOf('__/') + 3);
394
- let filePath = path.resolve(__dirname, '..', 'browserlib');
386
+ let filePath = path.resolve(scriptPath, '..', 'browserlib');
395
387
  while (depth < maxPathDepth - 1) {
396
388
  filePath = path.resolve(filePath, '..');
397
389
  depth += 1;
@@ -1041,7 +1033,7 @@ function getInterfaceTreeInfo(iface, interfaces) {
1041
1033
  * @return {function} The "validate" function for Ajv. The function returns null
1042
1034
  * if the requested schema does not exist.
1043
1035
  */
1044
- function getSchemaValidationFunction(schemaName) {
1036
+ async function getSchemaValidationFunction(schemaName) {
1045
1037
  // Helper function that selects the right schema file from the given
1046
1038
  // schema name.
1047
1039
  function getSchemaFileFromSchemaName(name) {
@@ -1065,11 +1057,11 @@ function getSchemaValidationFunction(schemaName) {
1065
1057
  }
1066
1058
  }
1067
1059
 
1068
- const schemasFolder = path.join(__dirname, '..', '..', 'schemas');
1060
+ const schemasFolder = path.resolve(scriptPath, '..', '..', 'schemas');
1069
1061
  const schemaFile = getSchemaFileFromSchemaName(schemaName);
1070
1062
  let schema;
1071
1063
  try {
1072
- schema = require(path.join(schemasFolder, schemaFile));
1064
+ schema = await loadJSON(path.join(schemasFolder, schemaFile));
1073
1065
  }
1074
1066
  catch (err) {
1075
1067
  return null;
@@ -1082,10 +1074,11 @@ function getSchemaValidationFunction(schemaName) {
1082
1074
  // The files schemas reference the browserlib ones, which need to
1083
1075
  // be explicitly added for Ajv to resolve references
1084
1076
  const folder = path.join(schemasFolder, 'browserlib');
1085
- const files = readdirSync(folder);
1077
+ const files = await fs.readdir(folder);
1086
1078
  for (const file of files) {
1087
1079
  if (file.endsWith('.json')) {
1088
- ajvWithSchemas = ajvWithSchemas.addSchema(require(path.join(schemasFolder, 'browserlib', file)));
1080
+ const json = await loadJSON(path.join(schemasFolder, 'browserlib', file));
1081
+ ajvWithSchemas = ajvWithSchemas.addSchema(json);
1089
1082
  }
1090
1083
  }
1091
1084
  }
@@ -1118,10 +1111,8 @@ function getSchemaValidationFunction(schemaName) {
1118
1111
  };
1119
1112
  }
1120
1113
 
1121
-
1122
- module.exports = {
1114
+ export {
1123
1115
  fetch,
1124
- requireFromWorkingDirectory,
1125
1116
  expandBrowserModules,
1126
1117
  setupBrowser,
1127
1118
  teardownBrowser,
@@ -1133,5 +1124,6 @@ module.exports = {
1133
1124
  getGeneratedIDLNamesByCSSProperty,
1134
1125
  createFolderIfNeeded,
1135
1126
  getInterfaceTreeInfo,
1136
- getSchemaValidationFunction
1127
+ getSchemaValidationFunction,
1128
+ loadJSON
1137
1129
  };
@@ -18,7 +18,7 @@ function canonicalizeUrl(url) {
18
18
 
19
19
  const needsSaving = {};
20
20
 
21
- module.exports = {
21
+ export default {
22
22
  dependsOn: ['links'],
23
23
  input: 'spec',
24
24
 
@@ -38,3 +38,4 @@ module.exports = {
38
38
  return spec;
39
39
  }
40
40
  };
41
+
@@ -8,9 +8,9 @@
8
8
  * rather completes the `css` property with additional info.
9
9
  */
10
10
 
11
- const { getGeneratedIDLNamesByCSSProperty } = require('../lib/util');
11
+ import { getGeneratedIDLNamesByCSSProperty } from '../lib/util.js';
12
12
 
13
- module.exports = {
13
+ export default {
14
14
  dependsOn: ['css', 'dfns'],
15
15
  input: 'spec',
16
16
 
@@ -3,9 +3,9 @@
3
3
  * per event.
4
4
  */
5
5
 
6
- const { isLatestLevelThatPasses, getInterfaceTreeInfo } = require('../lib/util');
6
+ import { isLatestLevelThatPasses, getInterfaceTreeInfo } from '../lib/util.js';
7
7
 
8
- module.exports = {
8
+ export default {
9
9
  dependsOn: ['events'],
10
10
  input: 'crawl',
11
11
  property: 'events',
@@ -7,20 +7,20 @@
7
7
  * to worry about ordering, "idlparsed" will always run before this one).
8
8
  */
9
9
 
10
- const fs = require('fs');
11
- const path = require('path');
12
- const {
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import {
13
13
  matchIdlDfn,
14
- getExpectedDfnFromIdlDesc } = require('../cli/check-missing-dfns');
15
- const {
14
+ getExpectedDfnFromIdlDesc } from '../cli/check-missing-dfns.js';
15
+ import {
16
16
  isLatestLevelThatPasses,
17
- createFolderIfNeeded } = require('../lib/util');
17
+ createFolderIfNeeded } from '../lib/util.js';
18
18
 
19
19
 
20
20
  /**
21
21
  * Definition of the post-processing module
22
22
  */
23
- module.exports = {
23
+ export default {
24
24
  dependsOn: ['idlparsed', 'dfns'],
25
25
  input: 'crawl',
26
26
  run: generateIdlNames,
@@ -5,9 +5,9 @@
5
5
  * The module runs at the spec level and generates an `idlparsed` property.
6
6
  */
7
7
 
8
- const webidlParser = require('../cli/parse-webidl');
8
+ import * as webidlParser from '../cli/parse-webidl.js';
9
9
 
10
- module.exports = {
10
+ export default {
11
11
  dependsOn: ['dfns', 'idl'],
12
12
  input: 'spec',
13
13
  property: 'idlparsed',
@@ -10,7 +10,7 @@
10
10
  * The module runs at the spec level.
11
11
  */
12
12
 
13
- module.exports = {
13
+ export default {
14
14
  dependsOn: ['dfns'],
15
15
  input: 'spec',
16
16