From cc2c6dc217b2f370c5eeb50cdfdb7fb7ebd994e7 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 22 Dec 2022 18:03:04 +0100 Subject: [PATCH] Allow external configurations - start --- package.json | 8 ++ scripts/settings-export.js | 43 ++++++++++ src/constants/settings.ts | 2 + src/helpers/SettingsHelper.ts | 148 +++++++++++++++++++++++++--------- 4 files changed, 165 insertions(+), 36 deletions(-) create mode 100644 scripts/settings-export.js diff --git a/package.json b/package.json index 5accf506..4015b7f5 100644 --- a/package.json +++ b/package.json @@ -79,6 +79,14 @@ "configuration": { "title": "Front Matter: use frontmatter.json for shared team settings", "properties": { + "frontMatter.extends": { + "type": "array", + "markdownDescription": "Specify the list of paths/URLs to extend the Front Matter CMS config. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.extends)", + "default": [], + "items": { + "type": "string" + } + }, "frontMatter.content.autoUpdateDate": { "type": "boolean", "default": false, diff --git a/scripts/settings-export.js b/scripts/settings-export.js new file mode 100644 index 00000000..0dea78ef --- /dev/null +++ b/scripts/settings-export.js @@ -0,0 +1,43 @@ + + +const packageJson = require('../package.json'); + +for (const key of Object.keys(packageJson.contributes.configuration.properties)) { + const type = packageJson.contributes.configuration.properties[key].type; + + if (type.includes('object') || type.includes('array')) { + console.log(`${key} - ${packageJson.contributes.configuration.properties[key].type}`); + } +} + +// TO IGNORE +// frontMatter.extends - array +// frontMatter.dashboard.mediaSnippet - array + +// TO PROCESS AS A WHOLE OBJECT +// frontMatter.content.draftField - object +// frontMatter.content.supportedFileTypes - array +// frontMatter.global.notifications - array +// frontMatter.global.disabledNotificaitons - array +// frontMatter.media.supportedMimeTypes - array +// frontMatter.taxonomy.commaSeparatedFields - array + +// MERGE ARRAYS +// frontMatter.taxonomy.categories - array +// frontMatter.taxonomy.tags - array +// frontMatter.taxonomy.noPropertyValueQuotes - array + +// PROCESS ITEM BY ITEM +// frontMatter.content.pageFolders - array - path +// frontMatter.content.placeholders - array - id +// frontMatter.content.sorting - array - id +// frontMatter.custom.scripts - array - id +// frontMatter.data.files - array - id +// frontMatter.data.folders - array - id +// frontMatter.data.types - array - id +// frontMatter.global.modes - array - id +// frontMatter.taxonomy.fieldGroups - array - id +// frontMatter.taxonomy.customTaxonomy - array - id +// frontMatter.taxonomy.contentTypes - array,null - name + +// frontMatter.content.snippets - object \ No newline at end of file diff --git a/src/constants/settings.ts b/src/constants/settings.ts index ca046281..740aac2b 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -2,6 +2,8 @@ export const EXTENSION_NAME = "Front Matter"; export const CONFIG_KEY = "frontMatter"; +export const SETTING_EXTENDS = "extends"; + export const SETTING_GLOBAL_NOTIFICATIONS = "global.notifications"; export const SETTING_GLOBAL_NOTIFICATIONS_DISABLED = "global.disabledNotifications"; export const SETTING_GLOBAL_MODES = "global.modes"; diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 04e69e03..5b26d163 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -3,8 +3,8 @@ import { Telemetry } from './Telemetry'; import { Notifications } from './Notifications'; import { commands, Uri, workspace, window } from 'vscode'; import * as vscode from 'vscode'; -import { ContentFolder, ContentType, CustomPlaceholder, CustomTaxonomy, DataFile, DataFolder, DataType, TaxonomyType } from '../models'; -import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, ExtensionState, SETTING_TAXONOMY_CUSTOM, TelemetryEvent, COMMAND_NAME, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_PAGE_FOLDERS, SETTING_CONTENT_SNIPPETS, SETTING_CONTENT_PLACEHOLDERS, SETTING_CUSTOM_SCRIPTS, SETTING_DATA_FILES, SETTING_DATA_TYPES, SETTING_DATA_FOLDERS } from '../constants'; +import { ContentType, CustomTaxonomy, TaxonomyType } from '../models'; +import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, ExtensionState, SETTING_TAXONOMY_CUSTOM, TelemetryEvent, COMMAND_NAME, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_PAGE_FOLDERS, SETTING_CONTENT_SNIPPETS, SETTING_CONTENT_PLACEHOLDERS, SETTING_CUSTOM_SCRIPTS, SETTING_DATA_FILES, SETTING_DATA_TYPES, SETTING_DATA_FOLDERS, SETTING_EXTENDS } from '../constants'; import { Folders } from '../commands/Folders'; import { join, basename, dirname, parse } from 'path'; import { existsSync } from 'fs'; @@ -13,6 +13,7 @@ import { debounceCallback } from './DebounceCallback'; import { Logger } from './Logger'; import * as jsoncParser from 'jsonc-parser'; import { existsAsync, readFileAsync, writeFileAsync } from '../utils'; +import fetch from 'node-fetch'; export class Settings { public static globalFile = "frontmatter.json"; @@ -432,6 +433,9 @@ export class Settings { Settings.globalConfig = undefined; } + // Check if the config got external configs + await Settings.processExternalConfig(); + // Read the files from the config folder let configFiles = await workspace.findFiles(`**/${Settings.globalConfigFolder}/**/*.json`); if (configFiles.length === 0) { @@ -440,7 +444,6 @@ export class Settings { // Sort the files by fsPath configFiles = configFiles.sort((a, b) => a.fsPath.localeCompare(b.fsPath)); - for await (const configFile of configFiles) { await Settings.processConfigFile(configFile); } @@ -453,6 +456,70 @@ export class Settings { Settings.readConfigPromise = undefined; } + /** + * Process the external configs + */ + private static async processExternalConfig() { + const extendsConfigName = `${CONFIG_KEY}.${SETTING_EXTENDS}`; + if (!Settings.globalConfig || !Settings.globalConfig[extendsConfigName]) { + return; + } + + const originalConfig = Object.assign({}, Settings.globalConfig); + const extendsConfig: string[] = Settings.globalConfig[extendsConfigName]; + for (const externalConfig of extendsConfig) { + if (externalConfig.endsWith(`.json`)) { + let config: any = undefined; + + if (externalConfig.startsWith('https://')) { + try { + const response = await fetch(externalConfig); + if (response.ok) { + config = await response.json(); + } + } catch (e) { + Logger.error(`Error fetching external config "${externalConfig}".`); + } + } else { + const configPath = join(Folders.getWorkspaceFolder()?.fsPath || '', externalConfig); + if (await existsAsync(configPath)) { + const configTxt = await readFileAsync(configPath, 'utf8'); + config = jsoncParser.parse(configTxt); + } else { + Logger.error(`External config "${externalConfig}" not found.`); + } + } + + // Check if the config contains data and loop through it + if (config) { + // We need to loop through the config to make sure the objects and arrays are merged + for (const key in config) { + if (config.hasOwnProperty(key)) { + const value = config[key]; + + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + if (typeof originalConfig[key] === 'undefined') { + Settings.globalConfig[key] = value; + } + } else if (typeof value === 'object' && value !== null) { + // Check if array + if (Array.isArray(value)) { + for (const item of value) { + Settings.updateGlobalConfigSetting(key.replace(`${CONFIG_KEY}.`, ''), item); + } + } else { + for (const itemKey in value) { + // Process the object key/item + } + } + } + } + } + } + } + } + } + /** * Process the config file * @param configFile @@ -482,45 +549,54 @@ export class Settings { Settings.globalConfig = {}; } - // Array settings - if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CUSTOM_SCRIPTS)) { - const crntValue = Settings.globalConfig[`${CONFIG_KEY}.${SETTING_CUSTOM_SCRIPTS}`] || []; - Settings.globalConfig[`${CONFIG_KEY}.${SETTING_CUSTOM_SCRIPTS}`] = [...crntValue, configJson]; - } - // Content types - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_TAXONOMY_CONTENT_TYPES)) { - Settings.updateGlobalConfigArraySetting(SETTING_TAXONOMY_CONTENT_TYPES, "name", configJson); - } - // Data files - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_FILES)) { - Settings.updateGlobalConfigArraySetting(SETTING_DATA_FILES, "id", configJson); - } - // Data folders - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_FOLDERS)) { - Settings.updateGlobalConfigArraySetting(SETTING_DATA_FOLDERS, "id", configJson); - } - // Data types - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_TYPES)) { - Settings.updateGlobalConfigArraySetting(SETTING_DATA_TYPES, "id", configJson); - } - // Page folders - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_PAGE_FOLDERS)) { - Settings.updateGlobalConfigArraySetting(SETTING_CONTENT_PAGE_FOLDERS, "path", configJson); - } - // Placeholders - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_PLACEHOLDERS)) { - Settings.updateGlobalConfigArraySetting(SETTING_CONTENT_PLACEHOLDERS, "id", configJson); - } - // Object settings - else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_SNIPPETS)) { - Settings.updateGlobalConfigObjectByNameSetting(SETTING_CONTENT_SNIPPETS, configFilePath, configJson, filePath); - } + Settings.updateGlobalConfigSetting(relSettingName, configJson, configFilePath, filePath); } catch (e) { Logger.error(`Error reading config file: ${configFile.fsPath}`); Logger.error((e as Error).message); } } + /** + * Update the global config array/object settings + * @param relSettingName + * @param configJson + */ + private static updateGlobalConfigSetting(relSettingName: string, configJson: any, configFilePath?: string, filePath?: string): void { + // Array settings + if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CUSTOM_SCRIPTS)) { + const crntValue = Settings.globalConfig[`${CONFIG_KEY}.${SETTING_CUSTOM_SCRIPTS}`] || []; + Settings.globalConfig[`${CONFIG_KEY}.${SETTING_CUSTOM_SCRIPTS}`] = [...crntValue, configJson]; + } + // Content types + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_TAXONOMY_CONTENT_TYPES)) { + Settings.updateGlobalConfigArraySetting(SETTING_TAXONOMY_CONTENT_TYPES, "name", configJson); + } + // Data files + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_FILES)) { + Settings.updateGlobalConfigArraySetting(SETTING_DATA_FILES, "id", configJson); + } + // Data folders + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_FOLDERS)) { + Settings.updateGlobalConfigArraySetting(SETTING_DATA_FOLDERS, "id", configJson); + } + // Data types + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_DATA_TYPES)) { + Settings.updateGlobalConfigArraySetting(SETTING_DATA_TYPES, "id", configJson); + } + // Page folders + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_PAGE_FOLDERS)) { + Settings.updateGlobalConfigArraySetting(SETTING_CONTENT_PAGE_FOLDERS, "path", configJson); + } + // Placeholders + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_PLACEHOLDERS)) { + Settings.updateGlobalConfigArraySetting(SETTING_CONTENT_PLACEHOLDERS, "id", configJson); + } + // Snippets + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_SNIPPETS) && configFilePath && filePath) { + Settings.updateGlobalConfigObjectByNameSetting(SETTING_CONTENT_SNIPPETS, configFilePath, configJson, filePath); + } + } + /** * Check if the setting name is equal or starts with the reference setting name * @param value