@ckeditor/ckeditor5-dev-docs 30.0.0 → 30.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.
@@ -9,8 +9,9 @@
9
9
 
10
10
  const puppeteer = require( 'puppeteer' );
11
11
  const chalk = require( 'chalk' );
12
+ const util = require( 'util' );
12
13
  const stripAnsiEscapeCodes = require( 'strip-ansi' );
13
- const { getBaseUrl, getFirstLineFromErrorMessage, toArray } = require( './utils' );
14
+ const { getBaseUrl, toArray } = require( './utils' );
14
15
  const { createSpinner, getProgressHandler } = require( './spinner' );
15
16
 
16
17
  const {
@@ -32,20 +33,40 @@ const {
32
33
  *
33
34
  * @param {Object} options Parsed CLI arguments.
34
35
  * @param {String} options.url The URL to start crawling. This argument is required.
35
- * @param {Number} options.depth Defines how many nested page levels should be examined. Infinity by default.
36
- * @param {Array.<String>} options.exclusions An array of patterns to exclude links. Empty array by default to not exclude anything.
37
- * @param {Number} options.concurrency Number of concurrent pages (browser tabs) to be used during crawling. One by default.
38
- * @param {Boolean} options.quit Terminates the scan as soon as an error is found. False (off) by default.
36
+ * @param {Number} [options.depth=Infinity] Defines how many nested page levels should be examined. Infinity by default.
37
+ * @param {Array.<String>} [options.exclusions=[]] An array of patterns to exclude links. Empty array by default to not exclude anything.
38
+ * @param {Number} [options.concurrency=1] Number of concurrent pages (browser tabs) to be used during crawling. One by default.
39
+ * @param {Boolean} [options.quit=false] Terminates the scan as soon as an error is found. False (off) by default.
39
40
  * @param {Boolean} [options.disableBrowserSandbox=false] Whether the browser should be created with the `--no-sandbox` flag.
40
41
  * @param {Boolean} [options.noSpinner=false] Whether to display the spinner with progress or a raw message with current progress.
41
42
  * @param {Boolean} [options.ignoreHTTPSErrors=false] Whether the browser should ignore invalid (self-signed) certificates.
42
43
  * @returns {Promise} Promise is resolved, when the crawler has finished the whole crawling procedure.
43
44
  */
44
45
  module.exports = async function verify( options ) {
45
- const { url, depth, exclusions, concurrency, quit, disableBrowserSandbox, noSpinner, ignoreHTTPSErrors } = options;
46
+ const {
47
+ url,
48
+ depth = Infinity,
49
+ exclusions = [],
50
+ concurrency = 1,
51
+ quit = false,
52
+ disableBrowserSandbox = false,
53
+ noSpinner = false,
54
+ ignoreHTTPSErrors = false
55
+ } = options;
46
56
 
47
57
  console.log( chalk.bold( '\n🔎 Starting the Crawler\n' ) );
48
58
 
59
+ process.on( 'unhandledRejection', reason => {
60
+ const error = util.inspect( reason, {
61
+ breakLength: Infinity,
62
+ compact: true
63
+ } );
64
+
65
+ console.log( chalk.red.bold( `\n🔥 Caught the \`unhandledRejection\` error: ${ error }\n` ) );
66
+
67
+ process.exit( 1 );
68
+ } );
69
+
49
70
  const spinner = createSpinner( { noSpinner } );
50
71
  const errors = new Map();
51
72
  const browser = await createBrowser( { disableBrowserSandbox, ignoreHTTPSErrors } );
@@ -128,18 +149,26 @@ function getErrorHandler( errors ) {
128
149
  errors.set( error.type, new Map() );
129
150
  }
130
151
 
131
- const message = getFirstLineFromErrorMessage( error.message );
152
+ // Split the message into the first line and all the rest. The first line is the key by which the errors are grouped together.
153
+ // All errors are grouped together only by the first message line (without the error call stack and other details, that could
154
+ // possibly exist after the first line), because there is a good chance that the same error can be triggered in a different
155
+ // contexts (so in a different call stacks). In order not to duplicate almost the same errors, we need to determine their common
156
+ // part.
157
+ const messageLines = error.message.split( '\n' );
158
+ const firstMessageLine = messageLines.shift();
159
+ const nextMessageLines = messageLines.join( '\n' );
132
160
 
133
161
  const errorCollection = errors.get( error.type );
134
162
 
135
- if ( !errorCollection.has( message ) ) {
136
- errorCollection.set( message, {
163
+ if ( !errorCollection.has( firstMessageLine ) ) {
164
+ errorCollection.set( firstMessageLine, {
137
165
  // Store only unique pages, because given error can occur multiple times on the same page.
138
- pages: new Set()
166
+ pages: new Set(),
167
+ details: nextMessageLines
139
168
  } );
140
169
  }
141
170
 
142
- errorCollection.get( message ).pages.add( error.pageUrl );
171
+ errorCollection.get( firstMessageLine ).pages.add( error.pageUrl );
143
172
  };
144
173
  }
145
174
 
@@ -240,7 +269,7 @@ async function openLink( browser, { baseUrl, link, foundLinks, exclusions } ) {
240
269
  // Consider navigation to be finished when the `load` event is fired and there are no network connections for at least 500 ms.
241
270
  await page.goto( link.url, { waitUntil: [ 'load', 'networkidle0' ] } );
242
271
  } catch ( error ) {
243
- const errorMessage = error.message || 'Unknown navigation error';
272
+ const errorMessage = error.message || '(empty message)';
244
273
 
245
274
  // All navigation errors starting with the `net::` prefix are already covered by the "request" error handler, so it should
246
275
  // not be also reported as the "navigation error".
@@ -261,6 +290,8 @@ async function openLink( browser, { baseUrl, link, foundLinks, exclusions } ) {
261
290
  // the page yet, because the page may contain error exclusions, that should be taken into account. Such a case can happen when,
262
291
  // for example, the `load` event was not fired because the external resource was not loaded yet.
263
292
  if ( !isResponding || page.url() !== link.url ) {
293
+ page.removeAllListeners();
294
+
264
295
  await page.close();
265
296
 
266
297
  return {
@@ -281,6 +312,8 @@ async function openLink( browser, { baseUrl, link, foundLinks, exclusions } ) {
281
312
  [] :
282
313
  await getLinksFromPage( page, { baseUrl, foundLinks, exclusions } );
283
314
 
315
+ page.removeAllListeners();
316
+
284
317
  await page.close();
285
318
 
286
319
  return {
@@ -424,9 +457,9 @@ function markErrorsAsIgnored( errors, errorIgnorePatterns ) {
424
457
  async function createPage( browser, { link, onError } ) {
425
458
  const page = await browser.newPage();
426
459
 
427
- page.setDefaultTimeout( DEFAULT_TIMEOUT );
460
+ await page.setDefaultTimeout( DEFAULT_TIMEOUT );
428
461
 
429
- page.setCacheEnabled( false );
462
+ await page.setCacheEnabled( false );
430
463
 
431
464
  dismissDialogs( page );
432
465
 
@@ -460,13 +493,13 @@ function registerErrorHandlers( page, { link, onError } ) {
460
493
  page.on( ERROR_TYPES.PAGE_CRASH.event, error => onError( {
461
494
  pageUrl: page.url(),
462
495
  type: ERROR_TYPES.PAGE_CRASH,
463
- message: error.message
496
+ message: error.message || '(empty message)'
464
497
  } ) );
465
498
 
466
499
  page.on( ERROR_TYPES.UNCAUGHT_EXCEPTION.event, error => onError( {
467
500
  pageUrl: page.url(),
468
501
  type: ERROR_TYPES.UNCAUGHT_EXCEPTION,
469
- message: error.message
502
+ message: error.message || '(empty message)'
470
503
  } ) );
471
504
 
472
505
  page.on( ERROR_TYPES.REQUEST_FAILURE.event, request => {
@@ -524,9 +557,14 @@ function registerErrorHandlers( page, { link, onError } ) {
524
557
  }
525
558
 
526
559
  const serializeArgumentInPageContext = argument => {
527
- // Since errors are not serializable, return message from this error as the output text.
560
+ // Since errors are not serializable, return the message with the call stack as the output text.
528
561
  if ( argument instanceof Error ) {
529
- return argument.message;
562
+ return argument.stack;
563
+ }
564
+
565
+ // Cast non-string iterable argument to an array.
566
+ if ( typeof argument !== 'string' && argument[ Symbol.iterator ] ) {
567
+ return [ ...argument ];
530
568
  }
531
569
 
532
570
  // Return argument right away. Since we use `executionContext().evaluate()`, it'll return JSON value of the
@@ -540,10 +578,24 @@ function registerErrorHandlers( page, { link, onError } ) {
540
578
 
541
579
  const serializedArguments = await Promise.all( message.args().map( serializeArguments ) );
542
580
 
581
+ const serializedMessage = serializedArguments
582
+ .map( argument => {
583
+ // Do not wrap the string in additional quotes and just return it as is.
584
+ if ( typeof argument === 'string' ) {
585
+ return argument;
586
+ }
587
+
588
+ return util.inspect( argument, {
589
+ breakLength: Infinity,
590
+ compact: true
591
+ } );
592
+ } )
593
+ .join( ' ' );
594
+
543
595
  onError( {
544
596
  pageUrl: page.url(),
545
597
  type: ERROR_TYPES.CONSOLE_ERROR,
546
- message: serializedArguments.length ? serializedArguments.join( '. ' ) : message.text()
598
+ message: serializedMessage || message.text() || '(empty message)'
547
599
  } );
548
600
  } );
549
601
  }
@@ -617,6 +669,10 @@ function logErrors( errors ) {
617
669
  errorCollection.forEach( ( error, message ) => {
618
670
  console.group( `\n❌ ${ message }` );
619
671
 
672
+ if ( error.details ) {
673
+ console.log( error.details );
674
+ }
675
+
620
676
  console.log( chalk.red( `\n…found on the following ${ error.pages.size > 1 ? 'pages' : 'page' }:` ) );
621
677
 
622
678
  error.pages.forEach( pageUrl => console.log( chalk.gray( `➥ ${ pageUrl }` ) ) );
@@ -658,6 +714,7 @@ function logErrors( errors ) {
658
714
  /**
659
715
  * @typedef {Object.<String, Set.<String>>} ErrorOccurrence
660
716
  * @property {Set.<String>} pages A set of unique pages, where error has been found.
717
+ * @property {Set.<String>} [details] Additional error details (i.e. an error stack).
661
718
  */
662
719
 
663
720
  /**
@@ -20,16 +20,6 @@ function getBaseUrl( url ) {
20
20
  return `${ origin }${ pathname }`;
21
21
  }
22
22
 
23
- /**
24
- * Extracts first line from error message.
25
- *
26
- * @param {String} message Error message.
27
- * @returns {String}
28
- */
29
- function getFirstLineFromErrorMessage( message ) {
30
- return message.split( '\n' ).shift();
31
- }
32
-
33
23
  /**
34
24
  * Checks, if provided string is a valid URL utilizing the HTTP or HTTPS protocols.
35
25
  *
@@ -56,7 +46,6 @@ function toArray( data ) {
56
46
 
57
47
  module.exports = {
58
48
  getBaseUrl,
59
- getFirstLineFromErrorMessage,
60
49
  isUrlValid,
61
50
  toArray
62
51
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-docs",
3
- "version": "30.0.0",
3
+ "version": "30.1.1",
4
4
  "description": "Tasks used to build and verify the documentation for CKEditor 5.",
5
5
  "keywords": [],
6
6
  "main": "lib/index.js",
7
7
  "dependencies": {
8
- "@ckeditor/ckeditor5-dev-utils": "^30.0.0",
9
- "@ckeditor/jsdoc-plugins": "^30.0.0",
8
+ "@ckeditor/ckeditor5-dev-utils": "^30.1.1",
9
+ "@ckeditor/jsdoc-plugins": "^30.1.1",
10
10
  "fast-glob": "^3.2.4",
11
11
  "fs-extra": "^9.0.0",
12
12
  "puppeteer": "^13.1.3",