piral-cli
Version:
The standard CLI for creating and building a Piral instance or a Pilet.
525 lines • 20.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.determineNpmClient = determineNpmClient;
exports.isMonorepoPackageRef = isMonorepoPackageRef;
exports.installNpmDependencies = installNpmDependencies;
exports.installNpmPackageFromOptionalRegistry = installNpmPackageFromOptionalRegistry;
exports.uninstallNpmPackage = uninstallNpmPackage;
exports.installNpmPackage = installNpmPackage;
exports.initNpmProject = initNpmProject;
exports.publishNpmPackage = publishNpmPackage;
exports.createNpmPackage = createNpmPackage;
exports.findNpmTarball = findNpmTarball;
exports.findSpecificVersion = findSpecificVersion;
exports.findLatestVersion = findLatestVersion;
exports.isLocalPackage = isLocalPackage;
exports.isNpmPackage = isNpmPackage;
exports.makeNpmAlias = makeNpmAlias;
exports.isGitPackage = isGitPackage;
exports.isRemotePackage = isRemotePackage;
exports.makeGitUrl = makeGitUrl;
exports.makeFilePath = makeFilePath;
exports.dissectPackageName = dissectPackageName;
exports.getCurrentPackageDetails = getCurrentPackageDetails;
exports.tryResolvePackage = tryResolvePackage;
exports.findPackageRoot = findPackageRoot;
exports.isLinkedPackage = isLinkedPackage;
exports.combinePackageRef = combinePackageRef;
exports.getPackageName = getPackageName;
exports.getFilePackageVersion = getFilePackageVersion;
exports.getGitPackageVersion = getGitPackageVersion;
exports.getPackageVersion = getPackageVersion;
exports.makePiletExternals = makePiletExternals;
exports.mergeExternals = mergeExternals;
exports.makeExternals = makeExternals;
const path_1 = require("path");
const fs_1 = require("fs");
const log_1 = require("./log");
const config_1 = require("./config");
const constants_1 = require("./constants");
const inspect_1 = require("./inspect");
const io_1 = require("./io");
const npm_clients_1 = require("../npm-clients");
const helpers_1 = require("../helpers");
const external_1 = require("../external");
const gitPrefix = 'git+';
const filePrefix = 'file:';
const npmPrefix = 'npm:';
const pathPrefixes = ['/', './', '../', '.\\', '..\\', '~/', '~\\', filePrefix];
function isProjectReference(name) {
const target = (0, path_1.resolve)(name, constants_1.packageJson);
return (0, io_1.checkExists)(target);
}
function resolveAbsPath(basePath, fullName) {
const prefixed = fullName.startsWith(filePrefix);
const relPath = !prefixed ? fullName : fullName.replace(filePrefix, '');
return (0, path_1.resolve)(basePath, relPath);
}
async function detectMonorepoRoot(root) {
let previous = root;
do {
if (await (0, io_1.checkExists)((0, path_1.resolve)(root, 'lerna.json'))) {
return [root, 'lerna'];
}
if (await (0, io_1.checkExists)((0, path_1.resolve)(root, 'rush.json'))) {
return [root, 'rush'];
}
if (await (0, io_1.checkExists)((0, path_1.resolve)(root, 'pnpm-workspace.yaml'))) {
return [root, 'pnpm'];
}
const pj = await (0, io_1.readJson)(root, constants_1.packageJson);
if (Array.isArray(pj?.workspaces)) {
if (await (0, io_1.checkExists)((0, path_1.resolve)(root, '.pnp.cjs'))) {
return [root, 'pnp'];
}
if (await (0, io_1.checkExists)((0, path_1.resolve)(root, 'yarn.lock'))) {
return [root, 'yarn'];
}
return [root, 'npm'];
}
previous = root;
root = (0, path_1.dirname)(root);
} while (root !== previous);
return [];
}
async function determineWrapperClient(root) {
const searchedClients = await (0, npm_clients_1.detectWrapperClients)(root);
const foundClients = searchedClients.filter((m) => m.result).map((m) => m.client);
if (foundClients.length > 0) {
const [client] = foundClients;
if (foundClients.length > 1) {
(0, log_1.log)('generalWarning_0001', `Found multiple clients via their lock or config files: "${foundClients.join('", "')}".`);
}
(0, log_1.log)('generalDebug_0003', `Found valid direct client via lock or config file: "${client}".`);
return client;
}
const defaultClient = config_1.config.npmClient;
if ((0, npm_clients_1.isWrapperClient)(defaultClient)) {
(0, log_1.log)('generalDebug_0003', `Using the default client: "${defaultClient}".`);
return defaultClient;
}
return undefined;
}
async function determineDirectClient(root) {
const searchedClients = await (0, npm_clients_1.detectDirectClients)(root);
const foundClients = searchedClients.filter((m) => m.result).map((m) => m.client);
if (foundClients.length > 0) {
const [client] = foundClients;
if (foundClients.length > 1) {
(0, log_1.log)('generalWarning_0001', `Found multiple clients via their lock or config files: "${foundClients.join('", "')}".`);
}
(0, log_1.log)('generalDebug_0003', `Found valid direct client via lock or config file: "${client}".`);
return client;
}
const defaultClient = config_1.config.npmClient;
if ((0, npm_clients_1.isDirectClient)(defaultClient)) {
(0, log_1.log)('generalDebug_0003', `Using the default client: "${defaultClient}".`);
return defaultClient;
}
(0, log_1.log)('generalDebug_0003', 'Using the fallback "npm" client.');
return 'npm';
}
async function getMonorepo(root, client) {
const [path, retrieved] = await detectMonorepoRoot(root);
if (path && retrieved === client) {
return path;
}
return undefined;
}
/**
* For details about how this works consult issue
* https://github.com/smapiot/piral/issues/203
* @param root The project's root directory.
* @param selected The proposed ("selected") npm client.
*/
async function determineNpmClient(root, selected) {
if (!selected || !helpers_1.clientTypeKeys.includes(selected)) {
(0, log_1.log)('generalDebug_0003', 'No npm client selected. Checking for lock or config files ...');
const [direct, wrapper] = await Promise.all([determineDirectClient(root), determineWrapperClient(root)]);
return {
direct,
wrapper,
monorepo: await getMonorepo(root, wrapper || direct),
};
}
else if ((0, npm_clients_1.isDirectClient)(selected)) {
return {
proposed: selected,
direct: selected,
monorepo: await getMonorepo(root, selected),
};
}
else {
return {
proposed: selected,
direct: await determineDirectClient(root),
wrapper: selected,
monorepo: await getMonorepo(root, selected),
};
}
}
async function isMonorepoPackageRef(refName, client) {
if (client.monorepo) {
const clientName = client.wrapper || client.direct;
const clientApi = npm_clients_1.clients[clientName];
return await clientApi.isProject(client.monorepo, refName);
}
return false;
}
function installNpmDependencies(client, target = '.') {
const { installDependencies } = npm_clients_1.clients[client.direct];
return installDependencies(target);
}
async function installNpmPackageFromOptionalRegistry(packageRef, target, registry) {
const client = await determineNpmClient(target, 'npm');
try {
await installNpmPackage(client, packageRef, target, '--registry', registry);
}
catch (e) {
if (registry === constants_1.defaultRegistry) {
throw e;
}
await installNpmPackage(client, packageRef, target, '--registry', constants_1.defaultRegistry);
}
}
async function uninstallNpmPackage(client, packageRef, target = '.', ...flags) {
const name = client.direct;
try {
const { uninstallPackage } = npm_clients_1.clients[name];
return await uninstallPackage(packageRef, target, ...flags);
}
catch (ex) {
(0, log_1.log)('generalError_0002', `Could not uninstall the package "${packageRef}" using ${name}. Make sure ${name} is correctly installed and accessible: ${ex}`);
throw ex;
}
}
async function installNpmPackage(client, packageRef, target = '.', ...flags) {
const name = client.direct;
try {
const { installPackage } = npm_clients_1.clients[name];
return await installPackage(packageRef, target, ...flags);
}
catch (ex) {
(0, log_1.log)('generalError_0002', `Could not install the package "${packageRef}" using ${name}. Make sure ${name} is correctly installed and accessible: ${ex}`);
throw ex;
}
}
function initNpmProject(client, projectName, target) {
const { initProject } = npm_clients_1.clients[client.wrapper || client.direct];
return initProject(projectName, target);
}
function publishNpmPackage(target = '.', file = '*.tgz', flags = [], interactive = false) {
const { publishPackage, loginUser } = npm_clients_1.clients.npm;
return publishPackage(target, file, ...flags).catch((err) => {
if (!interactive) {
throw err;
}
return loginUser().then(() => publishNpmPackage(target, file, flags, false));
});
}
function createNpmPackage(target = '.') {
const { createPackage } = npm_clients_1.clients.npm;
return createPackage(target);
}
function findNpmTarball(packageRef) {
const { findTarball } = npm_clients_1.clients.npm;
return findTarball(packageRef);
}
function findSpecificVersion(packageName, version) {
const { findSpecificVersion } = npm_clients_1.clients.npm;
return findSpecificVersion(packageName, version);
}
function findLatestVersion(packageName) {
const { findSpecificVersion } = npm_clients_1.clients.npm;
return findSpecificVersion(packageName, 'latest');
}
function isLocalPackage(baseDir, fullName) {
(0, log_1.log)('generalDebug_0003', 'Checking if its a local package ...');
if (fullName) {
if (pathPrefixes.some((prefix) => fullName.startsWith(prefix))) {
(0, log_1.log)('generalDebug_0003', 'Found a local package by name.');
return true;
}
else if (fullName.endsWith('.tgz')) {
(0, log_1.log)('generalDebug_0003', ' Verifying if local path exists ...');
if ((0, fs_1.existsSync)((0, path_1.resolve)(baseDir, fullName))) {
(0, log_1.log)('generalDebug_0003', 'Found a potential local package by path.');
return true;
}
}
return fullName.startsWith(filePrefix);
}
return false;
}
function isNpmPackage(fullName) {
(0, log_1.log)('generalDebug_0003', 'Checking if its an npm alias ...');
if (fullName) {
const npmed = fullName.startsWith(npmPrefix);
if (npmed && fullName.substring(npmPrefix.length + 1).indexOf('@') !== -1) {
(0, log_1.log)('generalDebug_0003', 'Found an npm package alias by name.');
return true;
}
}
return false;
}
function makeNpmAlias(name, version) {
return `${npmPrefix}${name}@${version}`;
}
function isGitPackage(fullName) {
(0, log_1.log)('generalDebug_0003', 'Checking if its a git package ...');
if (fullName) {
const gitted = fullName.startsWith(gitPrefix);
if (gitted || /^(https?|ssh):\/\/.*\.git$/.test(fullName)) {
(0, log_1.log)('generalDebug_0003', 'Found a git package by name.');
return true;
}
}
return false;
}
function isRemotePackage(fullName) {
(0, log_1.log)('generalDebug_0003', 'Checking if its a remote package ...');
if (fullName && /^https?:\/\/.*/.test(fullName)) {
(0, log_1.log)('generalDebug_0003', 'Found a remote package by name.');
return true;
}
return false;
}
function makeGitUrl(fullName) {
const gitted = fullName.startsWith(gitPrefix);
return gitted ? fullName : `${gitPrefix}${fullName}`;
}
function makeFilePath(basePath, fullName) {
const absPath = resolveAbsPath(basePath, fullName);
return `${filePrefix}${absPath}`;
}
/**
* Looks at the provided package name and normalizes it
* resulting in the following tuple:
* [
* normalized / real package name,
* found package version / version identifier,
* indicator if an explicit version was used,
* the used package type
* ]
* @param baseDir The base directory of the current operation.
* @param fullName The provided package name.
* @param client The used npm client.
*/
async function dissectPackageName(baseDir, fullName, client) {
if (isGitPackage(fullName)) {
const gitUrl = makeGitUrl(fullName);
return [gitUrl, 'latest', false, 'git'];
}
else if (isRemotePackage(fullName)) {
return [fullName, 'latest', false, 'remote'];
}
else if (isLocalPackage(baseDir, fullName)) {
const fullPath = resolveAbsPath(baseDir, fullName);
const exists = await (0, io_1.checkExists)(fullPath);
if (!exists) {
(0, log_1.fail)('scaffoldPathDoesNotExist_0030', fullPath);
}
const isReference = await isProjectReference(fullPath);
if (isReference) {
(0, log_1.fail)('projectReferenceNotSupported_0032', fullPath);
}
return [fullPath, 'latest', false, 'file'];
}
else if (await isMonorepoPackageRef(fullName, client)) {
return [fullName, '*', false, 'monorepo'];
}
else {
const index = fullName.indexOf('@', 1);
const type = 'registry';
if (index !== -1) {
return [fullName.substring(0, index), fullName.substring(index + 1), true, type];
}
return [fullName, 'latest', false, type];
}
}
/**
* Looks at the current package name / version and
* normalizes it resulting in the following tuple:
* [
* normalized / real package name,
* found package version / version identifier,
* ]
* @param baseDir The base directory of the current operation.
* @param sourceName The used package name.
* @param sourceVersion The used package version.
* @param desired The desired package version.
*/
async function getCurrentPackageDetails(baseDir, sourceName, sourceVersion, desired, root) {
(0, log_1.log)('generalDebug_0003', `Checking package details in "${baseDir}" ...`);
if (isLocalPackage(baseDir, desired)) {
const fullPath = (0, path_1.resolve)(baseDir, desired);
const exists = await (0, io_1.checkExists)(fullPath);
if (!exists) {
(0, log_1.fail)('upgradePathDoesNotExist_0031', fullPath);
}
const isReference = await isProjectReference(fullPath);
if (isReference) {
(0, log_1.fail)('projectReferenceNotSupported_0032', fullPath);
}
return [fullPath, getFilePackageVersion(fullPath, root)];
}
else if (isGitPackage(desired)) {
const gitUrl = makeGitUrl(desired);
return [gitUrl, getGitPackageVersion(gitUrl)];
}
else if (sourceVersion && sourceVersion.startsWith('file:')) {
(0, log_1.log)('localeFileForUpgradeMissing_0050');
}
else if (sourceVersion && sourceVersion.startsWith('git+')) {
if (desired === 'latest') {
const gitUrl = desired;
return [gitUrl, getGitPackageVersion(gitUrl)];
}
else {
(0, log_1.log)('gitLatestForUpgradeMissing_0051');
}
}
return [combinePackageRef(sourceName, desired, 'registry'), desired];
}
function tryResolve(packageName, baseDir = process.cwd()) {
try {
return (0, external_1.getModulePath)(baseDir, packageName);
}
catch {
return undefined;
}
}
function tryResolvePackage(name, baseDir = undefined) {
const path = baseDir ? tryResolve(name, baseDir) : tryResolve(name);
const root = baseDir || process.cwd();
if (!path) {
(0, log_1.log)('generalDebug_0003', `Could not resolve the package "${name}" in "${root}".`);
}
else {
(0, log_1.log)('generalVerbose_0004', `Resolved the package "${name}" (from "${root}") to be "${path}".`);
}
return path;
}
function findPackageRoot(pck, baseDir) {
return tryResolvePackage(`${pck}/${constants_1.packageJson}`, baseDir);
}
function isLinkedPackage(name, type, hadVersion, target) {
if (type === 'monorepo') {
return true;
}
else if (type === 'registry' && !hadVersion) {
const root = findPackageRoot(name, target);
return typeof root === 'string';
}
return false;
}
function combinePackageRef(name, version, type) {
if (type === 'registry') {
const tag = version || 'latest';
return `${name}@${tag}`;
}
return name;
}
async function getPackageName(root, name, type) {
switch (type) {
case 'file':
const originalPackageJson = await (0, io_1.readJson)(name, constants_1.packageJson);
if (!originalPackageJson.name) {
const p = (0, path_1.resolve)(process.cwd(), name);
try {
const s = (0, fs_1.createReadStream)(p);
const i = await (0, inspect_1.inspectPackage)(s);
return i.name;
}
catch (err) {
(0, log_1.log)('generalError_0002', `Could not open package tarball at "${p}": "${err}`);
return undefined;
}
}
return originalPackageJson.name;
case 'git':
const pj = await (0, io_1.readJson)(root, constants_1.packageJson);
const dd = pj.devDependencies || {};
return Object.keys(dd).filter((dep) => dd[dep] === name)[0];
case 'monorepo':
case 'registry':
return name;
case 'remote':
throw new Error('Cannot get the package name for a remote package!');
}
}
function getFilePackageVersion(sourceName, root) {
const path = (0, path_1.relative)(root, sourceName);
return `${filePrefix}${path}`;
}
function getGitPackageVersion(sourceName) {
return `${sourceName}`;
}
function getPackageVersion(hadVersion, sourceName, sourceVersion, type, root) {
switch (type) {
case 'monorepo':
return sourceVersion;
case 'registry':
return hadVersion && sourceVersion;
case 'file':
return getFilePackageVersion(sourceName, root);
case 'git':
return getGitPackageVersion(sourceName);
}
}
async function getExternalsFrom(root, packageName) {
try {
const target = (0, external_1.getModulePath)(root, `${packageName}/${constants_1.packageJson}`);
const dir = (0, path_1.dirname)(target);
const { sharedDependencies } = await (0, io_1.readJson)(dir, constants_1.packageJson);
return sharedDependencies;
}
catch (err) {
(0, log_1.log)('generalError_0002', `Could not get externals from "${packageName}": "${err}`);
return undefined;
}
}
async function getCoreExternals(root, dependencies) {
for (const frameworkLib of constants_1.frameworkLibs) {
if (dependencies[frameworkLib]) {
const deps = await getExternalsFrom(root, frameworkLib);
if (deps) {
return deps;
}
}
}
(0, log_1.log)('frameworkLibMissing_0078', constants_1.frameworkLibs);
return [];
}
async function makePiletExternals(root, dependencies, fromEmulator, piralInfo) {
if (fromEmulator) {
const { sharedDependencies = constants_1.legacyCoreExternals } = piralInfo;
return sharedDependencies;
}
else {
return await getCoreExternals(root, dependencies);
}
}
function mergeExternals(customExternals, coreExternals = []) {
if (customExternals && Array.isArray(customExternals)) {
const [include, exclude] = customExternals.reduce((prev, curr) => {
if (typeof curr === 'string') {
if (curr.startsWith('!')) {
prev[1].push(curr.substring(1));
}
else {
prev[0].push(curr);
}
}
return prev;
}, [[], []]);
const all = exclude.includes('*') ? include : [...include, ...coreExternals];
return all.filter((m, i, arr) => !exclude.includes(m) && arr.indexOf(m) === i);
}
return coreExternals;
}
async function makeExternals(root, dependencies, externals) {
const coreExternals = await getCoreExternals(root, dependencies);
return mergeExternals(externals, coreExternals);
}
//# sourceMappingURL=npm.js.map