hpal
Version:
hapi pal CLI
222 lines (175 loc) • 5.75 kB
JavaScript
const Wreck = require('@hapi/wreck');
const PkgDir = require('pkg-dir');
const Pluralize = require('pluralize');
const Somever = require('@hapi/somever');
const Print = require('../print');
const Helpers = require('../helpers');
const DisplayError = require('../display-error');
const internals = {};
module.exports = async (cwd, pkg, version, query, itemQuery, ctx) => {
const root = await PkgDir(cwd);
if (!version && root && internals.pkgScope[pkg]) {
version = internals.getPkgVersion(root, `@${internals.pkgScope[pkg]}/${pkg}`);
}
if (!version && root) {
version = internals.getPkgVersion(root, pkg);
}
query = query.toLowerCase();
let parsedVersion;
try {
parsedVersion = Somever.version(version);
}
catch (ignoreErr) {}
const ref = parsedVersion ? `v${parsedVersion.version}` : (version || 'master');
const owner = internals.pkgOwners[pkg] || 'hapijs';
ctx.options.out.write(ctx.colors.grey(`Searching docs from ${owner}/${pkg} @ ${ref}...\n\n`));
const getAPI = async () => {
try {
return await internals.getPkgAPI(owner, pkg, ref);
}
catch (err) {
if (err.isBoom && err.output.statusCode === 404) {
throw new DisplayError(ctx.colors.red(`Couldn\'t find docs for that version of ${pkg}. Are you sure ${owner}/${pkg} @ ${ref} exists?`));
}
if (err.syscall === 'getaddrinfo' && err.code === 'ENOTFOUND') {
throw new DisplayError(ctx.colors.red(`Could not fetch the ${pkg} docs. It seems you may be offline– ensure you have a connection then try again.`));
}
throw new DisplayError(ctx.colors.red(`Could not fetch the ${pkg} docs: ${err.message}`));
}
};
const getManifest = async () => {
if (!root) {
return null;
}
try {
return await Helpers.getManifest(root, ctx, true);
}
catch (err) {
// If we can't get the manifest for some reason we're aware of, that's okay
if (err instanceof DisplayError) {
return null;
}
throw err;
}
};
const [api, manifest] = await Promise.all([getAPI(), getManifest()]);
const pluralQuery = manifest && Pluralize.plural(query);
const item = manifest && (manifest.find((m) => m.place === query) || manifest.find((m) => m.place === pluralQuery));
const methodMatch = query.match(/^([a-z\.]+)/);
const method = methodMatch && methodMatch[1];
const headingMatchers = ([
item && `server.${item.method}(`,
method && `${method}(`,
(query.length >= 3) && query
])
.filter((x) => !!x)
.map((match) => {
return (section) => {
if (match[0] === '#') {
return internals.anchorize(section)
.some((anchor) => ~anchor.indexOf(match));
}
return ~section.toLowerCase().indexOf(match);
};
});
if (!itemQuery) {
return Print.markdownSection(api, headingMatchers);
}
const itemQueryEsc = internals.regEscape(itemQuery);
const itemRegExp = new RegExp(`^\s*\`?${itemQueryEsc}\`? -`, 'i');
const listItemMatcher = (listItem) => itemRegExp.test(listItem);
return Print.markdownListItem(api, headingMatchers, listItemMatcher);
};
internals.getPkgVersion = (root, pkg) => {
try {
// In node v8.9+ will be able to use require.resolve()'s { paths } option
return require(`${root}/node_modules/${pkg}/package.json`).version;
}
catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
return null;
}
throw err;
}
};
internals.getPkgAPI = async (owner, pkg, ref) => {
const { payload } = await Wreck.get(`https://raw.githubusercontent.com/${owner}/${pkg}/${ref}/API.md`);
return payload.toString();
};
internals.pkgScope = {
accept: 'hapi',
address: 'hapi',
ammo: 'hapi',
b64: 'hapi',
basic: 'hapi',
bell: 'hapi',
boom: 'hapi',
bossy: 'hapi',
bounce: 'hapi',
call: 'hapi',
catbox: 'hapi',
'catbox-memecached': 'hapi',
'catbox-memory': 'hapi',
'catbox-redis': 'hapi',
code: 'hapi',
content: 'hapi',
cookie: 'hapi',
crumb: 'hapi',
cryptiles: 'hapi',
glue: 'hapi',
good: 'hapi',
'good-console': 'hapi',
'good-squeeze': 'hapi',
h2o2: 'hapi',
hapi: 'hapi',
hawk: 'hapi',
heavy: 'hapi',
hoek: 'hapi',
inert: 'hapi',
iron: 'hapi',
joi: 'hapi',
'joi-date': 'hapi',
lab: 'hapi',
mimos: 'hapi',
nes: 'hapi',
nigel: 'hapi',
oppsy: 'hapi',
pez: 'hapi',
podium: 'hapi',
scooter: 'hapi',
shot: 'hapi',
sntp: 'hapi',
somever: 'hapi',
statehood: 'hapi',
subtext: 'hapi',
teamwork: 'hapi',
topo: 'hapi',
vise: 'hapi',
vision: 'hapi',
wreck: 'hapi',
yar: 'hapi'
};
internals.pkgOwners = {
schwifty: 'hapipal',
schmervice: 'hapipal',
toys: 'hapipal',
'haute-couture': 'hapipal',
hodgepodge: 'hapipal',
underdog: 'hapipal',
hecks: 'hapipal',
lalalambda: 'hapipal',
avocat: 'hapipal'
};
internals.anchorize = (str) => {
str = str.toLowerCase();
const anchors = [];
const tag = str.match(Print.headingAnchorTagRegex);
if (tag) {
str = str.replace(tag[0], '-');
anchors.push(`#${tag[1]}`);
}
anchors.push('#' + str.replace(/[^a-z0-9\s-]/ig, '').trim().replace(/\s/g, '-'));
return anchors;
};
internals.regEscape = (str) => str.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
;