inup 1.5.5 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -10
- package/dist/cli.js +53 -31
- package/dist/config/constants.js +4 -6
- package/dist/config/package-meta.js +10 -0
- package/dist/config/project-config.js +11 -1
- package/dist/core/package-detector.js +37 -5
- package/dist/core/upgrade-runner.js +8 -10
- package/dist/core/upgrader.js +15 -11
- package/dist/features/changelog/clients/github-client.js +5 -6
- package/dist/features/changelog/services/changelog-service.js +2 -4
- package/dist/features/changelog/services/package-metadata-service.js +7 -23
- package/dist/features/changelog/services/release-notes-service.js +4 -29
- package/dist/features/debug/services/performance-tracker.js +6 -8
- package/dist/features/headless/headless-runner.js +53 -0
- package/dist/features/headless/index.js +27 -0
- package/dist/features/headless/report.js +64 -0
- package/dist/features/headless/types.js +6 -0
- package/dist/features/headless/vulnerability-audit.js +106 -0
- package/dist/index.js +1 -2
- package/dist/interactive-ui.js +15 -606
- package/dist/services/background-audit.js +3 -5
- package/dist/services/cache-manager.js +5 -7
- package/dist/services/http/inflight.js +22 -0
- package/dist/services/http/retry.js +30 -0
- package/dist/services/index.js +0 -1
- package/dist/services/npm-registry.js +20 -97
- package/dist/services/package-manager-detector.js +6 -3
- package/dist/services/persistent-cache.js +8 -13
- package/dist/types/domain.js +3 -0
- package/dist/types/streaming.js +3 -0
- package/dist/types/ui.js +3 -0
- package/dist/types.js +17 -0
- package/dist/ui/controllers/package-info-modal-controller.js +22 -31
- package/dist/ui/controllers/vulnerability-audit-controller.js +17 -8
- package/dist/ui/index.js +3 -0
- package/dist/ui/input-handler.js +58 -75
- package/dist/ui/keymap.js +208 -0
- package/dist/ui/modal/package-info-sections/release-notes.js +161 -0
- package/dist/ui/modal/package-info-sections/sections.js +174 -0
- package/dist/ui/modal/package-info-sections/text.js +75 -0
- package/dist/ui/modal/package-info-sections.js +15 -369
- package/dist/ui/modal/package-info.js +1 -1
- package/dist/ui/presenters/health.js +24 -0
- package/dist/ui/renderer/help-modal.js +75 -0
- package/dist/ui/renderer/index.js +2 -5
- package/dist/ui/renderer/package-list/interface.js +184 -0
- package/dist/ui/renderer/package-list/rows.js +177 -0
- package/dist/ui/renderer/package-list.js +15 -455
- package/dist/ui/session/action-dispatcher.js +233 -0
- package/dist/ui/session/index.js +13 -0
- package/dist/ui/session/interactive-session.js +280 -0
- package/dist/ui/session/selection-state-builder.js +149 -0
- package/dist/ui/state/filter-manager.js +17 -6
- package/dist/ui/state/modal-manager.js +35 -0
- package/dist/ui/state/navigation-manager.js +33 -4
- package/dist/ui/state/state-manager.js +68 -3
- package/dist/ui/state/theme-manager.js +1 -0
- package/dist/ui/themes-colors.js +16 -4
- package/dist/ui/themes.js +11 -19
- package/dist/ui/utils/cursor.js +12 -7
- package/dist/ui/utils/index.js +6 -1
- package/dist/ui/utils/text.js +3 -2
- package/dist/ui/utils/version.js +60 -86
- package/dist/utils/color.js +38 -0
- package/dist/utils/config.js +13 -1
- package/dist/utils/engines.js +63 -0
- package/dist/utils/filesystem/io.js +103 -0
- package/dist/utils/filesystem/paths.js +19 -0
- package/dist/utils/filesystem/scan.js +280 -0
- package/dist/utils/filesystem.js +17 -335
- package/dist/utils/index.js +3 -0
- package/dist/utils/manifest.js +35 -0
- package/dist/utils/version.js +38 -1
- package/package.json +8 -7
- package/dist/services/jsdelivr-registry.js +0 -395
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildUsedBySections = buildUsedBySections;
|
|
7
|
+
exports.buildPackageInfoSections = buildPackageInfoSections;
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const themes_colors_1 = require("../../themes-colors");
|
|
11
|
+
const vulnerability_1 = require("../../presenters/vulnerability");
|
|
12
|
+
const utils_1 = require("../../utils");
|
|
13
|
+
const utils_2 = require("../../../utils");
|
|
14
|
+
const release_notes_1 = require("./release-notes");
|
|
15
|
+
function formatNumber(num) {
|
|
16
|
+
if (!num)
|
|
17
|
+
return 'N/A';
|
|
18
|
+
if (num >= 1000000)
|
|
19
|
+
return (num / 1000000).toFixed(1) + 'M';
|
|
20
|
+
if (num >= 1000)
|
|
21
|
+
return (num / 1000).toFixed(1) + 'K';
|
|
22
|
+
return num.toString();
|
|
23
|
+
}
|
|
24
|
+
function getUsedByPaths(state) {
|
|
25
|
+
return state.packageJsonPaths ?? [state.packageJsonPath];
|
|
26
|
+
}
|
|
27
|
+
function buildTabBarSuffix(activeTab, usedByCount) {
|
|
28
|
+
const styleFor = (tab) => tab === activeTab ? chalk_1.default.bold.underline : chalk_1.default.gray;
|
|
29
|
+
const usedByLabel = `Used by${usedByCount > 0 ? ` (${usedByCount})` : ''}`;
|
|
30
|
+
return (' ' +
|
|
31
|
+
chalk_1.default.gray('[ ') +
|
|
32
|
+
styleFor('info')('Info') +
|
|
33
|
+
chalk_1.default.gray(' │ ') +
|
|
34
|
+
styleFor('usedBy')(usedByLabel) +
|
|
35
|
+
chalk_1.default.gray(' ]'));
|
|
36
|
+
}
|
|
37
|
+
function buildUsedBySections(state, modalWidth) {
|
|
38
|
+
const paths = getUsedByPaths(state);
|
|
39
|
+
const cwd = process.cwd();
|
|
40
|
+
const contentWidth = Math.max(10, modalWidth - 4);
|
|
41
|
+
const formatRelative = (absolutePath) => {
|
|
42
|
+
const display = node_path_1.default.relative(cwd, absolutePath) || absolutePath;
|
|
43
|
+
return (0, utils_1.truncatePlainText)(display, contentWidth);
|
|
44
|
+
};
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
key: 'used-by-summary',
|
|
48
|
+
rows: [
|
|
49
|
+
chalk_1.default.bold(`${paths.length} package.json file${paths.length === 1 ? '' : 's'} depend on ${state.name}`),
|
|
50
|
+
chalk_1.default.gray(`Type: ${state.type}`),
|
|
51
|
+
],
|
|
52
|
+
required: true,
|
|
53
|
+
behavior: 'pinned',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key: 'used-by-list',
|
|
57
|
+
rows: paths.map((p) => `${chalk_1.default.gray('•')} ${formatRelative(p)}`),
|
|
58
|
+
behavior: 'body',
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
}
|
|
62
|
+
function buildPackageInfoSections(state, modalWidth, activeTab) {
|
|
63
|
+
const title = chalk_1.default.bold('Package: ') +
|
|
64
|
+
(0, themes_colors_1.getThemeColor)('packageName')(state.name) +
|
|
65
|
+
buildTabBarSuffix(activeTab, getUsedByPaths(state).length);
|
|
66
|
+
const authorLicense = chalk_1.default.gray(`${state.author || 'Unknown'} • ${state.license || 'MIT'}`);
|
|
67
|
+
const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
|
|
68
|
+
const targetVersion = chalk_1.default.green(state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion);
|
|
69
|
+
const sections = [
|
|
70
|
+
{
|
|
71
|
+
key: 'header',
|
|
72
|
+
rows: [title, authorLicense],
|
|
73
|
+
required: true,
|
|
74
|
+
behavior: 'pinned',
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
if (activeTab === 'usedBy') {
|
|
78
|
+
sections.push(...buildUsedBySections(state, modalWidth));
|
|
79
|
+
return sections;
|
|
80
|
+
}
|
|
81
|
+
sections.push({
|
|
82
|
+
key: 'meta',
|
|
83
|
+
rows: [
|
|
84
|
+
`Current: ${currentVersion} Target: ${targetVersion}`,
|
|
85
|
+
...(state.weeklyDownloads !== undefined
|
|
86
|
+
? [(0, themes_colors_1.getThemeColor)('primary')(`Downloads/week: ${formatNumber(state.weeklyDownloads)}`)]
|
|
87
|
+
: []),
|
|
88
|
+
],
|
|
89
|
+
required: true,
|
|
90
|
+
behavior: 'pinned',
|
|
91
|
+
});
|
|
92
|
+
const warningRows = [];
|
|
93
|
+
const warningContentWidth = Math.max(10, modalWidth - 4);
|
|
94
|
+
if (state.deprecated) {
|
|
95
|
+
// Wrap (don't truncate) so a deprecation URL stays whole and clickable —
|
|
96
|
+
// truncation with "..." produces a dead link. `wrapPlainText` breaks on
|
|
97
|
+
// spaces only, so the URL keeps its own intact line. No emoji marker:
|
|
98
|
+
// `getVisualLength` scores glyphs like ⚠ as width 2 while many terminals
|
|
99
|
+
// render them as width 1, which throws off the modal's border alignment.
|
|
100
|
+
for (const line of (0, utils_1.wrapPlainText)(`Deprecated: ${state.deprecated}`, warningContentWidth)) {
|
|
101
|
+
warningRows.push((0, themes_colors_1.getThemeColor)('warning')(line));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const engineWarning = (0, utils_2.checkNodeEngineCompatibility)(state.enginesNode);
|
|
105
|
+
if (engineWarning) {
|
|
106
|
+
warningRows.push((0, themes_colors_1.getThemeColor)('warning')(`Hold: ${engineWarning}`));
|
|
107
|
+
}
|
|
108
|
+
if (warningRows.length > 0) {
|
|
109
|
+
sections.push({
|
|
110
|
+
key: 'warnings',
|
|
111
|
+
rows: warningRows,
|
|
112
|
+
behavior: 'pinned',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (state.homepage) {
|
|
116
|
+
sections.push({
|
|
117
|
+
key: 'homepage',
|
|
118
|
+
rows: [
|
|
119
|
+
`Homepage: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.homepage, modalWidth - 14)))}`,
|
|
120
|
+
],
|
|
121
|
+
behavior: 'pinned',
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (state.description) {
|
|
125
|
+
sections.push({
|
|
126
|
+
key: 'description',
|
|
127
|
+
rows: (0, utils_1.wrapPlainText)(state.description, modalWidth - 4)
|
|
128
|
+
.slice(0, 4)
|
|
129
|
+
.map((line, index, rows) => index === rows.length - 1 && rows.length === 4
|
|
130
|
+
? (0, utils_1.truncatePlainText)(line, modalWidth - 4)
|
|
131
|
+
: line),
|
|
132
|
+
behavior: 'pinned',
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
if (state.vulnerability && state.vulnerability.count > 0) {
|
|
136
|
+
const representative = (0, vulnerability_1.selectRepresentativeAdvisory)(state.vulnerability);
|
|
137
|
+
const severityColor = (0, vulnerability_1.getVulnerabilitySeverityColor)(state.vulnerability.highestSeverity);
|
|
138
|
+
const vulnerabilityRows = [
|
|
139
|
+
chalk_1.default.red.bold(`${state.vulnerability.count} known vulnerabilit${state.vulnerability.count === 1 ? 'y' : 'ies'} (${severityColor(state.vulnerability.highestSeverity.toUpperCase())})`),
|
|
140
|
+
];
|
|
141
|
+
if (representative) {
|
|
142
|
+
const severityLabel = ` ${severityColor(`[${representative.severity.toUpperCase()}]`)} `;
|
|
143
|
+
const availableTitleWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(severityLabel));
|
|
144
|
+
vulnerabilityRows.push(`${severityLabel}${(0, utils_1.truncatePlainText)(representative.title, availableTitleWidth)}`);
|
|
145
|
+
}
|
|
146
|
+
const detailsUrl = state.vulnerability.detailsUrl || representative?.url;
|
|
147
|
+
if (detailsUrl) {
|
|
148
|
+
const linkPrefix = ` ${(0, vulnerability_1.getVulnerabilityLinkLabel)(detailsUrl)} `;
|
|
149
|
+
const availableLinkWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(linkPrefix));
|
|
150
|
+
vulnerabilityRows.push(`${linkPrefix}${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(detailsUrl, availableLinkWidth)))}`);
|
|
151
|
+
}
|
|
152
|
+
if (state.vulnerability.count > 1) {
|
|
153
|
+
vulnerabilityRows.push(chalk_1.default.gray(` ... and ${state.vulnerability.count - 1} more`));
|
|
154
|
+
}
|
|
155
|
+
sections.push({
|
|
156
|
+
key: 'vulnerability',
|
|
157
|
+
rows: vulnerabilityRows,
|
|
158
|
+
required: true,
|
|
159
|
+
behavior: 'pinned',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (state.repository) {
|
|
163
|
+
sections.push({
|
|
164
|
+
key: 'changelog',
|
|
165
|
+
rows: [
|
|
166
|
+
`Changelog: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.repository, modalWidth - 15)))}`,
|
|
167
|
+
],
|
|
168
|
+
behavior: 'pinned',
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
sections.push(...(0, release_notes_1.buildReleaseNotesSections)(state, modalWidth));
|
|
172
|
+
return sections;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=sections.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatTerminalLink = formatTerminalLink;
|
|
4
|
+
exports.getRepositoryBaseUrl = getRepositoryBaseUrl;
|
|
5
|
+
exports.sanitizeMarkdownText = sanitizeMarkdownText;
|
|
6
|
+
exports.linkifyContributorMentions = linkifyContributorMentions;
|
|
7
|
+
exports.linkifyRepositoryReferences = linkifyRepositoryReferences;
|
|
8
|
+
exports.linkifyMarkdownText = linkifyMarkdownText;
|
|
9
|
+
exports.pushWrappedLines = pushWrappedLines;
|
|
10
|
+
exports.isLowSignalTrailerLine = isLowSignalTrailerLine;
|
|
11
|
+
const utils_1 = require("../../utils");
|
|
12
|
+
function formatTerminalLink(label, url) {
|
|
13
|
+
return `]8;;${url}${label}]8;;`;
|
|
14
|
+
}
|
|
15
|
+
function getRepositoryBaseUrl(repositoryUrl) {
|
|
16
|
+
if (!repositoryUrl) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return repositoryUrl.replace(/\/releases\/?$/, '');
|
|
20
|
+
}
|
|
21
|
+
function sanitizeMarkdownText(text) {
|
|
22
|
+
return text
|
|
23
|
+
.replace(/<[^>]+>/g, '')
|
|
24
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
|
|
25
|
+
.replace(/[`*_~]/g, '')
|
|
26
|
+
.replace(/\s+/g, ' ')
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
function linkifyContributorMentions(text) {
|
|
30
|
+
return text.replace(/(^|[\s(])@([a-zA-Z0-9-]+)/g, (_match, prefix, username) => {
|
|
31
|
+
return `${prefix}${formatTerminalLink(`@${username}`, `https://github.com/${username}`)}`;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function linkifyRepositoryReferences(text, repositoryUrl) {
|
|
35
|
+
const repoBaseUrl = getRepositoryBaseUrl(repositoryUrl);
|
|
36
|
+
if (!repoBaseUrl || !repoBaseUrl.includes('github.com')) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
return text
|
|
40
|
+
.replace(/(^|[\s(])#(\d+)\b/g, (_match, prefix, number) => {
|
|
41
|
+
return `${prefix}${formatTerminalLink(`#${number}`, `${repoBaseUrl}/pull/${number}`)}`;
|
|
42
|
+
})
|
|
43
|
+
.replace(/(^|[\s(])([0-9a-f]{7,40})\b/gi, (_match, prefix, hash) => {
|
|
44
|
+
return `${prefix}${formatTerminalLink(hash, `${repoBaseUrl}/commit/${hash}`)}`;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function linkifyMarkdownText(text, repositoryUrl) {
|
|
48
|
+
return linkifyRepositoryReferences(linkifyContributorMentions(text), repositoryUrl);
|
|
49
|
+
}
|
|
50
|
+
function pushWrappedLines(lines, text, width, firstPrefix, restPrefix = firstPrefix, style) {
|
|
51
|
+
const firstWidth = Math.max(1, width - (0, utils_1.getVisualLength)(firstPrefix));
|
|
52
|
+
const restWidth = Math.max(1, width - (0, utils_1.getVisualLength)(restPrefix));
|
|
53
|
+
const segments = (0, utils_1.wrapPlainText)(text, firstWidth);
|
|
54
|
+
if (segments.length === 0) {
|
|
55
|
+
lines.push(firstPrefix.trimEnd());
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
lines.push(firstPrefix + (style ? style(segments[0]) : segments[0]));
|
|
59
|
+
for (let i = 1; i < segments.length; i++) {
|
|
60
|
+
const wrappedSegments = (0, utils_1.wrapPlainText)(segments[i], restWidth);
|
|
61
|
+
for (const segment of wrappedSegments) {
|
|
62
|
+
lines.push(restPrefix + (style ? style(segment) : segment));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function isLowSignalTrailerLine(text) {
|
|
67
|
+
const normalized = text.toLowerCase();
|
|
68
|
+
return (normalized.startsWith('compare') ||
|
|
69
|
+
normalized.startsWith('full changelog') ||
|
|
70
|
+
normalized.startsWith('see full changelog') ||
|
|
71
|
+
normalized.startsWith('release notes') ||
|
|
72
|
+
normalized.includes('/compare/') ||
|
|
73
|
+
/^\w+: https?:\/\//.test(normalized));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -1,373 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
4
15
|
};
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports
|
|
7
|
-
exports
|
|
8
|
-
exports.buildPackageInfoSections = buildPackageInfoSections;
|
|
9
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const themes_colors_1 = require("../themes-colors");
|
|
12
|
-
const vulnerability_1 = require("../presenters/vulnerability");
|
|
13
|
-
const utils_1 = require("../utils");
|
|
14
|
-
function formatTerminalLink(label, url) {
|
|
15
|
-
return `\u001b]8;;${url}\u0007${label}\u001b]8;;\u0007`;
|
|
16
|
-
}
|
|
17
|
-
function getRepositoryBaseUrl(repositoryUrl) {
|
|
18
|
-
if (!repositoryUrl) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
return repositoryUrl.replace(/\/releases\/?$/, '');
|
|
22
|
-
}
|
|
23
|
-
function sanitizeMarkdownText(text) {
|
|
24
|
-
return text
|
|
25
|
-
.replace(/<[^>]+>/g, '')
|
|
26
|
-
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
|
|
27
|
-
.replace(/[`*_~]/g, '')
|
|
28
|
-
.replace(/\s+/g, ' ')
|
|
29
|
-
.trim();
|
|
30
|
-
}
|
|
31
|
-
function linkifyContributorMentions(text) {
|
|
32
|
-
return text.replace(/(^|[\s(])@([a-zA-Z0-9-]+)/g, (match, prefix, username) => {
|
|
33
|
-
return `${prefix}${formatTerminalLink(`@${username}`, `https://github.com/${username}`)}`;
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
function linkifyRepositoryReferences(text, repositoryUrl) {
|
|
37
|
-
const repoBaseUrl = getRepositoryBaseUrl(repositoryUrl);
|
|
38
|
-
if (!repoBaseUrl || !repoBaseUrl.includes('github.com')) {
|
|
39
|
-
return text;
|
|
40
|
-
}
|
|
41
|
-
return text
|
|
42
|
-
.replace(/(^|[\s(])#(\d+)\b/g, (match, prefix, number) => {
|
|
43
|
-
return `${prefix}${formatTerminalLink(`#${number}`, `${repoBaseUrl}/pull/${number}`)}`;
|
|
44
|
-
})
|
|
45
|
-
.replace(/(^|[\s(])([0-9a-f]{7,40})\b/gi, (match, prefix, hash) => {
|
|
46
|
-
return `${prefix}${formatTerminalLink(hash, `${repoBaseUrl}/commit/${hash}`)}`;
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
function linkifyMarkdownText(text, repositoryUrl) {
|
|
50
|
-
return linkifyRepositoryReferences(linkifyContributorMentions(text), repositoryUrl);
|
|
51
|
-
}
|
|
52
|
-
function pushWrappedLines(lines, text, width, firstPrefix, restPrefix = firstPrefix, style) {
|
|
53
|
-
const firstWidth = Math.max(1, width - (0, utils_1.getVisualLength)(firstPrefix));
|
|
54
|
-
const restWidth = Math.max(1, width - (0, utils_1.getVisualLength)(restPrefix));
|
|
55
|
-
const segments = (0, utils_1.wrapPlainText)(text, firstWidth);
|
|
56
|
-
if (segments.length === 0) {
|
|
57
|
-
lines.push(firstPrefix.trimEnd());
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
lines.push(firstPrefix + (style ? style(segments[0]) : segments[0]));
|
|
61
|
-
for (let i = 1; i < segments.length; i++) {
|
|
62
|
-
const wrappedSegments = (0, utils_1.wrapPlainText)(segments[i], restWidth);
|
|
63
|
-
for (const segment of wrappedSegments) {
|
|
64
|
-
lines.push(restPrefix + (style ? style(segment) : segment));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
function isLowSignalTrailerLine(text) {
|
|
69
|
-
const normalized = text.toLowerCase();
|
|
70
|
-
return (normalized.startsWith('compare') ||
|
|
71
|
-
normalized.startsWith('full changelog') ||
|
|
72
|
-
normalized.startsWith('see full changelog') ||
|
|
73
|
-
normalized.startsWith('release notes') ||
|
|
74
|
-
normalized.includes('/compare/') ||
|
|
75
|
-
/^\w+: https?:\/\//.test(normalized));
|
|
76
|
-
}
|
|
77
|
-
function formatReleaseNotesMarkdown(markdown, width, repositoryUrl) {
|
|
78
|
-
const lines = [];
|
|
79
|
-
const rawLines = markdown.split('\n');
|
|
80
|
-
let prevBlank = false;
|
|
81
|
-
for (const rawLine of rawLines) {
|
|
82
|
-
const trimmed = rawLine.trim();
|
|
83
|
-
if (trimmed === '') {
|
|
84
|
-
if (!prevBlank && lines.length > 0) {
|
|
85
|
-
lines.push('');
|
|
86
|
-
prevBlank = true;
|
|
87
|
-
}
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
prevBlank = false;
|
|
91
|
-
if (/^```/.test(trimmed) || /^---+$/.test(trimmed)) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const cleaned = sanitizeMarkdownText(trimmed);
|
|
95
|
-
if (!cleaned) {
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
const quoteMatch = trimmed.match(/^>\s*(.+)/);
|
|
99
|
-
if (quoteMatch) {
|
|
100
|
-
const quoteBody = linkifyMarkdownText(sanitizeMarkdownText(quoteMatch[1]), repositoryUrl);
|
|
101
|
-
const admonitionMatch = quoteBody.match(/^\[!([A-Z]+)\]$/i);
|
|
102
|
-
if (admonitionMatch) {
|
|
103
|
-
const label = `${admonitionMatch[1][0]}${admonitionMatch[1].slice(1).toLowerCase()}`;
|
|
104
|
-
if (lines.length > 0 && lines[lines.length - 1] !== '') {
|
|
105
|
-
lines.push('');
|
|
106
|
-
}
|
|
107
|
-
lines.push(chalk_1.default.blue.bold(` ${label}`));
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
pushWrappedLines(lines, quoteBody, width, ' ', ' ', chalk_1.default.gray);
|
|
111
|
-
}
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
const headerMatch = cleaned.match(/^(#{1,6})\s+(.+)/);
|
|
115
|
-
if (headerMatch) {
|
|
116
|
-
const title = sanitizeMarkdownText(headerMatch[2]);
|
|
117
|
-
const lower = title.toLowerCase();
|
|
118
|
-
let style = chalk_1.default.white.bold;
|
|
119
|
-
if (lower.includes('breaking')) {
|
|
120
|
-
style = chalk_1.default.red.bold;
|
|
121
|
-
}
|
|
122
|
-
else if (lower.includes('feature') ||
|
|
123
|
-
lower.includes('added') ||
|
|
124
|
-
lower.includes('improvement')) {
|
|
125
|
-
style = chalk_1.default.green.bold;
|
|
126
|
-
}
|
|
127
|
-
else if (lower.includes('fix') || lower.includes('bug')) {
|
|
128
|
-
style = chalk_1.default.yellow.bold;
|
|
129
|
-
}
|
|
130
|
-
else if (lower.includes('deprecat')) {
|
|
131
|
-
style = chalk_1.default.magenta.bold;
|
|
132
|
-
}
|
|
133
|
-
if (lines.length > 0 && lines[lines.length - 1] !== '') {
|
|
134
|
-
lines.push('');
|
|
135
|
-
}
|
|
136
|
-
lines.push(style(` ${title}`));
|
|
137
|
-
continue;
|
|
138
|
-
}
|
|
139
|
-
const bulletMatch = cleaned.match(/^(\s*)[*-]\s+(.+)/);
|
|
140
|
-
if (bulletMatch) {
|
|
141
|
-
const indentLevel = Math.min(2, Math.floor(bulletMatch[1].length / 2));
|
|
142
|
-
const prefix = ` ${' '.repeat(indentLevel)}${chalk_1.default.gray('•')} `;
|
|
143
|
-
const restPrefix = ` ${' '.repeat(indentLevel + 1)}`;
|
|
144
|
-
const style = /breaking/i.test(bulletMatch[2]) ? chalk_1.default.red : undefined;
|
|
145
|
-
pushWrappedLines(lines, linkifyMarkdownText(sanitizeMarkdownText(bulletMatch[2]), repositoryUrl), width, prefix, restPrefix, style);
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
const orderedMatch = cleaned.match(/^(\s*)(\d+)\.\s+(.+)/);
|
|
149
|
-
if (orderedMatch) {
|
|
150
|
-
const indentLevel = Math.min(2, Math.floor(orderedMatch[1].length / 2));
|
|
151
|
-
const marker = `${orderedMatch[2]}.`;
|
|
152
|
-
const prefix = ` ${' '.repeat(indentLevel)}${marker} `;
|
|
153
|
-
const restPrefix = ` ${' '.repeat(indentLevel)}${' '.repeat(marker.length + 1)}`;
|
|
154
|
-
pushWrappedLines(lines, linkifyMarkdownText(sanitizeMarkdownText(orderedMatch[3]), repositoryUrl), width, prefix, restPrefix);
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
const style = isLowSignalTrailerLine(cleaned) ? chalk_1.default.gray : undefined;
|
|
158
|
-
pushWrappedLines(lines, linkifyMarkdownText(cleaned, repositoryUrl), width, ' ', ' ', style);
|
|
159
|
-
}
|
|
160
|
-
while (lines.length > 0 && lines[lines.length - 1] === '') {
|
|
161
|
-
lines.pop();
|
|
162
|
-
}
|
|
163
|
-
return lines;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Build release notes sections for the currently viewed version.
|
|
167
|
-
* Shows one version at a time with navigation indicators.
|
|
168
|
-
*/
|
|
169
|
-
function buildReleaseNotesSections(state, modalWidth) {
|
|
170
|
-
const sections = [];
|
|
171
|
-
if (!state.releaseNotesVersions || state.releaseNotesVersions.length === 0) {
|
|
172
|
-
return sections;
|
|
173
|
-
}
|
|
174
|
-
const loaded = state.releaseNotesLoaded;
|
|
175
|
-
const viewIndex = state.releaseNotesViewIndex ?? 0;
|
|
176
|
-
const totalVersions = state.releaseNotesVersions.length;
|
|
177
|
-
const currentVersion = state.releaseNotesVersions[viewIndex];
|
|
178
|
-
if (!currentVersion)
|
|
179
|
-
return sections;
|
|
180
|
-
// Show loading state for the viewed version
|
|
181
|
-
if (state.releaseNotesLoadingVersion === currentVersion) {
|
|
182
|
-
sections.push({
|
|
183
|
-
key: 'release-loading',
|
|
184
|
-
rows: [chalk_1.default.gray(`Loading release notes for v${currentVersion}...`)],
|
|
185
|
-
behavior: 'status',
|
|
186
|
-
});
|
|
187
|
-
return sections;
|
|
188
|
-
}
|
|
189
|
-
// Version not yet loaded (and not currently loading)
|
|
190
|
-
if (!loaded || !loaded.has(currentVersion)) {
|
|
191
|
-
sections.push({
|
|
192
|
-
key: 'release-pending',
|
|
193
|
-
rows: [chalk_1.default.gray(`Press ←/→ to load release notes for v${currentVersion}`)],
|
|
194
|
-
behavior: 'status',
|
|
195
|
-
});
|
|
196
|
-
return sections;
|
|
197
|
-
}
|
|
198
|
-
const content = loaded.get(currentVersion);
|
|
199
|
-
if (!content) {
|
|
200
|
-
// Version was loaded but had no release notes
|
|
201
|
-
sections.push({
|
|
202
|
-
key: 'release-none',
|
|
203
|
-
rows: [chalk_1.default.gray.italic(`No release notes found for v${currentVersion}`)],
|
|
204
|
-
behavior: 'status',
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
else {
|
|
208
|
-
const versionHeader = (0, themes_colors_1.getThemeColor)('primary')(`Version ${currentVersion}`);
|
|
209
|
-
const navHint = totalVersions > 1 ? chalk_1.default.gray(` (${viewIndex + 1}/${totalVersions})`) : '';
|
|
210
|
-
const rows = [chalk_1.default.bold(versionHeader) + navHint];
|
|
211
|
-
const formatted = formatReleaseNotesMarkdown(content, modalWidth - 4, state.repository);
|
|
212
|
-
rows.push(...formatted);
|
|
213
|
-
sections.push({
|
|
214
|
-
key: `release-${currentVersion}`,
|
|
215
|
-
rows,
|
|
216
|
-
behavior: 'body',
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
// Navigation hints
|
|
220
|
-
const canGoNewer = viewIndex > 0;
|
|
221
|
-
const canGoOlder = viewIndex < totalVersions - 1;
|
|
222
|
-
if (canGoNewer || canGoOlder) {
|
|
223
|
-
const hints = [];
|
|
224
|
-
if (canGoNewer)
|
|
225
|
-
hints.push('← newer');
|
|
226
|
-
if (canGoOlder)
|
|
227
|
-
hints.push('→ older');
|
|
228
|
-
sections.push({
|
|
229
|
-
key: 'release-nav',
|
|
230
|
-
rows: [chalk_1.default.gray(hints.join(' · '))],
|
|
231
|
-
behavior: 'status',
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
return sections;
|
|
235
|
-
}
|
|
236
|
-
function formatNumber(num) {
|
|
237
|
-
if (!num)
|
|
238
|
-
return 'N/A';
|
|
239
|
-
if (num >= 1000000)
|
|
240
|
-
return (num / 1000000).toFixed(1) + 'M';
|
|
241
|
-
if (num >= 1000)
|
|
242
|
-
return (num / 1000).toFixed(1) + 'K';
|
|
243
|
-
return num.toString();
|
|
244
|
-
}
|
|
245
|
-
function getUsedByPaths(state) {
|
|
246
|
-
return state.packageJsonPaths ?? [state.packageJsonPath];
|
|
247
|
-
}
|
|
248
|
-
function buildTabBarSuffix(activeTab, usedByCount) {
|
|
249
|
-
const styleFor = (tab) => tab === activeTab ? chalk_1.default.bold.underline : chalk_1.default.gray;
|
|
250
|
-
const usedByLabel = `Used by${usedByCount > 0 ? ` (${usedByCount})` : ''}`;
|
|
251
|
-
return (' ' +
|
|
252
|
-
chalk_1.default.gray('[ ') +
|
|
253
|
-
styleFor('info')('Info') +
|
|
254
|
-
chalk_1.default.gray(' │ ') +
|
|
255
|
-
styleFor('usedBy')(usedByLabel) +
|
|
256
|
-
chalk_1.default.gray(' ]'));
|
|
257
|
-
}
|
|
258
|
-
function buildUsedBySections(state, modalWidth) {
|
|
259
|
-
const paths = getUsedByPaths(state);
|
|
260
|
-
const cwd = process.cwd();
|
|
261
|
-
const contentWidth = Math.max(10, modalWidth - 4);
|
|
262
|
-
const formatRelative = (absolutePath) => {
|
|
263
|
-
const display = node_path_1.default.relative(cwd, absolutePath) || absolutePath;
|
|
264
|
-
return (0, utils_1.truncatePlainText)(display, contentWidth);
|
|
265
|
-
};
|
|
266
|
-
return [
|
|
267
|
-
{
|
|
268
|
-
key: 'used-by-summary',
|
|
269
|
-
rows: [
|
|
270
|
-
chalk_1.default.bold(`${paths.length} package.json file${paths.length === 1 ? '' : 's'} depend on ${state.name}`),
|
|
271
|
-
chalk_1.default.gray(`Type: ${state.type}`),
|
|
272
|
-
],
|
|
273
|
-
required: true,
|
|
274
|
-
behavior: 'pinned',
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
key: 'used-by-list',
|
|
278
|
-
rows: paths.map((p) => `${chalk_1.default.gray('•')} ${formatRelative(p)}`),
|
|
279
|
-
behavior: 'body',
|
|
280
|
-
},
|
|
281
|
-
];
|
|
282
|
-
}
|
|
283
|
-
function buildPackageInfoSections(state, modalWidth, activeTab) {
|
|
284
|
-
const title = chalk_1.default.bold('Package: ') +
|
|
285
|
-
(0, themes_colors_1.getThemeColor)('packageName')(state.name) +
|
|
286
|
-
buildTabBarSuffix(activeTab, getUsedByPaths(state).length);
|
|
287
|
-
const authorLicense = chalk_1.default.gray(`${state.author || 'Unknown'} • ${state.license || 'MIT'}`);
|
|
288
|
-
const currentVersion = chalk_1.default.yellow(state.currentVersionSpecifier);
|
|
289
|
-
const targetVersion = chalk_1.default.green(state.selectedOption === 'range' ? state.rangeVersion : state.latestVersion);
|
|
290
|
-
const sections = [
|
|
291
|
-
{
|
|
292
|
-
key: 'header',
|
|
293
|
-
rows: [title, authorLicense],
|
|
294
|
-
required: true,
|
|
295
|
-
behavior: 'pinned',
|
|
296
|
-
},
|
|
297
|
-
];
|
|
298
|
-
if (activeTab === 'usedBy') {
|
|
299
|
-
sections.push(...buildUsedBySections(state, modalWidth));
|
|
300
|
-
return sections;
|
|
301
|
-
}
|
|
302
|
-
sections.push({
|
|
303
|
-
key: 'meta',
|
|
304
|
-
rows: [
|
|
305
|
-
`Current: ${currentVersion} Target: ${targetVersion}`,
|
|
306
|
-
...(state.weeklyDownloads !== undefined
|
|
307
|
-
? [(0, themes_colors_1.getThemeColor)('primary')(`Downloads/week: ${formatNumber(state.weeklyDownloads)}`)]
|
|
308
|
-
: []),
|
|
309
|
-
],
|
|
310
|
-
required: true,
|
|
311
|
-
behavior: 'pinned',
|
|
312
|
-
});
|
|
313
|
-
if (state.homepage) {
|
|
314
|
-
sections.push({
|
|
315
|
-
key: 'homepage',
|
|
316
|
-
rows: [
|
|
317
|
-
`Homepage: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.homepage, modalWidth - 14)))}`,
|
|
318
|
-
],
|
|
319
|
-
behavior: 'pinned',
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
if (state.description) {
|
|
323
|
-
sections.push({
|
|
324
|
-
key: 'description',
|
|
325
|
-
rows: (0, utils_1.wrapPlainText)(state.description, modalWidth - 4)
|
|
326
|
-
.slice(0, 4)
|
|
327
|
-
.map((line, index, rows) => index === rows.length - 1 && rows.length === 4
|
|
328
|
-
? (0, utils_1.truncatePlainText)(line, modalWidth - 4)
|
|
329
|
-
: line),
|
|
330
|
-
behavior: 'pinned',
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
if (state.vulnerability && state.vulnerability.count > 0) {
|
|
334
|
-
const representative = (0, vulnerability_1.selectRepresentativeAdvisory)(state.vulnerability);
|
|
335
|
-
const severityColor = (0, vulnerability_1.getVulnerabilitySeverityColor)(state.vulnerability.highestSeverity);
|
|
336
|
-
const vulnerabilityRows = [
|
|
337
|
-
chalk_1.default.red.bold(`${state.vulnerability.count} known vulnerabilit${state.vulnerability.count === 1 ? 'y' : 'ies'} (${severityColor(state.vulnerability.highestSeverity.toUpperCase())})`),
|
|
338
|
-
];
|
|
339
|
-
if (representative) {
|
|
340
|
-
const severityLabel = ` ${severityColor(`[${representative.severity.toUpperCase()}]`)} `;
|
|
341
|
-
const availableTitleWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(severityLabel));
|
|
342
|
-
vulnerabilityRows.push(`${severityLabel}${(0, utils_1.truncatePlainText)(representative.title, availableTitleWidth)}`);
|
|
343
|
-
}
|
|
344
|
-
const detailsUrl = state.vulnerability.detailsUrl || representative?.url;
|
|
345
|
-
if (detailsUrl) {
|
|
346
|
-
const linkPrefix = ` ${(0, vulnerability_1.getVulnerabilityLinkLabel)(detailsUrl)} `;
|
|
347
|
-
const availableLinkWidth = Math.max(0, modalWidth - 4 - (0, utils_1.getVisualLength)(linkPrefix));
|
|
348
|
-
vulnerabilityRows.push(`${linkPrefix}${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(detailsUrl, availableLinkWidth)))}`);
|
|
349
|
-
}
|
|
350
|
-
if (state.vulnerability.count > 1) {
|
|
351
|
-
vulnerabilityRows.push(chalk_1.default.gray(` ... and ${state.vulnerability.count - 1} more`));
|
|
352
|
-
}
|
|
353
|
-
sections.push({
|
|
354
|
-
key: 'vulnerability',
|
|
355
|
-
rows: vulnerabilityRows,
|
|
356
|
-
required: true,
|
|
357
|
-
behavior: 'pinned',
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
if (state.repository) {
|
|
361
|
-
sections.push({
|
|
362
|
-
key: 'changelog',
|
|
363
|
-
rows: [
|
|
364
|
-
`Changelog: ${chalk_1.default.underline((0, themes_colors_1.getThemeColor)('primary')((0, utils_1.truncatePlainText)(state.repository, modalWidth - 15)))}`,
|
|
365
|
-
],
|
|
366
|
-
behavior: 'pinned',
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
const releaseNotesSections = buildReleaseNotesSections(state, modalWidth);
|
|
370
|
-
sections.push(...releaseNotesSections);
|
|
371
|
-
return sections;
|
|
372
|
-
}
|
|
17
|
+
__exportStar(require("./package-info-sections/sections"), exports);
|
|
18
|
+
__exportStar(require("./package-info-sections/release-notes"), exports);
|
|
373
19
|
//# sourceMappingURL=package-info-sections.js.map
|
|
@@ -40,7 +40,7 @@ function renderPackageInfoModal(state, terminalWidth = 80, terminalHeight = 24,
|
|
|
40
40
|
return {
|
|
41
41
|
lines: (0, layout_1.renderModalFrame)(compactSections, {
|
|
42
42
|
terminalWidth,
|
|
43
|
-
terminalHeight,
|
|
43
|
+
terminalHeight: Math.max(8, terminalHeight - 4),
|
|
44
44
|
minWidth: 60,
|
|
45
45
|
maxWidth: 120,
|
|
46
46
|
}),
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getHealthBadge = getHealthBadge;
|
|
4
|
+
const utils_1 = require("../../utils");
|
|
5
|
+
const themes_colors_1 = require("../themes-colors");
|
|
6
|
+
/**
|
|
7
|
+
* A compact badge flagging a package's health in the list:
|
|
8
|
+
* - `[DEPR]` when the latest version is deprecated (highest priority), or
|
|
9
|
+
* - `[ENG]` when its `engines.node` is incompatible with the running Node.
|
|
10
|
+
*
|
|
11
|
+
* Returns an empty string when neither applies. Deprecation wins because an
|
|
12
|
+
* engines mismatch on an abandoned package is moot. Both render in the theme's
|
|
13
|
+
* (amber) warning color — a caution signal, not an alarm.
|
|
14
|
+
*/
|
|
15
|
+
function getHealthBadge(state) {
|
|
16
|
+
if (state.deprecated) {
|
|
17
|
+
return (0, themes_colors_1.getThemeColor)('warning')('[DEPR]');
|
|
18
|
+
}
|
|
19
|
+
if ((0, utils_1.checkNodeEngineCompatibility)(state.enginesNode)) {
|
|
20
|
+
return (0, themes_colors_1.getThemeColor)('warning')('[ENG]');
|
|
21
|
+
}
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=health.js.map
|