reffy 20.0.13 → 20.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +151 -151
  3. package/index.js +29 -29
  4. package/package.json +5 -5
  5. package/reffy.js +324 -324
  6. package/schemas/browserlib/extract-algorithms.json +52 -52
  7. package/schemas/browserlib/extract-cssdfn.json +108 -108
  8. package/schemas/browserlib/extract-dfns.json +90 -90
  9. package/schemas/browserlib/extract-elements.json +17 -17
  10. package/schemas/browserlib/extract-events.json +31 -31
  11. package/schemas/browserlib/extract-headings.json +19 -19
  12. package/schemas/browserlib/extract-ids.json +7 -7
  13. package/schemas/browserlib/extract-links.json +12 -12
  14. package/schemas/browserlib/extract-refs.json +12 -12
  15. package/schemas/common.json +876 -876
  16. package/schemas/files/extracts/algorithms.json +12 -12
  17. package/schemas/files/extracts/css.json +16 -16
  18. package/schemas/files/extracts/dfns.json +12 -12
  19. package/schemas/files/extracts/elements.json +12 -12
  20. package/schemas/files/extracts/events.json +12 -12
  21. package/schemas/files/extracts/headings.json +12 -12
  22. package/schemas/files/extracts/ids.json +12 -12
  23. package/schemas/files/extracts/links.json +12 -12
  24. package/schemas/files/extracts/refs.json +12 -12
  25. package/schemas/files/index.json +59 -59
  26. package/schemas/postprocessing/events.json +50 -50
  27. package/schemas/postprocessing/idlnames-parsed.json +27 -27
  28. package/schemas/postprocessing/idlnames.json +17 -17
  29. package/schemas/postprocessing/idlparsed.json +67 -67
  30. package/src/browserlib/clone-and-clean.mjs +24 -24
  31. package/src/browserlib/create-outline.mjs +353 -353
  32. package/src/browserlib/extract-algorithms.mjs +723 -723
  33. package/src/browserlib/extract-cddl.mjs +125 -125
  34. package/src/browserlib/extract-dfns.mjs +1093 -1093
  35. package/src/browserlib/extract-headings.mjs +76 -76
  36. package/src/browserlib/extract-ids.mjs +28 -28
  37. package/src/browserlib/extract-links.mjs +45 -45
  38. package/src/browserlib/extract-references.mjs +308 -308
  39. package/src/browserlib/extract-webidl.mjs +89 -89
  40. package/src/browserlib/get-absolute-url.mjs +29 -29
  41. package/src/browserlib/get-code-elements.mjs +20 -20
  42. package/src/browserlib/get-generator.mjs +26 -26
  43. package/src/browserlib/get-lastmodified-date.mjs +13 -13
  44. package/src/browserlib/get-revision.mjs +12 -12
  45. package/src/browserlib/get-title.mjs +14 -14
  46. package/src/browserlib/informative-selector.mjs +24 -24
  47. package/src/browserlib/map-ids-to-headings.mjs +173 -173
  48. package/src/browserlib/reffy.json +85 -85
  49. package/src/browserlib/trim-spaces.mjs +35 -35
  50. package/src/cli/check-missing-dfns.js +587 -587
  51. package/src/cli/merge-crawl-results.js +132 -132
  52. package/src/cli/parse-webidl.js +447 -447
  53. package/src/lib/css-grammar-parse-tree.schema.json +109 -109
  54. package/src/lib/css-grammar-parser.js +440 -440
  55. package/src/lib/fetch.js +51 -51
  56. package/src/lib/markdown-report.js +360 -360
  57. package/src/lib/mock-server.js +218 -218
  58. package/src/lib/post-processor.js +322 -322
  59. package/src/lib/throttled-queue.js +129 -129
  60. package/src/postprocessing/annotate-links.js +41 -41
  61. package/src/postprocessing/csscomplete.js +48 -48
  62. package/src/postprocessing/idlnames.js +391 -391
  63. package/src/postprocessing/idlparsed.js +179 -179
  64. package/src/postprocessing/patch-dfns.js +51 -51
  65. package/src/specs/missing-css-rules.json +197 -197
  66. package/src/specs/spec-equivalents.json +149 -149
  67. package/src/browserlib/extract-editors.mjs~ +0 -14
  68. package/src/browserlib/extract-events.mjs~ +0 -3
  69. package/src/browserlib/generate-es-dfn-report.sh~ +0 -4
  70. package/src/browserlib/get-revision.mjs~ +0 -7
  71. package/src/cli/csstree-grammar-check.js +0 -28
  72. package/src/cli/csstree-grammar-check.js~ +0 -10
  73. package/src/cli/csstree-grammar-parser.js +0 -11
  74. package/src/cli/csstree-grammar-parser.js~ +0 -1
  75. package/src/cli/extract-editors.js~ +0 -38
  76. package/src/cli/process-specs.js~ +0 -28
  77. package/src/postprocessing/annotate-links.js~ +0 -8
  78. package/src/postprocessing/events.js~ +0 -245
@@ -1,173 +1,173 @@
1
- import createOutline from './create-outline.mjs';
2
- import getAbsoluteUrl from './get-absolute-url.mjs';
3
-
4
- // Regular expression to capture the numbering of a heading. The expression
5
- // extracts numbers such as "1.", "A.", "A.3", "13.3.4.". Notes:
6
- // - A top-level number always ends with a ".", except in CSS 2.1, some IETF RFCs
7
- // and WebGL specs.
8
- // - There may be no final "." in sublevels (Bikeshed adds one, not ReSpec)
9
- // - Top level appendices (e.g. in CSS 2.1, IETF RFCs and Bikeshed specs) start
10
- // with "Appendix", sometimes followed by ":"
11
- const reNumber = /^([A-Z\d]\.|[A-Z](\.\d+)+\.?|\d+(\.\d+)+\.?|\d|Appendix [A-Z][\.:])\s/;
12
-
13
- /**
14
- * Retrieve a "cleaned" version of the node's text content, without aside notes
15
- * such as links to tests, MDN or references.
16
- *
17
- * Note that this is mainly intended for CSS Color 3, which has test annotations
18
- * within headings.
19
- */
20
- function getCleanTextContent(node) {
21
- const asideSelector = 'aside, .mdn-anno, .wpt-tests-block, .annotation';
22
- const cleanedNode = node.cloneNode(true);
23
- const annotations = cleanedNode.querySelectorAll(asideSelector);
24
- annotations.forEach(n => n.remove());
25
- return cleanedNode.textContent.trim().replace(/\s+/g, ' ');
26
- }
27
-
28
- /**
29
- * Generate a mapping between elements that have an ID (or a "name") and the
30
- * closest heading (that also has an ID) under which these elements appear in
31
- * the DOM tree.
32
- *
33
- * The main difficulty is that the structure of a DOM tree does not necessarily
34
- * follow the structure of the outline of the document, which means that there
35
- * is no direct way to tell the conceptual section where an element is defined
36
- * just by looking at its list of ancestors in the DOM tree.
37
- *
38
- * In practice, the outline of the document needs to be prepared accordingly to
39
- * the HTML spec before the mapping can be done.
40
- *
41
- * @function
42
- * @public
43
- * @return {Object} A mapping table, where keys are IDs of all elements in the
44
- * document, and values are IDs of the heading elements under which these
45
- * elements are defined. The table only contains IDs for which there exists
46
- * such a heading.
47
- */
48
- export default function () {
49
- // Special-casing ecmascript specs which use special markup for sections
50
- // <emu-clause>
51
- if (document.querySelector("emu-clause")) {
52
- return esMapIdToHeadings();
53
- }
54
-
55
-
56
- // Get a flat list of all conceptual sections
57
- function flattenSections(outline) {
58
- return outline
59
- .concat(outline.flatMap(section => flattenSections(section.subSections)))
60
- .concat(outline.flatMap(section => flattenSections(section.subRoots)));
61
- }
62
-
63
- const { outline, nodeToSection } = createOutline(document.body);
64
- const sections = flattenSections(outline);
65
-
66
- // Compute once whether we created a single page version out of multiple pages
67
- const singlePage = !document.querySelector('[data-reffy-page]');
68
-
69
- const mappingTable = {};
70
- [...document.querySelectorAll('[id],[name]')].forEach(node => {
71
- let parentSection = nodeToSection.get(node);
72
- while (parentSection) {
73
- if (parentSection.heading !== '__implied') {
74
- break;
75
- }
76
- parentSection = sections.find(section =>
77
- section.subSections.includes(parentSection) ||
78
- section.subRoots.includes(parentSection));
79
- }
80
-
81
- // Compute the absolute URL with fragment
82
- // (Note the crawler merges pages of a multi-page spec in the first page
83
- // to ease parsing logic, and we want to get back to the URL of the page)
84
- const idAttr = node.id ? 'id' : 'name';
85
- const nodeid = getAbsoluteUrl(node, { singlePage, attribute: idAttr });
86
- let href = nodeid;
87
-
88
- if (parentSection) {
89
- const ids = [];
90
-
91
- const heading = parentSection.heading;
92
- const anchor = heading.querySelector('a[name]');
93
- if (anchor) {
94
- ids.push(anchor.getAttribute('name'));
95
- href = getAbsoluteUrl(anchor, { singlePage, attribute: 'name' });
96
- }
97
- if (heading.id) {
98
- ids.push(heading.id);
99
- href = getAbsoluteUrl(heading, { singlePage });
100
- }
101
-
102
- if (parentSection.root && parentSection.root.id) {
103
- ids.push(parentSection.root.id);
104
- href = getAbsoluteUrl(parentSection.root, { singlePage });
105
- }
106
-
107
- const trimmedText = getCleanTextContent(heading);
108
- const match = trimmedText.match(reNumber);
109
- const number = match ? match[1] : null;
110
-
111
- const mapping = {};
112
- if (ids.length) {
113
- mapping.id = ids.pop();
114
- }
115
- mapping.href = href;
116
- mapping.title = trimmedText.replace(reNumber, '');
117
- if (ids.length) {
118
- mapping.alternateIds = ids;
119
- }
120
- mappingTable[nodeid] = mapping;
121
-
122
- if (number) {
123
- // Store the number without the final "." or ":"
124
- // (and without the "Appendix" prefix in the CSS 2.1 case)
125
- mappingTable[nodeid].number = number.replace(/[\.:]$/, '').replace(/^Appendix /, '');
126
- }
127
- }
128
- });
129
-
130
- return mappingTable;
131
- }
132
-
133
- function esMapIdToHeadings() {
134
- // Based on https://tc39.es/ecmarkup/
135
- // and actual emu-* tags used in the ecmascript spec with ids
136
- const ignoreTags = ["emu-xref"];
137
- const sectionTags = ["emu-intro", "emu-clause", "emu-annex"];
138
-
139
- // Compute once whether we created a single page version out of multiple pages
140
- const singlePage = !document.querySelector('[data-reffy-page]');
141
-
142
- let mappingTable = {};
143
- [...document.querySelectorAll(`[id]:not(${ignoreTags.join(',')}`)]
144
- .forEach(el => {
145
- const section = el.closest(`${sectionTags.map(t => `${t}[id]`).join(',')}`);
146
-
147
- // These are spec UI-related ids, so not a loss
148
- if (!section) return;
149
-
150
- const heading = section.querySelector("h1");
151
- const trimmedText = getCleanTextContent(heading);
152
- const nodeid = getAbsoluteUrl(el, { singlePage });
153
- const href = getAbsoluteUrl(section, { singlePage });
154
-
155
- const match = trimmedText.match(reNumber);
156
- const number = match ? match[1] : null;
157
-
158
- const mapping = {};
159
- if (section.id) {
160
- mapping.id = section.id;
161
- }
162
- mapping.href = href;
163
- mapping.title = trimmedText.replace(reNumber, '');
164
- mappingTable[nodeid] = mapping;
165
-
166
- if (number) {
167
- // Store the number without the final "."
168
- mappingTable[nodeid].number = number.replace(/\.$/, '');
169
- }
170
-
171
- });
172
- return mappingTable;
173
- }
1
+ import createOutline from './create-outline.mjs';
2
+ import getAbsoluteUrl from './get-absolute-url.mjs';
3
+
4
+ // Regular expression to capture the numbering of a heading. The expression
5
+ // extracts numbers such as "1.", "A.", "A.3", "13.3.4.". Notes:
6
+ // - A top-level number always ends with a ".", except in CSS 2.1, some IETF RFCs
7
+ // and WebGL specs.
8
+ // - There may be no final "." in sublevels (Bikeshed adds one, not ReSpec)
9
+ // - Top level appendices (e.g. in CSS 2.1, IETF RFCs and Bikeshed specs) start
10
+ // with "Appendix", sometimes followed by ":"
11
+ const reNumber = /^([A-Z\d]\.|[A-Z](\.\d+)+\.?|\d+(\.\d+)+\.?|\d|Appendix [A-Z][\.:])\s/;
12
+
13
+ /**
14
+ * Retrieve a "cleaned" version of the node's text content, without aside notes
15
+ * such as links to tests, MDN or references.
16
+ *
17
+ * Note that this is mainly intended for CSS Color 3, which has test annotations
18
+ * within headings.
19
+ */
20
+ function getCleanTextContent(node) {
21
+ const asideSelector = 'aside, .mdn-anno, .wpt-tests-block, .annotation';
22
+ const cleanedNode = node.cloneNode(true);
23
+ const annotations = cleanedNode.querySelectorAll(asideSelector);
24
+ annotations.forEach(n => n.remove());
25
+ return cleanedNode.textContent.trim().replace(/\s+/g, ' ');
26
+ }
27
+
28
+ /**
29
+ * Generate a mapping between elements that have an ID (or a "name") and the
30
+ * closest heading (that also has an ID) under which these elements appear in
31
+ * the DOM tree.
32
+ *
33
+ * The main difficulty is that the structure of a DOM tree does not necessarily
34
+ * follow the structure of the outline of the document, which means that there
35
+ * is no direct way to tell the conceptual section where an element is defined
36
+ * just by looking at its list of ancestors in the DOM tree.
37
+ *
38
+ * In practice, the outline of the document needs to be prepared accordingly to
39
+ * the HTML spec before the mapping can be done.
40
+ *
41
+ * @function
42
+ * @public
43
+ * @return {Object} A mapping table, where keys are IDs of all elements in the
44
+ * document, and values are IDs of the heading elements under which these
45
+ * elements are defined. The table only contains IDs for which there exists
46
+ * such a heading.
47
+ */
48
+ export default function () {
49
+ // Special-casing ecmascript specs which use special markup for sections
50
+ // <emu-clause>
51
+ if (document.querySelector("emu-clause")) {
52
+ return esMapIdToHeadings();
53
+ }
54
+
55
+
56
+ // Get a flat list of all conceptual sections
57
+ function flattenSections(outline) {
58
+ return outline
59
+ .concat(outline.flatMap(section => flattenSections(section.subSections)))
60
+ .concat(outline.flatMap(section => flattenSections(section.subRoots)));
61
+ }
62
+
63
+ const { outline, nodeToSection } = createOutline(document.body);
64
+ const sections = flattenSections(outline);
65
+
66
+ // Compute once whether we created a single page version out of multiple pages
67
+ const singlePage = !document.querySelector('[data-reffy-page]');
68
+
69
+ const mappingTable = {};
70
+ [...document.querySelectorAll('[id],[name]')].forEach(node => {
71
+ let parentSection = nodeToSection.get(node);
72
+ while (parentSection) {
73
+ if (parentSection.heading !== '__implied') {
74
+ break;
75
+ }
76
+ parentSection = sections.find(section =>
77
+ section.subSections.includes(parentSection) ||
78
+ section.subRoots.includes(parentSection));
79
+ }
80
+
81
+ // Compute the absolute URL with fragment
82
+ // (Note the crawler merges pages of a multi-page spec in the first page
83
+ // to ease parsing logic, and we want to get back to the URL of the page)
84
+ const idAttr = node.id ? 'id' : 'name';
85
+ const nodeid = getAbsoluteUrl(node, { singlePage, attribute: idAttr });
86
+ let href = nodeid;
87
+
88
+ if (parentSection) {
89
+ const ids = [];
90
+
91
+ const heading = parentSection.heading;
92
+ const anchor = heading.querySelector('a[name]');
93
+ if (anchor) {
94
+ ids.push(anchor.getAttribute('name'));
95
+ href = getAbsoluteUrl(anchor, { singlePage, attribute: 'name' });
96
+ }
97
+ if (heading.id) {
98
+ ids.push(heading.id);
99
+ href = getAbsoluteUrl(heading, { singlePage });
100
+ }
101
+
102
+ if (parentSection.root && parentSection.root.id) {
103
+ ids.push(parentSection.root.id);
104
+ href = getAbsoluteUrl(parentSection.root, { singlePage });
105
+ }
106
+
107
+ const trimmedText = getCleanTextContent(heading);
108
+ const match = trimmedText.match(reNumber);
109
+ const number = match ? match[1] : null;
110
+
111
+ const mapping = {};
112
+ if (ids.length) {
113
+ mapping.id = ids.pop();
114
+ }
115
+ mapping.href = href;
116
+ mapping.title = trimmedText.replace(reNumber, '');
117
+ if (ids.length) {
118
+ mapping.alternateIds = ids;
119
+ }
120
+ mappingTable[nodeid] = mapping;
121
+
122
+ if (number) {
123
+ // Store the number without the final "." or ":"
124
+ // (and without the "Appendix" prefix in the CSS 2.1 case)
125
+ mappingTable[nodeid].number = number.replace(/[\.:]$/, '').replace(/^Appendix /, '');
126
+ }
127
+ }
128
+ });
129
+
130
+ return mappingTable;
131
+ }
132
+
133
+ function esMapIdToHeadings() {
134
+ // Based on https://tc39.es/ecmarkup/
135
+ // and actual emu-* tags used in the ecmascript spec with ids
136
+ const ignoreTags = ["emu-xref"];
137
+ const sectionTags = ["emu-intro", "emu-clause", "emu-annex"];
138
+
139
+ // Compute once whether we created a single page version out of multiple pages
140
+ const singlePage = !document.querySelector('[data-reffy-page]');
141
+
142
+ let mappingTable = {};
143
+ [...document.querySelectorAll(`[id]:not(${ignoreTags.join(',')}`)]
144
+ .forEach(el => {
145
+ const section = el.closest(`${sectionTags.map(t => `${t}[id]`).join(',')}`);
146
+
147
+ // These are spec UI-related ids, so not a loss
148
+ if (!section) return;
149
+
150
+ const heading = section.querySelector("h1");
151
+ const trimmedText = getCleanTextContent(heading);
152
+ const nodeid = getAbsoluteUrl(el, { singlePage });
153
+ const href = getAbsoluteUrl(section, { singlePage });
154
+
155
+ const match = trimmedText.match(reNumber);
156
+ const number = match ? match[1] : null;
157
+
158
+ const mapping = {};
159
+ if (section.id) {
160
+ mapping.id = section.id;
161
+ }
162
+ mapping.href = href;
163
+ mapping.title = trimmedText.replace(reNumber, '');
164
+ mappingTable[nodeid] = mapping;
165
+
166
+ if (number) {
167
+ // Store the number without the final "."
168
+ mappingTable[nodeid].number = number.replace(/\.$/, '');
169
+ }
170
+
171
+ });
172
+ return mappingTable;
173
+ }
@@ -1,85 +1,85 @@
1
- [
2
- {
3
- "label": "Title",
4
- "href": "./get-title.mjs",
5
- "property": "title",
6
- "metadata": true
7
- },
8
- {
9
- "label": "Authoring tool",
10
- "href": "./get-generator.mjs",
11
- "property": "generator",
12
- "metadata": true
13
- },
14
- {
15
- "label": "Date",
16
- "href": "./get-lastmodified-date.mjs",
17
- "property": "date",
18
- "metadata": true
19
- },
20
- {
21
- "label": "Revision ID",
22
- "href": "./get-revision.mjs",
23
- "property": "revision",
24
- "metadata": true
25
- },
26
- {
27
- "label": "Algorithms",
28
- "href": "./extract-algorithms.mjs",
29
- "property": "algorithms"
30
- },
31
- {
32
- "label": "Links",
33
- "href": "./extract-links.mjs",
34
- "property": "links"
35
- },
36
- {
37
- "label": "References",
38
- "href": "./extract-references.mjs",
39
- "property": "refs"
40
- },
41
- {
42
- "label": "Events",
43
- "href": "./extract-events.mjs",
44
- "property": "events"
45
- },
46
- {
47
- "label": "Web IDL",
48
- "href": "./extract-webidl.mjs",
49
- "property": "idl",
50
- "extractsPerSeries": true
51
- },
52
- {
53
- "label": "CSS",
54
- "href": "./extract-cssdfn.mjs",
55
- "property": "css",
56
- "extractsPerSeries": true
57
- },
58
- {
59
- "label": "Terms",
60
- "href": "./extract-dfns.mjs",
61
- "property": "dfns",
62
- "needsIdToHeadingMap": true
63
- },
64
- {
65
- "label": "Elements",
66
- "href": "./extract-elements.mjs",
67
- "property": "elements"
68
- },
69
- {
70
- "label": "Headings",
71
- "href": "./extract-headings.mjs",
72
- "property": "headings"
73
- },
74
- {
75
- "label": "IDs",
76
- "href": "./extract-ids.mjs",
77
- "property": "ids",
78
- "needsIdToHeadingMap": true
79
- },
80
- {
81
- "label": "CDDL",
82
- "href": "./extract-cddl.mjs",
83
- "property": "cddl"
84
- }
85
- ]
1
+ [
2
+ {
3
+ "label": "Title",
4
+ "href": "./get-title.mjs",
5
+ "property": "title",
6
+ "metadata": true
7
+ },
8
+ {
9
+ "label": "Authoring tool",
10
+ "href": "./get-generator.mjs",
11
+ "property": "generator",
12
+ "metadata": true
13
+ },
14
+ {
15
+ "label": "Date",
16
+ "href": "./get-lastmodified-date.mjs",
17
+ "property": "date",
18
+ "metadata": true
19
+ },
20
+ {
21
+ "label": "Revision ID",
22
+ "href": "./get-revision.mjs",
23
+ "property": "revision",
24
+ "metadata": true
25
+ },
26
+ {
27
+ "label": "Algorithms",
28
+ "href": "./extract-algorithms.mjs",
29
+ "property": "algorithms"
30
+ },
31
+ {
32
+ "label": "Links",
33
+ "href": "./extract-links.mjs",
34
+ "property": "links"
35
+ },
36
+ {
37
+ "label": "References",
38
+ "href": "./extract-references.mjs",
39
+ "property": "refs"
40
+ },
41
+ {
42
+ "label": "Events",
43
+ "href": "./extract-events.mjs",
44
+ "property": "events"
45
+ },
46
+ {
47
+ "label": "Web IDL",
48
+ "href": "./extract-webidl.mjs",
49
+ "property": "idl",
50
+ "extractsPerSeries": true
51
+ },
52
+ {
53
+ "label": "CSS",
54
+ "href": "./extract-cssdfn.mjs",
55
+ "property": "css",
56
+ "extractsPerSeries": true
57
+ },
58
+ {
59
+ "label": "Terms",
60
+ "href": "./extract-dfns.mjs",
61
+ "property": "dfns",
62
+ "needsIdToHeadingMap": true
63
+ },
64
+ {
65
+ "label": "Elements",
66
+ "href": "./extract-elements.mjs",
67
+ "property": "elements"
68
+ },
69
+ {
70
+ "label": "Headings",
71
+ "href": "./extract-headings.mjs",
72
+ "property": "headings"
73
+ },
74
+ {
75
+ "label": "IDs",
76
+ "href": "./extract-ids.mjs",
77
+ "property": "ids",
78
+ "needsIdToHeadingMap": true
79
+ },
80
+ {
81
+ "label": "CDDL",
82
+ "href": "./extract-cddl.mjs",
83
+ "property": "cddl"
84
+ }
85
+ ]
@@ -1,36 +1,36 @@
1
- /**
2
- * Helper function that trims individual lines in a code block, removing as
3
- * much space as possible from the beginning of the page while preserving
4
- * indentation.
5
- *
6
- * Typically useful for CDDL and IDL extracts
7
- *
8
- * Rules followed:
9
- * - Always trim the first line
10
- * - Remove whitespaces from the end of each line
11
- * - Replace lines that contain spaces with empty lines
12
- * - Drop same number of leading whitespaces from all other lines
13
- */
14
- export default function trimSpaces(code) {
15
- const lines = code.trim().split('\n');
16
- const toRemove = lines
17
- .slice(1)
18
- .filter(line => line.search(/\S/) > -1)
19
- .reduce(
20
- (min, line) => Math.min(min, line.search(/\S/)),
21
- Number.MAX_VALUE);
22
- return lines
23
- .map(line => {
24
- let firstRealChar = line.search(/\S/);
25
- if (firstRealChar === -1) {
26
- return '';
27
- }
28
- else if (firstRealChar === 0) {
29
- return line.replace(/\s+$/, '');
30
- }
31
- else {
32
- return line.substring(toRemove).replace(/\s+$/, '');
33
- }
34
- })
35
- .join('\n');
1
+ /**
2
+ * Helper function that trims individual lines in a code block, removing as
3
+ * much space as possible from the beginning of the page while preserving
4
+ * indentation.
5
+ *
6
+ * Typically useful for CDDL and IDL extracts
7
+ *
8
+ * Rules followed:
9
+ * - Always trim the first line
10
+ * - Remove whitespaces from the end of each line
11
+ * - Replace lines that contain spaces with empty lines
12
+ * - Drop same number of leading whitespaces from all other lines
13
+ */
14
+ export default function trimSpaces(code) {
15
+ const lines = code.trim().split('\n');
16
+ const toRemove = lines
17
+ .slice(1)
18
+ .filter(line => line.search(/\S/) > -1)
19
+ .reduce(
20
+ (min, line) => Math.min(min, line.search(/\S/)),
21
+ Number.MAX_VALUE);
22
+ return lines
23
+ .map(line => {
24
+ let firstRealChar = line.search(/\S/);
25
+ if (firstRealChar === -1) {
26
+ return '';
27
+ }
28
+ else if (firstRealChar === 0) {
29
+ return line.replace(/\s+$/, '');
30
+ }
31
+ else {
32
+ return line.substring(toRemove).replace(/\s+$/, '');
33
+ }
34
+ })
35
+ .join('\n');
36
36
  }