mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-07 01:13:08 +02:00
#788 - Safe setting update
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
- [#441](https://github.com/estruyf/vscode-front-matter/issues/441): Show input descriptions for snippet and data forms
|
||||
- [#442](https://github.com/estruyf/vscode-front-matter/issues/442): Hide sidebar on data view when data file is selecte + show dropdown of data files
|
||||
- [#788](https://github.com/estruyf/vscode-front-matter/issues/788): Show a warning on setting update when it exists in an extended configuration
|
||||
- [#798](https://github.com/estruyf/vscode-front-matter/issues/798): Changed dialog to slide-over for the snippet forms
|
||||
- [#799](https://github.com/estruyf/vscode-front-matter/issues/799): Added `frontMatter.logging` setting to define the logging output. Options are `info`, `warn`, `error`, and `verbose`. Default is `info`.
|
||||
- [#800](https://github.com/estruyf/vscode-front-matter/issues/800): Add colors for the Front Matter CMS output
|
||||
|
||||
@@ -467,9 +467,8 @@ export class Folders {
|
||||
path: Folders.relWsFolder(folder, wsFolder)
|
||||
};
|
||||
|
||||
if (detail['$schema'] || detail.extended) {
|
||||
return null;
|
||||
}
|
||||
delete detail['$schema'];
|
||||
delete detail.extended;
|
||||
|
||||
if (detail.locale && detail.locale === detail.defaultLocale) {
|
||||
// Check if the folder was on the original list
|
||||
@@ -490,7 +489,7 @@ export class Folders {
|
||||
})
|
||||
.filter((folder) => folder !== null);
|
||||
|
||||
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folderDetails, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_PAGE_FOLDERS, folderDetails, true);
|
||||
|
||||
// Reinitialize the folder listeners
|
||||
PagesListener.startWatchers();
|
||||
|
||||
@@ -82,7 +82,7 @@ categories: []
|
||||
await Settings.createTeamSettings();
|
||||
|
||||
// Add the default content type
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, [DEFAULT_CONTENT_TYPE], true);
|
||||
|
||||
if (sampleTemplate !== undefined) {
|
||||
await Project.createSampleTemplate();
|
||||
|
||||
@@ -303,7 +303,7 @@ export class ContentType {
|
||||
contentTypes.push(newContentType);
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
const configPath = await Settings.projectConfigPath();
|
||||
const notificationAction = await Notifications.info(
|
||||
@@ -350,7 +350,7 @@ export class ContentType {
|
||||
const index = contentTypes.findIndex((ct) => ct.name === contentType.name);
|
||||
contentTypes[index].fields = updatedFields;
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
const configPath = await Settings.projectConfigPath();
|
||||
const notificationAction = await Notifications.info(
|
||||
@@ -804,7 +804,7 @@ export class ContentType {
|
||||
fields: blockFields
|
||||
};
|
||||
fieldGroups.push(newFieldGroup);
|
||||
await Settings.update(SETTING_TAXONOMY_FIELD_GROUPS, fieldGroups, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_FIELD_GROUPS, fieldGroups, true);
|
||||
|
||||
fields.push({
|
||||
title: field,
|
||||
|
||||
@@ -236,7 +236,7 @@ export class Extension {
|
||||
}
|
||||
);
|
||||
|
||||
await Settings.update(
|
||||
await Settings.safeUpdate(
|
||||
SETTING_TEMPLATES_ENABLED,
|
||||
answer?.toLocaleLowerCase() === l10n.t(LocalizationKey.commonYes),
|
||||
true
|
||||
@@ -266,7 +266,7 @@ export class Extension {
|
||||
|
||||
return f;
|
||||
});
|
||||
await Settings.update(SETTING_CONTENT_PAGE_FOLDERS, folders, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_PAGE_FOLDERS, folders, true);
|
||||
}
|
||||
|
||||
// The tags and categories settings need to be moved to the database
|
||||
|
||||
@@ -244,7 +244,7 @@ export class FrameworkDetector {
|
||||
});
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, assetFoler, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, assetFoler, true);
|
||||
} catch (e) {
|
||||
Logger.error(
|
||||
`Something failed while processing your Hexo configuration. ${(e as Error).message}`
|
||||
|
||||
@@ -4,7 +4,7 @@ import { format } from 'date-fns';
|
||||
import { COMMAND_NAME, SETTING_LOGGING } from '../constants';
|
||||
import { Settings } from '.';
|
||||
|
||||
export type LoggerLocation = 'VSCODE' | 'DASHBOARD' | 'PANEL';
|
||||
export type LoggerLocation = 'VSCODE' | 'DASHBOARD' | 'PANEL' | 'SETTING';
|
||||
|
||||
export class Logger {
|
||||
private static instance: Logger;
|
||||
|
||||
@@ -43,6 +43,30 @@ export class Notifications {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a warning notification to the user with a link to the output channel
|
||||
* @param message
|
||||
* @param items
|
||||
* @returns
|
||||
*/
|
||||
public static warningWithOutput(message: string, ...items: any): Thenable<string | undefined> {
|
||||
Logger.info(`${EXTENSION_NAME}: ${message}`, 'VSCODE', 'WARNING');
|
||||
|
||||
if (this.shouldShow('WARNING')) {
|
||||
return window.showWarningMessage(
|
||||
`${EXTENSION_NAME}: ${message} ${l10n.t(
|
||||
LocalizationKey.notificationsOutputChannelDescription,
|
||||
`[${l10n.t(LocalizationKey.notificationsOutputChannelLink)}](command:${
|
||||
COMMAND_NAME.showOutputChannel
|
||||
})`
|
||||
)}`,
|
||||
...items
|
||||
);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show an error notification to the user
|
||||
* @param message
|
||||
|
||||
@@ -77,6 +77,8 @@ export class Settings {
|
||||
private static readConfigPromise: Promise<void> | undefined = undefined;
|
||||
private static project: Project | undefined = undefined;
|
||||
private static configDebouncer = debounceCallback();
|
||||
private static hasExtendedConfig: boolean = false;
|
||||
private static extendedConfig: { extended: any[]; splitted: any[]; dynamic: boolean } = {} as any;
|
||||
|
||||
public static async registerCommands() {
|
||||
const ext = Extension.getInstance();
|
||||
@@ -374,12 +376,54 @@ export class Settings {
|
||||
return setting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely updates a setting value.
|
||||
*
|
||||
* @param name - The name of the setting.
|
||||
* @param value - The new value for the setting.
|
||||
* @param updateGlobal - Indicates whether to update the global setting or not. Default is `false`.
|
||||
* @returns A promise that resolves when the setting is updated.
|
||||
*/
|
||||
public static async safeUpdate<T>(
|
||||
name: string,
|
||||
value: T,
|
||||
updateGlobal: boolean = false
|
||||
): Promise<void> {
|
||||
if (Settings.hasExtendedConfig) {
|
||||
const configKey = `${CONFIG_KEY}.${name}`;
|
||||
|
||||
if (
|
||||
Settings.extendedConfig.extended.includes(configKey) ||
|
||||
Settings.extendedConfig.splitted.includes(configKey) ||
|
||||
Settings.extendedConfig.dynamic
|
||||
) {
|
||||
Notifications.warningWithOutput(
|
||||
`Cannot update setting "${configKey}" because you've extended or split the Front Matter CMS configuration. Please manually add your changes. Check the output for the setting update.`
|
||||
);
|
||||
|
||||
Logger.info(`Updating setting: ${configKey}`, 'SETTING');
|
||||
Logger.info(
|
||||
`New value:
|
||||
${JSON.stringify(value, null, 2)}`,
|
||||
'SETTING'
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await Settings.update<T>(name, value, updateGlobal);
|
||||
}
|
||||
|
||||
/**
|
||||
* String update config setting
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
public static async update<T>(name: string, value: T, updateGlobal: boolean = false) {
|
||||
public static async update<T>(
|
||||
name: string,
|
||||
value: T,
|
||||
updateGlobal: boolean = false
|
||||
): Promise<void> {
|
||||
const fmConfig = await Settings.projectConfigPath();
|
||||
|
||||
if (updateGlobal) {
|
||||
@@ -423,7 +467,7 @@ export class Settings {
|
||||
|
||||
const workspaceSettingValue = Settings.hasWorkspaceSettings<ContentType[]>(name);
|
||||
if (workspaceSettingValue) {
|
||||
await Settings.update(name, undefined);
|
||||
await Settings.safeUpdate(name, undefined);
|
||||
}
|
||||
|
||||
// Make sure to reload the whole config + all the data files
|
||||
@@ -515,7 +559,7 @@ export class Settings {
|
||||
customTaxonomies[taxIdx].options.push(option);
|
||||
customTaxonomies[taxIdx].options = [...new Set(customTaxonomies[taxIdx].options)];
|
||||
customTaxonomies[taxIdx].options = customTaxonomies[taxIdx].options.sort().filter((o) => !!o);
|
||||
await Settings.update(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,7 +576,7 @@ export class Settings {
|
||||
customTaxonomies[taxIdx].options = options;
|
||||
}
|
||||
|
||||
await Settings.update(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CUSTOM, customTaxonomies, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -548,8 +592,8 @@ export class Settings {
|
||||
const setting = Settings.config.inspect(settingName);
|
||||
|
||||
if (setting && typeof setting.workspaceValue !== 'undefined') {
|
||||
await Settings.update(settingName, setting.workspaceValue, true);
|
||||
await Settings.update(settingName, undefined);
|
||||
await Settings.safeUpdate(settingName, setting.workspaceValue, true);
|
||||
await Settings.safeUpdate(settingName, undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -652,6 +696,13 @@ export class Settings {
|
||||
*/
|
||||
private static async readConfig() {
|
||||
try {
|
||||
Settings.hasExtendedConfig = false;
|
||||
Settings.extendedConfig = {
|
||||
extended: [],
|
||||
splitted: [],
|
||||
dynamic: false
|
||||
};
|
||||
|
||||
const fmConfig = await Settings.projectConfigPath();
|
||||
if (fmConfig && (await existsAsync(fmConfig))) {
|
||||
const localConfig = await readFileAsync(fmConfig, 'utf8');
|
||||
@@ -671,6 +722,8 @@ export class Settings {
|
||||
);
|
||||
if (configFiles.length === 0) {
|
||||
Logger.info(`No ".frontmatter/config" config files found.`);
|
||||
} else {
|
||||
Settings.hasExtendedConfig = true;
|
||||
}
|
||||
|
||||
// Sort the files by fsPath
|
||||
@@ -707,6 +760,8 @@ export class Settings {
|
||||
);
|
||||
|
||||
if (dynamicConfig) {
|
||||
Settings.hasExtendedConfig = true;
|
||||
Settings.extendedConfig.dynamic = true;
|
||||
Settings.globalConfig = dynamicConfig;
|
||||
Logger.info(`Dynamic config file loaded`);
|
||||
}
|
||||
@@ -738,11 +793,17 @@ export class Settings {
|
||||
return;
|
||||
}
|
||||
|
||||
Settings.hasExtendedConfig = true;
|
||||
const originalConfig = Object.assign({}, Settings.globalConfig);
|
||||
const extendsConfig: string[] = Settings.globalConfig[extendsConfigName];
|
||||
for (const externalConfig of extendsConfig) {
|
||||
if (externalConfig.endsWith(`.json`)) {
|
||||
const config = await Settings.getExternalConfig(externalConfig);
|
||||
|
||||
// Store the config keys
|
||||
const keys = Object.keys(config);
|
||||
Settings.extendedConfig.extended.push(...keys);
|
||||
|
||||
await Settings.extendConfig(config, originalConfig);
|
||||
}
|
||||
}
|
||||
@@ -777,6 +838,7 @@ export class Settings {
|
||||
Settings.globalConfig = {};
|
||||
}
|
||||
|
||||
Settings.extendedConfig.splitted.push(`${CONFIG_KEY}.${relSettingName}`);
|
||||
Settings.updateGlobalConfigSetting(relSettingName, configJson, configFilePath, filePath);
|
||||
} catch (e) {
|
||||
Logger.error(`Error reading config file: ${configFile.fsPath}`);
|
||||
|
||||
@@ -101,7 +101,7 @@ export class SettingsListener extends BaseListener {
|
||||
if (typeof setting.name !== 'undefined' && typeof setting.value !== 'undefined') {
|
||||
const value = Settings.get(setting.name);
|
||||
if (value !== setting.value) {
|
||||
await Settings.update(setting.name, setting.value, true);
|
||||
await Settings.safeUpdate(setting.name, setting.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ export class SettingsListener extends BaseListener {
|
||||
*/
|
||||
private static async update(data: { name: string; value: any; global?: boolean }) {
|
||||
if (data.name) {
|
||||
await Settings.update(data.name, data.value, data.global);
|
||||
await Settings.safeUpdate(data.name, data.value, data.global);
|
||||
this.getSettings(true);
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,7 @@ export class SettingsListener extends BaseListener {
|
||||
* @param frameworkId
|
||||
*/
|
||||
public static async setFramework(frameworkId: string | null) {
|
||||
await Settings.update(SETTING_FRAMEWORK_ID, frameworkId, true);
|
||||
await Settings.safeUpdate(SETTING_FRAMEWORK_ID, frameworkId, true);
|
||||
|
||||
if (frameworkId) {
|
||||
const allFrameworks = FrameworkDetector.getAll();
|
||||
@@ -222,16 +222,16 @@ export class SettingsListener extends BaseListener {
|
||||
);
|
||||
if (framework) {
|
||||
if (framework.static && typeof framework.static === 'string') {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, framework.static, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, framework.static, true);
|
||||
}
|
||||
|
||||
if (framework.server) {
|
||||
await Settings.update(SETTING_PREVIEW_HOST, framework.server, true);
|
||||
await Settings.safeUpdate(SETTING_PREVIEW_HOST, framework.server, true);
|
||||
}
|
||||
|
||||
await FrameworkDetector.checkDefaultSettings(framework);
|
||||
} else {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, '', true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, '', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ export class SettingsListener extends BaseListener {
|
||||
}
|
||||
|
||||
public static async addAssetsFolder(assetFolder: string | StaticFolder) {
|
||||
await Settings.update(SETTING_CONTENT_STATIC_FOLDER, assetFolder, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_STATIC_FOLDER, assetFolder, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export class SnippetListener extends BaseListener {
|
||||
|
||||
snippets[title] = snippetContent;
|
||||
|
||||
await Settings.update(SETTING_CONTENT_SNIPPETS, snippets, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_SNIPPETS, snippets, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export class SnippetListener extends BaseListener {
|
||||
return acc;
|
||||
}, {} as Snippets);
|
||||
|
||||
await Settings.update(SETTING_CONTENT_SNIPPETS, snippetsToStore, true);
|
||||
await Settings.safeUpdate(SETTING_CONTENT_SNIPPETS, snippetsToStore, true);
|
||||
SettingsListener.getSettings(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export class SsgListener extends BaseListener {
|
||||
}
|
||||
|
||||
contentTypes.push(contentType);
|
||||
await Settings.update(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
await Settings.safeUpdate(SETTING_TAXONOMY_CONTENT_TYPES, contentTypes, true);
|
||||
|
||||
SsgListener.sendRequest(command as any, requestId, {});
|
||||
SettingsListener.getSettings(true);
|
||||
|
||||
@@ -75,7 +75,7 @@ export class SettingsListener extends BaseListener {
|
||||
* @param value
|
||||
*/
|
||||
private static async updateSetting(setting: string, value: any) {
|
||||
await Settings.update(setting, value);
|
||||
await Settings.safeUpdate(setting, value);
|
||||
this.getSettings();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ export interface ContentFolder {
|
||||
filePrefix?: string;
|
||||
contentTypes?: string[];
|
||||
originalPath?: string;
|
||||
$schema?: string;
|
||||
extended?: boolean;
|
||||
$schema?: string; // Extended config
|
||||
extended?: boolean; // Extended config
|
||||
|
||||
locale?: string;
|
||||
localeTitle?: string;
|
||||
|
||||
Reference in New Issue
Block a user