react-native-integrate
Version:
Automate integration of additional code into React Native projects
327 lines (326 loc) • 16.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.integrate = integrate;
const picocolors_1 = __importDefault(require("picocolors"));
const preload_1 = __importDefault(require("semver/preload"));
const options_1 = require("./options");
const progress_1 = require("./progress");
const prompter_1 = require("./prompter");
const analyzePackages_1 = require("./utils/analyzePackages");
const checkCondition_1 = require("./utils/checkCondition");
const checkForUpdate_1 = require("./utils/checkForUpdate");
const getErrMessage_1 = require("./utils/getErrMessage");
const getIntegrateConfig_1 = require("./utils/getIntegrateConfig");
const getPackageConfig_1 = require("./utils/getPackageConfig");
const logInfoNote_1 = require("./utils/logInfoNote");
const parseConfig_1 = require("./utils/parseConfig");
const runTask_1 = require("./utils/runTask");
const setState_1 = require("./utils/setState");
const taskManager_1 = require("./utils/taskManager");
const topologicalSort_1 = require("./utils/topologicalSort");
const updateIntegrationStatus_1 = require("./utils/updateIntegrationStatus");
const variables_1 = require("./variables");
async function integrate(packageName) {
progress_1.progress.setOptions({
stage: 'checking for updates',
});
await (0, checkForUpdate_1.checkForUpdate)();
let stage = 1;
progress_1.progress.setOptions({
step: stage++,
stage: 'analyzing packages',
});
(0, prompter_1.startSpinner)('analyzing packages');
const analyzedPackages = (0, analyzePackages_1.analyzePackages)(packageName);
const { deletedPackages, installedPackages, integratedPackages } = analyzedPackages;
let { newPackages } = analyzedPackages;
const shouldBreak = checkIfJustCreatedLockFile(analyzedPackages);
let msg = `analyzed ${installedPackages.length} packages`;
if (packageName) {
// check if package is installed
newPackages = newPackages.filter(([newPackageName]) => newPackageName == packageName);
if (installedPackages.every(([installedPackageName]) => installedPackageName != packageName) ||
!newPackages.length) {
(0, prompter_1.stopSpinner)(msg);
(0, prompter_1.logWarning)(`${picocolors_1.default.bold(packageName)} is not installed - please install it first and try again`);
return;
}
}
else {
if (shouldBreak)
return;
// get packages that need to be implemented
if (newPackages.length || deletedPackages.length) {
if (newPackages.length > 0) {
msg += picocolors_1.default.green(` | ${newPackages.length} new package(s)`);
}
if (deletedPackages.length > 0) {
msg += picocolors_1.default.gray(` | ${deletedPackages.length} deleted package(s)`);
}
}
else {
msg += ' | no changes';
}
}
(0, prompter_1.stopSpinner)(msg);
const packageLockUpdates = [];
let packagesToIntegrate = [];
if (newPackages.length) {
const globalInfo = [];
progress_1.progress.setOptions({
step: stage++,
stage: 'checking package configuration',
});
(0, prompter_1.startSpinner)('checking package configuration');
for (let i = 0; i < newPackages.length; i++) {
const [packageName, version] = newPackages[i];
const configPath = await (0, getPackageConfig_1.getPackageConfig)(packageName, {
index: i,
count: newPackages.length,
});
if (!configPath)
packageLockUpdates.push({
packageName,
lockProjectData: {
version,
integrated: false,
},
});
else {
let config;
try {
config = (0, parseConfig_1.parseConfig)(configPath);
}
catch (e) {
(0, prompter_1.logError)(picocolors_1.default.bold(picocolors_1.default.bgRed(' error ')) +
picocolors_1.default.bold(picocolors_1.default.blue(` ${packageName} `)) +
picocolors_1.default.red('could not parse package configuration\n') +
picocolors_1.default.gray((0, getErrMessage_1.getErrMessage)(e, 'validation')), true);
continue;
}
const rnVersionEntry = installedPackages.find(entry => entry[0] === 'react-native');
if (!rnVersionEntry) {
(0, prompter_1.stopSpinner)('checked package configuration');
(0, prompter_1.logWarning)('React Native not installed!?');
return;
}
const rnVersion = rnVersionEntry[1];
const semRnVersion = preload_1.default.coerce(rnVersion);
if (!semRnVersion) {
(0, prompter_1.stopSpinner)('checked package configuration');
(0, prompter_1.logWarning)(`React Native version (${rnVersion}) is invalid!?`);
return;
}
variables_1.variables.setPredefined('RN_VERSION', {
major: semRnVersion.major,
minor: semRnVersion.minor,
patch: semRnVersion.patch,
});
if (config.minRNVersion) {
if (preload_1.default.lt(semRnVersion, preload_1.default.coerce(config.minRNVersion) || '0.0.0')) {
(0, prompter_1.stopSpinner)('checked package configuration');
(0, prompter_1.logWarning)(`${picocolors_1.default.bold(picocolors_1.default.blue(packageName))} requires React Native version ${picocolors_1.default.bold(picocolors_1.default.blue(config.minRNVersion))}`);
return;
}
}
if (config.minVersion) {
if (preload_1.default.lt(preload_1.default.coerce(version) || '0.0.0', preload_1.default.coerce(config.minVersion) || '0.0.0')) {
(0, prompter_1.stopSpinner)('checked package configuration');
(0, prompter_1.logWarning)(`${picocolors_1.default.bold(picocolors_1.default.blue(packageName))} requires version ${picocolors_1.default.bold(picocolors_1.default.blue(config.minVersion))} - please update it first and try again`);
return;
}
}
if (config.dependencies?.length) {
let warn = null;
const packageInfo = [];
for (const dependentPackageName of config.dependencies) {
// check if dependency is not integrated and not already in new package list
const isInNewPackages = newPackages.every(([packageName]) => packageName != dependentPackageName);
const isNotIntegrated = integratedPackages.every(([packageName]) => packageName != dependentPackageName);
if (isInNewPackages && isNotIntegrated) {
//check if dependent is installed
const installedPackage = installedPackages.find(([packageName]) => packageName == dependentPackageName);
if (!installedPackage) {
warn = `${picocolors_1.default.bold(picocolors_1.default.blue(dependentPackageName))} is not installed - please install it first and try again`;
break;
}
else {
// installed but not new, force add as new
newPackages.push(installedPackage);
packageInfo.push(`${picocolors_1.default.bold(picocolors_1.default.blue(dependentPackageName))}`);
}
}
}
if (warn) {
(0, prompter_1.stopSpinner)('checked package configuration');
(0, prompter_1.logWarning)(`${picocolors_1.default.bold(picocolors_1.default.blue(packageName))} has dependencies that require integration: \n${warn}`);
return;
}
else if (packageInfo.length) {
globalInfo.push(`${picocolors_1.default.bold(picocolors_1.default.blue(packageName))} has dependencies that require integration: ${getInnerLogList(packageInfo)}`);
}
}
packagesToIntegrate.push({
packageName,
version,
configPath,
config,
});
}
}
let msg = 'checked package configuration';
if (packageName) {
if (packagesToIntegrate.length == 0) {
(0, prompter_1.stopSpinner)(msg);
(0, prompter_1.logWarning)(`${picocolors_1.default.bold(packageName)} has no configuration specified`);
return;
}
}
else {
if (packagesToIntegrate.length > 0) {
msg += picocolors_1.default.green(` | ${packagesToIntegrate.length} package can be integrated`);
}
else {
msg += ' | no configuration specified';
}
}
(0, prompter_1.stopSpinner)(msg);
if (globalInfo.length)
globalInfo.forEach(prompter_1.logInfo);
}
if (packagesToIntegrate.length) {
packagesToIntegrate = (0, topologicalSort_1.topologicalSort)(packagesToIntegrate);
const integrateConfig = (0, getIntegrateConfig_1.getIntegrateConfig)();
for (let i = 0; i < packagesToIntegrate.length; i++) {
const { packageName, version, configPath, config } = packagesToIntegrate[i];
progress_1.progress.setOptions({
step: i + 1,
total: packagesToIntegrate.length,
stage: `integrating ${picocolors_1.default.blue(packageName)}`,
});
(0, prompter_1.logSuccess)(picocolors_1.default.bold(picocolors_1.default.bgBlue(' integration ')) +
picocolors_1.default.bold(picocolors_1.default.blue(` ${packageName} `)));
if (!options_1.options.get().interactive ||
(await (0, prompter_1.confirm)('would you like to integrate this package?'))) {
variables_1.variables.clear(); // reset variables
if (config.env) {
Object.entries(config.env).forEach(([name, value]) => variables_1.variables.set(name, (0, variables_1.transformTextInObject)(value)));
}
if (integrateConfig) {
const integratePackageconfig = (0, getIntegrateConfig_1.getIntegratePackageConfig)(integrateConfig, packageName);
if (integratePackageconfig)
variables_1.variables.set('config', integratePackageconfig);
}
let failedTaskCount = 0, completedTaskCount = 0;
await (0, logInfoNote_1.logInfoNote)(config.preInfo);
for (const task of config.steps) {
if (task.when && !(0, checkCondition_1.checkCondition)(task.when)) {
(0, setState_1.setState)(task.name, {
state: 'skipped',
});
continue;
}
(0, setState_1.setState)(task.name, {
state: 'progress',
});
const isNonSystemTask = !taskManager_1.taskManager.isSystemTask(task.task);
if (isNonSystemTask) {
if (task.label)
task.label = (0, variables_1.getText)(task.label);
else
task.label = taskManager_1.taskManager.task[task.task].summary;
(0, prompter_1.logInfo)(picocolors_1.default.bold(picocolors_1.default.inverse(picocolors_1.default.cyan(' task '))) +
picocolors_1.default.bold(picocolors_1.default.cyan(` ${task.label} `)));
}
await (0, logInfoNote_1.logInfoNote)(task.preInfo);
try {
await (0, runTask_1.runTask)({
configPath,
packageName,
task,
});
completedTaskCount++;
(0, setState_1.setState)(task.name, {
state: 'done',
});
await (0, logInfoNote_1.logInfoNote)(task.postInfo);
}
catch (e) {
failedTaskCount++;
const errMessage = (0, getErrMessage_1.getErrMessage)(e);
(0, prompter_1.logError)(errMessage);
(0, setState_1.setState)(task.name, {
state: 'error',
reason: errMessage,
});
}
}
await (0, logInfoNote_1.logInfoNote)(config.postInfo);
if (failedTaskCount) {
(0, prompter_1.logWarning)(picocolors_1.default.inverse(picocolors_1.default.bold(picocolors_1.default.yellow(' done with errors '))) +
picocolors_1.default.bold(picocolors_1.default.blue(` ${packageName} `)) +
picocolors_1.default.yellow(`failed to complete ${failedTaskCount} task(s) - please complete this integration manually`), true);
}
else {
(0, prompter_1.logSuccess)(picocolors_1.default.inverse(picocolors_1.default.bold(picocolors_1.default.green(' done '))) +
picocolors_1.default.green(` completed ${completedTaskCount} task(s) successfully`));
}
packageLockUpdates.push({
packageName,
lockProjectData: {
version,
integrated: true,
},
});
}
else {
packageLockUpdates.push({
packageName,
lockProjectData: {
version,
integrated: false,
},
});
(0, prompter_1.log)(picocolors_1.default.gray(picocolors_1.default.italic('skipped package integration')));
}
}
}
for (let i = 0; i < deletedPackages.length; i++) {
const [packageName, lockProjectData] = deletedPackages[i];
lockProjectData.deleted = true;
packageLockUpdates.push({
packageName,
lockProjectData,
});
}
(0, updateIntegrationStatus_1.updateIntegrationStatus)(packageLockUpdates);
}
function checkIfJustCreatedLockFile(analyzedPackages) {
const { newPackages, justCreatedLockFile } = analyzedPackages;
if (justCreatedLockFile) {
if (!analyzedPackages.forceIntegratePackageName)
(0, prompter_1.stopSpinner)(picocolors_1.default.gray(picocolors_1.default.italic('first run, skipped integration checks')));
const packageLockUpdates = [];
for (let i = 0; i < newPackages.length; i++) {
const [packageName, version] = newPackages[i];
packageLockUpdates.push({
packageName,
lockProjectData: {
version,
integrated: false,
},
});
}
(0, updateIntegrationStatus_1.updateIntegrationStatus)(packageLockUpdates);
return true;
}
return false;
}
function getInnerLogList(strings) {
return strings.reduce((o, s, index) => {
return o + picocolors_1.default.gray(index == strings.length - 1 ? '\n└ ' : '\n├ ') + s;
}, '');
}