Skip to content

Commit e60b9c4

Browse files
authored
Add a pre-commit hook to check whether API docs are updated (WordPress#18820)
1 parent 4c178b8 commit e60b9c4

9 files changed

+176
-99
lines changed

.github/CODEOWNERS

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
# Tooling
3333
/bin @ntwb @nerrad @ajitbohra
34-
/bin/update-readmes.js @ntwb @nerrad @ajitbohra @nosolosw
34+
/bin/api-docs @ntwb @nerrad @ajitbohra @nosolosw
3535
/docs/tool @youknowriad @chrisvanpatten @ajitbohra @nosolosw
3636
/packages/babel-plugin-import-jsx-pragma @gziolo @ntwb @nerrad @ajitbohra
3737
/packages/babel-plugin-makepot @ntwb @nerrad @ajitbohra

bin/api-docs/are-readmes-unstaged.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Node dependencies.
5+
*/
6+
const { join } = require( 'path' );
7+
const chalk = require( 'chalk' );
8+
const execSync = require( 'child_process' ).execSync;
9+
10+
/**
11+
* Local dependencies.
12+
*/
13+
const getPackages = require( './packages' );
14+
15+
const getUnstagedFiles = () => execSync( 'git diff --name-only', { encoding: 'utf8' } ).split( '\n' ).filter( ( element ) => '' !== element );
16+
const readmeFiles = getPackages().map( ( [ packageName ] ) => join( 'packages', packageName, 'README.md' ) );
17+
const unstagedReadmes = getUnstagedFiles().filter( ( element ) => readmeFiles.includes( element ) );
18+
19+
if ( unstagedReadmes.length > 0 ) {
20+
process.exitCode = 1;
21+
process.stdout.write( chalk.red(
22+
'\n',
23+
'Some API docs may be out of date:',
24+
unstagedReadmes.toString(),
25+
'Either stage them or continue with --no-verify.',
26+
'\n'
27+
) );
28+
}

bin/api-docs/packages.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const packages = [
2+
'a11y',
3+
'autop',
4+
'blob',
5+
'block-editor',
6+
'block-library',
7+
'block-serialization-default-parser',
8+
'blocks',
9+
'compose',
10+
[ 'core-data', {
11+
'Autogenerated actions': 'src/actions.js',
12+
'Autogenerated selectors': 'src/selectors.js',
13+
} ],
14+
'data',
15+
'data-controls',
16+
'date',
17+
'deprecated',
18+
'dom',
19+
'dom-ready',
20+
'e2e-test-utils',
21+
'edit-post',
22+
'element',
23+
'escape-html',
24+
'html-entities',
25+
'i18n',
26+
'keycodes',
27+
'plugins',
28+
'priority-queue',
29+
'redux-routine',
30+
'rich-text',
31+
'shortcode',
32+
'url',
33+
'viewport',
34+
'wordcount',
35+
];
36+
37+
module.exports = function() {
38+
return packages.map( ( entry ) => {
39+
if ( ! Array.isArray( entry ) ) {
40+
entry = [ entry, { 'Autogenerated API docs': 'src/index.js' } ];
41+
}
42+
return entry;
43+
} );
44+
};

bin/api-docs/update-readmes.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* Node dependencies.
3+
*/
4+
const { join } = require( 'path' );
5+
const spawnSync = require( 'child_process' ).spawnSync;
6+
7+
/**
8+
* Local dependencies.
9+
*/
10+
const getPackages = require( './packages' );
11+
12+
getPackages().forEach( ( entry ) => {
13+
const [ packageName, targetFiles ] = entry;
14+
15+
Object.entries( targetFiles ).forEach( ( [ token, path ] ) => {
16+
// Each target operates over the same file, so it needs to be processed synchronously,
17+
// as to make sure the processes don't overwrite each other.
18+
const { status, stderr } = spawnSync(
19+
join( __dirname, '..', '..', 'node_modules', '.bin', 'docgen' ).replace( / /g, '\\ ' ),
20+
[
21+
join( 'packages', packageName, path ),
22+
`--output packages/${ packageName }/README.md`,
23+
'--to-token',
24+
`--use-token "${ token }"`,
25+
'--ignore "/unstable|experimental/i"',
26+
],
27+
{ shell: true },
28+
);
29+
30+
if ( status !== 0 ) {
31+
process.stderr.write( `${ packageName } ${ stderr.toString() }\n` );
32+
process.exit( 1 );
33+
}
34+
} );
35+
} );

bin/update-readmes.js

-70
This file was deleted.

docs/tool/are-data-files-unstaged.js

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Node dependencies.
5+
*/
6+
const chalk = require( 'chalk' );
7+
const execSync = require( 'child_process' ).execSync;
8+
9+
/**
10+
* Local dependencies.
11+
*/
12+
const getPackages = require( './packages' );
13+
14+
const getUnstagedFiles = () => execSync( 'git diff --name-only', { encoding: 'utf8' } ).split( '\n' ).filter( ( element ) => '' !== element );
15+
const readmeFiles = getPackages().map( ( [ packageName ] ) => `docs/designers-developers/developers/data/data-${ packageName.replace( '/', '-' ) }.md` );
16+
const unstagedReadmes = getUnstagedFiles().filter( ( element ) => readmeFiles.includes( element ) );
17+
18+
if ( unstagedReadmes.length > 0 ) {
19+
process.exitCode = 1;
20+
process.stdout.write( chalk.red(
21+
'\n',
22+
'Some API docs may be out of date:',
23+
unstagedReadmes.toString(),
24+
'Either stage them or continue with --no-verify.',
25+
'\n'
26+
) );
27+
}

docs/tool/packages.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const packages = [
2+
[ 'core', {
3+
'Autogenerated actions': 'packages/core-data/src/actions.js',
4+
'Autogenerated selectors': 'packages/core-data/src/selectors.js',
5+
} ],
6+
'core/annotations',
7+
'core/blocks',
8+
'core/block-editor',
9+
'core/editor',
10+
'core/edit-post',
11+
'core/notices',
12+
'core/nux',
13+
'core/viewport',
14+
];
15+
16+
module.exports = function() {
17+
return packages.map( ( entry ) => {
18+
if ( ! Array.isArray( entry ) ) {
19+
entry = [ entry, {
20+
'Autogenerated actions': `packages/${ entry.replace( 'core/', '' ) }/src/store/actions.js`,
21+
'Autogenerated selectors': `packages/${ entry.replace( 'core/', '' ) }/src/store/selectors.js`,
22+
} ];
23+
}
24+
return entry;
25+
} );
26+
};

docs/tool/update-data.js

+10-25
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,23 @@
44
const { join } = require( 'path' );
55
const spawnSync = require( 'child_process' ).spawnSync;
66

7-
const modules = [
8-
[ 'core', {
9-
'Autogenerated actions': 'packages/core-data/src/actions.js',
10-
'Autogenerated selectors': 'packages/core-data/src/selectors.js',
11-
} ],
12-
'core/annotations',
13-
'core/blocks',
14-
'core/block-editor',
15-
'core/editor',
16-
'core/edit-post',
17-
'core/notices',
18-
'core/nux',
19-
'core/viewport',
20-
];
7+
/**
8+
* Local dependencies.
9+
*/
10+
const getPackages = require( './packages' );
2111

22-
modules.forEach( ( entry ) => {
23-
if ( ! Array.isArray( entry ) ) {
24-
entry = [ entry, {
25-
'Autogenerated actions': `packages/${ entry.replace( 'core/', '' ) }/src/store/actions.js`,
26-
'Autogenerated selectors': `packages/${ entry.replace( 'core/', '' ) }/src/store/selectors.js`,
27-
} ];
28-
}
29-
const [ namespace, targets ] = entry;
12+
getPackages().forEach( ( entry ) => {
13+
const [ packageName, targetFiles ] = entry;
3014

31-
Object.entries( targets ).forEach( ( [ token, target ] ) => {
15+
Object.entries( targetFiles ).forEach( ( [ token, target ] ) => {
3216
// Note that this needs to be a sync process for each output file that is updated:
3317
// until docgen provides a way to update many tokens at once, we need to make sure
3418
// the output file is updated before starting the second pass for the next token.
3519
const { status, stderr } = spawnSync(
3620
join( __dirname, '..', '..', 'node_modules', '.bin', 'docgen' ).replace( / /g, '\\ ' ),
3721
[
3822
target,
39-
`--output docs/designers-developers/developers/data/data-${ namespace.replace( '/', '-' ) }.md`,
23+
`--output docs/designers-developers/developers/data/data-${ packageName.replace( '/', '-' ) }.md`,
4024
'--to-token',
4125
`--use-token "${ token }"`,
4226
'--ignore "/unstable|experimental/i"',
@@ -45,7 +29,8 @@ modules.forEach( ( entry ) => {
4529
);
4630

4731
if ( status !== 0 ) {
48-
throw stderr.toString();
32+
process.stderr.write( `${ packageName } ${ stderr.toString() }\n` );
33+
process.exit( 1 );
4934
}
5035
} );
5136
} );

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
"predev": "npm run check-engines",
175175
"dev": "npm run build:packages && concurrently \"wp-scripts start\" \"npm run dev:packages\"",
176176
"dev:packages": "node ./bin/packages/watch.js",
177-
"docs:build": "node ./docs/tool/index.js && node ./bin/update-readmes.js",
177+
"docs:build": "node ./docs/tool/index.js && node ./bin/api-docs/update-readmes.js",
178178
"fixtures:clean": "rimraf \"packages/e2e-tests/fixtures/blocks/*.+(json|serialized.html)\"",
179179
"fixtures:server-registered": "npm run wp-env run tests-wordpress-phpunit './bin/get-server-blocks.php > test/integration/full-content/server-registered.json'",
180180
"fixtures:generate": "npm run fixtures:server-registered && cross-env GENERATE_MISSING_FIXTURES=y npm run test-unit",
@@ -233,10 +233,12 @@
233233
"wp-scripts lint-js"
234234
],
235235
"{docs/{toc.json,tool/*.js},packages/{*/README.md,*/src/{actions,selectors}.js,components/src/*/**/README.md}}": [
236-
"node ./docs/tool/index.js"
236+
"node ./docs/tool/index.js",
237+
"node ./docs/tool/are-data-files-unstaged.js"
237238
],
238239
"packages/**/*.js": [
239-
"node ./bin/update-readmes.js"
240+
"node ./bin/api-docs/update-readmes.js",
241+
"node ./bin/api-docs/are-readmes-unstaged.js"
240242
]
241243
},
242244
"wp-env": {

0 commit comments

Comments
 (0)