@caweb/css-audit-webpack-plugin 2.0.2 → 2.0.3

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,11 +9,12 @@ export function hasOwnProperty( obj, prop ) {
9
9
 
10
10
  export default {
11
11
  format: 'html',
12
- filename: 'index',
12
+ filename: 'reports',
13
13
  outputFolder: '/audits/css',
14
14
  audits: [
15
15
  'colors',
16
16
  'important',
17
+ 'alphas',
17
18
  'display-none',
18
19
  'selectors',
19
20
  'media-queries',
package/index.js CHANGED
@@ -5,10 +5,10 @@
5
5
  */
6
6
  import { sync as resolveBin } from 'resolve-bin';
7
7
  import spawn from 'cross-spawn';
8
- import { getAllFilesSync } from 'get-all-files'
9
8
  import EntryDependency from "webpack/lib/dependencies/EntryDependency.js";
9
+ import { flagExists, getArgVal, getAllFlags } from '@caweb/webpack/lib/args.js';
10
10
 
11
- import path from 'path';
11
+ import path, { resolve } from 'path';
12
12
  import fs from 'fs';
13
13
  import deepmerge from 'deepmerge';
14
14
  import chalk from 'chalk';
@@ -16,44 +16,33 @@ import { fileURLToPath, URL } from 'url';
16
16
 
17
17
  // default configuration
18
18
  import {default as DefaultConfig} from './css-audit.config.js';
19
+ import { get } from 'http';
19
20
 
20
21
  const boldWhite = chalk.bold.white;
21
22
  const boldGreen = chalk.bold.green;
22
23
  const boldBlue = chalk.bold.hex('#03a7fc');
23
24
  const currentPath = path.dirname(fileURLToPath(import.meta.url));
24
25
 
26
+ const pluginName = 'CAWebCSSAuditPlugin';
27
+
25
28
  // CSS Audit Plugin
26
- class CSSAuditPlugin {
29
+ class CAWebCSSAuditPlugin {
27
30
  config = {};
28
31
 
29
32
  constructor(opts = {}) {
30
- // the default publicPath is always the outputFolder
31
- DefaultConfig.publicPath = DefaultConfig.outputFolder;
32
-
33
- // the default output folder is always relative to the current working directory.
34
- DefaultConfig.outputFolder = path.join( process.cwd(), DefaultConfig.outputFolder );
35
-
36
- // if opts.outputFolder is defined
37
- if( opts.outputFolder && ! path.isAbsolute(opts.outputFolder) ){
38
- opts.publicPath = opts.outputFolder;
39
-
40
- // we join the current working directory with the opts.outputFolder
41
- opts.outputFolder = path.join( process.cwd(), opts.outputFolder );
42
- }
43
-
44
33
  this.config = deepmerge(DefaultConfig, opts);
45
34
  }
46
35
 
47
36
  apply(compiler) {
48
37
  const staticDir = {
49
- directory: this.config.outputFolder,
50
- publicPath: encodeURI(this.config.publicPath).replace(':', ''),
38
+ directory: path.join( process.cwd(), this.config.outputFolder ),
39
+ publicPath: encodeURI(this.config.outputFolder).replace(':', ''),
51
40
  watch: true
52
41
  }
53
42
 
54
43
  let { devServer } = compiler.options;
55
44
  let auditUrl = `${devServer.server}://${devServer.host}:${devServer.port}`;
56
- let nodeModulePath = encodeURI(this.config.publicPath).replace(':', '') + '/node_modules';
45
+ let nodeModulePath = encodeURI(staticDir.publicPath) + '/node_modules';
57
46
  let pathRewrite = {};
58
47
  pathRewrite[`^${nodeModulePath}`] = '';
59
48
 
@@ -80,124 +69,89 @@ class CSSAuditPlugin {
80
69
  // if dev server allows for multiple pages to be opened
81
70
  // add filename.html to open property.
82
71
  if( Array.isArray(devServer.open) ){
83
- devServer.open.push(`${staticDir.publicPath}/${this.config.filename}.html`)
72
+ devServer.open.push(`${auditUrl}${this.config.outputFolder}/${this.config.filename}.html`)
84
73
  }else if( 'object' === typeof devServer.open && Array.isArray(devServer.open.target) ){
85
- devServer.open.target.push(`${staticDir.publicPath}/${this.config.filename}.html`)
74
+ devServer.open.target.push(`${auditUrl}${this.config.outputFolder}/${this.config.filename}.html`)
86
75
  }
87
76
 
88
- // we always make sure the output folder exists
89
- fs.mkdirSync( staticDir.directory, { recursive: true } );
90
-
91
77
  // Wait for configuration preset plugins to apply all configure webpack defaults
92
- compiler.hooks.initialize.tap('CSS Audit Plugin', () => {
93
- compiler.hooks.compilation.tap(
94
- "CSS Audit Plugin",
95
- (compilation, { normalModuleFactory }) => {
96
- compilation.dependencyFactories.set(
97
- EntryDependency,
98
- normalModuleFactory
99
- );
100
- }
101
- );
102
-
103
- const { entry, options, context } = {
104
- entry: path.join(staticDir.directory, 'css-audit.update.js'),
105
- options: {
106
- name: 'css-audit.update'
107
- },
108
- context: staticDir.directory
109
- };
110
-
111
- const dep = new EntryDependency(entry);
112
- dep.loc = {
113
- name: options.name
114
- };
78
+ // compiler.hooks.initialize.tap(pluginName, () => {
79
+ // compiler.hooks.compilation.tap(
80
+ // pluginName,
81
+ // (compilation, { normalModuleFactory }) => {
82
+ // compilation.dependencyFactories.set(
83
+ // EntryDependency,
84
+ // normalModuleFactory
85
+ // );
86
+ // }
87
+ // );
88
+
89
+ // // const { entry, options, context } = {
90
+ // // entry: path.join(staticDir.directory, 'css-audit.update.js'),
91
+ // // options: {
92
+ // // name: 'css-audit.update'
93
+ // // },
94
+ // // context: staticDir.directory
95
+ // // };
96
+
97
+ // // const dep = new EntryDependency(entry);
98
+ // // dep.loc = {
99
+ // // name: options.name
100
+ // // };
115
101
 
116
- fs.writeFileSync(
117
- path.join(staticDir.directory, `css-audit.update.js`),
118
- `` // required for hot-update to compile on our page, blank script for now
119
- );
120
-
121
- compiler.hooks.thisCompilation.tap('CSS Audit Plugin',
122
- /**
123
- * Hook into the webpack compilation
124
- * @param {Compilation} compilation
125
- */
126
- (compilation) => {
102
+ // // fs.writeFileSync(
103
+ // // path.join(staticDir.directory, `css-audit.update.js`),
104
+ // // `` // required for hot-update to compile on our page, blank script for now
105
+ // // );
106
+
107
+ // // compiler.hooks.thisCompilation.tap(pluginName,
108
+ // // /**
109
+ // // * Hook into the webpack compilation
110
+ // // * @param {Compilation} compilation
111
+ // // */
112
+ // // (compilation) => {
127
113
 
128
- compiler.hooks.make.tapAsync("CSS Audit Plugin", (compilation, callback) => {
114
+ // // compiler.hooks.make.tapAsync(pluginName, (compilation, callback) => {
129
115
 
130
- compilation.addEntry(
131
- context,
132
- dep,
133
- options,
134
- err => {
135
- callback(err);
136
- });
137
- });
116
+ // // compilation.addEntry(
117
+ // // context,
118
+ // // dep,
119
+ // // options,
120
+ // // err => {
121
+ // // callback(err);
122
+ // // });
123
+ // // });
138
124
 
139
125
 
140
126
 
141
- });
127
+ // // });
128
+ compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
142
129
 
143
- compiler.hooks.done.tapAsync('CSS Audit Plugin',
130
+ compiler.hooks.done.tapAsync(pluginName,
144
131
  (stats, callback) => {
145
- let files = [];
146
- getAllFilesSync(compiler.options.output.path).toArray().forEach(f => {
147
- // we skip any Right to Left style sheets
148
- if( f.endsWith('.css') && ! f.endsWith('-rtl.css') ){
149
- files.push(f)
150
- }
151
- })
152
- console.log(`<i> ${boldGreen('[webpack-dev-middleware] Running CSS Audit...')}`);
153
-
154
- let audits = {};
155
- this.config.audits.forEach( (audit) => {
156
- let key = 'string' === typeof audit ? audit : audit[0];
157
- let value = 'string' === typeof audit ? true : audit[1];
158
-
159
- // fix key
160
- key = key.replace(/-\w/g, (m) => m[1].toUpperCase());
161
-
162
- // if key already exists, push value to array
163
- if( audits.hasOwnProperty(key) ){
164
- audits[key].push(value);
165
- }else{
166
- // otherwise, if the audit is an array create a new array with the value
167
- audits[key] = ! Array.isArray(audit) ? value : [value];
168
- }
169
- });
170
-
171
- let result = this.audit(files, {
172
- format: this.config.format,
173
- filename: this.config.filename,
174
- outputFolder: this.config.outputFolder,
175
- ...audits,
176
- } );
177
-
178
- if( result ){
179
- // we have to inject the css-audit.update.js file into the head in order for the webpack-dev-server scripts to load.
180
- let pageContent = fs.readFileSync(path.join(staticDir.directory, `${this.config.filename}.html`))
181
-
182
- fs.writeFileSync(
183
- path.join(staticDir.directory, `${this.config.filename}.html`),
184
- pageContent.toString().replace('</head>', `<script src="./css-audit.update.js"></script>\n</head>`)
185
- )
186
- }
132
+ console.log('<i> \x1b[32m[webpack-dev-middleware] Running CSS Auditor...\x1b[0m');
133
+ /**
134
+ * We run the css audit
135
+ *
136
+ * we scan the output.publicPath if its set and not set to 'auto'
137
+ * otherwise we scan the output.path
138
+ */
139
+ let scanDir = compiler.options.output.publicPath && 'auto' !== compiler.options.output.publicPath ?
140
+ compiler.options.output.publicPath :
141
+ compiler.options.output.path;
142
+
143
+ // get all .css files
144
+ let files = fs.readdirSync( scanDir, { recursive: true } ).filter( f => f.endsWith( '.css' ) ).map( f => path.join( scanDir, f ) );
145
+
146
+ this.audit(files, this.config );
147
+ console.log(`<i> \x1b[32m[webpack-dev-middleware] CSS Auditor Report can be viewed at \x1b[34m ${new URL(`${auditUrl}${staticDir.publicPath}/${this.config.outputFilename}.html`).toString()}\x1b[0m`);
187
148
 
188
- console.log(`<i> ${boldGreen('[webpack-dev-middleware] CSS Audit can be viewed at')} ${ boldBlue(new URL(`${auditUrl}${staticDir.publicPath}/${this.config.filename}.html`).toString()) }`);
189
-
190
- callback();
191
- }
192
- )
193
-
149
+ });
194
150
  });
151
+ // });
195
152
 
196
153
  }
197
154
 
198
-
199
-
200
-
201
155
  /**
202
156
  * Run WordPress CSS Audit
203
157
  *
@@ -222,143 +176,93 @@ class CSSAuditPlugin {
222
176
  format,
223
177
  filename,
224
178
  outputFolder,
225
- colors,
226
- important,
227
- displayNone,
228
- selectors,
229
- mediaQueries,
230
- typography,
231
- propertyValues
179
+ audits
232
180
  }){
233
181
 
234
- let filesToBeAudited = [];
235
- let filesWithIssues = [];
182
+ // outputFolder should not be absolute
183
+ outputFolder = path.isAbsolute(outputFolder) ? path.join( process.cwd(), outputFolder ) : outputFolder;
236
184
 
237
- // the css audit tool always outputs to its own public directory
238
- let defaultOutputPath = path.join(currentPath, 'bin', 'auditor', 'public');
239
-
240
- // we always make sure the output folder exists
241
- fs.mkdirSync( outputFolder, { recursive: true } );
242
-
243
- files.forEach( (paths, i) => {
244
- let resolvePath = path.resolve(paths);
245
-
246
- try {
247
- // if given path is a directory
248
- if( fs.statSync(resolvePath).isDirectory() ){
249
-
250
- // get all .css files
251
- getAllFilesSync(resolvePath).toArray().forEach(f => {
252
- if( f.endsWith('.css') ){
253
- filesToBeAudited.push(f)
254
- }
255
- })
256
- // if given path is a file and a .css file
257
- }else if( fs.statSync(paths).isFile() && (paths.endsWith('.css') || paths.endsWith('.scss')) ){
258
- filesToBeAudited.push(paths)
259
- }
260
- // invalid path/file
261
- } catch (error) {
262
- filesWithIssues.push(paths)
185
+
186
+ // pass all audits except propertyValues since those take a value there may be multiple
187
+ let propertyValues = [];
188
+ let auditArgs = audits.map( (audit) => {
189
+ if('string' === typeof audit) {
190
+ return `--${audit}`;
191
+ }else if( Array.isArray(audit) && 'property-values' === audit[0] ){
192
+ // propertyValues += audit[1].split(',').map( v => v.trim() ).join(',');
193
+ propertyValues = propertyValues.concat( audit[1].split(',').map( v => v.trim() ) );
194
+ return false;
263
195
  }
264
-
265
- });
266
-
267
- if( ! filesToBeAudited.length ){
268
- console.log('No file(s) or directory path(s) were given or default directory was not found.')
269
- console.log('Auditor did not execute.');
270
-
271
- fs.copyFileSync(
272
- path.join(currentPath, 'sample', 'no-files.html'),
273
- path.join(defaultOutputPath, `${filename}.html`),
274
- )
275
-
276
- return false;
196
+ }).filter( Boolean );
197
+
198
+ // push property values separately
199
+ if( propertyValues ){
200
+ auditArgs.push(`--property-values=${propertyValues.join(',')}`);
277
201
  }
278
202
 
279
- /**
280
- * We combine process arguments from argv, argv0
281
- */
282
- const processArgs = [
283
- ...process.argv,
284
- ...process.argv0.split(' '),
285
-
286
- ]
287
-
288
- // we also add args from env.NODE_OPTIONS
289
- if( process.env.NODE_OPTIONS ){
290
- processArgs.push( ...process.env.NODE_OPTIONS.split(' ').filter(e=>e).map((o) => o.replaceAll("'", '')) )
203
+ // add the format if set
204
+ if( format ){
205
+ auditArgs.push( `--format=${format}` );
291
206
  }
292
-
207
+
293
208
  /**
294
209
  * the css audit uses the filename for the title, rather than the project name
295
210
  * we fix that by passing the project name for the file name
296
211
  * then renaming the file to the intended file name.
297
212
  */
298
- let auditArgs = [
299
- colors && ! processArgs.includes('--no-colors') ? '--colors' : '',
300
- important && ! processArgs.includes('--no-important') ? '--important' : '',
301
- displayNone && ! processArgs.includes('--no-display-none') ? '--display-none' : '',
302
- selectors && ! processArgs.includes('--no-selectors') ? '--selectors' : '',
303
- // mediaQueries && ! processArgs.includes('--no-media-queries') ? '--media-queries' : '',
304
- typography && ! processArgs.includes('--no-typography') ? '--typography' : '',
305
- format ? `--format=${format}` : '',
306
- filename ? `--filename=${path.basename(process.cwd())}` : ''
307
- ].filter( e => e)
308
-
309
- if( propertyValues && ! processArgs.includes('--no-property-values') ){
310
- propertyValues.forEach((p) => {
311
- auditArgs.push(`--property-values=${p.replace(' ',',')}`)
312
- })
313
- }
213
+ auditArgs.push( `--filename=${path.basename(process.cwd())}` );
314
214
 
315
215
  let { stdout, stderr } = spawn.sync(
316
216
  'node',
317
217
  [
318
- resolveBin('@caweb/css-audit-webpack-plugin', {executable: 'auditor'}),
319
- // '--',
320
- ...filesToBeAudited,
218
+ resolveBin(currentPath, {executable: 'auditor'}),
219
+ ...files,
321
220
  ...auditArgs
322
221
  ],
323
222
  {
324
- shell: false,
325
223
  stdio: 'pipe',
326
- cwd: fs.existsSync(path.join(process.cwd(), 'css-audit.config.cjs')) ? process.cwd() : currentPath
224
+ cwd: path.join( currentPath, 'bin', 'auditor' ), // has to be set to the bin/auditor directory
327
225
  }
328
226
  )
329
227
 
228
+ // if there was an error with the audit process
330
229
  if( stderr && stderr.toString() ){
331
- console.log( stderr.toString() )
230
+ console.log( 'CSS Audit Error: ', stderr.toString() );
332
231
  }
232
+
333
233
 
334
234
  if( stdout && stdout.toString() ){
235
+ // the css audit tool always outputs to its own public directory
236
+ let defaultOutputPath = path.join(currentPath, 'bin', 'auditor', 'public');
237
+
238
+ // rename the file back to the intended file name instead of the project name
239
+ fs.renameSync(
240
+ path.join(defaultOutputPath, `${path.basename(process.cwd())}.html`),
241
+ path.join(defaultOutputPath, `${filename}.html`)
242
+ )
335
243
 
336
- // rename the file back to the intended file name instead of the project name
337
- let outputFile = path.join(outputFolder, `${filename}.html`);
244
+ // ensure the output folder exists before moving files
245
+ fs.mkdirSync( outputFolder, { recursive: true } );
338
246
 
247
+ // move all files except .gitkeep to the output folder
248
+ fs.readdirSync( defaultOutputPath ).filter( f => ! f.endsWith('.gitkeep')).forEach( (f) => {
339
249
  fs.renameSync(
340
- path.join(defaultOutputPath, `${path.basename(process.cwd())}.html`),
341
- outputFile
250
+ path.join( defaultOutputPath, f ),
251
+ path.join( outputFolder, f )
342
252
  )
343
-
344
- // we also move the style.css as well case the output path is different than the default.
345
- if( fs.existsSync( path.join( defaultOutputPath, 'style.css' ) ) ){
346
- fs.renameSync(
347
- path.join( defaultOutputPath, 'style.css' ),
348
- path.join( outputFolder, 'style.css' )
349
- )
350
- }
253
+ })
351
254
 
352
- let msg = stdout.toString().replace('undefined', '').replace('template', 'css audit');
353
-
354
- // the command was ran via cli
355
- if( 'audit' === process.argv[2] ){
356
- console.log( msg );
357
- console.log( path.resolve(outputFile) )
358
- // otherwise it's being applied during the webpack process.
359
- }else{
360
- return msg;
361
- }
255
+ // clean up the default output message
256
+ let msg = stdout.toString().replace('undefined', '').replace('template', 'css audit');
257
+
258
+ // the command was ran via cli
259
+ if( 'audit' === process.argv[2] ){
260
+ console.log( msg );
261
+ console.log( path.join(outputFolder, `${filename}.html`) )
262
+ // otherwise it's being applied during the webpack process.
263
+ }else{
264
+ return msg;
265
+ }
362
266
 
363
267
  }
364
268
 
@@ -367,4 +271,4 @@ class CSSAuditPlugin {
367
271
  } // end of class
368
272
 
369
273
 
370
- export default CSSAuditPlugin;
274
+ export default CAWebCSSAuditPlugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caweb/css-audit-webpack-plugin",
3
- "version": "2.0.2",
3
+ "version": "2.0.3",
4
4
  "description": "CAWebPublishing Webpack Plugin to run WordPress CSS Audit",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -36,9 +36,11 @@
36
36
  "scripts": {
37
37
  "webpack": "webpack",
38
38
  "postinstall": "cd bin/auditor && npm ci",
39
+ "serve": "webpack serve --config ./node_modules/@caweb/webpack/webpack.config.js ./tests/webpack.tests.js --merge",
39
40
  "test": "echo \"Error: run tests from root\" && exit 0"
40
41
  },
41
42
  "dependencies": {
43
+ "@caweb/webpack": "^1.6.3",
42
44
  "chalk": "^5.6.2",
43
45
  "cross-spawn": "^7.0.6",
44
46
  "deepmerge": "^4.3.1",
@@ -47,7 +49,8 @@
47
49
  "twig": "^1.17.1"
48
50
  },
49
51
  "devDependencies": {
50
- "webpack": "^5.102.1",
52
+ "@caweb/html-webpack-plugin": "^2.0.2",
53
+ "webpack": "^5.104.1",
51
54
  "webpack-cli": "^6.0.1"
52
55
  }
53
56
  }