umberto 9.0.0 → 9.1.1
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/CHANGELOG.md +21 -16
- package/package.json +8 -13
- package/scripts/filter/after-post-render/fix-code-samples.js +82 -18
- package/scripts/filter/after-post-render/gloria.js +27 -0
- package/scripts/filter/after-post-render/insert-error-codes.js +34 -26
- package/scripts/filter/after-post-render/validate-after-render.js +27 -6
- package/scripts/filter/after-render/process-svg.js +21 -0
- package/scripts/filter/before-post-render/gloria/render-post-render-pug-components.js +46 -18
- package/scripts/helper/u-extract-and-cache-title.js +27 -8
- package/scripts/helper/u-split-to-title-and-content.js +32 -8
- package/scripts/utils/gloria-after-post-render/append-copy-heading-buttons.js +119 -0
- package/scripts/utils/gloria-after-post-render/apply-design-doc-classes.js +157 -0
- package/scripts/utils/gloria-after-post-render/wrap-table-into-wrappers.js +25 -0
- package/scripts/utils/inline-svg.js +63 -94
- package/scripts/utils/spritesheet-svg.js +82 -102
- package/scripts/utils/toc.js +85 -31
- package/src/api-builder/api-builder.js +53 -40
- package/src/api-builder/build-page-worker.js +35 -0
- package/src/api-builder/classes/description-parser.js +77 -38
- package/src/data-converter/converters/jsduck2umberto.js +43 -15
- package/src/hexo/filter/project-locals.js +3 -0
- package/src/sdk-builder/get-sdk-sources.js +81 -44
- package/src/tasks/build-documentation.js +4 -0
- package/src/tasks/minify-html.js +1 -1
- package/src/tasks/validate-links-collect-worker.js +34 -0
- package/src/tasks/validate-links-worker.js +127 -0
- package/src/tasks/validate-links.js +61 -259
- package/themes/umberto/layout/gloria/_head/head.pug +3 -0
- package/themes/umberto/layout/gloria/_modules/index.pug +1 -0
- package/themes/umberto/layout/gloria/_modules/kapa/index.pug +0 -1
- package/themes/umberto/layout/gloria/_modules/sentry/index.pug +28 -0
- package/scripts/filter/after-post-render/gloria/append-copy-heading-buttons.js +0 -90
- package/scripts/filter/after-post-render/gloria/apply-design-doc-classes.js +0 -96
- package/scripts/filter/after-post-render/gloria/wrap-table-into-wrappers.js +0 -36
- package/scripts/filter/after-render/gloria/inline-svg.js +0 -14
- package/scripts/filter/after-render/gloria/spritesheet-svg.js +0 -14
- package/scripts/utils/apply-design-doc-classes.js +0 -82
- /package/src/tasks/{minify-worker.js → minify-html-worker.js} +0 -0
|
@@ -5,179 +5,159 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const cheerio = require( 'cheerio' );
|
|
9
8
|
const fs = require( 'fs' );
|
|
10
9
|
const path = require( 'path' );
|
|
10
|
+
const { parseDocument } = require( 'htmlparser2' );
|
|
11
|
+
const { Element, cloneNode } = require( 'domhandler' );
|
|
12
|
+
const { selectAll, selectOne } = require( 'css-select' );
|
|
13
|
+
const { getAttributeValue, removeElement, appendChild, prependChild } = require( 'domutils' );
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
module.exports = function spritesheetSvg( doc, themeDir ) {
|
|
16
|
+
const elements = selectAll( '[data-spritesheet-svg]', doc );
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
* Cache for parsed SVG elements.
|
|
16
|
-
*/
|
|
17
|
-
const SVG_CACHE = new Map();
|
|
18
|
-
|
|
19
|
-
module.exports = function spritesheetSvg( str, isDocument = true ) {
|
|
20
|
-
const $ = cheerio.load( str, null, isDocument );
|
|
21
|
-
const elements = $( '[data-spritesheet-svg]' );
|
|
22
|
-
|
|
23
|
-
// Skip if there are no elements with data-spritesheet-svg
|
|
18
|
+
// Skip if there are no elements with data-spritesheet-svg.
|
|
24
19
|
if ( elements.length === 0 ) {
|
|
25
|
-
return
|
|
20
|
+
return doc;
|
|
26
21
|
}
|
|
27
22
|
|
|
28
|
-
// Collect all unique SVG files to include in the spritesheet
|
|
23
|
+
// Collect all unique SVG files to include in the spritesheet.
|
|
29
24
|
const svgFiles = new Map();
|
|
30
25
|
|
|
31
|
-
// Helper function to extract SVG file information
|
|
32
|
-
function collectSvgFileInfo(
|
|
33
|
-
const svgPath =
|
|
34
|
-
const iconId =
|
|
26
|
+
// Helper function to extract SVG file information.
|
|
27
|
+
function collectSvgFileInfo( el ) {
|
|
28
|
+
const svgPath = getAttributeValue( el, 'data-spritesheet-svg' );
|
|
29
|
+
const iconId = getAttributeValue( el, 'data-spritesheet-id' );
|
|
35
30
|
|
|
36
|
-
if ( !svgFiles.has( iconId ) ) {
|
|
31
|
+
if ( iconId && !svgFiles.has( iconId ) ) {
|
|
37
32
|
svgFiles.set( iconId, svgPath );
|
|
38
33
|
}
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
// Process normal SVG elements
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
collectSvgFileInfo( $element );
|
|
46
|
-
} );
|
|
47
|
-
|
|
48
|
-
// Process and remove preload elements
|
|
49
|
-
const preloadElements = $( '[data-spritesheet-preload="true"]' );
|
|
50
|
-
preloadElements.each( function() {
|
|
51
|
-
const $element = $( this );
|
|
36
|
+
// Process normal SVG elements.
|
|
37
|
+
for ( const el of elements ) {
|
|
38
|
+
collectSvgFileInfo( el );
|
|
39
|
+
}
|
|
52
40
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
// Process and remove preload elements.
|
|
42
|
+
for ( const el of selectAll( '[data-spritesheet-preload="true"]', doc ) ) {
|
|
43
|
+
collectSvgFileInfo( el );
|
|
44
|
+
removeElement( el );
|
|
45
|
+
}
|
|
56
46
|
|
|
57
47
|
if ( svgFiles.size === 0 ) {
|
|
58
|
-
return
|
|
48
|
+
return doc;
|
|
59
49
|
}
|
|
60
50
|
|
|
61
|
-
// Create spritesheet and get viewBox data
|
|
62
|
-
const { spritesheetElement, iconViewBoxes } = createSpritesheet(
|
|
51
|
+
// Create spritesheet and get viewBox data.
|
|
52
|
+
const { spritesheetElement, iconViewBoxes } = createSpritesheet( svgFiles, themeDir );
|
|
63
53
|
|
|
64
|
-
// Add spritesheet to the beginning of the body
|
|
65
|
-
|
|
66
|
-
$( 'body' ).prepend( spritesheetElement );
|
|
67
|
-
}
|
|
54
|
+
// Add spritesheet to the beginning of the body.
|
|
55
|
+
prependChild( selectOne( 'body', doc ), spritesheetElement );
|
|
68
56
|
|
|
69
|
-
// Set viewBox for original SVG elements
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const iconId = element.attr( 'data-spritesheet-id' );
|
|
57
|
+
// Set viewBox for original SVG elements.
|
|
58
|
+
for ( const el of elements ) {
|
|
59
|
+
const iconId = getAttributeValue( el, 'data-spritesheet-id' );
|
|
73
60
|
const viewBox = iconViewBoxes.get( iconId );
|
|
74
61
|
|
|
75
|
-
if ( viewBox &&
|
|
76
|
-
|
|
62
|
+
if ( viewBox && el.name === 'svg' ) {
|
|
63
|
+
el.attribs.viewBox = viewBox;
|
|
77
64
|
}
|
|
78
|
-
}
|
|
65
|
+
}
|
|
79
66
|
|
|
80
|
-
|
|
81
|
-
|
|
67
|
+
for ( const el of selectAll( '[data-spritesheet-svg]', doc ) ) {
|
|
68
|
+
delete el.attribs[ 'data-spritesheet-svg' ];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for ( const el of selectAll( '[data-spritesheet-id]', doc ) ) {
|
|
72
|
+
delete el.attribs[ 'data-spritesheet-id' ];
|
|
73
|
+
}
|
|
82
74
|
|
|
83
|
-
return
|
|
75
|
+
return doc;
|
|
84
76
|
};
|
|
85
77
|
|
|
86
78
|
/**
|
|
87
79
|
* Creates an SVG spritesheet from the provided SVG files.
|
|
88
80
|
* Returns an object containing the spritesheet element and a map of icon IDs to their viewBox strings.
|
|
89
81
|
*/
|
|
90
|
-
function createSpritesheet(
|
|
82
|
+
function createSpritesheet( svgFiles, themeDir ) {
|
|
91
83
|
const symbols = [];
|
|
92
84
|
const iconViewBoxes = new Map();
|
|
93
85
|
|
|
94
86
|
for ( const [ iconId, svgPath ] of svgFiles.entries() ) {
|
|
95
|
-
const absolutePath = path.join(
|
|
87
|
+
const absolutePath = path.join( themeDir, svgPath );
|
|
96
88
|
|
|
97
89
|
if ( !fs.existsSync( absolutePath ) ) {
|
|
98
90
|
console.warn( `SVG file not found: ${ absolutePath }` );
|
|
99
91
|
continue;
|
|
100
92
|
}
|
|
101
93
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const svgContent = fs.readFileSync( absolutePath, 'utf8' );
|
|
106
|
-
const $svg = cheerio.load( svgContent, { xmlMode: true } )( 'svg' );
|
|
107
|
-
SVG_CACHE.set( absolutePath, $svg );
|
|
108
|
-
} catch ( error ) {
|
|
109
|
-
console.error( `Error reading SVG file: ${ absolutePath }`, error );
|
|
110
|
-
continue;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
94
|
+
const svgContent = fs.readFileSync( absolutePath, 'utf8' );
|
|
95
|
+
const svgDoc = parseDocument( svgContent, { xmlMode: true } );
|
|
96
|
+
const svgRoot = selectOne( 'svg', svgDoc );
|
|
113
97
|
|
|
114
|
-
|
|
98
|
+
if ( !svgRoot ) {
|
|
99
|
+
console.warn( `No <svg> root element found in: ${ absolutePath }` );
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
115
102
|
|
|
116
|
-
// Create symbol element from SVG
|
|
117
|
-
const
|
|
118
|
-
$symbol.attr( 'id', iconId );
|
|
103
|
+
// Create <symbol> element from SVG.
|
|
104
|
+
const symbolEl = new Element( 'symbol', { id: iconId } );
|
|
119
105
|
|
|
120
|
-
//
|
|
106
|
+
// Compute and set viewBox on <symbol>, also store for consumers.
|
|
121
107
|
let viewBoxValue;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
|
|
109
|
+
if ( svgRoot.attribs.viewBox ) {
|
|
110
|
+
viewBoxValue = svgRoot.attribs.viewBox;
|
|
111
|
+
} else if ( svgRoot.attribs.width && svgRoot.attribs.height ) {
|
|
112
|
+
viewBoxValue = `0 0 ${ svgRoot.attribs.width } ${ svgRoot.attribs.height }`;
|
|
126
113
|
}
|
|
127
114
|
|
|
128
115
|
if ( viewBoxValue ) {
|
|
129
|
-
|
|
116
|
+
symbolEl.attribs.viewBox = viewBoxValue;
|
|
130
117
|
iconViewBoxes.set( iconId, viewBoxValue );
|
|
131
118
|
}
|
|
132
119
|
|
|
133
|
-
// Copy
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if ( $child[ 0 ].tagName.toLowerCase() !== 'title' ) {
|
|
138
|
-
$symbol.append( $child.clone() );
|
|
120
|
+
// Copy element children (<title> is skipped).
|
|
121
|
+
for ( const child of svgRoot.children ) {
|
|
122
|
+
if ( child && child.type === 'tag' && child.name.toLowerCase() !== 'title' ) {
|
|
123
|
+
appendChild( symbolEl, cloneNode( child, true ) );
|
|
139
124
|
}
|
|
140
|
-
}
|
|
125
|
+
}
|
|
141
126
|
|
|
142
|
-
symbols.push(
|
|
143
|
-
resetFillColor( $, $symbol )
|
|
144
|
-
);
|
|
127
|
+
symbols.push( resetFillColor( symbolEl ) );
|
|
145
128
|
}
|
|
146
129
|
|
|
147
|
-
// Create the spritesheet container
|
|
148
|
-
const
|
|
149
|
-
$spritesheet.attr( {
|
|
130
|
+
// Create the spritesheet container.
|
|
131
|
+
const spritesheet = new Element( 'svg', {
|
|
150
132
|
'class': 'svg-spritesheet',
|
|
151
133
|
'xmlns': 'http://www.w3.org/2000/svg',
|
|
152
134
|
'style': 'display: none;',
|
|
153
135
|
'aria-hidden': 'true'
|
|
154
136
|
} );
|
|
155
137
|
|
|
156
|
-
// Add all symbols to the spritesheet
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
138
|
+
// Add all symbols to the spritesheet.
|
|
139
|
+
for ( const symbol of symbols ) {
|
|
140
|
+
appendChild( spritesheet, symbol );
|
|
141
|
+
}
|
|
160
142
|
|
|
161
143
|
return {
|
|
162
|
-
spritesheetElement:
|
|
144
|
+
spritesheetElement: spritesheet,
|
|
163
145
|
iconViewBoxes
|
|
164
146
|
};
|
|
165
147
|
}
|
|
166
148
|
|
|
167
149
|
/**
|
|
168
|
-
* Resets the fill color of an SVG
|
|
169
|
-
*
|
|
150
|
+
* Resets the fill color of an SVG container (e.g., <symbol>) to 'currentColor'.
|
|
151
|
+
* Ensures descendants that explicitly set 'fill' will inherit from text color in consumers.
|
|
170
152
|
*/
|
|
171
|
-
function resetFillColor(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
} );
|
|
153
|
+
function resetFillColor( el ) {
|
|
154
|
+
for ( const node of selectAll( '[fill]', el ) ) {
|
|
155
|
+
node.attribs.fill = 'currentColor';
|
|
156
|
+
}
|
|
176
157
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
$svg.attr( 'fill', 'currentColor' );
|
|
158
|
+
if ( el.attribs?.fill !== undefined ) {
|
|
159
|
+
el.attribs.fill = 'currentColor';
|
|
180
160
|
}
|
|
181
161
|
|
|
182
|
-
return
|
|
162
|
+
return el;
|
|
183
163
|
}
|
package/scripts/utils/toc.js
CHANGED
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const { parseDocument } = require( 'htmlparser2' );
|
|
9
|
+
const { Element, Text, cloneNode } = require( 'domhandler' );
|
|
10
|
+
const { default: render } = require( 'dom-serializer' );
|
|
11
|
+
const { selectAll } = require( 'css-select' );
|
|
12
|
+
const { appendChild, getAttributeValue, removeElement, textContent } = require( 'domutils' );
|
|
9
13
|
|
|
10
14
|
module.exports = function toc( data, options = {} ) {
|
|
11
|
-
const
|
|
15
|
+
const doc = parseDocument( data );
|
|
12
16
|
|
|
13
17
|
let usedHeadings = [ 'h2', 'h3', 'h4', 'h5', 'h6' ];
|
|
14
18
|
|
|
@@ -16,56 +20,106 @@ module.exports = function toc( data, options = {} ) {
|
|
|
16
20
|
usedHeadings = usedHeadings.slice( 0, options.tocLimit );
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
const selector = usedHeadings.join( ',' );
|
|
24
|
+
|
|
25
|
+
const headings = selectAll( selector, doc ).filter( heading => {
|
|
26
|
+
return !hasAncestorWithClassIn( heading, [ 'live-snippet', 'collapsing-list__item' ] );
|
|
27
|
+
} );
|
|
20
28
|
|
|
21
|
-
const headings = $( usedHeadings )
|
|
22
|
-
.filter( function() {
|
|
23
|
-
return !$( this ).parents( '.live-snippet, .collapsing-list__item' ).length;
|
|
24
|
-
} );
|
|
25
29
|
const className = options.class || 'secondary-navigation';
|
|
26
30
|
|
|
27
31
|
if ( !headings.length ) {
|
|
28
32
|
return '';
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const nav = new Element( 'nav', { class: className } );
|
|
36
|
+
const h3 = new Element( 'h3', {} );
|
|
37
|
+
appendChild( h3, new Text( 'Table of contents' ) );
|
|
38
|
+
appendChild( nav, h3 );
|
|
39
|
+
|
|
40
|
+
const tocLastLevels = [ nav, null, null, null, null, null, null ];
|
|
41
|
+
let lastLi = null;
|
|
42
|
+
|
|
43
|
+
for ( const heading of headings ) {
|
|
44
|
+
const hLevel = Number( heading.name[ 1 ] ); // 'h2' -> 2
|
|
45
|
+
const text = getHeadingText( heading ).trim();
|
|
46
|
+
const li = new Element( 'li', {} );
|
|
47
|
+
const a = new Element( 'a', {
|
|
48
|
+
class: 'a11y-focusable',
|
|
49
|
+
href: '#' + getAttributeValue( heading, 'id' ),
|
|
50
|
+
title: text
|
|
51
|
+
} );
|
|
41
52
|
|
|
42
|
-
|
|
53
|
+
appendChild( a, new Text( text ) );
|
|
54
|
+
appendChild( li, a );
|
|
43
55
|
|
|
44
56
|
if ( tocLastLevels[ hLevel ] ) {
|
|
45
|
-
//
|
|
46
|
-
tocLastLevels[ hLevel ]
|
|
57
|
+
// Append to existing <ol> for this heading level.
|
|
58
|
+
appendChild( tocLastLevels[ hLevel ], li );
|
|
47
59
|
} else {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
// No <ol> for this level yet – create it and attach to last <li> or <nav>.
|
|
61
|
+
const ol = new Element( 'ol', {} );
|
|
62
|
+
appendChild( ol, li );
|
|
63
|
+
|
|
64
|
+
if ( lastLi ) {
|
|
65
|
+
appendChild( lastLi, ol );
|
|
51
66
|
} else {
|
|
52
|
-
|
|
67
|
+
appendChild( nav, ol );
|
|
53
68
|
}
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
tocLastLevels[ hLevel ] = $r( 'ol' ).last();
|
|
70
|
+
tocLastLevels[ hLevel ] = ol;
|
|
57
71
|
}
|
|
58
72
|
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
lastLi = li;
|
|
74
|
+
|
|
75
|
+
// Clear deeper levels; they no longer apply after adding an item at this level.
|
|
61
76
|
clearLowerLevels( tocLastLevels, hLevel );
|
|
62
|
-
}
|
|
77
|
+
}
|
|
63
78
|
|
|
64
|
-
return
|
|
79
|
+
return render( nav );
|
|
65
80
|
};
|
|
66
81
|
|
|
67
82
|
function clearLowerLevels( levels, current ) {
|
|
68
83
|
for ( let i = current + 1; i < levels.length; i++ ) {
|
|
69
|
-
levels[ i ] =
|
|
84
|
+
levels[ i ] = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns true if `node` has an ancestor with any of the given classes.
|
|
90
|
+
*/
|
|
91
|
+
function hasAncestorWithClassIn( node, classNames ) {
|
|
92
|
+
for ( let p = node && node.parent; p; p = p.parent ) {
|
|
93
|
+
const cls = getAttributeValue( p, 'class' );
|
|
94
|
+
|
|
95
|
+
if ( !cls ) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const classes = cls.split( /\s+/ ).filter( Boolean );
|
|
100
|
+
|
|
101
|
+
if ( classes.some( c => classNames.includes( c ) ) ) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
70
104
|
}
|
|
105
|
+
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the heading text without content from descendants with class "headerlink".
|
|
111
|
+
*/
|
|
112
|
+
function getHeadingText( root ) {
|
|
113
|
+
if ( !root ) {
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Clone so we don't mutate the original AST.
|
|
118
|
+
const clone = cloneNode( root, true );
|
|
119
|
+
|
|
120
|
+
for ( const node of selectAll( '.headerlink', clone ) ) {
|
|
121
|
+
removeElement( node );
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return textContent( clone );
|
|
71
125
|
}
|
|
@@ -7,7 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
const upath = require( 'upath' );
|
|
9
9
|
const fs = require( 'fs-extra' );
|
|
10
|
-
const
|
|
10
|
+
const { parseDocument } = require( 'htmlparser2' );
|
|
11
|
+
const { selectAll } = require( 'css-select' );
|
|
12
|
+
const { textContent, removeElement } = require( 'domutils' );
|
|
13
|
+
const { default: TinyPool } = require( 'tinypool' );
|
|
11
14
|
const FileNameManager = require( './classes/file-name-manager' );
|
|
12
15
|
const HtmlFile = require( './classes/html-file' );
|
|
13
16
|
const DocDataFactory = require( './classes/doc-data-factory' );
|
|
@@ -21,11 +24,8 @@ const githubUrlUtils = require( '../helpers/github-url' );
|
|
|
21
24
|
const getReportIssueWidgetUrl = require( '../../scripts/utils/getreportissuewidgeturl' );
|
|
22
25
|
const getPageGroupHelper = require( '../../src/hexo/helper/get-page-group' );
|
|
23
26
|
const findTargetDoclet = require( './utils/findtargetdoclet' );
|
|
24
|
-
const writeHtmlFiles = require( '../../src/tasks/write-html-files' );
|
|
25
27
|
|
|
26
28
|
const { randomId } = require( '../../scripts/utils/random-id' );
|
|
27
|
-
const spritesheetSvg = require( '../../scripts/utils/spritesheet-svg' );
|
|
28
|
-
const inlineSvg = require( '../../scripts/utils/inline-svg' );
|
|
29
29
|
const parseHref = require( '../../scripts/utils/parse-href' );
|
|
30
30
|
const hexoManager = require( '../hexo-manager' );
|
|
31
31
|
|
|
@@ -204,12 +204,18 @@ module.exports = class ApiBuilder {
|
|
|
204
204
|
this._buildNavTree( concated );
|
|
205
205
|
this._filterHtml = this._tmplCol.renderTemplate( '_partial/filter' );
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
await this._getPages(
|
|
212
|
-
await this._getPages(
|
|
207
|
+
const pool = new TinyPool( {
|
|
208
|
+
filename: require.resolve( './build-page-worker.js' )
|
|
209
|
+
} );
|
|
210
|
+
|
|
211
|
+
await this._getPages( pool, modulesData, 'module' );
|
|
212
|
+
await this._getPages( pool, classesData, 'class' );
|
|
213
|
+
await this._getPages( pool, interfacesData, 'interface' );
|
|
214
|
+
await this._getPages( pool, typedefsData, 'typedef' );
|
|
215
|
+
await this._getPages( pool, mixinsData, 'mixin' );
|
|
216
|
+
await this._getPages( pool, namespacesData, 'namespace' );
|
|
217
|
+
|
|
218
|
+
await pool.destroy();
|
|
213
219
|
|
|
214
220
|
this._renderedFiles = [];
|
|
215
221
|
|
|
@@ -379,34 +385,30 @@ module.exports = class ApiBuilder {
|
|
|
379
385
|
}
|
|
380
386
|
}
|
|
381
387
|
|
|
382
|
-
this._navTreeHtml =
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
projectLocals: {
|
|
386
|
-
apiTree: this._navTree
|
|
387
|
-
}
|
|
388
|
+
this._navTreeHtml = this._tmplCol.renderTemplate( '_partial/navtree', {
|
|
389
|
+
projectLocals: {
|
|
390
|
+
apiTree: this._navTree
|
|
388
391
|
}
|
|
389
|
-
|
|
392
|
+
} );
|
|
390
393
|
}
|
|
391
394
|
|
|
392
395
|
/**
|
|
393
396
|
* Renders HTML of each API docs page and creates an HtmlFile instance for that page.
|
|
394
397
|
*/
|
|
395
|
-
async _getPages( items, type ) {
|
|
396
|
-
const
|
|
398
|
+
async _getPages( pool, items, type ) {
|
|
399
|
+
const jobs = [];
|
|
400
|
+
const groups = this._groups.map( g => {
|
|
401
|
+
g._url = upath.join( this._BASE_PATH, g.slug, 'index.html' );
|
|
397
402
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
403
|
+
return g;
|
|
404
|
+
} );
|
|
405
|
+
|
|
406
|
+
for ( const item of items ) {
|
|
407
|
+
const data = this._getPageData( item, type );
|
|
408
|
+
const filename = this._fileNameManager.getFilename( item.longname );
|
|
401
409
|
|
|
402
410
|
if ( filename ) {
|
|
403
411
|
const filePath = upath.join( this._destinationDir, filename );
|
|
404
|
-
const groups = this._groups.map( g => {
|
|
405
|
-
g._url = upath.join( this._BASE_PATH, g.slug, 'index.html' );
|
|
406
|
-
|
|
407
|
-
return g;
|
|
408
|
-
} );
|
|
409
|
-
|
|
410
412
|
const shortModulePath = getShortModulePath( data.longname );
|
|
411
413
|
const split = splitLongname( data.longname );
|
|
412
414
|
const splitParts = [ split.packageName, ...split.directoryNames ];
|
|
@@ -420,10 +422,20 @@ module.exports = class ApiBuilder {
|
|
|
420
422
|
title = `${ capitalize( data.kind ) } ${ capitalize( split.name ) }`;
|
|
421
423
|
}
|
|
422
424
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
425
|
+
let desc = '';
|
|
426
|
+
|
|
427
|
+
if ( data.description ) {
|
|
428
|
+
const descDoc = parseDocument( data.description.content );
|
|
429
|
+
|
|
430
|
+
for ( const pre of selectAll( 'pre', descDoc ) ) {
|
|
431
|
+
removeElement( pre );
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
desc = textContent( descDoc )
|
|
435
|
+
.trim()
|
|
436
|
+
.replace( /\n/g, ' ' )
|
|
437
|
+
.replace( / +/g, ' ' );
|
|
438
|
+
}
|
|
427
439
|
|
|
428
440
|
// eslint-disable-next-line @stylistic/max-len
|
|
429
441
|
const metaDescription = `${ this._projectName } API Documentation. The ${ capitalize( data.kind ) } ${ data.kind === 'module' ? data.longname.replace( 'module:', '' ) : capitalize( split.name ) }.${ desc.length ? ' ' + desc : '' }`;
|
|
@@ -483,17 +495,18 @@ module.exports = class ApiBuilder {
|
|
|
483
495
|
filePath
|
|
484
496
|
);
|
|
485
497
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
498
|
+
jobs.push(
|
|
499
|
+
pool.run( {
|
|
500
|
+
content: view.content,
|
|
501
|
+
navTreeHtml: this._navTreeHtml,
|
|
502
|
+
outputPath: upath.join( view.dirname, view.basename ),
|
|
503
|
+
themeDir: hexoManager.hexo.theme_dir
|
|
504
|
+
} )
|
|
505
|
+
);
|
|
493
506
|
}
|
|
494
507
|
}
|
|
495
508
|
|
|
496
|
-
|
|
509
|
+
await Promise.all( jobs );
|
|
497
510
|
|
|
498
511
|
/**
|
|
499
512
|
* Function trim text to the last sentence.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const fs = require( 'fs' );
|
|
9
|
+
const upath = require( 'upath' );
|
|
10
|
+
const { parseDocument } = require( 'htmlparser2' );
|
|
11
|
+
const { default: render } = require( 'dom-serializer' );
|
|
12
|
+
const inlineSvg = require( '../../scripts/utils/inline-svg' );
|
|
13
|
+
const spritesheetSvg = require( '../../scripts/utils/spritesheet-svg' );
|
|
14
|
+
|
|
15
|
+
module.exports = function( {
|
|
16
|
+
content,
|
|
17
|
+
navTreeHtml,
|
|
18
|
+
outputPath,
|
|
19
|
+
themeDir
|
|
20
|
+
} ) {
|
|
21
|
+
let doc = parseDocument( content );
|
|
22
|
+
doc = inlineSvg( doc, themeDir );
|
|
23
|
+
doc = spritesheetSvg( doc, themeDir );
|
|
24
|
+
content = render( doc ).replaceAll( '%%_RENDERED_NAV_TREE_%%', navTreeHtml );
|
|
25
|
+
|
|
26
|
+
const dirname = upath.dirname( outputPath );
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
fs.accessSync( dirname );
|
|
30
|
+
} catch {
|
|
31
|
+
fs.mkdirSync( dirname, { recursive: true } );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync( outputPath, content, 'utf-8' );
|
|
35
|
+
};
|