Skip to content

Commit b51c322

Browse files
committedNov 12, 2023
Build: remove async and spawnback dependencies
Use async-await, for-of, and cp.spawn directly.
1 parent 8f769df commit b51c322

File tree

7 files changed

+248
-222
lines changed

7 files changed

+248
-222
lines changed
 

‎Gruntfile.js

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
module.exports = function( grunt ) {
44
grunt.initConfig( {
5+
xmllint: {
6+
all: [
7+
"fixture/entries/**",
8+
"entries2html.xsl"
9+
]
10+
},
511
"build-posts": {
612
page: "fixture/pages/**"
713
},
@@ -21,5 +27,6 @@ module.exports = function( grunt ) {
2127

2228
grunt.loadTasks( "tasks" );
2329

30+
grunt.registerTask( "lint", [ "xmllint" ] );
2431
grunt.registerTask( "build", [ "build-posts", "build-resources", "build-xml-entries" ] );
2532
};

‎lib/util.js

+5-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use strict";
22

33
const fs = require( "fs" );
4-
const async = require( "async" );
54
const marked = require( "marked" );
65

76
function htmlEscape( text ) {
@@ -69,23 +68,13 @@ function parseMarkdown( src, options ) {
6968
return marked.parser( tokens );
7069
}
7170

72-
function eachFile( files, stepFn, complete ) {
73-
var count = 0;
74-
75-
async.forEachSeries( files, function( fileName, fileDone ) {
71+
async function eachFile( files, stepFn ) {
72+
for ( const fileName of files ) {
7673
if ( !fs.statSync( fileName ).isFile() ) {
77-
return fileDone();
78-
}
79-
80-
count++;
81-
stepFn( fileName, fileDone );
82-
}, function( error ) {
83-
if ( error ) {
84-
return complete( error );
74+
continue;
8575
}
86-
87-
complete( null, count );
88-
} );
76+
await stepFn( fileName );
77+
}
8978
}
9079

9180
exports.htmlEscape = htmlEscape;

‎package-lock.json

-12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,18 @@
2020
}
2121
],
2222
"dependencies": {
23-
"async": "^3.2.0",
2423
"cheerio": "^1.0.0-rc.12",
2524
"grunt-check-modules": "^1.1.0",
2625
"gilded-wordpress": "1.0.6",
2726
"he": "^1.2.0",
2827
"highlight.js": "^10.7.2",
2928
"marked": "^4.0.0",
30-
"spawnback": "^1.0.1",
3129
"which": "^4.0.0",
3230
"wordpress": "^1.4.1"
3331
},
3432
"scripts": {
3533
"test": "eslint . && qunit test/*.js",
34+
"lint-example": "grunt lint",
3635
"build-example": "grunt build"
3736
},
3837
"devDependencies": {

‎tasks/build-xml.js

+132-125
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module.exports = function( grunt ) {
55
const fs = require( "fs" );
66
const path = require( "path" );
77
const which = require( "which" );
8-
const spawn = require( "spawnback" );
8+
const cp = require( "child_process" );
99
const util = require( "../lib/util" );
1010
const syntaxHighlight = require( "../lib/highlight" );
1111

@@ -30,39 +30,38 @@ function checkXsltproc() {
3030
return checkLibxml2( "xsltproc" );
3131
}
3232

33-
grunt.registerMultiTask( "xmllint", "Lint xml files", function() {
33+
grunt.registerMultiTask( "xmllint", "Lint xml files", async function() {
3434
var task = this,
3535
taskDone = task.async();
3636

3737
if ( !checkXmllint() ) {
3838
return taskDone( false );
3939
}
4040

41-
util.eachFile( this.filesSrc, function( fileName, fileDone ) {
41+
var count = 0;
42+
async function lintFile( fileName ) {
43+
count++;
4244
grunt.verbose.write( "Linting " + fileName + "..." );
43-
spawn( "xmllint", [ "--noout", fileName ], function( error ) {
44-
if ( error ) {
45-
grunt.verbose.error();
46-
grunt.log.error( error );
47-
return fileDone();
48-
}
49-
50-
grunt.verbose.ok();
51-
fileDone();
52-
} );
53-
}, function( _error, count ) {
54-
if ( task.errorCount ) {
55-
grunt.warn( "Task \"" + task.name + "\" failed." );
56-
return taskDone();
57-
}
45+
cp.spawnSync( "xmllint", [ "--noout", fileName ] );
46+
grunt.verbose.ok();
47+
}
5848

59-
grunt.log.writeln( "Lint free files: " + count );
49+
try {
50+
await util.eachFile( this.filesSrc, lintFile );
51+
} catch ( e ) {
52+
grunt.verbose.error();
53+
grunt.log.error( e );
54+
grunt.warn( "Task \"" + task.name + "\" failed." );
6055
taskDone();
61-
} );
56+
return;
57+
}
58+
59+
grunt.log.writeln( "Lint free files: " + count );
60+
taskDone();
6261
} );
6362

6463
grunt.registerMultiTask( "build-xml-entries",
65-
"Process API xml files with xsl and syntax highlight", function() {
64+
"Process API xml files with xsl and syntax highlight", async function() {
6665
var task = this,
6766
taskDone = task.async(),
6867
targetDir = grunt.config( "wordpress.dir" ) + "/posts/post/";
@@ -73,45 +72,45 @@ grunt.registerMultiTask( "build-xml-entries",
7372

7473
grunt.file.mkdir( targetDir );
7574

76-
util.eachFile( this.filesSrc, function( fileName, fileDone ) {
75+
var count = 0;
76+
async function transformFile( fileName ) {
77+
count++;
7778
grunt.verbose.write( "Transforming " + fileName + "..." );
78-
spawn( "xsltproc",
79-
[ "--xinclude", "entries2html.xsl", fileName ],
80-
function( error, content, stderr ) {
81-
82-
// Certain errors won't cause the tranform to fail. For example, a
83-
// broken include will write to stderr, but still exit cleanly.
84-
if ( stderr && !error ) {
85-
error = new Error( stderr );
86-
}
79+
const result = cp.spawnSync( "xsltproc",
80+
[ "--xinclude", "entries2html.xsl", fileName ]
81+
);
8782

88-
if ( error ) {
89-
grunt.verbose.error();
90-
grunt.log.error( error );
91-
return fileDone( error );
92-
}
93-
94-
grunt.verbose.ok();
95-
96-
var targetFileName = targetDir + path.basename( fileName, ".xml" ) + ".html";
83+
// Certain errors won't cause the tranform to fail. For example, a
84+
// broken include will write to stderr, but still exit cleanly.
85+
const error = result.stderr && result.stderr.toString();
86+
if ( error ) {
87+
throw new Error( error );
88+
}
9789

98-
// Syntax highlight code blocks
99-
if ( !grunt.option( "nohighlight" ) ) {
100-
content = syntaxHighlight( content );
101-
}
90+
let content = result.stdout.toString();
10291

103-
grunt.file.write( targetFileName, content );
104-
fileDone();
105-
} );
106-
}, function( _error, count ) {
107-
if ( task.errorCount ) {
108-
grunt.warn( "Task \"" + task.name + "\" failed." );
109-
return taskDone();
92+
// Syntax highlight code blocks
93+
if ( !grunt.option( "nohighlight" ) ) {
94+
content = syntaxHighlight( content );
11095
}
11196

112-
grunt.log.writeln( "Built " + count + " entries." );
97+
const targetFileName = targetDir + path.basename( fileName, ".xml" ) + ".html";
98+
grunt.file.write( targetFileName, content );
99+
grunt.verbose.ok();
100+
}
101+
102+
try {
103+
await util.eachFile( this.filesSrc, transformFile );
104+
} catch ( e ) {
105+
grunt.verbose.error();
106+
grunt.log.error( e );
107+
grunt.warn( "Task \"" + task.name + "\" failed." );
113108
taskDone();
114-
} );
109+
return;
110+
}
111+
112+
grunt.log.writeln( "Built " + count + " entries." );
113+
taskDone();
115114
} );
116115

117116
grunt.registerTask( "build-xml-categories", function() {
@@ -122,68 +121,70 @@ grunt.registerTask( "build-xml-categories", function() {
122121
return taskDone( false );
123122
}
124123

125-
spawn( "xsltproc",
126-
[ "--output", "taxonomies.xml",
127-
path.join( __dirname, "jquery-xml/cat2tax.xsl" ), "categories.xml" ],
128-
function( error ) {
129-
if ( error ) {
130-
grunt.verbose.error();
131-
grunt.log.error( error );
132-
return taskDone();
124+
try {
125+
cp.spawnSync( "xsltproc", [
126+
"--output", "taxonomies.xml",
127+
path.join( __dirname, "jquery-xml/cat2tax.xsl" ),
128+
"categories.xml"
129+
] );
130+
} catch ( error ) {
131+
grunt.verbose.error();
132+
grunt.log.error( error );
133+
return taskDone();
134+
}
135+
136+
try {
137+
cp.spawnSync( "xsltproc", [
138+
"--output", targetPath,
139+
path.join( __dirname, "jquery-xml/xml2json.xsl" ),
140+
"taxonomies.xml"
141+
] );
142+
} catch ( error ) {
143+
grunt.verbose.error();
144+
grunt.log.error( error );
145+
return taskDone();
146+
}
147+
148+
// xml2json can't determine when to use an array if there is only one child,
149+
// so we need to ensure all child terms are stored in an array
150+
var taxonomies = grunt.file.readJSON( targetPath );
151+
function normalize( term ) {
152+
if ( term.children && term.children.item ) {
153+
term.children = [ term.children.item ];
133154
}
134155

135-
spawn( "xsltproc",
136-
[ "--output", targetPath,
137-
path.join( __dirname, "jquery-xml/xml2json.xsl" ), "taxonomies.xml" ],
138-
function( error ) {
139-
if ( error ) {
140-
grunt.verbose.error();
141-
grunt.log.error( error );
142-
return taskDone();
143-
}
156+
if ( term.children ) {
157+
term.children.forEach( normalize );
158+
}
159+
}
160+
taxonomies.category.forEach( normalize );
161+
grunt.file.write( targetPath, JSON.stringify( taxonomies ) );
144162

145-
// xml2json can't determine when to use an array if there is only one child,
146-
// so we need to ensure all child terms are stored in an array
147-
var taxonomies = grunt.file.readJSON( targetPath );
148-
function normalize( term ) {
149-
if ( term.children && term.children.item ) {
150-
term.children = [ term.children.item ];
151-
}
152-
153-
if ( term.children ) {
154-
term.children.forEach( normalize );
155-
}
156-
}
157-
taxonomies.category.forEach( normalize );
158-
grunt.file.write( targetPath, JSON.stringify( taxonomies ) );
159-
160-
// Syntax highlight code blocks
161-
function highlightDescription( category ) {
162-
if ( category.description ) {
163-
category.description = syntaxHighlight( category.description );
164-
}
165-
}
163+
// Syntax highlight code blocks
164+
function highlightDescription( category ) {
165+
if ( category.description ) {
166+
category.description = syntaxHighlight( category.description );
167+
}
168+
}
166169

167-
function highlightCategories( categories ) {
168-
categories.forEach( function( category ) {
169-
highlightDescription( category );
170-
if ( category.children ) {
171-
highlightCategories( category.children );
172-
}
173-
} );
170+
function highlightCategories( categories ) {
171+
categories.forEach( function( category ) {
172+
highlightDescription( category );
173+
if ( category.children ) {
174+
highlightCategories( category.children );
174175
}
176+
} );
177+
}
175178

176-
if ( !grunt.option( "nohighlight" ) ) {
177-
taxonomies = grunt.file.readJSON( targetPath );
178-
highlightCategories( taxonomies.category );
179-
grunt.file.write( targetPath, JSON.stringify( taxonomies ) );
180-
}
179+
if ( !grunt.option( "nohighlight" ) ) {
180+
taxonomies = grunt.file.readJSON( targetPath );
181+
highlightCategories( taxonomies.category );
182+
grunt.file.write( targetPath, JSON.stringify( taxonomies ) );
183+
}
181184

182-
fs.unlinkSync( "taxonomies.xml" );
183-
grunt.verbose.ok();
184-
taskDone();
185-
} );
186-
} );
185+
fs.unlinkSync( "taxonomies.xml" );
186+
grunt.verbose.ok();
187+
taskDone();
187188
} );
188189

189190
grunt.registerTask( "build-xml-full", function() {
@@ -202,26 +203,32 @@ grunt.registerTask( "build-xml-full", function() {
202203
}
203204
} );
204205

205-
spawn( "xsltproc",
206-
[ "--xinclude", "--path", process.cwd(),
207-
208-
// "--output", grunt.config( "wordpress.dir" ) + "/resources/api.xml",
209-
path.join( __dirname, "jquery-xml/all-entries.xsl" ), "all-entries.xml" ],
210-
function( error, result ) {
206+
let result;
207+
let error;
208+
try {
209+
result = cp.spawnSync( "xsltproc", [
210+
"--xinclude",
211+
"--path", process.cwd(),
212+
path.join( __dirname, "jquery-xml/all-entries.xsl" ),
213+
"all-entries.xml"
214+
] );
215+
} catch ( e ) {
216+
error = e;
217+
}
211218

212-
// For some reason using --output with xsltproc kills the --xinclude option,
213-
// so we let it write to stdout, then save it to a file
214-
grunt.file.write( grunt.config( "wordpress.dir" ) + "/resources/api.xml", result );
215-
fs.unlinkSync( "all-entries.xml" );
219+
// For some reason using --output with xsltproc kills the --xinclude option,
220+
// so we let it write to stdout, then save it to a file
221+
const content = result.stdout.toString();
222+
grunt.file.write( grunt.config( "wordpress.dir" ) + "/resources/api.xml", content );
223+
fs.unlinkSync( "all-entries.xml" );
216224

217-
if ( error ) {
218-
grunt.verbose.error();
219-
grunt.log.error( error );
220-
return taskDone( false );
221-
}
225+
if ( error ) {
226+
grunt.verbose.error();
227+
grunt.log.error( error );
228+
return taskDone( false );
229+
}
222230

223-
taskDone();
224-
} );
231+
taskDone();
225232
} );
226233

227234
};

‎tasks/build.js

+73-67
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ grunt.registerTask( "lint", [] );
2121

2222
grunt.registerTask( "build-wordpress", [ "check-modules", "lint", "clean-dist", "build" ] );
2323

24-
grunt.registerMultiTask( "build-posts", "Process html and markdown files as posts", function() {
24+
grunt.registerMultiTask( "build-posts",
25+
"Process html and markdown files as posts", async function() {
2526
var task = this,
2627
taskDone = task.async(),
2728
wordpressClient = wordpress.createClient( grunt.config( "wordpress" ) ),
@@ -32,74 +33,78 @@ grunt.registerMultiTask( "build-posts", "Process html and markdown files as post
3233

3334
grunt.file.mkdir( targetDir );
3435

35-
function parsePost( fileName, callback ) {
36-
wordpressClient.parsePost( fileName, function( error, post ) {
37-
if ( error ) {
38-
return callback( error );
39-
}
40-
41-
preprocessor( post, fileName, callback );
36+
async function parsePost( fileName ) {
37+
return new Promise( function( resolve, reject ) {
38+
wordpressClient.parsePost( fileName, function( error, post ) {
39+
if ( error ) {
40+
return reject( error );
41+
}
42+
43+
preprocessor( post, fileName, function( err, res ) {
44+
if ( error ) {
45+
return reject( err );
46+
}
47+
resolve( res );
48+
} );
49+
} );
4250
} );
4351
}
4452

45-
util.eachFile( this.filesSrc, function( fileName, fileDone ) {
53+
var count = 0;
54+
async function processFile( fileName ) {
55+
count++;
4656
grunt.verbose.write( "Processing " + fileName + "..." );
4757

48-
parsePost( fileName, function( error, post ) {
49-
if ( error ) {
50-
return fileDone( error );
51-
}
58+
const post = await parsePost( fileName );
5259

53-
var content = post.content,
54-
fileType = /\.(\w+)$/.exec( fileName )[ 1 ],
55-
targetFileName = targetDir +
56-
( post.fileName || fileName.replace( /^.+?\/(.+)\.\w+$/, "$1" ) + ".html" );
60+
var content = post.content,
61+
fileType = /\.(\w+)$/.exec( fileName )[ 1 ],
62+
targetFileName = targetDir +
63+
( post.fileName || fileName.replace( /^.+?\/(.+)\.\w+$/, "$1" ) + ".html" );
5764

58-
delete post.content;
59-
delete post.fileName;
65+
delete post.content;
66+
delete post.fileName;
6067

61-
// Convert markdown to HTML
62-
if ( fileType === "md" ) {
63-
content = util.parseMarkdown( content, {
64-
generateLinks: post.toc || !post.noHeadingLinks,
65-
generateToc: post.toc
66-
} );
67-
delete post.noHeadingLinks;
68-
delete post.toc;
69-
}
70-
71-
// Replace partials
72-
content = content.replace( /@partial\((.+)\)/g,
73-
function( _match, input ) {
74-
return util.htmlEscape( grunt.file.read( input ) );
68+
// Convert markdown to HTML
69+
if ( fileType === "md" ) {
70+
content = util.parseMarkdown( content, {
71+
generateLinks: post.toc || !post.noHeadingLinks,
72+
generateToc: post.toc
7573
} );
74+
delete post.noHeadingLinks;
75+
delete post.toc;
76+
}
7677

77-
// Syntax highlight code blocks
78-
if ( !grunt.option( "nohighlight" ) ) {
79-
content = syntaxHighlight( content );
80-
}
81-
82-
post.customFields = post.customFields || [];
83-
post.customFields.push( {
84-
key: "source_path",
85-
value: fileName
86-
} );
78+
// Replace partials
79+
content = content.replace( /@partial\((.+)\)/g, function( _match, input ) {
80+
return util.htmlEscape( grunt.file.read( input ) );
81+
} );
8782

88-
// Write file
89-
grunt.file.write( targetFileName,
90-
"<script>" + JSON.stringify( post ) + "</script>\n" + content );
83+
// Syntax highlight code blocks
84+
if ( !grunt.option( "nohighlight" ) ) {
85+
content = syntaxHighlight( content );
86+
}
9187

92-
fileDone();
88+
post.customFields = post.customFields || [];
89+
post.customFields.push( {
90+
key: "source_path",
91+
value: fileName
9392
} );
94-
}, function( _error, count ) {
95-
if ( task.errorCount ) {
96-
grunt.warn( "Task \"" + task.name + "\" failed." );
97-
return taskDone();
98-
}
9993

100-
grunt.log.writeln( "Built " + count + " pages." );
101-
taskDone();
102-
} );
94+
// Write file
95+
grunt.file.write( targetFileName,
96+
"<script>" + JSON.stringify( post ) + "</script>\n" + content );
97+
}
98+
99+
try {
100+
await util.eachFile( this.filesSrc, processFile );
101+
} catch ( e ) {
102+
grunt.warn( "Task \"" + task.name + "\" failed." );
103+
return taskDone();
104+
}
105+
106+
grunt.log.writeln( "Built " + count + " pages." );
107+
taskDone();
103108
} );
104109

105110
grunt.registerMultiTask( "build-resources", "Copy resources", function() {
@@ -109,20 +114,21 @@ grunt.registerMultiTask( "build-resources", "Copy resources", function() {
109114

110115
grunt.file.mkdir( targetDir );
111116

112-
util.eachFile( this.filesSrc, function( fileName, fileDone ) {
113-
if ( grunt.file.isFile( fileName ) ) {
114-
grunt.file.copy( fileName, targetDir + fileName.replace( /^.+?\//, "" ) );
115-
}
116-
fileDone();
117-
}, function( _error, count ) {
118-
if ( task.errorCount ) {
119-
grunt.warn( "Task \"" + task.name + "\" failed." );
120-
return taskDone();
117+
var count = 0;
118+
try {
119+
for ( const fileName of this.filesSrc ) {
120+
if ( grunt.file.isFile( fileName ) ) {
121+
grunt.file.copy( fileName, targetDir + fileName.replace( /^.+?\//, "" ) );
122+
count++;
123+
}
121124
}
125+
} catch ( e ) {
126+
grunt.warn( "Task \"" + task.name + "\" failed." );
127+
return taskDone();
128+
}
122129

123-
grunt.log.writeln( "Built " + count + " resources." );
124-
taskDone();
125-
} );
130+
grunt.log.writeln( "Built " + count + " resources." );
131+
taskDone();
126132
} );
127133

128134
};

‎test/lint.test.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* eslint-env qunit */
2+
"use strict";
3+
4+
const cp = require( "child_process" );
5+
const path = require( "path" );
6+
const ROOT = path.join( __dirname, ".." );
7+
8+
QUnit.module( "lint", function() {
9+
QUnit.test( "fixture", function( assert ) {
10+
let stdout;
11+
try {
12+
const result = cp.execSync( "npm run -s lint-example", {
13+
cwd: ROOT,
14+
env: { PATH: process.env.PATH, NPM_CONFIG_UPDATE_NOTIFIER: "false" },
15+
encoding: "utf8"
16+
} );
17+
stdout = result.toString().trim();
18+
} catch ( e ) {
19+
assert.equal( e.stdout || e.toString(), "", "error" );
20+
return;
21+
}
22+
23+
const expect =
24+
`Running "xmllint:all" (xmllint) task
25+
Lint free files: 2
26+
27+
Done.`;
28+
assert.strictEqual( stdout, expect, "stdout" );
29+
} );
30+
} );

0 commit comments

Comments
 (0)
Please sign in to comment.