umberto 10.7.0 → 10.7.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 CHANGED
@@ -1,6 +1,17 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ ## [10.7.1](https://github.com/cksource/umberto/compare/v10.7.0...v10.7.1) (July 15, 2026)
5
+
6
+ ### Bug fixes
7
+
8
+ * Fixed source links for CKEditor 5 packages in API documentation so they now point to the public repository. Links for the latest documentation point to the `master` branch, and links for LTS documentation point to the matching `release-v*` branch.
9
+
10
+ ### Other changes
11
+
12
+ * Improved the performance of the documentation build. Pug templates of XML components used in Markdown are now compiled once and reused across pages, instead of being recompiled for every component occurrence. This shortens the page generation phase by about 20%.
13
+
14
+
4
15
  ## [10.7.0](https://github.com/cksource/umberto/compare/v10.6.1...v10.7.0) (July 2, 2026)
5
16
 
6
17
  ### Features
@@ -46,13 +57,6 @@ Changelog
46
57
 
47
58
  * Changed the call for feedback title from `<h3>` to `<div>` to prevent it from appearing in the table of contents.
48
59
 
49
-
50
- ## [10.5.0](https://github.com/cksource/umberto/compare/v10.4.1...v10.5.0) (April 14, 2026)
51
-
52
- ### Features
53
-
54
- * Added the Call for feedback component for documentation pages.
55
-
56
60
  ---
57
61
 
58
62
  To see all releases, visit the [release page](https://github.com/cksource/umberto/releases).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umberto",
3
- "version": "10.7.0",
3
+ "version": "10.7.1",
4
4
  "description": "CKSource Documentation builder",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -15,8 +15,8 @@
15
15
  "CHANGELOG.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@babel/core": "^7.18.10",
19
- "@babel/preset-env": "^7.29.5",
18
+ "@babel/core": "^7.29.7",
19
+ "@babel/preset-env": "^7.29.7",
20
20
  "@minify-html/node": "^0.17.1",
21
21
  "@vscode/markdown-it-katex": "^1.1.2",
22
22
  "babel-loader": "^10.0.0",
@@ -41,7 +41,7 @@
41
41
  "jquery": "~3.7.1",
42
42
  "js-beautify": "^1.14.4",
43
43
  "lodash": "^4.17.21",
44
- "markdown-it": "^14.1.1",
44
+ "markdown-it": "^14.2.0",
45
45
  "markdown-it-abbr": "^1.0.4",
46
46
  "markdown-it-deflist": "^2.1.0",
47
47
  "markdown-it-emoji": "^2.0.2",
@@ -9,6 +9,39 @@ const transformXMLTreeToPug = require( './transform-xml-tree-to-pug.cjs' );
9
9
  const XMLComponentsParser = require( './parser/xml-components-parser.cjs' );
10
10
  const { walkXMLTree, TreeWalkActions } = require( './parser/walk-xml-tree.cjs' );
11
11
 
12
+ // Compiled Pug templates of component mixin calls, keyed by the generated template text.
13
+ // Compiling a template is expensive because Pug lexes and parses the whole `include` tree
14
+ // on every compilation, and the same mixin calls repeat across pages. The compiled function
15
+ // is not bound to any locals, so it can be safely reused with per-page locals.
16
+ //
17
+ // The cache lives for the whole process, like the cache in `createPrerenderPugTemplate()`,
18
+ // so changes to component Pug files require a restart in the watch mode.
19
+ const compiledTemplateCache = new Map();
20
+
21
+ /**
22
+ * Compiles a Pug template through the Hexo Pug renderer, reusing previously compiled templates.
23
+ *
24
+ * Falls back to `null` when the renderer does not support compilation, in which case the caller
25
+ * should render through `hexo.render.renderSync()`.
26
+ */
27
+ function getCompiledPugTemplate( hexo, text, path ) {
28
+ const cacheKey = `${ path }\n${ text }`;
29
+ let template = compiledTemplateCache.get( cacheKey );
30
+
31
+ if ( !template ) {
32
+ const pugRenderer = hexo.render.renderer?.get( 'pug' );
33
+
34
+ if ( !pugRenderer?.compile ) {
35
+ return null;
36
+ }
37
+
38
+ template = pugRenderer.compile( { text, path } );
39
+ compiledTemplateCache.set( cacheKey, template );
40
+ }
41
+
42
+ return template;
43
+ }
44
+
12
45
  /**
13
46
  * Slices XML components from Markdown content and transforms them to Pug format.
14
47
  *
@@ -93,14 +126,18 @@ function renderXMLPugComponentsInMarkdown( xml, options ) {
93
126
  let finalReplacement = null;
94
127
 
95
128
  try {
96
- finalReplacement = hexo.render.renderSync(
97
- {
98
- engine: 'pug',
99
- text: content,
100
- path: basePath
101
- },
102
- locals
103
- ).trim();
129
+ const template = getCompiledPugTemplate( hexo, content, basePath );
130
+
131
+ finalReplacement = template ?
132
+ template( locals ).trim() :
133
+ hexo.render.renderSync(
134
+ {
135
+ engine: 'pug',
136
+ text: content,
137
+ path: basePath
138
+ },
139
+ locals
140
+ ).trim();
104
141
  } catch ( error ) {
105
142
  console.error( error );
106
143
  console.error( `Error rendering component "${ node.name }" in file ${ sourceName }` );
@@ -19,6 +19,7 @@ import { getShortModulePath } from '../helpers/get-short-module-path.js';
19
19
  import { getDocsearchConfig as getDocSearchConfig } from '../helpers/get-docsearch-config.js';
20
20
  import { splitLongname } from '../helpers/split-longname.js';
21
21
  import { getIssueUrl, getFullGithubLink } from '../helpers/github-url.js';
22
+ import { rewritePublicSourceUrl } from '../helpers/rewrite-public-source-url.js';
22
23
  import getReportIssueWidgetUrl from '../../scripts/utils/getreportissuewidgeturl.cjs';
23
24
  import { randomId } from '../../scripts/utils/random-id.cjs';
24
25
  import parseHref from '../../scripts/utils/parse-href.cjs';
@@ -521,6 +522,7 @@ export class ApiBuilder {
521
522
  groups,
522
523
  seeSourceRepositoryUrl: this._projectConfig.github !== undefined ? this._projectConfig.github.url : realImportPath,
523
524
  getLinkToSource: getFullGithubLink.bind( this ),
525
+ rewritePublicSourceUrl: url => rewritePublicSourceUrl( url, this._projectConfig.ckeditor5SourceLinkBranch ),
524
526
  docSearchConfig: getDocSearchConfig( this._docSearch, {
525
527
  groups: this._groups,
526
528
  slug: this._projectSlug,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license Copyright (c) 2017-2026, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ /**
7
+ * Rewrites CKEditor 5 public package source links generated from the commercial repository checkout.
8
+ *
9
+ * @param {String} sourceUrl
10
+ * @param {String} sourceBranch
11
+ * @returns {String}
12
+ */
13
+ export function rewritePublicSourceUrl( sourceUrl, sourceBranch ) {
14
+ if ( !sourceUrl || !sourceBranch ) {
15
+ return sourceUrl;
16
+ }
17
+
18
+ let url;
19
+
20
+ try {
21
+ url = new URL( sourceUrl );
22
+ } catch {
23
+ return sourceUrl;
24
+ }
25
+
26
+ if ( url.hostname !== 'github.com' ) {
27
+ return sourceUrl;
28
+ }
29
+
30
+ const match = url.pathname.match( /^\/[^/]+\/ckeditor5-commercial\/blob\/[^/]+\/external\/ckeditor5\/(.+)$/ );
31
+
32
+ if ( !match ) {
33
+ return sourceUrl;
34
+ }
35
+
36
+ return [
37
+ 'https://github.com/ckeditor/ckeditor5',
38
+ 'blob',
39
+ sourceBranch,
40
+ match[ 1 ]
41
+ ].join( '/' ) + url.search + url.hash;
42
+ }
@@ -92,6 +92,7 @@ export const getProjectConfig = async ( rootPath, options = {} ) => {
92
92
  validateConfiguration( config );
93
93
 
94
94
  config.version = getProjectVersion( rootPath );
95
+ config.ckeditor5SourceLinkBranch = getCKEditor5SourceLinkBranch( rootPath, config );
95
96
 
96
97
  parseGitHubUrls( config, rootPath );
97
98
 
@@ -304,6 +305,44 @@ function getProjectVersion( rootPath ) {
304
305
  return version || 'latest';
305
306
  }
306
307
 
308
+ /**
309
+ * Returns CKEditor 5 source link branch based on LTS metadata from `package.json`.
310
+ *
311
+ * @param {String} rootPath
312
+ * @param {Object} config
313
+ * @param {String} config.slug
314
+ * @param {String} config.version
315
+ * @return {String|undefined}
316
+ */
317
+ function getCKEditor5SourceLinkBranch( rootPath, config ) {
318
+ if ( config.slug !== 'ckeditor5' ) {
319
+ return;
320
+ }
321
+
322
+ const packageJson = getPackageJson( rootPath );
323
+ const ltsVersions = packageJson ? packageJson[ 'ck-lts-versions' ] : null;
324
+ const releaseLine = getReleaseLine( config.version );
325
+ const ltsReleaseLines = Array.isArray( ltsVersions ) ? ltsVersions.map( Number ) : null;
326
+
327
+ if ( !ltsReleaseLines || releaseLine === null ) {
328
+ return;
329
+ }
330
+
331
+ const isLtsVersion = ltsReleaseLines.includes( releaseLine );
332
+
333
+ return isLtsVersion ? `release-v${ releaseLine }` : 'master';
334
+ }
335
+
336
+ /**
337
+ * @param {String} version
338
+ * @return {Number|null}
339
+ */
340
+ function getReleaseLine( version ) {
341
+ const match = version.match( /^(\d+)\./ );
342
+
343
+ return match ? Number( match[ 1 ] ) : null;
344
+ }
345
+
307
346
  /**
308
347
  * Tries to find a URL to the repository based on the value in `package.json`, under the "repository" object.
309
348
  *
@@ -1,7 +1,8 @@
1
1
  mixin seeSource( item )
2
2
  //- Do not render "See source" button if the module or the parsed item (e.g. a function) has the `@skipsource` annotation
3
3
  if ( projectLocals.seeSourceRepositoryUrl && item.file && !( data.skipSource || item.skipSource ) )
4
- - const link = item.file.url || projectLocals.getLinkToSource( projectLocals.seeSourceRepositoryUrl, item.file );
4
+ - const sourceLink = item.file.url || projectLocals.getLinkToSource( projectLocals.seeSourceRepositoryUrl, item.file );
5
+ - const link = projectLocals.rewritePublicSourceUrl( sourceLink );
5
6
 
6
7
  if link
7
8
  .see-source