umberto 4.3.0 → 4.4.0-alpha.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/CHANGELOG.md +7 -0
- package/package.json +2 -2
- package/scripts/utils/execute-and-insert-function-results.js +1 -1
- package/src/helpers/import-module.js +31 -0
- package/src/index.js +19 -17
- package/src/tasks/build-documentation.js +27 -15
- package/src/tasks/create-sym-links.js +7 -4
- package/src/tasks/execute-hooks.js +35 -29
- package/src/tasks/get-project-config.js +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
## [4.4.0-alpha.0](https://github.com/cksource/umberto/compare/v4.3.0...v4.4.0-alpha.0) (2024-09-20)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Added support for loading hooks (`beforeHexo` and `afterHexo`) and `scripts` in ESM format in Umberto. Closes [#1214](https://github.com/cksource/umberto/issues/1214). ([commit](https://github.com/cksource/umberto/commit/2849bef1e59108f863ea0a64314ab980a7f543b6))
|
|
9
|
+
|
|
10
|
+
|
|
4
11
|
## [4.3.0](https://github.com/cksource/umberto/compare/v4.2.6...v4.3.0) (2024-09-16)
|
|
5
12
|
|
|
6
13
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "umberto",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.0-alpha.0",
|
|
4
4
|
"description": "CKSource Documentation builder",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"url": "https://github.com/cksource/umberto.git"
|
|
69
69
|
},
|
|
70
70
|
"lint-staged": {
|
|
71
|
-
"**/*.js": [
|
|
71
|
+
"**/*.{js,mjs,cjs}": [
|
|
72
72
|
"eslint --quiet"
|
|
73
73
|
]
|
|
74
74
|
},
|
|
@@ -9,7 +9,7 @@ const upath = require( 'upath' );
|
|
|
9
9
|
const fs = require( 'fs' );
|
|
10
10
|
|
|
11
11
|
const EXEC_REGEXP = /\\?{@exec ([^}]+)\\?}/g;
|
|
12
|
-
const PATH_REGEXP = /[\w.\\/-]+\.js$/;
|
|
12
|
+
const PATH_REGEXP = /[\w.\\/-]+\.c?js$/;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Replaces the `{@exec path/to/function.js}` expression with the module results.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2017-2024, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const upath = require( 'upath' );
|
|
9
|
+
const { pathToFileURL } = require( 'url' );
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Loads the specified module using `import()` call.
|
|
13
|
+
*
|
|
14
|
+
* @param {String} modulePath
|
|
15
|
+
* @returns {Promise.<Function>}
|
|
16
|
+
*/
|
|
17
|
+
module.exports = async function importModule( modulePath ) {
|
|
18
|
+
const modulePathAsFileURL = getPathAsFileURL( modulePath );
|
|
19
|
+
|
|
20
|
+
return ( await import( modulePathAsFileURL ) ).default;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function getPathAsFileURL( modulePath ) {
|
|
24
|
+
const extension = upath.extname( modulePath );
|
|
25
|
+
|
|
26
|
+
if ( extension === '.js' || extension === '.mjs' || extension === '.cjs' ) {
|
|
27
|
+
return pathToFileURL( modulePath );
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return pathToFileURL( modulePath + '.js' );
|
|
31
|
+
}
|
package/src/index.js
CHANGED
|
@@ -47,25 +47,27 @@ module.exports = {
|
|
|
47
47
|
* @returns {Promise}
|
|
48
48
|
*/
|
|
49
49
|
buildSingleProject( options ) {
|
|
50
|
-
const pConfig = getProjectConfig( process.cwd(), options );
|
|
51
50
|
const timer = process.hrtime();
|
|
52
51
|
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
52
|
+
return getProjectConfig( process.cwd(), options )
|
|
53
|
+
.then( pConfig => {
|
|
54
|
+
return buildDocumentation( Object.assign(
|
|
55
|
+
options,
|
|
56
|
+
{
|
|
57
|
+
mainConfig: {
|
|
58
|
+
projects: [ '.' ],
|
|
59
|
+
docsearch: pConfig.docsearch,
|
|
60
|
+
googleoptimize: pConfig.googleoptimize,
|
|
61
|
+
googletagmanager: pConfig.googletagmanager,
|
|
62
|
+
googleanalytics: pConfig.googleanalytics,
|
|
63
|
+
feedbackWidget: pConfig.feedbackWidget,
|
|
64
|
+
isSingleProject: true,
|
|
65
|
+
canonicalUrlBeginning: pConfig.canonicalUrlBeginning,
|
|
66
|
+
vwo: pConfig.vwo
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
) );
|
|
70
|
+
} )
|
|
69
71
|
.then( hexoManager => postBuild( options, hexoManager ) )
|
|
70
72
|
.then( hexoManager => {
|
|
71
73
|
const time = process.hrtime( timer );
|
|
@@ -127,7 +127,11 @@ module.exports = options => {
|
|
|
127
127
|
variables: mainConfig.variables
|
|
128
128
|
} );
|
|
129
129
|
} )
|
|
130
|
-
.then( () =>
|
|
130
|
+
.then( async () => {
|
|
131
|
+
const configs = await getProjectConfigs( rootPath, projectPaths );
|
|
132
|
+
|
|
133
|
+
return executeHooks( configs, 'beforeHexo' );
|
|
134
|
+
} )
|
|
131
135
|
.then( () => {
|
|
132
136
|
return buildProjects( rootPath, projectPaths, {
|
|
133
137
|
skipApi,
|
|
@@ -155,12 +159,12 @@ module.exports = options => {
|
|
|
155
159
|
.then( () => copyProjectIcons( hexoManager.hexo.projectGlobals, hexoManager.getPublicDir() ) )
|
|
156
160
|
// A workaround for API guides when API generation is skipped.
|
|
157
161
|
// Used for a local build (dev==true) to reuse existing API doc files.
|
|
158
|
-
.then( () => {
|
|
162
|
+
.then( async () => {
|
|
159
163
|
if ( !dev ) {
|
|
160
164
|
return Promise.resolve();
|
|
161
165
|
}
|
|
162
166
|
|
|
163
|
-
const projectConfigs = getProjectConfigs( rootPath, projectPaths, {
|
|
167
|
+
const projectConfigs = await getProjectConfigs( rootPath, projectPaths, {
|
|
164
168
|
skipLiveSnippets
|
|
165
169
|
} );
|
|
166
170
|
const buildDir = hexoManager.getPublicDir();
|
|
@@ -192,8 +196,8 @@ module.exports = options => {
|
|
|
192
196
|
return Promise.resolve();
|
|
193
197
|
} )
|
|
194
198
|
// Links validation.
|
|
195
|
-
.then( () => {
|
|
196
|
-
const projectConfigs = getProjectConfigs( rootPath, projectPaths, {
|
|
199
|
+
.then( async () => {
|
|
200
|
+
const projectConfigs = await getProjectConfigs( rootPath, projectPaths, {
|
|
197
201
|
skipLiveSnippets
|
|
198
202
|
} );
|
|
199
203
|
|
|
@@ -219,10 +223,10 @@ module.exports = options => {
|
|
|
219
223
|
} );
|
|
220
224
|
} )
|
|
221
225
|
// Create sitemap.xml file.
|
|
222
|
-
.then( () => {
|
|
226
|
+
.then( async () => {
|
|
223
227
|
let hostname;
|
|
224
228
|
let dst = '';
|
|
225
|
-
const projectConfigs = getProjectConfigs( rootPath, projectPaths );
|
|
229
|
+
const projectConfigs = await getProjectConfigs( rootPath, projectPaths );
|
|
226
230
|
const config = mainConfig.isSingleProject && projectConfigs && projectConfigs.length > 0 ?
|
|
227
231
|
projectConfigs[ 0 ] : {};
|
|
228
232
|
const sitemapConfig = mainConfig.sitemap ? mainConfig.sitemap : config.sitemap;
|
|
@@ -275,7 +279,12 @@ module.exports = options => {
|
|
|
275
279
|
extraUrlSettings
|
|
276
280
|
} );
|
|
277
281
|
} )
|
|
278
|
-
|
|
282
|
+
|
|
283
|
+
.then( async () => {
|
|
284
|
+
const configs = await getProjectConfigs( rootPath, projectPaths );
|
|
285
|
+
|
|
286
|
+
return executeHooks( configs, 'afterHexo' );
|
|
287
|
+
} )
|
|
279
288
|
.then( () => hexoManager );
|
|
280
289
|
};
|
|
281
290
|
|
|
@@ -291,8 +300,8 @@ module.exports = options => {
|
|
|
291
300
|
* @param {Boolean} options.skipGuides Whether to skip processing guides.
|
|
292
301
|
* @return {Promise}
|
|
293
302
|
*/
|
|
294
|
-
function buildProjects( rootPath, projectPaths, options = {} ) {
|
|
295
|
-
const projectConfigs = getProjectConfigs( rootPath, projectPaths, options );
|
|
303
|
+
async function buildProjects( rootPath, projectPaths, options = {} ) {
|
|
304
|
+
const projectConfigs = await getProjectConfigs( rootPath, projectPaths, options );
|
|
296
305
|
const promises = [];
|
|
297
306
|
|
|
298
307
|
// If the `guides` (non-empty array) or `skipGuides` options are specified, disable the `{@link...}` validator.
|
|
@@ -416,19 +425,22 @@ function buildProjects( rootPath, projectPaths, options = {} ) {
|
|
|
416
425
|
}
|
|
417
426
|
|
|
418
427
|
// Gets every project's umberto.json config file.
|
|
419
|
-
function getProjectConfigs( rootPath, projectPaths, options = {} ) {
|
|
420
|
-
|
|
428
|
+
async function getProjectConfigs( rootPath, projectPaths, options = {} ) {
|
|
429
|
+
const promises = projectPaths.map( async pPath => {
|
|
421
430
|
const projectRootPath = upath.join( rootPath, pPath );
|
|
422
431
|
|
|
423
432
|
return Object.assign(
|
|
424
|
-
|
|
425
|
-
skipLiveSnippets: options.skipLiveSnippets
|
|
426
|
-
} ),
|
|
433
|
+
{},
|
|
427
434
|
{
|
|
435
|
+
...await getProjectConfig( projectRootPath, {
|
|
436
|
+
skipLiveSnippets: options.skipLiveSnippets
|
|
437
|
+
} ),
|
|
428
438
|
projectRootPath
|
|
429
439
|
}
|
|
430
440
|
);
|
|
431
441
|
} );
|
|
442
|
+
|
|
443
|
+
return Promise.all( promises );
|
|
432
444
|
}
|
|
433
445
|
|
|
434
446
|
// Renders API docs HTML. API docs are rendered separate from hexo so hexo filters and helpers are not available.
|
|
@@ -11,19 +11,20 @@ const upath = require( 'upath' );
|
|
|
11
11
|
const fs = require( 'fs' );
|
|
12
12
|
const chalk = require( 'chalk' );
|
|
13
13
|
|
|
14
|
-
module.exports = ( {
|
|
14
|
+
module.exports = async ( {
|
|
15
15
|
rootPath,
|
|
16
16
|
isSingleProject = false
|
|
17
17
|
} ) => {
|
|
18
18
|
const configs = [];
|
|
19
19
|
|
|
20
20
|
if ( isSingleProject ) {
|
|
21
|
-
configs.push( getProjectConfig( rootPath, { skipLiveSnippets: true } ) );
|
|
21
|
+
configs.push( await getProjectConfig( rootPath, { skipLiveSnippets: true } ) );
|
|
22
22
|
} else {
|
|
23
23
|
const projectPaths = getMainConfig( rootPath ).projects;
|
|
24
|
-
projectPaths.
|
|
25
|
-
|
|
24
|
+
const promises = projectPaths.map( projectPath => {
|
|
25
|
+
return getProjectConfig( upath.join( rootPath, projectPath ), { skipLiveSnippets: true } );
|
|
26
26
|
} );
|
|
27
|
+
configs.push( ...await Promise.all( promises ) );
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
configs
|
|
@@ -32,6 +33,8 @@ module.exports = ( {
|
|
|
32
33
|
createSymbolicLink( config );
|
|
33
34
|
} );
|
|
34
35
|
|
|
36
|
+
return Promise.resolve();
|
|
37
|
+
|
|
35
38
|
function createSymbolicLink( config ) {
|
|
36
39
|
const destinationPath = upath.join( rootPath, 'build', 'docs', config.slug, 'latest' );
|
|
37
40
|
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const upath = require( 'upath' );
|
|
9
|
+
const importModule = require( '../helpers/import-module' );
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* This function executes all hooks of the specified type for each config passed in the configs array.
|
|
@@ -22,42 +23,47 @@ const upath = require( 'upath' );
|
|
|
22
23
|
* }
|
|
23
24
|
*
|
|
24
25
|
* @param {Array} configs Array of all the configurations that might contain hooks to execute.
|
|
25
|
-
* @param {
|
|
26
|
+
* @param {'beforeHexo'|'afterHexo'} hookName Name of a hook to execute.
|
|
26
27
|
* @returns {Promise}
|
|
27
28
|
*/
|
|
28
|
-
module.exports = function executeHooks( configs, hookName ) {
|
|
29
|
-
|
|
29
|
+
module.exports = async function executeHooks( configs, hookName ) {
|
|
30
|
+
try {
|
|
31
|
+
for ( const config of configs ) {
|
|
32
|
+
if ( !config.hooks || !config.hooks[ hookName ] ) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
for ( const scriptPath of config.hooks[ hookName ] ) {
|
|
37
|
+
await processSingleHook( upath.dirname( config.__configPath ), scriptPath );
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
promise = promise
|
|
40
|
-
.then( () => {
|
|
41
|
-
// The `callback` may be synchronous function. Let's wrap it in the promise chain.
|
|
42
|
-
return new Promise( resolve => {
|
|
43
|
-
const callback = require( callbackAbsolutePath );
|
|
44
|
-
|
|
45
|
-
return resolve( callback() );
|
|
46
|
-
} ).catch( error => {
|
|
47
|
-
// Store a path to the executed file.
|
|
48
|
-
error._filePath = scriptPath;
|
|
49
|
-
|
|
50
|
-
// Rejecting the promise allows catching it later.
|
|
51
|
-
return Promise.reject( error );
|
|
52
|
-
} );
|
|
53
|
-
} );
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return promise.catch( error => {
|
|
41
|
+
return Promise.resolve();
|
|
42
|
+
} catch ( error ) {
|
|
58
43
|
console.error( `The "${ hookName }" hook pointing to the "${ error._filePath }" file ended with an error.`, error.message );
|
|
59
44
|
|
|
60
45
|
// Re-throwing the error as we want to break the build process.
|
|
61
46
|
throw error;
|
|
62
|
-
}
|
|
47
|
+
}
|
|
63
48
|
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {String} configPath
|
|
52
|
+
* @param {String} scriptPath
|
|
53
|
+
* @return {Promise}
|
|
54
|
+
*/
|
|
55
|
+
async function processSingleHook( configPath, scriptPath ) {
|
|
56
|
+
const callbackAbsolutePath = upath.join( configPath, scriptPath );
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const callback = await importModule( callbackAbsolutePath );
|
|
60
|
+
|
|
61
|
+
await callback();
|
|
62
|
+
} catch ( error ) {
|
|
63
|
+
// Store a path to the executed file.
|
|
64
|
+
error._filePath = scriptPath;
|
|
65
|
+
|
|
66
|
+
// Rejecting the promise allows catching it later.
|
|
67
|
+
return Promise.reject( error );
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const upath = require( 'upath' );
|
|
9
9
|
const fs = require( 'fs' );
|
|
10
10
|
const glob = require( 'glob' );
|
|
11
|
+
const importModule = require( '../helpers/import-module' );
|
|
11
12
|
|
|
12
13
|
const cache = new Map();
|
|
13
14
|
|
|
@@ -17,9 +18,9 @@ const cache = new Map();
|
|
|
17
18
|
* @param {String} rootPath
|
|
18
19
|
* @param {Object} [options={}]
|
|
19
20
|
* @param {Boolean} [options.skipLiveSnippets=false] If set to `true`, the `snippetAdapter` module will not be added to the configuration.
|
|
20
|
-
* @returns {
|
|
21
|
+
* @returns {Promise}
|
|
21
22
|
*/
|
|
22
|
-
module.exports = ( rootPath, options = {} ) => {
|
|
23
|
+
module.exports = async ( rootPath, options = {} ) => {
|
|
23
24
|
// Let's normalize the root path to fix mixed slashes and backslashes. Root path should contain only slashes.
|
|
24
25
|
rootPath = rootPath.split( /[\\/]/g ).join( '/' );
|
|
25
26
|
|
|
@@ -35,7 +36,7 @@ module.exports = ( rootPath, options = {} ) => {
|
|
|
35
36
|
|
|
36
37
|
// Get the `realImportPath()` function. It displays an import path of a class below the class title.
|
|
37
38
|
// See: https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html.
|
|
38
|
-
loadScriptToConfig( config, {
|
|
39
|
+
await loadScriptToConfig( config, {
|
|
39
40
|
scriptKey: 'import-path',
|
|
40
41
|
configKey: 'getRealImportPath',
|
|
41
42
|
configPath,
|
|
@@ -47,7 +48,7 @@ module.exports = ( rootPath, options = {} ) => {
|
|
|
47
48
|
} );
|
|
48
49
|
|
|
49
50
|
// Load the `insertChangelog()` script that allows inserting a changelog to a guide.
|
|
50
|
-
loadScriptToConfig( config, {
|
|
51
|
+
await loadScriptToConfig( config, {
|
|
51
52
|
scriptKey: 'insert-changelog',
|
|
52
53
|
configKey: 'insertChangelog',
|
|
53
54
|
configPath,
|
|
@@ -58,7 +59,7 @@ module.exports = ( rootPath, options = {} ) => {
|
|
|
58
59
|
|
|
59
60
|
// Load the snippet adapter only if snippets will be built.
|
|
60
61
|
if ( !options.skipLiveSnippets ) {
|
|
61
|
-
loadScriptToConfig( config, {
|
|
62
|
+
await loadScriptToConfig( config, {
|
|
62
63
|
scriptKey: 'snippet-adapter',
|
|
63
64
|
configKey: 'snippetAdapter',
|
|
64
65
|
configPath,
|
|
@@ -146,7 +147,7 @@ function getConfigurationFile( configPath ) {
|
|
|
146
147
|
* @param {String} options.configPath An absolute path to the configuration file. It is used to obtain a path of the function to load.
|
|
147
148
|
* @param {Function} options.onError A callback will be executed if the function could not be load.
|
|
148
149
|
*/
|
|
149
|
-
function loadScriptToConfig( config, options ) {
|
|
150
|
+
async function loadScriptToConfig( config, options ) {
|
|
150
151
|
if ( !config.scripts ) {
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
@@ -160,13 +161,13 @@ function loadScriptToConfig( config, options ) {
|
|
|
160
161
|
|
|
161
162
|
try {
|
|
162
163
|
// Tries to load the function from the current working directory.
|
|
163
|
-
config[ configKey ] =
|
|
164
|
+
config[ configKey ] = await importModule( config.scripts[ scriptKey ] );
|
|
164
165
|
} catch ( err ) {
|
|
165
166
|
// If failed, let's resolve the path from the directory where the configuration file is located.
|
|
166
167
|
const fnPath = upath.join( upath.dirname( options.configPath ), config.scripts[ scriptKey ] );
|
|
167
168
|
|
|
168
169
|
try {
|
|
169
|
-
config[ configKey ] =
|
|
170
|
+
config[ configKey ] = await importModule( fnPath );
|
|
170
171
|
} catch ( nestedErr ) {
|
|
171
172
|
// If still fails, let's call the callback and throw an error.
|
|
172
173
|
options.onError( err );
|