generated from salesforcecli/plugin-template
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdisplay.ts
139 lines (109 loc) · 4.7 KB
/
display.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
* Copyright (c) 2021, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { EOL } from 'node:os';
import { marked } from 'marked';
import TerminalRenderer from 'marked-terminal';
import { Env } from '@salesforce/kit';
import { Flags, SfCommand, loglevel } from '@salesforce/sf-plugins-core';
import { Lifecycle, Logger, Messages } from '@salesforce/core';
import type { AnyJson, JsonMap } from '@salesforce/ts-types';
import shared from '../../../shared/index.js';
// Initialize Messages with the current plugin directory
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const helpers = ['stable', 'stable-rc', 'latest', 'latest-rc', 'rc'];
// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core,
// or any library that is using the messages framework can also be loaded this way.
const messages = Messages.loadMessages('@salesforce/plugin-info', 'display');
export default class Display extends SfCommand<DisplayOutput | undefined> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly aliases = ['whatsnew'];
public static readonly examples = messages.getMessages('examples', [helpers.join(', ')]);
public static readonly flags = {
version: Flags.string({
char: 'v',
summary: messages.getMessage('flags.version.summary'),
}),
hook: Flags.boolean({
hidden: true,
summary: messages.getMessage('flags.hook.summary'),
}),
loglevel,
};
public async run(): Promise<DisplayOutput | undefined> {
const HIDE_NOTES = this.config.bin === 'sf' ? 'SF_HIDE_RELEASE_NOTES' : 'SFDX_HIDE_RELEASE_NOTES';
const HIDE_FOOTER = this.config.bin === 'sf' ? 'SF_HIDE_RELEASE_NOTES_FOOTER' : 'SFDX_HIDE_RELEASE_NOTES_FOOTER';
const logger = Logger.childFromRoot(this.constructor.name);
const { flags } = await this.parse(Display);
const env = new Env();
if (env.getBoolean(HIDE_NOTES) && flags.hook) {
// We don't ever want to exit the process for info:releasenotes:display (whatsnew)
// In most cases we will log a message, but here we only trace log in case someone using stdout of the update command
logger.trace(`release notes disabled via env var: ${HIDE_NOTES}`);
logger.trace('exiting');
await Lifecycle.getInstance().emitTelemetry({ eventName: 'NOTES_HIDDEN' });
return;
}
try {
const installedVersion = this.config.pjson.version;
const infoConfig = await shared.getInfoConfig(this.config.root);
const { distTagUrl, releaseNotesPath, releaseNotesFilename } = infoConfig.releasenotes;
let version = flags.version ?? installedVersion;
if (helpers.includes(version)) {
version = await shared.getDistTagVersion(distTagUrl, version);
}
const releaseNotes = await shared.getReleaseNotes(releaseNotesPath, releaseNotesFilename, version);
const tokens = shared.parseReleaseNotes(releaseNotes, version, releaseNotesPath);
marked.setOptions({
renderer: new TerminalRenderer({ emoji: false }),
});
tokens.unshift(marked.lexer(`# Release notes for '${this.config.bin}':`)[0]);
if (flags.json) {
const body = tokens.map((token) => token.raw).join(EOL);
return { body, url: releaseNotesPath };
} else {
this.log(marked.parser(tokens));
}
if (flags.hook) {
if (env.getBoolean(HIDE_FOOTER)) {
await Lifecycle.getInstance().emitTelemetry({ eventName: 'FOOTER_HIDDEN' });
} else {
const footer = messages.getMessage('footer', [this.config.bin, releaseNotesPath, HIDE_NOTES, HIDE_FOOTER]);
this.log(marked.parse(footer));
}
}
} catch (err) {
if (flags.hook) {
// Do not throw error if --hook is passed, just warn so we don't exit any processes.
// --hook is passed in the post install/update scripts
const { message, stack, name } = err as Error;
this.warn(`${this.id ?? '<no command id>'} failed: ${message}`);
logger.trace(stack);
await Lifecycle.getInstance().emitTelemetry({
eventName: 'COMMAND_ERROR',
type: 'EXCEPTION',
errorName: name,
errorMessage: message,
Error: Object.assign(
{
name,
message,
stack,
} as JsonMap,
err
) as AnyJson,
});
return;
}
throw err;
}
}
}
export type DisplayOutput = {
body: string;
url: string;
};