2017-12-17 14:31:37 +00:00
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
const fs = require( 'fs' );
|
|
|
|
const path = require( 'path' );
|
|
|
|
|
|
|
|
const cheerio = require( 'cheerio' );
|
|
|
|
const marked = require( 'marked' );
|
|
|
|
|
2017-12-18 22:08:09 +00:00
|
|
|
let errorCount = 0;
|
|
|
|
|
|
|
|
|
2017-12-29 10:40:05 +00:00
|
|
|
/**
|
|
|
|
* Accept an optional directory name where the content files live.
|
|
|
|
*/
|
|
|
|
const contentPath = (
|
|
|
|
process.argv[ 2 ]
|
|
|
|
? path.resolve( process.argv[ 2 ] )
|
|
|
|
: path.join( __dirname, '..' )
|
|
|
|
);
|
|
|
|
|
|
|
|
|
2017-12-18 22:08:09 +00:00
|
|
|
/**
|
|
|
|
* Build list of Markdown files containing company profiles.
|
|
|
|
*/
|
|
|
|
|
2017-12-29 10:40:05 +00:00
|
|
|
const profilesPath = path.join( contentPath, 'company-profiles' );
|
2017-12-18 22:08:09 +00:00
|
|
|
const profileFilenames = fs.readdirSync( profilesPath );
|
2017-12-17 14:31:37 +00:00
|
|
|
|
|
|
|
|
2017-12-18 22:08:09 +00:00
|
|
|
/**
|
|
|
|
* Scan the company table in the readme.
|
|
|
|
*/
|
|
|
|
|
|
|
|
const readmeCompanies = [];
|
|
|
|
|
|
|
|
const readmeMarkdown = fs.readFileSync(
|
2017-12-29 10:40:05 +00:00
|
|
|
path.join( contentPath, 'README.md' ),
|
2017-12-18 22:08:09 +00:00
|
|
|
'utf8'
|
|
|
|
);
|
|
|
|
|
|
|
|
const $ = cheerio.load( marked( readmeMarkdown ) );
|
|
|
|
|
|
|
|
function readmeError( msg, ...params ) {
|
|
|
|
errorCount++;
|
|
|
|
console.log(
|
|
|
|
'README.md: ' + msg,
|
|
|
|
...params
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-12-29 10:40:05 +00:00
|
|
|
let lastCompanyName = null;
|
|
|
|
|
2017-12-18 22:08:09 +00:00
|
|
|
$( 'tr' ).each( ( i, tr ) => {
|
|
|
|
if ( i === 0 ) {
|
|
|
|
// Skip the table header row.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const $td = $( tr ).children( 'td' );
|
|
|
|
if ( $td.length !== 3 ) {
|
|
|
|
readmeError(
|
|
|
|
'Expected 3 table cells but found %d: %s',
|
|
|
|
$td.length,
|
|
|
|
$( tr ).html().replace( /\n/g, '' )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const entry = {
|
|
|
|
name: $td.eq( 0 ).text(),
|
|
|
|
website: $td.eq( 1 ).text(),
|
|
|
|
shortRegion: $td.eq( 2 ).text(),
|
|
|
|
};
|
|
|
|
|
|
|
|
if ( ! entry.name ) {
|
|
|
|
readmeError(
|
|
|
|
'Missing company name: %s',
|
2017-12-29 10:40:05 +00:00
|
|
|
$( tr ).html().replace( /\n/g, '' )
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
lastCompanyName &&
|
|
|
|
entry.name.toLowerCase() < lastCompanyName.toLowerCase()
|
|
|
|
) {
|
|
|
|
readmeError(
|
|
|
|
'Company is listed out of order: "%s" (should be before "%s")',
|
|
|
|
entry.name,
|
|
|
|
lastCompanyName
|
2017-12-18 22:08:09 +00:00
|
|
|
);
|
|
|
|
}
|
2017-12-29 10:40:05 +00:00
|
|
|
lastCompanyName = entry.name;
|
2017-12-18 22:08:09 +00:00
|
|
|
|
|
|
|
const profileLink = $td.eq( 0 ).find( 'a' ).attr( 'href' );
|
|
|
|
|
|
|
|
if ( profileLink ) {
|
|
|
|
const match = profileLink.match( /^\/company-profiles\/(.*\.md)$/ );
|
|
|
|
|
|
|
|
if ( match ) {
|
|
|
|
entry.linkedFilename = match[ 1 ];
|
|
|
|
if ( profileFilenames.indexOf( entry.linkedFilename ) === -1 ) {
|
|
|
|
readmeError(
|
|
|
|
'Broken link to company "%s": "%s"',
|
|
|
|
entry.name,
|
|
|
|
profileLink
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
readmeError(
|
|
|
|
'Invalid link to company "%s": "%s"',
|
|
|
|
entry.name,
|
|
|
|
profileLink
|
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We're not ready to do this check yet!
|
|
|
|
/* readmeError(
|
|
|
|
'Company "%s" has no linked Markdown profile',
|
|
|
|
entry.name
|
|
|
|
); */
|
|
|
|
}
|
|
|
|
|
|
|
|
readmeCompanies.push( entry );
|
|
|
|
} );
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Scan the individual Markdown files containing the company profiles.
|
|
|
|
*/
|
|
|
|
|
|
|
|
profileFilenames.forEach( filename => {
|
2017-12-17 14:31:37 +00:00
|
|
|
function error( msg, ...params ) {
|
|
|
|
errorCount++;
|
|
|
|
console.log(
|
|
|
|
filename + ': ' + msg,
|
|
|
|
...params
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const profileMarkdown = fs.readFileSync(
|
|
|
|
path.join( profilesPath, filename ),
|
|
|
|
'utf8'
|
|
|
|
);
|
|
|
|
const $ = cheerio.load( marked( profileMarkdown ) );
|
|
|
|
|
|
|
|
if ( $( 'h1' ).length !== 1 ) {
|
|
|
|
error(
|
|
|
|
'Expected 1 first-level heading but found %d',
|
|
|
|
$( 'h1' ).length
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const companyName = $( 'h1' ).text();
|
|
|
|
|
|
|
|
if ( ! /[a-z]/i.test( companyName ) ) {
|
|
|
|
error(
|
|
|
|
'Company name looks wrong: "%s"',
|
|
|
|
companyName
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const filenameBase = filename.replace( /\.md$/, '' );
|
|
|
|
const filenameExpected = companyName.toLowerCase()
|
|
|
|
.replace( /&/g, ' and ' )
|
|
|
|
.replace( /[^a-z0-9]+/gi, '-' )
|
|
|
|
.replace( /^-|-$/g, '' );
|
|
|
|
if (
|
|
|
|
filenameBase !== filenameExpected &&
|
|
|
|
// Some profile files just have shorter names than the company name,
|
|
|
|
// which is fine.
|
|
|
|
filenameExpected.substring( 0, filenameBase.length + 1 ) !== filenameBase + '-'
|
|
|
|
) {
|
|
|
|
error(
|
|
|
|
'Expected filename "%s.md" for company "%s"',
|
|
|
|
filenameExpected,
|
|
|
|
companyName
|
|
|
|
);
|
|
|
|
}
|
2017-12-18 22:08:09 +00:00
|
|
|
|
|
|
|
if (
|
|
|
|
filename !== 'example.md' &&
|
|
|
|
! readmeCompanies.some( entry => entry.linkedFilename === filename )
|
|
|
|
) {
|
|
|
|
error( 'No link to company profile from readme' );
|
|
|
|
}
|
2017-12-17 14:31:37 +00:00
|
|
|
} );
|
|
|
|
|
|
|
|
console.log();
|
|
|
|
console.log(
|
|
|
|
'%d problem%s detected',
|
|
|
|
errorCount,
|
|
|
|
( errorCount === 1 ? '' : 's' )
|
|
|
|
);
|
|
|
|
|
|
|
|
process.exitCode = Math.min( errorCount, 99 );
|