@caweb/webpack 1.2.27 → 1.3.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.
Files changed (83) hide show
  1. package/.github/workflows/config-publish.yml +33 -0
  2. package/.github/workflows/plugins-publish.yml +45 -0
  3. package/.github/workflows/test +29 -0
  4. package/.gitmodules +3 -0
  5. package/README.md +1 -2
  6. package/changelog.txt +137 -0
  7. package/package.json +9 -16
  8. package/plugins/a11y/README.md +19 -0
  9. package/plugins/a11y/aceconfig.js +44 -0
  10. package/plugins/a11y/changelog.txt +31 -0
  11. package/plugins/a11y/index.js +271 -0
  12. package/plugins/a11y/package-lock.json +3303 -0
  13. package/plugins/a11y/package.json +42 -0
  14. package/plugins/css-audit/README.md +20 -0
  15. package/plugins/css-audit/changelog.txt +41 -0
  16. package/plugins/css-audit/css-audit.config.cjs +5 -0
  17. package/plugins/css-audit/default.config.js +19 -0
  18. package/plugins/css-audit/index.js +326 -0
  19. package/plugins/css-audit/package-lock.json +1522 -0
  20. package/plugins/css-audit/package.json +53 -0
  21. package/plugins/css-audit/sample/no-files.html +9 -0
  22. package/plugins/html/README.md +40 -0
  23. package/plugins/html/changelog.txt +134 -0
  24. package/plugins/html/entry/delta.js +20 -0
  25. package/plugins/html/entry/eureka.js +20 -0
  26. package/plugins/html/entry/mono.js +20 -0
  27. package/plugins/html/entry/oceanside.js +20 -0
  28. package/plugins/html/entry/orangecounty.js +20 -0
  29. package/plugins/html/entry/pasorobles.js +20 -0
  30. package/plugins/html/entry/sacramento.js +20 -0
  31. package/plugins/html/entry/santabarbara.js +20 -0
  32. package/plugins/html/entry/santacruz.js +20 -0
  33. package/plugins/html/entry/shasta.js +20 -0
  34. package/plugins/html/entry/sierra.js +20 -0
  35. package/plugins/html/entry/trinity.js +20 -0
  36. package/plugins/html/index.js +96 -0
  37. package/plugins/html/package-lock.json +21977 -0
  38. package/plugins/html/package.json +70 -0
  39. package/plugins/html/sample/blank.html +13 -0
  40. package/plugins/html/sample/components/alerts.html +32 -0
  41. package/plugins/html/sample/components/animations.html +7 -0
  42. package/plugins/html/sample/components/buttons.html +5 -0
  43. package/plugins/html/sample/components/cards.html +73 -0
  44. package/plugins/html/sample/components/lists.html +75 -0
  45. package/plugins/html/sample/components/locations.html +49 -0
  46. package/plugins/html/sample/components/profile-banners.html +48 -0
  47. package/plugins/html/sample/components/sections.html +16 -0
  48. package/plugins/html/sample/components/service-tiles.html +73 -0
  49. package/plugins/html/sample/components/typography.html +42 -0
  50. package/plugins/html/sample/default.html +31 -0
  51. package/plugins/html/sample/favicon.ico +0 -0
  52. package/plugins/html/sample/images/black_puppy.jpg +0 -0
  53. package/plugins/html/sample/images/dolphin_marine_mammals_water.jpg +0 -0
  54. package/plugins/html/sample/images/grayscale_mountain.jpg +0 -0
  55. package/plugins/html/sample/images/silhouette.png +0 -0
  56. package/plugins/html/sample/images/squirrel_tail_bushy_tail.jpg +0 -0
  57. package/plugins/html/sample/index.html +48 -0
  58. package/plugins/html/sample/missing/content.html +4 -0
  59. package/plugins/html/sample/structural/branding.html +15 -0
  60. package/plugins/html/sample/structural/footer.html +47 -0
  61. package/plugins/html/sample/structural/header.html +16 -0
  62. package/plugins/html/sample/structural/mobile-controls.html +5 -0
  63. package/plugins/html/sample/structural/navigation.html +26 -0
  64. package/plugins/html/sample/structural/search.html +15 -0
  65. package/plugins/html/sample/structural/utility-header.html +23 -0
  66. package/plugins/html/scripts/create-entrypoints.js +62 -0
  67. package/plugins/html/scripts/icon.js +60 -0
  68. package/plugins/html/scripts/webpack.test.js +41 -0
  69. package/plugins/html/webpack.config.js +279 -0
  70. package/plugins/jshint/.jshintrc +31 -0
  71. package/plugins/jshint/README.md +26 -0
  72. package/plugins/jshint/changelog.txt +30 -0
  73. package/plugins/jshint/index.js +296 -0
  74. package/plugins/jshint/package-lock.json +1779 -0
  75. package/plugins/jshint/package.json +45 -0
  76. package/plugins/jshint/reporter.cjs +663 -0
  77. package/plugins/jshint/sample/jshint.css +247 -0
  78. package/plugins/jshint/sample/jshint.html +7 -0
  79. package/plugins/jshint/sample/jshint.js +25 -0
  80. package/plugins/jshint/sample/no-files.html +9 -0
  81. package/sample/index.html +364 -0
  82. package/scripts/update-deps.js +38 -0
  83. package/webpack.config.js +0 -54
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@caweb/jshint-webpack-plugin",
3
+ "version": "1.0.9",
4
+ "description": "CAWebPublishing Webpack Plugin to run JSHint",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "files": [
8
+ "sample",
9
+ ".jshintrc",
10
+ "index.js",
11
+ "reporter.cjs",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "test": "echo \"Error: run tests from root\" && exit 0"
16
+ },
17
+ "author": "CAWebPublishing",
18
+ "license": "ISC",
19
+ "bugs": {
20
+ "url": "https://github.com/CAWebPublishing/webpack/issues"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "homepage": "https://github.com/CAWebPublishing/webpack/plugins/jshint#readme",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/CAWebPublishing/webpack.git",
29
+ "directory": "plugins/jshint"
30
+ },
31
+ "keywords": [
32
+ "caweb",
33
+ "cagov",
34
+ "webpack"
35
+ ],
36
+ "dependencies": {
37
+ "get-all-files": "^5.0.0",
38
+ "html-format": "^1.1.7",
39
+ "jshint": "^2.13.6"
40
+ },
41
+ "devDependencies": {
42
+ "webpack": "^5.96.1",
43
+ "webpack-cli": "^5.1.4"
44
+ }
45
+ }
@@ -0,0 +1,663 @@
1
+ //"use strict";
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const htmlFormat = require('html-format');
5
+
6
+ let sortedReport = {
7
+ errors: [],
8
+ warnings: [],
9
+ info: []
10
+ };
11
+
12
+ let title = process.cwd().split('\\').pop();
13
+
14
+ let output = [];
15
+
16
+ /**
17
+ * Adds Head to output.
18
+ */
19
+ function addHead(){
20
+
21
+ output.push(
22
+ '<head>',
23
+ '<meta charset="UTF-8">',
24
+ '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
25
+ `<title>JSHint Report for ${title}</title>`,
26
+ '<script src="./jshint.js"></script>',
27
+ '<link rel="stylesheet" href="./jshint.css">',
28
+ '</head>'
29
+ )
30
+ }
31
+
32
+ /**
33
+ * Adds Header to output.
34
+ */
35
+ function addHeader(){
36
+ let navItems = []
37
+
38
+ // add dark mode switch just for fun.
39
+ navItems.push(
40
+ '<li>',
41
+ '<button onclick="jsHint.toggleDarkMode()" class="btn button__theme-toggle" aria-label="Dark Mode"></button>',
42
+ '</li>'
43
+ );
44
+ // open header and add title
45
+ output.push(
46
+ '<header id="top" class="site__header">',
47
+ `<h1>JSHint Report for ${title}</h1>`,
48
+ )
49
+
50
+ // add navigation if items were found
51
+ if( navItems.length ){
52
+ output.push(
53
+ '<nav class="nav">',
54
+ '<ul>',
55
+ ...navItems,
56
+ '</ul>',
57
+ '</nav>',
58
+ )
59
+ }
60
+
61
+ // close the header.
62
+ output.push(
63
+ '</header>',
64
+ )
65
+
66
+ }
67
+
68
+ /**
69
+ * Adds Result Summary to output.
70
+ * @param {Array} results
71
+ */
72
+ function addSummary(results){
73
+ let items = [];
74
+
75
+ // summary
76
+ results.forEach(function(result) {
77
+ var file = result.file;
78
+ var error = result.error;
79
+
80
+ items.push(
81
+ '<li>',
82
+ `<a href="#${file.replace(/[\\:\.]/g, '-').toLowerCase()}" target="_blank">${file}</a>: line ${error.line}, col ${error.character}, ${error.reason}`,
83
+ '</li>'
84
+ )
85
+ })
86
+
87
+ let summary = [];
88
+ for(let [category] of Object.entries(sortedReport) ){
89
+ if( sortedReport[category].length ){
90
+ let cCase = capitalCase(category);
91
+ // if there is just 1, drop the plural
92
+ if( 1 === sortedReport[category].length ){
93
+ cCase = cCase.slice(0, -1)
94
+ }
95
+ summary.push(`${sortedReport[category].length} ${cCase} Detected`)
96
+ }
97
+ }
98
+
99
+ output.push(
100
+ '<section class="hint" id="summary">',
101
+ '<header class="hint__header">',
102
+ `<h2 class="hint__title">Summary: <em>${summary.join(' & ')}</em></h2>`,
103
+ '</header>',
104
+ '<ol>',
105
+ ...items,
106
+ '</ol>',
107
+ '</section>'
108
+ );
109
+
110
+ }
111
+
112
+ /**
113
+ * Process data
114
+ *
115
+ * Data Object
116
+ * {
117
+ * functions,
118
+ * options,
119
+ * errors,
120
+ * globals,
121
+ * unused,
122
+ * member,
123
+ * file
124
+ * }
125
+ */
126
+ function addBreakdown({
127
+ functions,
128
+ options,
129
+ errors,
130
+ implieds,
131
+ globals,
132
+ unused,
133
+ member,
134
+ file
135
+ }){
136
+ let functionList = [];
137
+ let errorList = [];
138
+ let unusedList = [];
139
+
140
+ /**
141
+ * Process function data
142
+ *
143
+ * Function Data Object
144
+ * {
145
+ * name,
146
+ * param,
147
+ * line,
148
+ * character,
149
+ * last,
150
+ * lastcharacter,
151
+ * metrics {
152
+ * complexity,
153
+ * parameters,
154
+ * statements
155
+ * }
156
+ * }
157
+ */
158
+ if( functions ){
159
+ functions.forEach(({ name, param, line, character, metrics }) => {
160
+ let { complexity, parameters, statements } = metrics;
161
+
162
+ functionList.push(
163
+ '<li>',
164
+ `<p><b>Name:</b> ${name}</p>`,
165
+ param && param.length ? `<p><b>Parameters:</b> ${param}</p>` : '',
166
+ `<p><b>Line:</b> ${line}</p>`,
167
+ `<p><b>Col:</b> ${character}</p>`,
168
+ `<p><b>Metrics:</b></p>`,
169
+ '<ul>',
170
+ `<li><b>Cyclomatic Complexity Number:</b> ${complexity}</li>`,
171
+ `<li><b>Arguments:</b> ${parameters}</li>`,
172
+ `<li><b>Statements:</b> ${statements}</li>`,
173
+ '</ul>',
174
+ '</li>'
175
+ )
176
+ })
177
+ }
178
+
179
+
180
+ /**
181
+ * Process error data
182
+ *
183
+ * Error Data Object
184
+ * {
185
+ * id,
186
+ * raw,
187
+ * code,
188
+ * evidence,
189
+ * line,
190
+ * character,
191
+ * scope,
192
+ * a,
193
+ * b,
194
+ * c,
195
+ * d,
196
+ * reason
197
+ * }
198
+ */
199
+ if( errors ){
200
+ errors.forEach(({reason, evidence, line, character}) => {
201
+ errorList.push(
202
+ '<li>',
203
+ `<p><b>Reason:</b> ${reason}</p>`,
204
+ `<p><b>Evidence:</b> ${evidence}</p>`,
205
+ `<p><b>Line:</b> ${line}</p>`,
206
+ `<p><b>Col:</b> ${character}</p>`,
207
+ '</li>'
208
+ )
209
+ })
210
+ }
211
+
212
+ /**
213
+ * Unused Data
214
+ * {
215
+ * name,
216
+ * line,
217
+ * character
218
+ * }
219
+ */
220
+ if( unused ){
221
+ unused.forEach(({name, line, character}) => {
222
+ unusedList.push(
223
+ '<li>',
224
+ `<p><b>Name:</b> ${name}</p>`,
225
+ `<p><b>Line:</b> ${line}</p>`,
226
+ `<p><b>Col:</b> ${character}</p>`,
227
+ '</li>'
228
+ )
229
+ })
230
+ }
231
+
232
+ output.push(
233
+ `<section id="${file.replace(/[\\:\.]/g, '-').toLowerCase()}" class="hint">`,
234
+ '<header class="hint__header">',
235
+ `<h2 class="hint__title"><a href="${file}" class="link" target="_blank"></a>File: <em>${file}</em></h2>`,
236
+ '<a class="btn back-to-top" href="#top" title="Back to top">⬆</a>',
237
+ '</header>',
238
+ `<h3>Functions: <span class="count">${functions.length}</span></h3>`,
239
+ functionList.length ? `<ol>${functionList.join('\n')}</ol>` : '',
240
+ `<h3>Errors: <span class="count">${errors ? errors.length : 0}</span></h3>`,
241
+ errorList.length ? `<ol>${errorList.join('\n')}</ol>` : '',
242
+ `<h3>Unused: <span class="count">${unused ? unused.length : 0}</span></h3>`,
243
+ unusedList.length ? `<ol>${unusedList.join('\n')}</ol>` : '',
244
+ '</section>'
245
+ )
246
+ }
247
+
248
+ /**
249
+ * Add file list sidebar
250
+ */
251
+ function addSidebar(data){
252
+ let fileList = [];
253
+
254
+ data.forEach(({file}) => {
255
+ fileList.push(
256
+ `<li><a href="#${ file.replace(/[\\:\.]/g, '-').toLowerCase() }">${file}</a></li>`
257
+ )
258
+ })
259
+ output.push(
260
+ '<aside>',
261
+ '<section>',
262
+ '<header class="hint__header"><h2 class="hint__title">Files</h2></header>',
263
+ '<ol>',
264
+ fileList.join('\n'),
265
+ '</section>',
266
+ '</ol>',
267
+ '</aside>'
268
+ )
269
+ }
270
+
271
+ /**
272
+ * JSHint Reporter
273
+ *
274
+ * @param {*} results
275
+ * @param {*} data
276
+ * @param {*} opts
277
+ */
278
+ function reporter(results, data, opts){
279
+ let outputFolder = process.env.JSHINT_OUTPUT_DIR ?
280
+ path.resolve( process.env.JSHINT_OUTPUT_DIR ) :
281
+ path.join(__dirname, 'public')
282
+ let outputFilename = path.join(outputFolder, `${process.env.JSHINT_OUTPUT_FILENAME ?? 'jshint'}.html` );
283
+
284
+ // process results.
285
+ results.forEach((result) => {
286
+ var file = result.file;
287
+ var error = result.error;
288
+
289
+ // add error to sorted report array
290
+ switch( error.code.charAt(0) ){
291
+ case 'E': // errors
292
+ sortedReport.errors.push({...error, file: file})
293
+ break;
294
+ case 'I': // info
295
+ sortedReport.info.push({...error, file: file})
296
+ break;
297
+ case 'W': // warnings
298
+ sortedReport.warnings.push({...error, file: file})
299
+ break;
300
+ }
301
+ })
302
+
303
+ try{
304
+
305
+ // Add Head Section.
306
+ addHead();
307
+
308
+ // Add Header Section.
309
+ addHeader();
310
+
311
+ output.push ('<div id="main">');
312
+
313
+ addSidebar(data);
314
+
315
+ output.push ('<div id="information">');
316
+
317
+ // Add Summary.
318
+ addSummary(results);
319
+
320
+ // process data.
321
+ data.forEach((d) => {
322
+ /*
323
+ Only used to see what the data is passing
324
+ for(let [prop, value] of Object.entries(d) ){
325
+ output.push(prop)
326
+ output.push('object' == typeof value ? value[0] : value)
327
+ output.push('<br />')
328
+ }
329
+ */
330
+ addBreakdown(d)
331
+ })
332
+
333
+ output.push (
334
+ '</div> <!-- Close information div -->',
335
+ '</div> <!-- Close main div -->'
336
+ );
337
+
338
+ }catch(err){
339
+ process.stderr.write(err.toString())
340
+ process.exit(1)
341
+ }
342
+
343
+ // wrap the output in html tags.
344
+ output = ['<html>', ...output, '</html>']
345
+
346
+ fs.mkdirSync( outputFolder, {recursive: true} );
347
+
348
+ // write html file
349
+ fs.writeFileSync(
350
+ outputFilename,
351
+ htmlFormat(output.join('\n')," ".repeat(4), 250)
352
+ );
353
+
354
+ // write jshint.js file.
355
+ fs.writeFileSync(
356
+ path.join(outputFolder, 'jshint.js'),
357
+ jsContent()
358
+ );
359
+
360
+ // write jshint.css file.
361
+ fs.writeFileSync(
362
+ path.join(outputFolder, 'jshint.css'),
363
+ cssContent()
364
+ );
365
+
366
+ // console.log( htmlFormat(output.join('\n')," ".repeat(4), 250) );
367
+ }
368
+
369
+ function capitalCase(str){
370
+ return str.charAt(0).toUpperCase() + str.slice(1)
371
+ }
372
+
373
+ /**
374
+ * Returns string content for css file
375
+ * @returns string
376
+ */
377
+ function cssContent(){
378
+ return `
379
+ /*
380
+ * JSHint
381
+ */
382
+ body {
383
+ --body-margin: 2rem;
384
+
385
+ /* Body colours */
386
+ --body-bg: #fff;
387
+ --body-fg: #111;
388
+
389
+ /* Nav & link colours */
390
+ --nav-bg: #eee;
391
+ --nav-bg-hover: #bbb;
392
+ --nav-fg: #11c;
393
+
394
+ /* Heading colours */
395
+ --h-fg: #222;
396
+ --h-bg: #eee;
397
+
398
+ /* Border style for bordered components */
399
+ --component-border: 1px solid #aaa;
400
+
401
+ /* Consistent component margins */
402
+ --component-margin-sm: 1.5rem;
403
+ --component-margin-lg: 3rem;
404
+
405
+ /* Colour chips */
406
+ --count-bg: #eee;
407
+ --count-border: var(--component-border);
408
+ }
409
+ /*
410
+ * Dark mode
411
+ */
412
+ body.is-dark-mode {
413
+ /* Body colours */
414
+ --body-bg: #111;
415
+ --body-fg: #fff;
416
+
417
+ /* Nav & link colours */
418
+ --nav-bg: #444;
419
+ --nav-bg-hover: #222;
420
+ --nav-fg: #bcf;
421
+
422
+ /* Heading colours */
423
+ --h-fg: #eee;
424
+ --h-bg: #222;
425
+
426
+ /* Border style for bordered components */
427
+ --component-border: 1px solid #666;
428
+
429
+ /* Consistent component margins */
430
+ --component-margin-sm: 1.5rem;
431
+ --component-margin-lg: 3rem;
432
+
433
+
434
+ /* Colour chips */
435
+ --count-bg: #111;
436
+ --count-border: var(--component-border);
437
+ }
438
+ /*
439
+ * General
440
+ */
441
+ * {
442
+ box-sizing: border-box;
443
+ }
444
+
445
+ html {
446
+ scroll-behavior: smooth;
447
+
448
+ @media (prefers-reduced-motion: reduce) {
449
+ scroll-behavior: auto;
450
+ }
451
+ }
452
+
453
+ body {
454
+ background-color: var(--body-bg);
455
+ color: var(--body-fg);
456
+ font-family: sans-serif;
457
+ margin: var(--body-margin);
458
+ }
459
+
460
+ a {
461
+ color: var(--nav-fg);
462
+ }
463
+ p{
464
+ margin: 0 0 .5em 0
465
+ }
466
+ h1, h2 {
467
+ font-weight: normal;
468
+ }
469
+
470
+ h1, h2, h3 {
471
+ color: var(--h-fg);
472
+ margin: 0 0 var(--component-margin-sm) 0;
473
+ }
474
+ /*
475
+ * Layout
476
+ */
477
+ #main{
478
+ display: flex;
479
+ }
480
+ aside {
481
+ margin-right: calc(1.5* var(--body-margin));
482
+ padding-right: calc(1* var(--body-margin));
483
+ border-right: var(--component-border);
484
+ }
485
+ #information{
486
+ flex-grow: 1
487
+ }
488
+ /*
489
+ * Hints
490
+ */
491
+ .hint {
492
+ margin-bottom: var(--component-margin-lg);
493
+ }
494
+ .hint__header {
495
+ align-items: baseline;
496
+ background-color: var(--h-bg);
497
+ display: flex;
498
+ justify-content: space-between;
499
+ margin-right: calc(-1 * var(--body-margin));
500
+ margin-left: calc(-1 * var(--body-margin));
501
+ margin-bottom: var(--component-margin-sm);
502
+ padding: var(--component-margin-sm) var(--body-margin) 0;
503
+ position: sticky;
504
+ top: 0;
505
+ z-index: 1;
506
+ }
507
+
508
+ /*
509
+ * Lists
510
+ */
511
+ ul,
512
+ ol {
513
+ padding: 0 0 0 2em;
514
+ }
515
+ li {
516
+ margin: 0 0 2em 0;
517
+ }
518
+ li li {
519
+ margin: 0;
520
+ }
521
+ /*
522
+ * Header, nav, 'back to top' button
523
+ */
524
+ .site__header {
525
+ align-items: baseline;
526
+ display: flex;
527
+ flex-wrap: wrap;
528
+ justify-content: space-between;
529
+ }
530
+ .nav {
531
+ margin-bottom: var(--component-margin-sm);
532
+ }
533
+
534
+ .nav ul {
535
+ display: flex;
536
+ justify-content: flex-start;
537
+ align-items: center;
538
+ list-style: none;
539
+ padding: 0;
540
+ }
541
+
542
+ .nav li {
543
+ margin-bottom: var(--component-margin-sm);
544
+ margin: 0 0.5em 0 0;
545
+ }
546
+
547
+ .nav a {
548
+ display: block;
549
+ height: 100%;
550
+ }
551
+
552
+ .nav a,
553
+ .btn {
554
+ background-color: var(--nav-bg);
555
+ padding: 0.5em;
556
+ text-decoration: none;
557
+ transition: background-color 0.2s ease;
558
+ }
559
+
560
+ .nav a:hover,
561
+ .nav a:focus,
562
+ .btn:hover,
563
+ .btn:focus {
564
+ background-color: var(--nav-bg-hover);
565
+ }
566
+
567
+ .nav a:focus,
568
+ .btn:focus {
569
+ outline: 4px dashed var(--nav-fg);
570
+ }
571
+
572
+ .btn {
573
+ font-size: 1.2em;
574
+ margin-bottom: var(--component-margin-sm);
575
+ text-align: center;
576
+ }
577
+ /*
578
+ * Count component on colour chips
579
+ */
580
+ .count {
581
+ background-color: var(--count-bg);
582
+ border: var(--count-border);
583
+ border-radius: 50%;
584
+ box-sizing: border-box;
585
+ color: var(--count-fg);
586
+ display: inline-block;
587
+ height: auto;
588
+ min-width: 2.3em;
589
+ padding: 0.5em;
590
+ text-align: center;
591
+
592
+ /* Counteract .chip:hover */
593
+ font-weight: normal;
594
+ text-shadow: none;
595
+ }
596
+ /*
597
+ * Darkmode switch
598
+ */
599
+ .button__theme-toggle {
600
+ background: transparent;
601
+ border: none;
602
+ cursor: pointer;
603
+ margin-bottom: 0;
604
+ }
605
+
606
+ .button__theme-toggle::before {
607
+ content: "🌛";
608
+ font-size: 1.5rem;
609
+ }
610
+
611
+ body.is-dark-mode .button__theme-toggle::before {
612
+ content: "🌞";
613
+ }
614
+
615
+ /*
616
+ * Link Icon
617
+ */
618
+ .link{
619
+ text-decoration: none;
620
+ cursor: pointer;
621
+ }
622
+ .link::before{
623
+ content: "🔗";
624
+ }
625
+ `;
626
+ }
627
+
628
+ /**
629
+ * Returns string content for js file
630
+ * @returns string
631
+ */
632
+ function jsContent(){
633
+ return `
634
+
635
+ const jsHint = {};
636
+
637
+ document.addEventListener( 'DOMContentLoaded', () => {
638
+ const button = document.querySelector( '.button__theme-toggle' );
639
+
640
+ jsHint.toggleDarkMode = function() {
641
+ const isDarkMode = document.body.classList.contains( 'is-dark-mode' );
642
+ if ( isDarkMode ) {
643
+ button.setAttribute( 'aria-pressed', 'false' );
644
+ document.body.classList.remove( 'is-dark-mode' );
645
+ } else {
646
+ button.setAttribute( 'aria-pressed', 'true' );
647
+ document.body.classList.add( 'is-dark-mode' );
648
+ }
649
+ }
650
+
651
+ // Set is-dark-mode class if user has requested dark mode.
652
+ if ( window.matchMedia( '(prefers-color-scheme: dark)' ).matches ){
653
+ document.body.classList.add( 'is-dark-mode' );
654
+ button.setAttribute( 'aria-pressed', 'true' );
655
+ }
656
+
657
+ });
658
+ `;
659
+ }
660
+
661
+ module.exports = {
662
+ reporter
663
+ }