@caweb/cli 1.4.3 → 1.4.5
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/README.md +2 -206
- package/commands/index.js +9 -2
- package/commands/test.js +0 -3
- package/commands/webpack/webpack.js +59 -26
- package/configs/webpack.config.js +34 -12
- package/lib/cli.js +65 -38
- package/lib/helpers.js +0 -1
- package/lib/webpack/plugins/a11y/aceconfig.js +44 -0
- package/lib/webpack/plugins/a11y/index.js +272 -0
- package/lib/webpack/plugins/a11y/package.json +12 -0
- package/lib/webpack/plugins/css-audit/css-audit.config.cjs +5 -0
- package/lib/webpack/plugins/css-audit/default.config.js +19 -0
- package/lib/webpack/plugins/css-audit/index.js +297 -0
- package/lib/webpack/plugins/css-audit/package.json +12 -0
- package/lib/webpack/plugins/jshint/.jshintrc +31 -0
- package/lib/webpack/plugins/jshint/index.js +286 -0
- package/lib/webpack/plugins/jshint/package-lock.json +22 -0
- package/lib/webpack/plugins/jshint/package.json +15 -0
- package/lib/webpack/plugins/jshint/reporter.cjs +663 -0
- package/package.json +5 -5
- package/assets/logo.ico +0 -0
- package/commands/a11y.js +0 -95
- package/commands/audit.js +0 -135
- package/configs/aceconfig.js +0 -28
- package/configs/css-audit.config.cjs +0 -9
- package/docs/CREDITS.MD +0 -32
- package/docs/ISSUES.MD +0 -7
- package/docs/OVERRIDES.md +0 -53
- package/docs/ROADMAP.MD +0 -20
- package/docs/SYNC.MD +0 -29
- package/docs/tool/index.js +0 -45
- package/gen/parser.js +0 -166
- package/gen/site-generator.js +0 -144
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* External dependencies
|
|
5
|
+
*/
|
|
6
|
+
import { sync as resolveBin } from 'resolve-bin';
|
|
7
|
+
import spawn from 'cross-spawn';
|
|
8
|
+
import { getAllFilesSync } from 'get-all-files'
|
|
9
|
+
import EntryDependency from "webpack/lib/dependencies/EntryDependency.js";
|
|
10
|
+
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import fs, { stat } from 'fs';
|
|
13
|
+
import deepmerge from 'deepmerge';
|
|
14
|
+
import chalk from 'chalk';
|
|
15
|
+
import { fileURLToPath, URL } from 'url';
|
|
16
|
+
|
|
17
|
+
// default configuration
|
|
18
|
+
import {default as DefaultConfig} from './default.config.js';
|
|
19
|
+
|
|
20
|
+
const boldWhite = chalk.bold.white;
|
|
21
|
+
const boldGreen = chalk.bold.green;
|
|
22
|
+
const boldBlue = chalk.bold.hex('#03a7fc');
|
|
23
|
+
const currentPath = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
|
|
25
|
+
// CSS Audit Plugin
|
|
26
|
+
class CSSAuditPlugin {
|
|
27
|
+
config = {}
|
|
28
|
+
|
|
29
|
+
constructor(opts = {}) {
|
|
30
|
+
this.config = deepmerge(DefaultConfig, opts);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
apply(compiler) {
|
|
34
|
+
const staticDir = {
|
|
35
|
+
directory: path.join(path.resolve(currentPath, '../../../../'), 'bin', 'css-audit', 'public'),
|
|
36
|
+
watch: true
|
|
37
|
+
}
|
|
38
|
+
let { devServer, output } = compiler.options;
|
|
39
|
+
let hostUrl = 'localhost' === devServer.host ? `http://${devServer.host}`: devServer.host;
|
|
40
|
+
let hostPort = devServer.port;
|
|
41
|
+
|
|
42
|
+
if( hostPort && 80 !== hostPort )
|
|
43
|
+
{
|
|
44
|
+
hostUrl = `${hostUrl}:${hostPort}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// if dev server allows for multiple pages to be opened
|
|
48
|
+
// add css-audit.html to open property.
|
|
49
|
+
if( Array.isArray(devServer.open) ){
|
|
50
|
+
devServer.open.push(`${hostUrl}/${this.config.rewrite ? this.config.rewrite : this.config.filename}.html`)
|
|
51
|
+
}else if( 'object' === typeof devServer.open && Array.isArray(devServer.open.target) ){
|
|
52
|
+
devServer.open.target.push(`${hostUrl}/${this.config.filename}.html`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// add our static directory
|
|
56
|
+
if( Array.isArray(devServer.static) ){
|
|
57
|
+
devServer.static.push(staticDir)
|
|
58
|
+
}else{
|
|
59
|
+
devServer.static = [].concat(devServer.static, staticDir );
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
// Wait for configuration preset plugins to apply all configure webpack defaults
|
|
64
|
+
compiler.hooks.initialize.tap('CSS Audit Plugin', () => {
|
|
65
|
+
compiler.hooks.compilation.tap(
|
|
66
|
+
"CSS Audit Plugin",
|
|
67
|
+
(compilation, { normalModuleFactory }) => {
|
|
68
|
+
compilation.dependencyFactories.set(
|
|
69
|
+
EntryDependency,
|
|
70
|
+
normalModuleFactory
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const { entry, options, context } = {
|
|
76
|
+
entry: path.join(staticDir.directory, 'css-audit.update.js'),
|
|
77
|
+
options: {
|
|
78
|
+
name: 'css-audit.update'
|
|
79
|
+
},
|
|
80
|
+
context: staticDir.directory
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const dep = new EntryDependency(entry);
|
|
84
|
+
dep.loc = {
|
|
85
|
+
name: options.name
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
path.join(staticDir.directory, `css-audit.update.js`),
|
|
90
|
+
`` // required for hot-update to compile on our page, blank script for now
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
compiler.hooks.thisCompilation.tap('CSS Audit Plugin',
|
|
94
|
+
/**
|
|
95
|
+
* Hook into the webpack compilation
|
|
96
|
+
* @param {Compilation} compilation
|
|
97
|
+
*/
|
|
98
|
+
(compilation) => {
|
|
99
|
+
|
|
100
|
+
compiler.hooks.make.tapAsync("CSS Audit Plugin", (compilation, callback) => {
|
|
101
|
+
|
|
102
|
+
compilation.addEntry(
|
|
103
|
+
context,
|
|
104
|
+
dep,
|
|
105
|
+
options,
|
|
106
|
+
err => {
|
|
107
|
+
callback(err);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// process assets and run the css-audit on appropriate assets.
|
|
112
|
+
compilation.hooks.processAssets.tapAsync(
|
|
113
|
+
{
|
|
114
|
+
name: 'CSS Audit Plugin',
|
|
115
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* Hook into the process assets hook
|
|
119
|
+
* @param {any} _
|
|
120
|
+
* @param {(err?: Error) => void} callback
|
|
121
|
+
*/
|
|
122
|
+
(assets, callback) => {
|
|
123
|
+
let files = [];
|
|
124
|
+
|
|
125
|
+
Object.entries(assets).forEach(([pathname, source]) => {
|
|
126
|
+
if( pathname.endsWith('.js') ){
|
|
127
|
+
if( source['_source'] && source['_source']['_children'] ){
|
|
128
|
+
source['_source']['_children'].forEach((s, i) => {
|
|
129
|
+
if(
|
|
130
|
+
'string' === typeof s && // is a string and
|
|
131
|
+
0 < s.indexOf('.css') && // has a .css reference and
|
|
132
|
+
0 > s.indexOf('node_modules') // not referencing node_modules directory
|
|
133
|
+
){
|
|
134
|
+
files.push( path.resolve(s.replace(/[\n\s\S\w]*"(.*)"[\n\s\S\w]*/, '$1')) )
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
console.log(`<i> ${boldGreen('[webpack-dev-middleware] Running CSS Audit...')}`);
|
|
141
|
+
|
|
142
|
+
this.audit(files, this.config );
|
|
143
|
+
|
|
144
|
+
// we have to inject the css-audit.update.js file into the head in order for the webpack-dev-server scripts to load.
|
|
145
|
+
let pageContent = fs.readFileSync(path.join(staticDir.directory, `${this.config.filename}.html`))
|
|
146
|
+
|
|
147
|
+
fs.writeFileSync(
|
|
148
|
+
path.join(staticDir.directory, `${this.config.filename}.html`),
|
|
149
|
+
pageContent.toString().replace('</head>', '<script src="/css-audit.update.js"></script>\n</head>')
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
console.log(`<i> ${boldGreen('[webpack-dev-middleware] CSS Audit can be viewed at')} ${ boldBlue(new URL(`${hostUrl}/${this.config.filename}.html`).toString()) }`);
|
|
153
|
+
|
|
154
|
+
callback();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
compiler.hooks.watchClose.tap( 'CSS Audit Plugin', () => {
|
|
159
|
+
getAllFilesSync(compiler.options.output.path).toArray().forEach(f => {
|
|
160
|
+
if(
|
|
161
|
+
f.includes('css-audit') || // delete any css-audit files
|
|
162
|
+
f.includes('.hot-update.js') // delete any HMR files
|
|
163
|
+
){
|
|
164
|
+
fs.rmSync(f)
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Run CSS Auditor
|
|
176
|
+
*
|
|
177
|
+
* @param {Array} files
|
|
178
|
+
* @param {Object} options
|
|
179
|
+
* @param {boolean} options.debug
|
|
180
|
+
* @param {boolean} options.format
|
|
181
|
+
* @param {boolean} options.filename
|
|
182
|
+
* @param {boolean} options.colors
|
|
183
|
+
* @param {boolean} options.important
|
|
184
|
+
* @param {boolean} options.displayNone
|
|
185
|
+
* @param {boolean} options.selectors
|
|
186
|
+
* @param {boolean} options.mediaQueries
|
|
187
|
+
* @param {boolean} options.typography
|
|
188
|
+
* @param {Array} options.propertyValues
|
|
189
|
+
*/
|
|
190
|
+
audit(files, {
|
|
191
|
+
debug,
|
|
192
|
+
format,
|
|
193
|
+
filename,
|
|
194
|
+
colors,
|
|
195
|
+
important,
|
|
196
|
+
displayNone,
|
|
197
|
+
selectors,
|
|
198
|
+
mediaQueries,
|
|
199
|
+
typography,
|
|
200
|
+
propertyValues
|
|
201
|
+
}){
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
let filesToBeAuditted = [];
|
|
205
|
+
let filesWithIssues = [];
|
|
206
|
+
|
|
207
|
+
files.forEach( (paths, i) => {
|
|
208
|
+
let resolvePath = path.resolve(paths);
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
// if given path is a directory
|
|
212
|
+
if( fs.statSync(resolvePath).isDirectory() ){
|
|
213
|
+
|
|
214
|
+
// get all .css files
|
|
215
|
+
getAllFilesSync(resolvePath).toArray().forEach(f => {
|
|
216
|
+
if( f.endsWith('.css') ){
|
|
217
|
+
filesToBeAuditted.push(f)
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
// if given path is a file and a .css file
|
|
221
|
+
}else if( fs.statSync(paths).isFile() && paths.endsWith('.css') ){
|
|
222
|
+
filesToBeAuditted.push(paths)
|
|
223
|
+
}
|
|
224
|
+
// invalid path/file
|
|
225
|
+
} catch (error) {
|
|
226
|
+
filesWithIssues.push(paths)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if( ! filesToBeAuditted.length ){
|
|
232
|
+
console.log('No file(s) or directory path(s) were given or default directory was not found.')
|
|
233
|
+
console.log('Auditor did not execute.');
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* the css audit uses the filename for the title, rather than the project name
|
|
239
|
+
* we fix that by passing the project name for the file name
|
|
240
|
+
* then renaming the file to the intended file name.
|
|
241
|
+
*/
|
|
242
|
+
let auditArgs = [
|
|
243
|
+
colors ? '--colors' : '',
|
|
244
|
+
important ? '--important' : '',
|
|
245
|
+
displayNone ? '--display-none' : '',
|
|
246
|
+
selectors ? '--selectors' : '',
|
|
247
|
+
mediaQueries ? '--media-queries' : '',
|
|
248
|
+
typography ? '--typography' : '',
|
|
249
|
+
format ? `--format=${format}` : '',
|
|
250
|
+
filename ? `--filename=${path.basename(process.cwd())}` : ''
|
|
251
|
+
].filter( e => e)
|
|
252
|
+
|
|
253
|
+
if( propertyValues ){
|
|
254
|
+
propertyValues.forEach((p) => {
|
|
255
|
+
auditArgs.push(`--property-values=${p.replace(' ',',')}`)
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
let { stdout, stderr } = spawn.sync(
|
|
261
|
+
'node ' + resolveBin('@caweb/cli', {executable: 'auditor'}),
|
|
262
|
+
[
|
|
263
|
+
...filesToBeAuditted,
|
|
264
|
+
...auditArgs
|
|
265
|
+
],
|
|
266
|
+
{
|
|
267
|
+
stdio: 'pipe',
|
|
268
|
+
cwd: fs.existsSync(path.join(process.cwd(), 'css-audit.config.cjs')) ? process.cwd() : currentPath
|
|
269
|
+
}
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
if( stderr && stderr.toString() ){
|
|
273
|
+
console.log( stderr.toString())
|
|
274
|
+
}
|
|
275
|
+
if( stdout ){
|
|
276
|
+
// rename the file back to the intended file name instead of the project name
|
|
277
|
+
fs.renameSync(
|
|
278
|
+
path.join(path.resolve(currentPath, '../../../../'), 'bin', 'css-audit', 'public', `${path.basename(process.cwd())}.html`),
|
|
279
|
+
path.join(path.resolve(currentPath, '../../../../'), 'bin', 'css-audit', 'public', `${filename}.html`)
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
let msg = stdout.toString().replace('undefined', '');
|
|
283
|
+
|
|
284
|
+
if( 'audit' === process.argv[2] ){
|
|
285
|
+
console.log( msg )
|
|
286
|
+
}else{
|
|
287
|
+
return msg;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
} // end of audit
|
|
293
|
+
|
|
294
|
+
} // end of class
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
export default CSSAuditPlugin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caweb/css-audit-webpack-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Webpack Plugin to run WordPress CSS Audit",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 0"
|
|
9
|
+
},
|
|
10
|
+
"author": "Danny Guzman",
|
|
11
|
+
"license": "ISC"
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"boss": true,
|
|
3
|
+
"curly": true,
|
|
4
|
+
"eqeqeq": true,
|
|
5
|
+
"eqnull": true,
|
|
6
|
+
"esversion": 6,
|
|
7
|
+
"expr": true,
|
|
8
|
+
"immed": true,
|
|
9
|
+
"noarg": true,
|
|
10
|
+
"nonbsp": true,
|
|
11
|
+
"quotmark": "single",
|
|
12
|
+
"undef": true,
|
|
13
|
+
"unused": true,
|
|
14
|
+
|
|
15
|
+
"browser": true,
|
|
16
|
+
|
|
17
|
+
"globals": {
|
|
18
|
+
"_": false,
|
|
19
|
+
"Backbone": false,
|
|
20
|
+
"jQuery": false,
|
|
21
|
+
"JSON": false,
|
|
22
|
+
"wp": false,
|
|
23
|
+
"export": false,
|
|
24
|
+
"module": false,
|
|
25
|
+
"require": false,
|
|
26
|
+
"WorkerGlobalScope": false,
|
|
27
|
+
"self": false,
|
|
28
|
+
"OffscreenCanvas": false,
|
|
29
|
+
"Promise": false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* External dependencies
|
|
5
|
+
*/
|
|
6
|
+
import spawn from 'cross-spawn';
|
|
7
|
+
import { getAllFilesSync } from 'get-all-files'
|
|
8
|
+
import EntryDependency from "webpack/lib/dependencies/EntryDependency.js";
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import deepmerge from 'deepmerge';
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { fileURLToPath, URL } from 'url';
|
|
14
|
+
|
|
15
|
+
const boldWhite = chalk.bold.white;
|
|
16
|
+
const boldGreen = chalk.bold.green;
|
|
17
|
+
const boldBlue = chalk.bold.hex('#03a7fc');
|
|
18
|
+
const currentPath = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
// JSHint Plugin
|
|
21
|
+
class JSHintPlugin {
|
|
22
|
+
config = {
|
|
23
|
+
outputFilename: 'jshint',
|
|
24
|
+
outputFolder: path.join(currentPath, 'public'),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor(opts = {}) {
|
|
28
|
+
// outputFolder must be resolved
|
|
29
|
+
if( opts.outputFolder ){
|
|
30
|
+
opts.outputFolder = path.join(process.cwd(), opts.outputFolder);
|
|
31
|
+
}
|
|
32
|
+
this.config = deepmerge(this.config, opts);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
apply(compiler) {
|
|
36
|
+
const staticDir = {
|
|
37
|
+
directory: this.config.outputFolder,
|
|
38
|
+
watch: true
|
|
39
|
+
}
|
|
40
|
+
let { devServer } = compiler.options;
|
|
41
|
+
let hostUrl = 'localhost' === devServer.host ? `http://${devServer.host}`: devServer.host;
|
|
42
|
+
let hostPort = devServer.port;
|
|
43
|
+
|
|
44
|
+
if( hostPort && 80 !== hostPort )
|
|
45
|
+
{
|
|
46
|
+
hostUrl = `${hostUrl}:${hostPort}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// if dev server allows for multiple pages to be opened
|
|
50
|
+
// add jshint.html to open property.
|
|
51
|
+
if( Array.isArray(devServer.open) ){
|
|
52
|
+
devServer.open.push(`${hostUrl}/${this.config.outputFilename}.html`)
|
|
53
|
+
}else if( 'object' === typeof devServer.open && Array.isArray(devServer.open.target) ){
|
|
54
|
+
devServer.open.target.push(`${hostUrl}/${this.config.outputFilename}.html`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// add our static directory
|
|
58
|
+
if( Array.isArray(devServer.static) ){
|
|
59
|
+
devServer.static.push(staticDir)
|
|
60
|
+
}else{
|
|
61
|
+
devServer.static = [].concat(devServer.static, staticDir );
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Wait for configuration preset plugins to apply all configure webpack defaults
|
|
65
|
+
compiler.hooks.initialize.tap('JSHint Plugin', () => {
|
|
66
|
+
compiler.hooks.compilation.tap(
|
|
67
|
+
"JSHint Plugin",
|
|
68
|
+
(compilation, { normalModuleFactory }) => {
|
|
69
|
+
compilation.dependencyFactories.set(
|
|
70
|
+
EntryDependency,
|
|
71
|
+
normalModuleFactory
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const { entry, options, context } = {
|
|
77
|
+
entry: path.join( this.config.outputFolder, 'jshint.update.js'),
|
|
78
|
+
options: {
|
|
79
|
+
name: 'jshint'
|
|
80
|
+
},
|
|
81
|
+
context: 'jshint'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const dep = new EntryDependency(entry);
|
|
85
|
+
dep.loc = {
|
|
86
|
+
name: options.name
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if( ! fs.existsSync(path.resolve(this.config.outputFolder))){
|
|
90
|
+
fs.mkdirSync( path.resolve(this.config.outputFolder), {recursive: true} );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
path.join(this.config.outputFolder, `jshint.update.js`),
|
|
95
|
+
`` // required for hot-update to compile on our page, blank script for now
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
compiler.hooks.thisCompilation.tap('JSHint Plugin',
|
|
100
|
+
/**
|
|
101
|
+
* Hook into the webpack compilation
|
|
102
|
+
* @param {Compilation} compilation
|
|
103
|
+
*/
|
|
104
|
+
(compilation) => {
|
|
105
|
+
|
|
106
|
+
compiler.hooks.make.tapAsync("JSHint Plugin", (compilation, callback) => {
|
|
107
|
+
|
|
108
|
+
compilation.addEntry(
|
|
109
|
+
context,
|
|
110
|
+
dep,
|
|
111
|
+
options,
|
|
112
|
+
err => {
|
|
113
|
+
callback(err);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// process assets and run the jshint on appropriate assets.
|
|
118
|
+
compilation.hooks.processAssets.tapAsync(
|
|
119
|
+
{
|
|
120
|
+
name: 'JSHint Plugin',
|
|
121
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* Hook into the process assets hook
|
|
125
|
+
* @param {any} _
|
|
126
|
+
* @param {(err?: Error) => void} callback
|
|
127
|
+
*/
|
|
128
|
+
(assets, callback) => {
|
|
129
|
+
let files = [];
|
|
130
|
+
|
|
131
|
+
Object.entries(assets).forEach(([pathname, source]) => {
|
|
132
|
+
if( pathname.endsWith('.js') ){
|
|
133
|
+
if( source['_source'] && source['_source']['_children'] ){
|
|
134
|
+
source['_source']['_children'].forEach((s, i) => {
|
|
135
|
+
if(
|
|
136
|
+
'string' === typeof s && // is a string and
|
|
137
|
+
0 < s.indexOf('.js') && // has a .js reference and
|
|
138
|
+
0 > s.indexOf('node_modules') && // not referencing node_modules directory
|
|
139
|
+
0 > s.indexOf('jshint.update.js') // not referencing our update javascript
|
|
140
|
+
){
|
|
141
|
+
files.push( path.resolve(s.replace(/[\n\s\S\w]*"(.*)"[\n\s\S\w]*/, '$1')) )
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
console.log(`<i> ${boldGreen('[webpack-dev-middleware] Running JSHint...')}`);
|
|
149
|
+
|
|
150
|
+
this.hint(files, this.config);
|
|
151
|
+
|
|
152
|
+
console.log(`<i> ${boldGreen('[webpack-dev-middleware] JSHint can be viewed at')} ${ boldBlue(new URL(`${hostUrl}/${this.config.outputFilename}.html`).toString()) }`);
|
|
153
|
+
|
|
154
|
+
callback();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
compiler.hooks.watchClose.tap( 'JSHint Plugin', () => {
|
|
159
|
+
getAllFilesSync(compiler.options.output.path).toArray().forEach(f => {
|
|
160
|
+
if(
|
|
161
|
+
f.includes('jshint') || // delete any jshint files
|
|
162
|
+
f.includes('.hot-update.js') // delete any HMR files
|
|
163
|
+
){
|
|
164
|
+
fs.rmSync(f)
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Run JSHint
|
|
176
|
+
*
|
|
177
|
+
* @param {Array} files
|
|
178
|
+
* @param {Object} options
|
|
179
|
+
* @param {boolean} options.debug
|
|
180
|
+
* @param {boolean} options.outputFilename Filename for the scan results.
|
|
181
|
+
* @param {boolean} options.outputFolder Where the scan results should be saved.
|
|
182
|
+
*/
|
|
183
|
+
hint(files, {
|
|
184
|
+
debug,
|
|
185
|
+
outputFolder,
|
|
186
|
+
outputFilename
|
|
187
|
+
}){
|
|
188
|
+
|
|
189
|
+
let filesToBeAuditted = [];
|
|
190
|
+
let filesWithIssues = [];
|
|
191
|
+
|
|
192
|
+
files.forEach( (paths, i) => {
|
|
193
|
+
let resolvePath = path.resolve(paths);
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
// if given path is a directory
|
|
197
|
+
if( fs.statSync(resolvePath).isDirectory() ){
|
|
198
|
+
|
|
199
|
+
// get all .js files
|
|
200
|
+
getAllFilesSync(resolvePath).toArray().forEach(f => {
|
|
201
|
+
if( f.endsWith('.js') ){
|
|
202
|
+
filesToBeAuditted.push(f)
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
// if given path is a file and a .js file
|
|
206
|
+
}else if( fs.statSync(paths).isFile() && paths.endsWith('.js') ){
|
|
207
|
+
filesToBeAuditted.push(paths)
|
|
208
|
+
}
|
|
209
|
+
// invalid path/file
|
|
210
|
+
} catch (error) {
|
|
211
|
+
filesWithIssues.push(paths)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if( ! filesToBeAuditted.length ){
|
|
217
|
+
console.log('No file(s) or directory path(s) were given or default directory was not found.')
|
|
218
|
+
console.log('Hinter did not execute.');
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
let hintConfigFile = path.join(currentPath, '.jshintrc');
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* JSHint does not allow for multiple configs so we have to merge and write 1 file
|
|
226
|
+
*/
|
|
227
|
+
if( fs.existsSync(path.join(process.cwd(), '.jshintrc')) ){
|
|
228
|
+
let hintConfig = fs.readFileSync(hintConfigFile);
|
|
229
|
+
let customConfig = fs.readFileSync(path.join(process.cwd(), '.jshintrc'));
|
|
230
|
+
|
|
231
|
+
hintConfig = hintConfig.toString().replace(/[\r\t\n]|\/{2}.*/g, '')
|
|
232
|
+
hintConfig = JSON.parse(hintConfig)
|
|
233
|
+
|
|
234
|
+
customConfig = customConfig.toString().replace(/[\r\t\n]|\/{2}.*/g, '')
|
|
235
|
+
customConfig = JSON.parse(customConfig)
|
|
236
|
+
|
|
237
|
+
hintConfigFile = path.join(currentPath, '.customrc');
|
|
238
|
+
fs.writeFileSync(
|
|
239
|
+
hintConfigFile ,
|
|
240
|
+
JSON.stringify(deepmerge(hintConfig, customConfig), null, 4)
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Set the env for our reporter.
|
|
245
|
+
process.env.JSHINT_OUTPUT_DIR = outputFolder;
|
|
246
|
+
process.env.JSHINT_OUTPUT_FILENAME = outputFilename;
|
|
247
|
+
|
|
248
|
+
let hintArgs = [
|
|
249
|
+
'--config',
|
|
250
|
+
hintConfigFile,
|
|
251
|
+
'--reporter',
|
|
252
|
+
path.join(currentPath, 'reporter.cjs')
|
|
253
|
+
].filter( e => e)
|
|
254
|
+
|
|
255
|
+
let { stdout, stderr } = spawn.sync(
|
|
256
|
+
'jshint',
|
|
257
|
+
[
|
|
258
|
+
...filesToBeAuditted,
|
|
259
|
+
...hintArgs
|
|
260
|
+
],
|
|
261
|
+
{
|
|
262
|
+
stdio: 'pipe',
|
|
263
|
+
}
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if( stderr && stderr.toString() ){
|
|
267
|
+
console.log( stderr.toString())
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if( stdout ){
|
|
271
|
+
if( 'jshint' === process.argv[2] && debug ){
|
|
272
|
+
console.log( stdout.toString() );
|
|
273
|
+
}else{
|
|
274
|
+
return stdout.toString();
|
|
275
|
+
}
|
|
276
|
+
}else{
|
|
277
|
+
console.log( 'No output generated.')
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
} // end of hint
|
|
282
|
+
|
|
283
|
+
} // end of class
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
export default JSHintPlugin;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caweb/jshint-webpack-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "@caweb/jshint-webpack-plugin",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"html-format": "^1.1.7"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"node_modules/html-format": {
|
|
16
|
+
"version": "1.1.7",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/html-format/-/html-format-1.1.7.tgz",
|
|
18
|
+
"integrity": "sha512-ba6woq8dni6HkfRFpsu27PrCM/sxEz9KrVW22xVyMbXsZHbSpJE6Z1gAl7OeqEfU92MKYb9Pn7MnIyqV5tJHKA==",
|
|
19
|
+
"license": "MIT"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@caweb/jshint-webpack-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Webpack Plugin to run JSHint",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 0"
|
|
9
|
+
},
|
|
10
|
+
"author": "Danny Guzman",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"html-format": "^1.1.7"
|
|
14
|
+
}
|
|
15
|
+
}
|