213 lines
5.9 KiB
JavaScript
213 lines
5.9 KiB
JavaScript
/* eslint-disable no-shadow */
|
|
const pathLib = require('path');
|
|
const child_process = require('child_process'); // eslint-disable-line camelcase
|
|
const glob = require('glob');
|
|
const readPackageTree = require('../program/utils/read-package-tree-with-bower-support.js');
|
|
const { LogService } = require('../program/core/LogService.js');
|
|
const { toPosixPath } = require('../program/utils/to-posix-path.js');
|
|
|
|
/**
|
|
* @param {any[]} arr
|
|
* @returns {any[]}
|
|
*/
|
|
function flatten(arr) {
|
|
return Array.prototype.concat.apply([], arr);
|
|
}
|
|
|
|
/**
|
|
* @param {string} v
|
|
* @returns {string[]}
|
|
*/
|
|
function csToArray(v) {
|
|
return v.split(',').map(v => v.trim());
|
|
}
|
|
|
|
/**
|
|
* @param {string} v like 'js,html'
|
|
* @returns {string[]} like ['.js', '.html']
|
|
*/
|
|
function extensionsFromCs(v) {
|
|
return csToArray(v).map(v => `.${v}`);
|
|
}
|
|
|
|
function setQueryMethod(m) {
|
|
const allowedMehods = ['grep', 'ast'];
|
|
if (allowedMehods.includes(m)) {
|
|
return m;
|
|
}
|
|
LogService.error(`Please provide one of the following methods: ${allowedMehods.join(', ')}`);
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* @param {string} t
|
|
* @returns {string[]|undefined}
|
|
*/
|
|
function pathsArrayFromCs(t, cwd = process.cwd()) {
|
|
if (!t) {
|
|
return undefined;
|
|
}
|
|
|
|
return flatten(
|
|
t.split(',').map(t => {
|
|
if (t.startsWith('/')) {
|
|
return t;
|
|
}
|
|
if (t.includes('*')) {
|
|
if (!t.endsWith('/')) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
t = `${t}/`;
|
|
}
|
|
return glob.sync(t, { cwd, absolute: true }).map(toPosixPath);
|
|
}
|
|
return toPosixPath(pathLib.resolve(cwd, t.trim()));
|
|
}),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {string} name collection name found in eCfg
|
|
* @param {'search-target'|'reference'} collectionType collection type
|
|
* @param {{searchTargetCollections: {[repo:string]:string[]}; referenceCollections:{[repo:string]:string[]}}} [eCfg] external configuration. Usually providence.conf.js
|
|
* @param {string} [cwd]
|
|
* @returns {string[]|undefined}
|
|
*/
|
|
function pathsArrayFromCollectionName(
|
|
name,
|
|
collectionType = 'search-target',
|
|
eCfg,
|
|
cwd = process.cwd(),
|
|
) {
|
|
let collection;
|
|
if (collectionType === 'search-target') {
|
|
collection = eCfg?.searchTargetCollections;
|
|
} else if (collectionType === 'reference') {
|
|
collection = eCfg?.referenceCollections;
|
|
}
|
|
if (collection?.[name]) {
|
|
return pathsArrayFromCs(collection[name].join(','), cwd);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* @param {string} processArgStr
|
|
* @param {object} [opts]
|
|
* @returns {Promise<{ code:string; number:string }>}
|
|
* @throws {Error}
|
|
*/
|
|
function spawnProcess(processArgStr, opts) {
|
|
const processArgs = processArgStr.split(' ');
|
|
// eslint-disable-next-line camelcase
|
|
const proc = child_process.spawn(processArgs[0], processArgs.slice(1), opts);
|
|
/** @type {string} */
|
|
let output;
|
|
proc.stdout.on('data', data => {
|
|
output += data;
|
|
LogService.debug(data.toString());
|
|
});
|
|
return new Promise((resolve, reject) => {
|
|
proc.stderr.on('data', data => {
|
|
LogService.error(data.toString());
|
|
reject(data.toString());
|
|
});
|
|
proc.on('close', code => {
|
|
resolve({ code, output });
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* When providence is called from the root of a repo and no target is provided,
|
|
* this will provide the default fallback (the project itself)
|
|
* @param {string} cwd
|
|
* @returns {string[]}
|
|
*/
|
|
function targetDefault(cwd) {
|
|
return [toPosixPath(cwd)];
|
|
}
|
|
|
|
/**
|
|
* Returns all sub projects matching condition supplied in matchFn
|
|
* @param {string[]} rootPaths all search-target project paths
|
|
* @param {string} [matchPattern] base for RegExp
|
|
* @param {('npm'|'bower')[]} [modes]
|
|
*/
|
|
async function appendProjectDependencyPaths(rootPaths, matchPattern, modes = ['npm', 'bower']) {
|
|
let matchFn;
|
|
if (matchPattern) {
|
|
if (matchPattern.startsWith('/') && matchPattern.endsWith('/')) {
|
|
matchFn = (/** @type {any} */ _, /** @type {string} */ d) => {
|
|
const reString = matchPattern.slice(1, -1);
|
|
const result = new RegExp(reString).test(d);
|
|
LogService.debug(`[appendProjectDependencyPaths]: /${reString}/.test(${d} => ${result})`);
|
|
return result;
|
|
};
|
|
} else {
|
|
LogService.error(
|
|
`[appendProjectDependencyPaths] Please provide a matchPattern enclosed by '/'. Found: ${matchPattern}`,
|
|
);
|
|
}
|
|
}
|
|
/** @type {string[]} */
|
|
const depProjectPaths = [];
|
|
for (const targetPath of rootPaths) {
|
|
for (const mode of modes) {
|
|
await readPackageTree(
|
|
targetPath,
|
|
matchFn,
|
|
(/** @type {string | undefined} */ err, /** @type {{ children: any[]; }} */ tree) => {
|
|
if (err) {
|
|
throw new Error(err);
|
|
}
|
|
const paths = tree.children.map(child => child.realpath);
|
|
depProjectPaths.push(...paths);
|
|
},
|
|
mode,
|
|
);
|
|
}
|
|
}
|
|
// Write all data to {outputPath}/projectDeps.json
|
|
// const projectDeps = {};
|
|
// rootPaths.forEach(rootP => {
|
|
// depProjectPaths.filter(depP => depP.startsWith(rootP)).;
|
|
// });
|
|
|
|
return depProjectPaths.concat(rootPaths).map(toPosixPath);
|
|
}
|
|
|
|
/**
|
|
* Will install all npm and bower deps, so an analysis can be performed on them as well.
|
|
* Relevant when '--target-dependencies' is supplied.
|
|
* @param {string[]} searchTargetPaths
|
|
*/
|
|
async function installDeps(searchTargetPaths) {
|
|
for (const targetPath of searchTargetPaths) {
|
|
LogService.info(`Installing npm dependencies for ${pathLib.basename(targetPath)}`);
|
|
try {
|
|
await spawnProcess('npm i --no-progress', { cwd: targetPath });
|
|
} catch (e) {
|
|
LogService.error(e);
|
|
}
|
|
|
|
LogService.info(`Installing bower dependencies for ${pathLib.basename(targetPath)}`);
|
|
try {
|
|
await spawnProcess(`bower i --production --force-latest`, { cwd: targetPath });
|
|
} catch (e) {
|
|
LogService.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
csToArray,
|
|
extensionsFromCs,
|
|
setQueryMethod,
|
|
pathsArrayFromCs,
|
|
targetDefault,
|
|
appendProjectDependencyPaths,
|
|
spawnProcess,
|
|
installDeps,
|
|
pathsArrayFromCollectionName,
|
|
flatten,
|
|
};
|