@keymanapp/kmc
Version:
Keyman Developer compiler command line tools
166 lines • 6.56 kB
JavaScript
import { CompilerError, CompilerErrorSeverity } from '@keymanapp/developer-utils';
import { InfrastructureMessages } from '../messages/infrastructureMessages.js';
import { messageNamespaceKeys, messageSources } from '../messages/messageNamespaces.js';
;
/**
* converts an --message command line parameter `<id>[:level]` and verifies that
* it is a valid message, returning `null` if it is not a valid id or level. If
* level is omitted, the message is disabled. Valid levels are Disable, Info,
* Hint, Warn or Error (can shorten or first character of each).
*
* Will report details of any errors to callbacks.reportMessage
* @param message command line parameter in format '["KM"]<id>[":"level]'
* @param callbacks
* @returns CompilerMessageOverride map with the new severity level for the
* message; or `null` if parameter is invalid.
*/
function commanderOptionsMessageToCompilerOptionsMessage(message, callbacks) {
const pattern = /^(KM)?([0-9a-f]+)(:(D|Disable|I|Info|H|Hint|W|Warn|E|Error))?$/i;
const result = message.match(pattern);
if (!result) {
callbacks.reportMessage(InfrastructureMessages.Error_InvalidMessageFormat({ message }));
return null;
}
const code = Number.parseInt(result[2], 16);
const inputLevel = (result[4] ?? '').toLowerCase();
const level = (inputLevel == '' || 'disable'.startsWith(inputLevel)) ? 'disable' :
CompilerError.severityNameToValue(inputLevel);
const override = { code, level };
if (!checkMessageOverride(override, callbacks)) {
return null;
}
return override;
}
function commanderOptionsMessagesToCompilerOptionsMessages(messages, callbacks) {
if (!messages || !Array.isArray(messages)) {
return {};
}
const result = {};
for (let message of messages) {
const override = commanderOptionsMessageToCompilerOptionsMessage(message, callbacks);
if (!override) {
return null;
}
result[override.code] = override.level;
}
return result;
}
export function findMessageDetails(code, callbacks) {
const namespace = CompilerError.namespace(code);
if (!messageNamespaceKeys.includes(namespace)) {
callbacks.reportMessage(InfrastructureMessages.Error_MessageNamespaceNotFound({ code }));
return null;
}
const source = messageSources[namespace];
if (!source) {
throw new Error(`Unexpected missing namespace for code ${code.toString(16)}`);
}
// For the given namespace object, iterate through the members to find the matching value
const keys = Object.keys(source.class);
const m = source.class;
const id = keys.find(key => typeof m[key] == 'number' && CompilerError.error(m[key]) === code);
if (!id) {
callbacks.reportMessage(InfrastructureMessages.Error_MessageCodeNotFound({ code }));
return null;
}
return { code: m[id], id, module: source.module, class: source.class };
}
export const getMessageIdentifiersSorted = (cls) => Object.keys(cls)
.filter(id => typeof cls[id] == 'number')
.sort((a, b) => CompilerError.error(cls[a]) - CompilerError.error(cls[b]));
/**
* Gets an array of compiler messages matching the search identifier. Substrings
* are supported for the id portion of the searchId
* @param searchNamespace optional namespace to search in, if omitted, searches
* all namespaces
* @param searchId a substring to match with optional namespace prefix, e.g.
* "INFO_"
*/
export function findMessagesById(searchNamespace, searchId) {
searchId = searchId.toLowerCase();
const messages = [];
messageNamespaceKeys.forEach((namespace) => {
if (searchNamespace && searchNamespace != namespace) {
return;
}
const ms = messageSources[namespace];
const ids = getMessageIdentifiersSorted(ms.class);
for (const id of ids) {
const code = ms.class[id];
const lid = id.toLowerCase();
if (typeof code != 'number') {
continue;
}
if (lid.includes(searchId)) {
messages.push({
code,
id,
class: ms.class,
module: ms.module
});
}
}
});
return messages;
}
/**
* Verifies that a given message is valid and that the severity is allowed to be
* modified (Info, Hint and Warn only -- Error and Fatal cannot be modified)
* @param override
* @param callbacks
* @returns true if the message can be overridden
*/
function checkMessageOverride(override, callbacks) {
const details = findMessageDetails(override.code, callbacks);
if (!details) {
return false;
}
const validSeverityMasks = [CompilerErrorSeverity.Info, CompilerErrorSeverity.Hint, CompilerErrorSeverity.Warn];
if (!validSeverityMasks.includes(CompilerError.severity(details.code))) {
callbacks.reportMessage(InfrastructureMessages.Error_MessageCannotBeCoerced({ code: override.code }));
return false;
}
return true;
}
export function commanderOptionsToBaseOptions(options) {
return {
// CompilerBaseOptions
logLevel: options.logLevel,
logFormat: options.logFormat,
color: options.color,
};
}
/**
* Maps command line compiler options to the compiler API options.
* @param options
* @param callbacks
* @returns
*/
export function commanderOptionsToCompilerOptions(options, callbacks) {
const overrides = commanderOptionsMessagesToCompilerOptionsMessages(options.message, callbacks);
if (!overrides) {
return null;
}
// We don't want to rename command line options to match the precise
// properties that we have in CompilerOptions, but nor do we want to rename
// CompilerOptions properties...
return {
...commanderOptionsToBaseOptions(options),
// CompilerOptions
shouldAddCompilerVersion: options.compilerVersion,
saveDebug: options.debug,
compilerWarningsAsErrors: options.compilerWarningsAsErrors,
warnDeprecatedCode: options.warnDeprecatedCode,
// ExtendedOptions
forPublishing: options.forPublishing,
messageOverrides: overrides,
};
}
/**
* these are exported only for unit tests, do not use
*/
export const unitTestEndpoints = {
checkMessageOverride,
commanderOptionsMessageToCompilerOptionsMessage
};
//# sourceMappingURL=extendedCompilerOptions.js.map