@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/lib/cli.js CHANGED
@@ -4,9 +4,7 @@
4
4
  //const wpenv_cli = require('@wordpress/env/lib/cli');
5
5
 
6
6
  import path from 'path';
7
- import chalk from 'chalk';
8
7
  import fs from 'fs';
9
- import terminalLink from 'terminal-link';
10
8
  import { Command, Argument, Option } from 'commander';
11
9
 
12
10
  /**
@@ -14,10 +12,6 @@ import { Command, Argument, Option } from 'commander';
14
12
  */
15
13
  import * as env from '../commands/index.js';
16
14
  import {
17
- wpPrimary,
18
- wpGreen,
19
- wpYellow,
20
- wpRed,
21
15
  withSpinner,
22
16
  projectPath,
23
17
  } from './index.js';
@@ -55,9 +49,7 @@ function addWPEnvCommands(){
55
49
  // Start command.
56
50
  program.command('start')
57
51
  .description(
58
- wpGreen(
59
- chalk`Starts two CAWebPublishing WordPress instances\ndevelopment on port ${ terminalLink( '8888', 'http://localhost:8888' ) } (override with WP_ENV_PORT)\ntests on port {bold.underline ${ terminalLink( '8889', 'http://localhost:8889' ) }} (override with WP_ENV_TESTS_PORT).\nAfter first install, use the '--update' flag to download updates to mapped sources and to re-apply CAWeb configuration options.`
60
- )
52
+ `Starts two CAWebPublishing WordPress instances\ndevelopment on port http://localhost:8888 (override with WP_ENV_PORT)\ntests on port http://localhost:8889 (override with WP_ENV_TESTS_PORT).`
61
53
  )
62
54
  .option(
63
55
  '--update',
@@ -104,9 +96,7 @@ function addWPEnvCommands(){
104
96
  // Destroy Command.
105
97
  program.command('destroy')
106
98
  .description(
107
- wpRed(
108
- 'Deletes docker containers, volumes, and networks associated with the CAWebPublishing instances and removes local files.'
109
- )
99
+ 'Deletes docker containers, volumes, and networks associated with the CAWebPublishing instances and removes local files.'
110
100
  )
111
101
  .option(
112
102
  '--scripts',
@@ -119,9 +109,7 @@ function addWPEnvCommands(){
119
109
  // Stop Command.
120
110
  program.command('stop')
121
111
  .description(
122
- wpRed(
123
- 'Stops running WordPress for development and tests and frees the ports.'
124
- )
112
+ 'Stops running WordPress for development and tests and frees the ports.'
125
113
  )
126
114
  .allowUnknownOption(true)
127
115
  .action( withSpinner(env.stop) )
@@ -135,7 +123,7 @@ function addWPEnvCommands(){
135
123
  // Clean Command.
136
124
  program.command('clean')
137
125
  .description(
138
- wpYellow( 'Cleans the WordPress databases.' )
126
+ 'Cleans the WordPress databases.'
139
127
  )
140
128
  .addArgument(envArg)
141
129
  .option(
@@ -190,15 +178,10 @@ export default function cli() {
190
178
 
191
179
 
192
180
  program
193
- .name(wpPrimary( 'caweb'))
194
- .usage( wpYellow( '<command>' ) )
181
+ .name('caweb')
182
+ .usage( '<command>' )
195
183
  .description('Command Line Interface utilized by CAWebPublishing to accomplish several tasks.')
196
184
  .version( pkg.version )
197
- .option(
198
- '--debug',
199
- 'Enable debug output.',
200
- false
201
- )
202
185
  .allowUnknownOption(true)
203
186
  .configureHelp({
204
187
  sortSubcommands: true,
@@ -212,34 +195,78 @@ export default function cli() {
212
195
  program.command('build')
213
196
  .description('Builds the current project.')
214
197
  .allowUnknownOption(true)
215
- .action(withSpinner(env.webpack))
198
+ .action(env.webpack)
216
199
 
217
200
 
218
201
  // Serve Command.
219
202
  program.command('serve')
220
- .description('Serve the current project')
221
- .option(
222
- '--audit',
223
- 'Add CSS-Audit Page to pages served.',
224
- false
225
- )
203
+ .description('Serve the current project')
204
+ .option( '--audit', 'Performs WordPress CSS-Audit.', true )
205
+ .option( '--no-audit', 'Skips WordPress CSS-Audit.', false )
206
+ .option( '--a11y', 'Performs IBM Accessibility Checker.', true )
207
+ .option( '--no-a11y', 'Skips IBM Accessibility Checker.', false )
226
208
  .allowUnknownOption(true)
227
- .action(withSpinner(env.webpack))
209
+ //.action(withSpinner(env.webpack))
210
+ .action(env.webpack)
228
211
 
229
212
  // a11y Command.
230
213
  program.command('a11y')
231
- .addArgument(new Argument('<url>', 'URL to scan for accessibility checks.'))
232
214
  .description('Runs accessibility checks.')
215
+ .addArgument(new Argument('<url>', 'URL to scan for accessibility checks.'))
216
+ .option( '--rule-archive <rule>', 'Specify the rule archive.', 'latest')
217
+ .option( '--policies <policy...>', 'Specify one or many policies to scan.', ['WCAG_2_1'])
218
+ .option( '--fail-levels <levels...>', 'Specify one or many violation levels on which to fail the test.', [
219
+ 'violation',
220
+ 'potentialviolation'
221
+ ])
222
+ .option( '--report-levels <levels...>', 'Specify one or many violation levels that should be reported.', [
223
+ 'violation',
224
+ 'potentialviolation',
225
+ 'recommendation',
226
+ 'potentialrecommendation',
227
+ 'manual',
228
+ 'pass'
229
+ ])
230
+ .option( '--labels <label...>', 'Specify labels that you would like associated to your scan.', [])
231
+ .option( '--output-format <format>', 'In which formats should the results be output.', ['html'])
232
+ .option( '--output-filename <name>', 'Filename for the scan results.')
233
+ .option( '--output-folder <folder>', 'Where the scan results should be saved.', 'a11y')
234
+ .option( '--output-filename-timestamp', 'Should the timestamp be included in the filename of the reports?', false)
233
235
  .allowUnknownOption(true)
234
- .action(withSpinner(env.a11y))
236
+ .action(env.a11y)
235
237
 
236
238
 
237
239
  // audit Command.
238
240
  program.command('audit')
239
- .addArgument(new Argument('[files...]', 'Files or directory path to CSS files.').default('build'))
240
241
  .description('Runs WordPress CSS Audit tool against projects.')
242
+ .addArgument(new Argument('[files...]', 'Files or directory path to CSS files.').default(['./build']))
243
+ .option('--format [format]', 'Format to use for displaying report.', 'html' )
244
+ .option('--filename [name]', 'If using a format that outputs to a file, specify the file name.', 'css-audit' )
245
+ .option('--colors', 'Runs colors audit.', true )
246
+ .addOption(new Option('--no-colors', 'Skips colors audit.', false ).hideHelp())
247
+ .option('--important', 'Runs !important audit.', true )
248
+ .addOption(new Option('--no-important', 'Skips !important audit.', false ).hideHelp())
249
+ .option('--display-none', 'Runs display: none audit.', true )
250
+ .addOption(new Option('--no-display-none', 'Skips display: none audit.', false ).hideHelp())
251
+ .option('--selectors', 'Runs selectors audit.', true )
252
+ .addOption(new Option('--no-selectors', 'Skips selectors audit.', false ).hideHelp())
253
+ .option('--media-queries', 'Runs media queries audit.', true )
254
+ .addOption(new Option('--no-media-queries', 'Skips media queries audit.', false ).hideHelp())
255
+ .option('--typography', 'Runs typography audit.', true )
256
+ .addOption(new Option('--no-typography', 'Skips typography audit.', false ).hideHelp())
257
+ .option('--property-values <values...>', 'Runs property value audit.', ['font-size', 'padding,padding-top,padding-bottom,padding-right,padding-left', 'margin,margin-top,marin-bottom,marin-right,marin-left'] )
258
+ .addOption(new Option('--no-property-values', 'Skips property values audit.', false ).hideHelp())
259
+ .allowUnknownOption(true)
260
+ .action(env.audit)
261
+
262
+ // JSHint Command.
263
+ program.command('jshint')
264
+ .description('Runs JSHint tool against projects.')
265
+ .addArgument(new Argument('[files...]', 'Files or directory path to JS files.').default(['./src']))
266
+ .option( '--output-filename <name>', 'Filename for the scan results.')
267
+ .option( '--output-folder <folder>', 'Where the hint results should be saved.')
241
268
  .allowUnknownOption(true)
242
- .action(withSpinner(env.audit))
269
+ .action(env.hint)
243
270
 
244
271
  // Update Plugins Command.
245
272
  program.command('update-plugins')
@@ -266,9 +293,9 @@ export default function cli() {
266
293
 
267
294
  // Update a Design System Block Command.
268
295
  program.command('sync')
269
- .description('Sync changes from one destination to another.')
270
- .argument('<from>', 'Target Site URL with current changes.')
271
- .argument('<to>', 'Destination Site URL that should be synced.')
296
+ .description('Sync changes from one WordPress instance to another.')
297
+ .argument('<from>', 'Target Site URL.')
298
+ .argument('<to>', 'Destination Site URL.')
272
299
  .addOption(new Option(
273
300
  '-t,--tax [tax...]',
274
301
  'Taxonomy that should be synced. Omitting this option will sync the full site.'
package/lib/helpers.js CHANGED
@@ -52,7 +52,6 @@ async function runCmd(cmd, args,opts = { stdio: ['inherit', 'pipe'] }){
52
52
  }
53
53
 
54
54
  return spawn.sync( cmd, args, {...opts, env: process.env});
55
-
56
55
  }
57
56
 
58
57
  /**
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Configuration for Accessibility Checker
3
+ * @link https://www.npmjs.com/package/accessibility-checker
4
+ */
5
+
6
+ let levels = [
7
+ 'violation',
8
+ 'potentialviolation',
9
+ 'recommendation',
10
+ 'potentialrecommendation',
11
+ 'manual',
12
+ 'pass'
13
+ ];
14
+ let reportLevels = levels;
15
+ let failLevels = levels;
16
+
17
+ // process args
18
+ process.argv.forEach((arg) => {
19
+ // remove any report levels
20
+ if( arg.includes('--no-report-levels-') ){
21
+ let r = arg.replace('--no-report-levels-', '')
22
+ delete reportLevels[reportLevels.indexOf(r)]
23
+ }
24
+ // remove any fails levels
25
+ if( arg.includes('--no-fail-levels-') ){
26
+ let f = arg.replace('--no-fail-levels-', '')
27
+ delete failLevels[failLevels.indexOf(f)]
28
+ }
29
+ })
30
+
31
+ export default {
32
+ ruleArchive: "latest",
33
+ policies: [
34
+ 'WCAG_2_1'
35
+ ],
36
+ failLevels: failLevels.filter(e=>e),
37
+ reportLevels: reportLevels.filter(e=>e),
38
+ outputFilename: 'a11y',
39
+ outputFolder: "public",
40
+ outputFormat: [
41
+ 'html'
42
+ ],
43
+ outputFilenameTimestamp: false
44
+ }
@@ -0,0 +1,272 @@
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
+ import path from 'path';
11
+ import { isUrl, isValidUrl } from 'check-valid-url';
12
+ import fs 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 './aceconfig.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
+ // IBM Accessibility Checker Plugin
26
+ class A11yPlugin {
27
+ config = {}
28
+
29
+ constructor(opts = {}) {
30
+ // outputFolder must be resolved
31
+ if( opts.outputFolder ){
32
+ opts.outputFolder = path.join(process.cwd(), opts.outputFolder);
33
+ }
34
+ this.config = deepmerge(
35
+ DefaultConfig,
36
+ {
37
+ outputFolder: path.join(currentPath, DefaultConfig.outputFolder)
38
+ },
39
+ opts
40
+ );
41
+ }
42
+
43
+ apply(compiler) {
44
+ const staticDir = {
45
+ directory: this.config.outputFolder,
46
+ watch: true
47
+ }
48
+ let { devServer, output } = compiler.options;
49
+ let hostUrl = 'localhost' === devServer.host ? `http://${devServer.host}`: devServer.host;
50
+ let hostPort = devServer.port;
51
+
52
+ if( hostPort && 80 !== hostPort )
53
+ {
54
+ hostUrl = `${hostUrl}:${hostPort}`;
55
+ }
56
+
57
+ // if dev server allows for multiple pages to be opened
58
+ // add outputFilename.html to open property.
59
+ if( Array.isArray(devServer.open) ){
60
+ devServer.open.push(`${hostUrl}/${this.config.outputFilename}.html`)
61
+ }else if( 'object' === typeof devServer.open && Array.isArray(devServer.open.target) ){
62
+ devServer.open.target.push(`${hostUrl}/${this.config.outputFilename}.html`)
63
+ }
64
+
65
+ // add our static directory
66
+ if( Array.isArray(devServer.static) ){
67
+ devServer.static.push(staticDir)
68
+ }else{
69
+ devServer.static = [].concat(devServer.static, staticDir );
70
+ }
71
+
72
+ // Wait for configuration preset plugins to apply all configure webpack defaults
73
+ compiler.hooks.initialize.tap('IBM Accessibility Plugin', () => {
74
+ compiler.hooks.compilation.tap(
75
+ "IBM Accessibility Plugin",
76
+ (compilation, { normalModuleFactory }) => {
77
+ compilation.dependencyFactories.set(
78
+ EntryDependency,
79
+ normalModuleFactory
80
+ );
81
+ }
82
+ );
83
+
84
+ const { entry, options, context } = {
85
+ entry: path.join( this.config.outputFolder, 'a11y.update.js'),
86
+ options: {
87
+ name: 'a11y'
88
+ },
89
+ context: 'a11y'
90
+ };
91
+
92
+ const dep = new EntryDependency(entry);
93
+ dep.loc = {
94
+ name: options.name
95
+ };
96
+ if( ! fs.existsSync(path.resolve(this.config.outputFolder))){
97
+ fs.mkdirSync( path.resolve(this.config.outputFolder), {recursive: true} );
98
+ }
99
+
100
+ fs.writeFileSync(
101
+ path.join(this.config.outputFolder, `a11y.update.js`),
102
+ `` // required for hot-update to compile on our page, blank script for now
103
+ );
104
+
105
+
106
+ compiler.hooks.thisCompilation.tap('IBM Accessibility Plugin',
107
+ /**
108
+ * Hook into the webpack compilation
109
+ * @param {Compilation} compilation
110
+ */
111
+ (compilation) => {
112
+
113
+ compiler.hooks.make.tapAsync("IBM Accessibility Plugin", (compilation, callback) => {
114
+
115
+ compilation.addEntry(
116
+ context,
117
+ dep,
118
+ options,
119
+ err => {
120
+ callback(err);
121
+ });
122
+ });
123
+
124
+ compiler.hooks.done.tapAsync(
125
+ 'IBM Accessibility Plugin',
126
+ /**
127
+ * Hook into the process assets hook
128
+ * @param {any} _
129
+ * @param {(err?: Error) => void} callback
130
+ */
131
+ ({compilation}, callback) => {
132
+
133
+ console.log(`<i> ${boldGreen('[webpack-dev-middleware] Running IBM Accessibility scan...')}`);
134
+
135
+ this.a11yCheck(path.join(process.cwd(), output.publicPath ?? '/' ), this.config );
136
+
137
+ console.log(`<i> ${boldGreen('[webpack-dev-middleware] IBM Accessibilty Report can be viewed at')} ${ boldBlue(new URL(`${hostUrl}/${this.config.outputFilename}.html`).toString()) }`);
138
+
139
+ callback();
140
+ });
141
+
142
+ compiler.hooks.watchClose.tap( 'IBM Accessibility Plugin', () => {
143
+ getAllFilesSync(compiler.options.output.path).toArray().forEach(f => {
144
+ if(
145
+ f.includes('a11y') || // delete any a11y files
146
+ f.includes('.hot-update.js') // delete any HMR files
147
+ ){
148
+ fs.rmSync(f)
149
+ }
150
+ })
151
+ });
152
+
153
+ });
154
+
155
+ });
156
+
157
+ }
158
+
159
+ /**
160
+ * Run accessibility checks
161
+ *
162
+ * @param {Object} options
163
+ * @param {boolean} options.debug True if debug mode is enabled.
164
+ * @param {boolean} options.ruleArchive Specify the rule archive.
165
+ * @param {boolean} options.policies Specify one or many policies to scan.
166
+ * @param {boolean} options.failLevels Specify one or many violation levels on which to fail the test.
167
+ * @param {boolean} options.reportLevels Specify one or many violation levels that should be reported.
168
+ * @param {boolean} options.labels Specify labels that you would like associated to your scan.
169
+ * @param {boolean} options.outputFormat In which formats should the results be output.
170
+ * @param {boolean} options.outputFilename Filename for the scan results.
171
+ * @param {boolean} options.outputFolder Where the scan results should be saved.
172
+ * @param {boolean} options.outputFilenameTimestamp Should the timestamp be included in the filename of the reports?
173
+ */
174
+ a11yCheck(url, {
175
+ debug,
176
+ ruleArchive,
177
+ policies,
178
+ failLevels,
179
+ reportLevels,
180
+ labels,
181
+ outputFormat,
182
+ outputFilename,
183
+ outputFolder,
184
+ outputFilenameTimestamp
185
+ }){
186
+
187
+ let acheckerArgs = [
188
+ '--ruleArchive',
189
+ ruleArchive,
190
+ '--policies',
191
+ Array.isArray(policies) ? policies.filter(e => e).join(',') : policies,
192
+ '--failLevels',
193
+ Array.isArray(failLevels) ? failLevels.filter(e => e).join(',') : failLevels,
194
+ '--reportLevels',
195
+ Array.isArray(reportLevels) ? reportLevels.filter(e => e).join(',') : reportLevels,
196
+ '--outputFolder',
197
+ outputFolder,
198
+ '--outputFormat',
199
+ outputFormat,
200
+ '---outputFilenameTimestamp',
201
+ outputFilenameTimestamp,
202
+ url
203
+ ];
204
+
205
+ let isValid = false;
206
+
207
+ if( fs.existsSync( url ) ){
208
+ if( fs.statSync(url).isDirectory() && path.join( url, 'index.html') ){
209
+ url = path.join( url, 'index.html')
210
+ }
211
+ isValid = true;
212
+ }else{
213
+ isValid = 'localhost' === new URL(url).hostname || isUrl( url )
214
+ }
215
+
216
+ if( isValid ){
217
+ let originalFileName = `${fs.existsSync( url ) ?
218
+ path.resolve(url).replace(':', '_') :
219
+ url.replace(/http[s]+:\/\//, '')}.html`;
220
+ let originalJsonFileName = `${fs.existsSync( url ) ?
221
+ path.resolve(url).replace(':', '_') :
222
+ url.replace(/http[s]+:\/\//, '')}.json`;
223
+
224
+ let outputDir = path.resolve('.', outputFolder );
225
+
226
+ let {stderr, stdout} = spawn.sync(
227
+ resolveBin('accessibility-checker', {executable: 'achecker'}),
228
+ acheckerArgs,
229
+ {
230
+ stdio: 'pipe',
231
+ timeout: 30000 // stop after 30 seconds
232
+ }
233
+ )
234
+
235
+ if( stderr && stderr.toString() ){
236
+ console.log( stderr.toString() );
237
+ }
238
+
239
+ if( stdout && stdout.toString()){
240
+ let reportedFile = path.join(outputDir, originalFileName );
241
+ let reportedJSon = path.join(outputDir, originalJsonFileName );
242
+
243
+ // if output file name option was passed
244
+ if( outputFilename ){
245
+
246
+ reportedFile = path.join( outputDir, `${outputFilename}.html` );
247
+ reportedJSon = path.join( outputDir, `${outputFilename}.json` );
248
+
249
+ // rename the output files
250
+ fs.renameSync(path.join(outputDir, originalFileName), reportedFile );
251
+ fs.renameSync(path.join(outputDir, originalJsonFileName), reportedJSon );
252
+
253
+ // delete any empty directories.
254
+ fs.rmSync( path.join(outputDir, originalFileName.split(path.sep).shift()), {recursive: true} )
255
+ }
256
+
257
+ if( 'a11y' === process.argv[2] ){
258
+ console.log( reportedFile )
259
+ }else{
260
+ return reportedFile;
261
+ }
262
+ }
263
+ }else{
264
+ console.log( `${url} is not a valid url.` )
265
+ }
266
+
267
+ } // end of a11yCheck
268
+
269
+ } // end of class
270
+
271
+
272
+ export default A11yPlugin;
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "@caweb/a11y-webpack-plugin",
3
+ "version": "1.0.0",
4
+ "description": "Webpack Plugin to run Accessibility Scans",
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,5 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+
5
+ module.exports = {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+
5
+ export default {
6
+ format: 'html',
7
+ filename: 'css-audit',
8
+ colors: true,
9
+ important: true,
10
+ displayNone: true,
11
+ selectors: true,
12
+ mediaQueries: true,
13
+ typography: true,
14
+ propertyValues: [
15
+ 'font-size',
16
+ 'padding,padding-top,padding-bottom,padding-right,padding-left' ,
17
+ 'property-values', 'margin,margin-top,marin-bottom,marin-right,marin-left',
18
+ ]
19
+ };