reffy 7.2.8 → 8.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/README.md +2 -9
- package/index.js +3 -5
- package/package.json +5 -5
- package/reffy.js +61 -15
- package/src/browserlib/extract-events.mjs +20 -20
- package/src/browserlib/reffy.json +1 -1
- package/src/lib/post-processor.js +269 -0
- package/src/lib/specs-crawler.js +93 -127
- package/src/lib/util.js +81 -8
- package/src/postprocessing/csscomplete.js +50 -0
- package/src/postprocessing/events.js +195 -0
- package/src/{cli/generate-idlnames.js → postprocessing/idlnames.js} +102 -143
- package/src/postprocessing/idlparsed.js +31 -0
- package/src/cli/generate-idlparsed.js +0 -139
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-processing module that consolidates events extracts into one extract
|
|
3
|
+
* per event.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { isLatestLevelThatPasses, getInterfaceTreeInfo } = require('../lib/util');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
dependsOn: ['events'],
|
|
10
|
+
input: 'crawl',
|
|
11
|
+
property: 'events',
|
|
12
|
+
|
|
13
|
+
run: async function(crawl, options) {
|
|
14
|
+
// Collect list of mixin interfaces
|
|
15
|
+
const mixins = {};
|
|
16
|
+
const parsedInterfaces = [];
|
|
17
|
+
crawl.results.forEach(s => {
|
|
18
|
+
if (s.idlparsed) {
|
|
19
|
+
if (s.idlparsed.idlNames) {
|
|
20
|
+
Object.values(s.idlparsed.idlNames).forEach(dfn => {
|
|
21
|
+
if (dfn.type === 'interface' && !dfn.partial) {
|
|
22
|
+
parsedInterfaces.push(dfn);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (s.idlparsed.idlExtendedNames) {
|
|
27
|
+
Object.keys(s.idlparsed.idlExtendedNames).forEach(n => {
|
|
28
|
+
s.idlparsed.idlExtendedNames[n].forEach(f => {
|
|
29
|
+
if (f.type === 'includes') {
|
|
30
|
+
if (!mixins[f.includes]) mixins[f.includes] = [];
|
|
31
|
+
mixins[f.includes].push(n);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Only consider latest spec in a series to avoid fake duplicates
|
|
40
|
+
const results = crawl.results.filter(spec =>
|
|
41
|
+
(spec.seriesComposition !== 'delta' && isLatestLevelThatPasses(spec, crawl.results, s => s.events)) ||
|
|
42
|
+
(spec.seriesComposition === 'delta' && spec.events));
|
|
43
|
+
|
|
44
|
+
// Update events in place
|
|
45
|
+
const events = results.map(spec => spec.events.map(e => Object.assign({ spec: spec }, e))).flat();
|
|
46
|
+
for (const event of events) {
|
|
47
|
+
expandMixinTargets(event, mixins);
|
|
48
|
+
setBubblingPerTarget(event, parsedInterfaces);
|
|
49
|
+
cleanTargetInTrees(event, parsedInterfaces);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Consolidate events extended in other specs
|
|
53
|
+
const eventsToDrop = events
|
|
54
|
+
.filter(event => event.isExtension)
|
|
55
|
+
.map(event => {
|
|
56
|
+
const err = extendEvent(event, events);
|
|
57
|
+
if (err) {
|
|
58
|
+
console.warn(err);
|
|
59
|
+
return event;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.filter(event => !!event);
|
|
66
|
+
|
|
67
|
+
return events
|
|
68
|
+
.filter(event => !eventsToDrop.includes(event))
|
|
69
|
+
.map(event => {
|
|
70
|
+
delete event.spec;
|
|
71
|
+
return event;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
function expandMixinTargets(event, mixins) {
|
|
78
|
+
const expandedTargets = event.targets?.map(i => mixins[i] || i)?.flat();
|
|
79
|
+
// This assumes a mixin matches more than one interface
|
|
80
|
+
if (expandedTargets && expandedTargets.length !== event.targets?.length) {
|
|
81
|
+
event.targets = expandedTargets;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
function setBubblingPerTarget(event, parsedInterfaces) {
|
|
89
|
+
// if an event targets an interface in a tree
|
|
90
|
+
// but the root of the tree wasn't detected as a target
|
|
91
|
+
// we can assume bubbles is false
|
|
92
|
+
// (ideally, we should check the existence of the event handler on the
|
|
93
|
+
// root interface, but there is no easy way to get a consolidated IDL view
|
|
94
|
+
// of the root at the moment)
|
|
95
|
+
if (!event.targets) return;
|
|
96
|
+
const updatedTargets = [];
|
|
97
|
+
const detected = {};
|
|
98
|
+
const treeInterfaces = [];
|
|
99
|
+
for (let iface of event.targets) {
|
|
100
|
+
const treeInfo = getInterfaceTreeInfo(iface, parsedInterfaces);
|
|
101
|
+
if (!treeInfo) {
|
|
102
|
+
updatedTargets.push({target: iface});
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const { tree, depth } = treeInfo;
|
|
106
|
+
if (!detected[tree]) {
|
|
107
|
+
detected[tree] = {root: false, nonroot: false};
|
|
108
|
+
}
|
|
109
|
+
if (depth === 0) {
|
|
110
|
+
// bubbling doesn't matter on the root interface
|
|
111
|
+
updatedTargets.push({target: iface});
|
|
112
|
+
detected[tree].root = true;
|
|
113
|
+
} else {
|
|
114
|
+
treeInterfaces.push(iface);
|
|
115
|
+
detected[tree].nonroot = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// if the event is sent at targets in a tree, but isn't detected
|
|
119
|
+
// on the root target, and no bubbling info is available,
|
|
120
|
+
// assume it doesn't bubble
|
|
121
|
+
if (Object.values(detected).length) {
|
|
122
|
+
if (!event.hasOwnProperty('bubbles') && Object.values(detected).every(x => !x.root && x.nonroot )) {
|
|
123
|
+
event.bubbles = false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (let iface of treeInterfaces) {
|
|
127
|
+
if (event.hasOwnProperty('bubbles')) {
|
|
128
|
+
updatedTargets.push({target: iface, bubbles: event.bubbles});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
event.targets = updatedTargets;
|
|
132
|
+
delete event.bubbles;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
function cleanTargetInTrees(event, parsedInterfaces) {
|
|
137
|
+
// When several targets are attached to an event that bubbles
|
|
138
|
+
// keep only the "deepest" target
|
|
139
|
+
if (event.bubbles && event.targets?.length > 1) {
|
|
140
|
+
const filteredTargets = deepestInterfaceInTree(event.targets, parsedInterfaces);
|
|
141
|
+
if (filteredTargets.length !== event.targets.length) {
|
|
142
|
+
event.targets = filteredTargets;
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
function deepestInterfaceInTree(targets, parsedInterfaces) {
|
|
151
|
+
let deepestInTrees = {};
|
|
152
|
+
let filteredTargets = [];
|
|
153
|
+
for (let {target, bubbles} of targets) {
|
|
154
|
+
const treeInfo = getInterfaceTreeInfo(target, parsedInterfaces);
|
|
155
|
+
if (!treeInfo) { // Not in a tree, we keep it in
|
|
156
|
+
filteredTargets.push({target});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const { tree, depth } = treeInfo;
|
|
160
|
+
const currentDeepest = deepestInTrees[tree]?.target;
|
|
161
|
+
if (currentDeepest) {
|
|
162
|
+
const { depth: currentDeepestDepth } = getInterfaceTreeInfo(currentDeepest, parsedInterfaces);
|
|
163
|
+
if (depth > currentDeepestDepth) {
|
|
164
|
+
deepestInTrees[tree] = {target, bubbles};
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
deepestInTrees[tree] = {target, bubbles};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return filteredTargets.concat(Object.values(deepestInTrees));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
function extendEvent(event, events) {
|
|
175
|
+
const extendedEvent =
|
|
176
|
+
events.find(e => !e.isExtension && e.href === event.href) ||
|
|
177
|
+
events.find(e => !e.isExtension && event.href.startsWith(e.spec.crawled) && e.type === event.type);
|
|
178
|
+
if (!extendedEvent) {
|
|
179
|
+
// make this a fatal error
|
|
180
|
+
return `Found extended event with link ${event.href} in ${event.spec.shortname}, but did not find a matching original event`;
|
|
181
|
+
}
|
|
182
|
+
if (extendedEvent.interface && event.interface && extendedEvent.interface !== event.interface) {
|
|
183
|
+
return `Found extended event with link ${event.href} in ${event.spec.shortname} set to use interface ${event.interface}, different from original event interface ${extendedEvent.interface} in ${extendedEvent.spec.shortname}`;
|
|
184
|
+
}
|
|
185
|
+
// Document potential additional targets
|
|
186
|
+
const newTargets = event.targets?.filter(t => !extendedEvent.targets?.find(tt => tt.target === t.target));
|
|
187
|
+
if (newTargets) {
|
|
188
|
+
extendedEvent.targets = (extendedEvent.targets || []).concat(newTargets);
|
|
189
|
+
}
|
|
190
|
+
// Document the fact that the event has been extended
|
|
191
|
+
if (!extendedEvent.extendedIn) {
|
|
192
|
+
extendedEvent.extendedIn = [];
|
|
193
|
+
}
|
|
194
|
+
extendedEvent.extendedIn.push({ spec: event.spec.series.shortname, href: event.src?.href });
|
|
195
|
+
}
|
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* that defines the name across all specs.
|
|
2
|
+
* Post-processing module that creates an index of IDL names and a list of
|
|
3
|
+
* IDL extracts per IDL name.
|
|
6
4
|
*
|
|
7
|
-
* The
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* where `crawl report` is the path to the folder that contains the
|
|
12
|
-
* `index.json` file and all other crawl results produced by specs-crawler.js,
|
|
13
|
-
* `dfns` a param to set to "true" or "dfns" to embed dfns in the generated
|
|
14
|
-
* report, and `save folder` is an optional folder (which must exist) where IDL
|
|
15
|
-
* name extracts are to be saved. In the absence of this parameter, the report
|
|
16
|
-
* is written to the console.
|
|
17
|
-
*
|
|
18
|
-
* When a folder is provided, the IDL name extracts are saved as a JSON
|
|
19
|
-
* structure in an "idlnamesparsed" subfolder, and as IDL fragments in an
|
|
20
|
-
* "idlnames" folder.
|
|
21
|
-
*
|
|
22
|
-
* @module checker
|
|
5
|
+
* The module runs at the crawl level. It depends on another post-processing
|
|
6
|
+
* module, namely the "idlparsed" one, which runs at the spec level (so no need
|
|
7
|
+
* to worry about ordering, "idlparsed" will always run before this one).
|
|
23
8
|
*/
|
|
24
9
|
|
|
25
10
|
const fs = require('fs');
|
|
26
11
|
const path = require('path');
|
|
27
|
-
const { matchIdlDfn, getExpectedDfnFromIdlDesc } = require('./check-missing-dfns');
|
|
28
12
|
const {
|
|
29
|
-
|
|
13
|
+
matchIdlDfn,
|
|
14
|
+
getExpectedDfnFromIdlDesc } = require('../cli/check-missing-dfns');
|
|
15
|
+
const {
|
|
30
16
|
isLatestLevelThatPasses,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
createFolderIfNeeded } = require('../lib/util');
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Definition of the post-processing module
|
|
22
|
+
*/
|
|
23
|
+
module.exports = {
|
|
24
|
+
dependsOn: ['idlparsed', 'dfns'],
|
|
25
|
+
input: 'crawl',
|
|
26
|
+
run: generateIdlNames,
|
|
27
|
+
save: saveIdlNames
|
|
28
|
+
};
|
|
34
29
|
|
|
35
30
|
|
|
36
31
|
/**
|
|
@@ -92,12 +87,83 @@ function getRelatedDfns(desc, idlNode, results) {
|
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Save IDL names to individual JSON files in the given folder
|
|
92
|
+
*
|
|
93
|
+
* @function
|
|
94
|
+
* @private
|
|
95
|
+
* @param {Object} names Report generated by generateIdlNames
|
|
96
|
+
* @param {String} folder Path to folder
|
|
97
|
+
*/
|
|
98
|
+
async function saveParsedIdlNames(names, folder) {
|
|
99
|
+
await createFolderIfNeeded(folder);
|
|
100
|
+
await Promise.all(Object.entries(names).map(([name, idl]) => {
|
|
101
|
+
const json = JSON.stringify(idl, null, 2);
|
|
102
|
+
const filename = path.join(folder, name + '.json');
|
|
103
|
+
return fs.promises.writeFile(filename, json);
|
|
104
|
+
}));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Save IDL fragments to individual text files in the given folder
|
|
111
|
+
*
|
|
112
|
+
* @function
|
|
113
|
+
* @private
|
|
114
|
+
* @param {Object} names Report generated by generateIdlNames
|
|
115
|
+
* @param {String} folder Path to folder
|
|
116
|
+
*/
|
|
117
|
+
async function saveIdlNamesFragments(names, folder) {
|
|
118
|
+
function serializeNode(node) {
|
|
119
|
+
return `// Source: ${node.spec.title} (${node.spec.url})\n` +
|
|
120
|
+
node.fragment;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
await createFolderIfNeeded(folder);
|
|
124
|
+
await Promise.all(Object.entries(names).map(([name, idl]) => {
|
|
125
|
+
const res = [];
|
|
126
|
+
if (idl.defined) {
|
|
127
|
+
res.push(serializeNode(idl.defined));
|
|
128
|
+
}
|
|
129
|
+
if (idl.extended) {
|
|
130
|
+
idl.extended.map(node => res.push(serializeNode(node)));
|
|
131
|
+
}
|
|
132
|
+
const filename = path.join(folder, name + '.idl');
|
|
133
|
+
return fs.promises.writeFile(filename, res.join('\n\n'));
|
|
134
|
+
}));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generate an `idlnames.json` index file that contains all IDL names with
|
|
140
|
+
* pointers to files in idlnames and idlnamesparsed subfolder.
|
|
141
|
+
*
|
|
142
|
+
* @function
|
|
143
|
+
* @private
|
|
144
|
+
* @param {Object} names Report generated by generateIdlNames
|
|
145
|
+
* @param {String} folder Path to folder where index file is to be saved
|
|
146
|
+
*/
|
|
147
|
+
async function saveIndex(names, folder) {
|
|
148
|
+
await createFolderIfNeeded(folder);
|
|
149
|
+
let index = {};
|
|
150
|
+
Object.keys(names).sort().forEach(name => {
|
|
151
|
+
index[name] = {
|
|
152
|
+
fragment: `idlnames/${name}.idl`,
|
|
153
|
+
parsed: `idlnamesparsed/${name}.json`
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
const filename = path.join(folder, 'idlnames.json');
|
|
157
|
+
return fs.promises.writeFile(filename, JSON.stringify(index, null, 2));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
95
161
|
/**
|
|
96
162
|
* Generate a report per referenceable IDL name from the given crawl results.
|
|
97
163
|
*
|
|
98
164
|
* @function
|
|
99
165
|
* @public
|
|
100
|
-
* @param {
|
|
166
|
+
* @param {Object} crawl Crawl results
|
|
101
167
|
* @param {Object} options Generation options. Set "dfns" to true to embed
|
|
102
168
|
* definitions in the final export.
|
|
103
169
|
* @return {Object} A list indexed by referenceable IDL name that details, for
|
|
@@ -105,7 +171,7 @@ function getRelatedDfns(desc, idlNode, results) {
|
|
|
105
171
|
* along with links to the actual definition of the terms in the specs
|
|
106
172
|
* (when known).
|
|
107
173
|
*/
|
|
108
|
-
function generateIdlNames(
|
|
174
|
+
async function generateIdlNames(crawl, options) {
|
|
109
175
|
function specInfo(spec) {
|
|
110
176
|
return {
|
|
111
177
|
spec: {
|
|
@@ -123,8 +189,8 @@ function generateIdlNames(results, options = {}) {
|
|
|
123
189
|
}
|
|
124
190
|
|
|
125
191
|
// Only keep latest version of specs and delta specs that define some IDL
|
|
126
|
-
results = results.filter(spec =>
|
|
127
|
-
(spec.seriesComposition !== 'delta' && isLatestLevelThatPasses(spec, results, defineIDLContent)) ||
|
|
192
|
+
const results = crawl.results.filter(spec =>
|
|
193
|
+
(spec.seriesComposition !== 'delta' && isLatestLevelThatPasses(spec, crawl.results, defineIDLContent)) ||
|
|
128
194
|
(spec.seriesComposition === 'delta' && defineIDLContent(spec)));
|
|
129
195
|
|
|
130
196
|
// Add main definitions of all IDL names
|
|
@@ -302,84 +368,6 @@ function generateIdlNames(results, options = {}) {
|
|
|
302
368
|
}
|
|
303
369
|
|
|
304
370
|
|
|
305
|
-
async function generateIdlNamesFromPath(crawlPath, options = {}) {
|
|
306
|
-
const crawlIndex = requireFromWorkingDirectory(path.resolve(crawlPath, 'index.json'));
|
|
307
|
-
const crawlResults = await expandCrawlResult(crawlIndex, crawlPath, ['idlparsed', 'dfns']);
|
|
308
|
-
return generateIdlNames(crawlResults.results, options);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Save IDL names to individual JSON files in the given folder
|
|
314
|
-
*
|
|
315
|
-
* @function
|
|
316
|
-
* @private
|
|
317
|
-
* @param {Object} names Report generated by generateIdlNames
|
|
318
|
-
* @param {String} folder Path to folder
|
|
319
|
-
*/
|
|
320
|
-
async function saveParsedIdlNames(names, folder) {
|
|
321
|
-
await createFolderIfNeeded(folder);
|
|
322
|
-
await Promise.all(Object.entries(names).map(([name, idl]) => {
|
|
323
|
-
const json = JSON.stringify(idl, null, 2);
|
|
324
|
-
const filename = path.join(folder, name + '.json');
|
|
325
|
-
return fs.promises.writeFile(filename, json);
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Save IDL fragments to individual text files in the given folder
|
|
333
|
-
*
|
|
334
|
-
* @function
|
|
335
|
-
* @private
|
|
336
|
-
* @param {Object} names Report generated by generateIdlNames
|
|
337
|
-
* @param {String} folder Path to folder
|
|
338
|
-
*/
|
|
339
|
-
async function saveIdlNamesFragments(names, folder) {
|
|
340
|
-
function serializeNode(node) {
|
|
341
|
-
return `// Source: ${node.spec.title} (${node.spec.url})\n` +
|
|
342
|
-
node.fragment;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
await createFolderIfNeeded(folder);
|
|
346
|
-
await Promise.all(Object.entries(names).map(([name, idl]) => {
|
|
347
|
-
const res = [];
|
|
348
|
-
if (idl.defined) {
|
|
349
|
-
res.push(serializeNode(idl.defined));
|
|
350
|
-
}
|
|
351
|
-
if (idl.extended) {
|
|
352
|
-
idl.extended.map(node => res.push(serializeNode(node)));
|
|
353
|
-
}
|
|
354
|
-
const filename = path.join(folder, name + '.idl');
|
|
355
|
-
return fs.promises.writeFile(filename, res.join('\n\n'));
|
|
356
|
-
}));
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Generate an `idlnames.json` index file that contains all IDL names with
|
|
362
|
-
* pointers to files in idlnames and idlnamesparsed subfolder.
|
|
363
|
-
*
|
|
364
|
-
* @function
|
|
365
|
-
* @private
|
|
366
|
-
* @param {Object} names Report generated by generateIdlNames
|
|
367
|
-
* @param {String} folder Path to folder where index file is to be saved
|
|
368
|
-
*/
|
|
369
|
-
async function saveIndex(names, folder) {
|
|
370
|
-
await createFolderIfNeeded(folder);
|
|
371
|
-
let index = {};
|
|
372
|
-
Object.keys(names).sort().forEach(name => {
|
|
373
|
-
index[name] = {
|
|
374
|
-
fragment: `idlnames/${name}.idl`,
|
|
375
|
-
parsed: `idlnamesparsed/${name}.json`
|
|
376
|
-
};
|
|
377
|
-
});
|
|
378
|
-
const filename = path.join(folder, 'idlnames.json');
|
|
379
|
-
return fs.promises.writeFile(filename, JSON.stringify(index, null, 2));
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
371
|
/**
|
|
384
372
|
* Generate all IDL names exports: IDL fragments in `idlnames`, parsed
|
|
385
373
|
* structures in `idlnamesparsed`, and index file.
|
|
@@ -387,44 +375,15 @@ async function saveIndex(names, folder) {
|
|
|
387
375
|
* @function
|
|
388
376
|
* @public
|
|
389
377
|
* @param {Object} names Report generated by generateIdlNames
|
|
390
|
-
* @param {
|
|
378
|
+
* @param {Object} options Crawl options ("output" will be used)
|
|
391
379
|
*/
|
|
392
|
-
async function saveIdlNames(names,
|
|
380
|
+
async function saveIdlNames(names, options) {
|
|
381
|
+
if (!options?.output) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const folder = options.output;
|
|
393
386
|
await saveParsedIdlNames(names, path.join(folder, 'idlnamesparsed'));
|
|
394
387
|
await saveIdlNamesFragments(names, path.join(folder, 'idlnames'));
|
|
395
388
|
await saveIndex(names, folder);
|
|
396
389
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
/**************************************************
|
|
403
|
-
Export methods for use as module
|
|
404
|
-
**************************************************/
|
|
405
|
-
module.exports.generateIdlNames = generateIdlNames;
|
|
406
|
-
module.exports.saveIdlNames = saveIdlNames;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
/**************************************************
|
|
410
|
-
Code run if the code is run as a stand-alone module
|
|
411
|
-
**************************************************/
|
|
412
|
-
if (require.main === module) {
|
|
413
|
-
const crawlPath = process.argv[2];
|
|
414
|
-
if (!crawlPath) {
|
|
415
|
-
console.error('Required path to crawl results folder is missing');
|
|
416
|
-
process.exit(2);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
const dfns = process.argv[3] === 'dfns' || process.argv[3] === 'true';
|
|
420
|
-
const savePath = process.argv[4];
|
|
421
|
-
generateIdlNamesFromPath(crawlPath, { dfns })
|
|
422
|
-
.then(report => {
|
|
423
|
-
if (savePath) {
|
|
424
|
-
return saveIdlNames(report, savePath);
|
|
425
|
-
}
|
|
426
|
-
else {
|
|
427
|
-
console.log(JSON.stringify(report, null, 2));
|
|
428
|
-
}
|
|
429
|
-
});
|
|
430
|
-
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Post-processing module that creates a parsed IDL structure out of the IDL
|
|
3
|
+
* extract.
|
|
4
|
+
*
|
|
5
|
+
* The module runs at the spec level and generates an `idlparsed` property.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const webidlParser = require('../cli/parse-webidl');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
dependsOn: ['idl'],
|
|
12
|
+
input: 'spec',
|
|
13
|
+
property: 'idlparsed',
|
|
14
|
+
|
|
15
|
+
run: async function(spec, options) {
|
|
16
|
+
if (!spec?.idl) {
|
|
17
|
+
return spec;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
spec.idlparsed = await webidlParser.parse(spec.idl);
|
|
21
|
+
spec.idlparsed.hasObsoleteIdl = webidlParser.hasObsoleteIdl(spec.idl);
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
// IDL content is invalid and cannot be parsed.
|
|
25
|
+
// Let's return the error, along with the raw IDL
|
|
26
|
+
// content so that it may be saved to a file.
|
|
27
|
+
spec.idlparsed = err.toString();
|
|
28
|
+
}
|
|
29
|
+
return spec;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* The parsed IDL generator takes a crawl report or a single spec as input, and
|
|
4
|
-
* generates (or re-generates if it already exists) a parsed IDL structure from
|
|
5
|
-
* the raw IDL that the spec defines. Result is dumped to the console or saved
|
|
6
|
-
* to the given folder.
|
|
7
|
-
*
|
|
8
|
-
* The parsed IDL generator is used by the crawler to create and save the parsed
|
|
9
|
-
* IDL structures. It is also useful to re-generated the parsed IDL info when
|
|
10
|
-
* an IDL patch has been applied to the raw IDL.
|
|
11
|
-
*
|
|
12
|
-
* The parsed IDL generator can be called directly through:
|
|
13
|
-
*
|
|
14
|
-
* `node generate-idlparsed.js [crawl report] [save folder]`
|
|
15
|
-
*
|
|
16
|
-
* where `crawl report` is the path to the folder that contains the
|
|
17
|
-
* `index.json` file and all other crawl results produced by specs-crawler.js,
|
|
18
|
-
* and `save folder` is an optional folder (which must exist) where IDL
|
|
19
|
-
* name extracts are to be saved. In the absence of this parameter, the report
|
|
20
|
-
* is written to the console.
|
|
21
|
-
*
|
|
22
|
-
* When a folder is provided, the IDL name extracts are saved as a JSON
|
|
23
|
-
* structure in an `idlparsed` subfolder.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
const fs = require('fs');
|
|
27
|
-
const path = require('path');
|
|
28
|
-
const webidlParser = require('../cli/parse-webidl');
|
|
29
|
-
const {
|
|
30
|
-
expandCrawlResult,
|
|
31
|
-
requireFromWorkingDirectory,
|
|
32
|
-
createFolderIfNeeded
|
|
33
|
-
} = require('../lib/util');
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Update the spec object in place with parsed IDL information.
|
|
38
|
-
*
|
|
39
|
-
* @function
|
|
40
|
-
* @public
|
|
41
|
-
* @param {Object} spec The spec object to update. The function looks for the
|
|
42
|
-
* raw IDL in the `idl` property.
|
|
43
|
-
* @return {Object} The updated spec with an `idl` property that contains the
|
|
44
|
-
* parsed version of the IDL, and the raw IDL moved under the `idl.idl`
|
|
45
|
-
* sub-property. Note the spec object is updated in place.
|
|
46
|
-
*/
|
|
47
|
-
async function generateIdlParsed(spec) {
|
|
48
|
-
if (!spec?.idl) {
|
|
49
|
-
return spec;
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
spec.idlparsed = await webidlParser.parse(spec.idl);
|
|
53
|
-
spec.idlparsed.hasObsoleteIdl = webidlParser.hasObsoleteIdl(spec.idl);
|
|
54
|
-
}
|
|
55
|
-
catch (err) {
|
|
56
|
-
// IDL content is invalid and cannot be parsed.
|
|
57
|
-
// Let's return the error, along with the raw IDL
|
|
58
|
-
// content so that it may be saved to a file.
|
|
59
|
-
spec.idlparsed = err.toString();
|
|
60
|
-
}
|
|
61
|
-
return spec;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
async function generateIdlParsedFromPath(crawlPath) {
|
|
66
|
-
const crawlIndex = requireFromWorkingDirectory(path.resolve(crawlPath, 'index.json'));
|
|
67
|
-
const crawlResults = await expandCrawlResult(crawlIndex, crawlPath, ['idl']);
|
|
68
|
-
await Promise.all(crawlResults.results.map(generateIdlParsed));
|
|
69
|
-
return crawlResults;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Generate the `idlparsed` export for the spec.
|
|
75
|
-
*
|
|
76
|
-
* Note that the raw IDL (under `spec.idl.idl`) gets deleted in the process.
|
|
77
|
-
*
|
|
78
|
-
* @function
|
|
79
|
-
* @public
|
|
80
|
-
* @param {Object} spec Spec object with the parsed IDL
|
|
81
|
-
* @param {String} folder Path to root folder where `idlparsed` folder needs to
|
|
82
|
-
* appear.
|
|
83
|
-
* @return {String} The relative path from the root folder to the generated file
|
|
84
|
-
*/
|
|
85
|
-
async function saveIdlParsed(spec, folder) {
|
|
86
|
-
function specInfo(spec) {
|
|
87
|
-
return {
|
|
88
|
-
spec: {
|
|
89
|
-
title: spec.title,
|
|
90
|
-
url: spec.crawled
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const subfolder = path.join(folder, 'idlparsed');
|
|
96
|
-
await createFolderIfNeeded(subfolder);
|
|
97
|
-
|
|
98
|
-
if (!spec?.idlparsed) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const json = JSON.stringify(
|
|
103
|
-
Object.assign(specInfo(spec), { idlparsed: spec.idlparsed }),
|
|
104
|
-
null, 2);
|
|
105
|
-
const filename = path.join(subfolder, spec.shortname + '.json');
|
|
106
|
-
await fs.promises.writeFile(filename, json);
|
|
107
|
-
return `idlparsed/${spec.shortname}.json`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
/**************************************************
|
|
112
|
-
Export methods for use as module
|
|
113
|
-
**************************************************/
|
|
114
|
-
module.exports.generateIdlParsed = generateIdlParsed;
|
|
115
|
-
module.exports.saveIdlParsed = saveIdlParsed;
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
/**************************************************
|
|
119
|
-
Code run if the code is run as a stand-alone module
|
|
120
|
-
**************************************************/
|
|
121
|
-
if (require.main === module) {
|
|
122
|
-
const crawlPath = process.argv[2];
|
|
123
|
-
if (!crawlPath) {
|
|
124
|
-
console.error('Required path to crawl results folder is missing');
|
|
125
|
-
process.exit(2);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const savePath = process.argv[3];
|
|
129
|
-
generateIdlParsedFromPath(crawlPath)
|
|
130
|
-
.then(report => {
|
|
131
|
-
if (savePath) {
|
|
132
|
-
return Promise.all(report.results.map(
|
|
133
|
-
spec => saveIdlParsed(spec, savePath)));
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
console.log(JSON.stringify(report, null, 2));
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|