Skip to content

Commit

Permalink
fix: Move SettingsLoaders to own files to help with TreeShaking
Browse files Browse the repository at this point in the history
  • Loading branch information
andymac4182 committed May 3, 2023
1 parent bb56ed9 commit f241a11
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 297 deletions.
4 changes: 2 additions & 2 deletions packages/cli/src/index.ts
Expand Up @@ -5,7 +5,7 @@ process.setMaxListeners(Infinity);
import chalk from "chalk";
import boxen from "boxen";
import {
ConfluenceUploadSettings,
AutoSettingsLoader,
FileSystemAdaptor,
Publisher,
} from "@markdown-confluence/lib";
Expand All @@ -14,7 +14,7 @@ import { ConfluenceClient } from "confluence.js";

// Define the main function
async function main() {
const settingLoader = new ConfluenceUploadSettings.AutoSettingsLoader();
const settingLoader = new AutoSettingsLoader();
const settings = settingLoader.load();

const adaptor = new FileSystemAdaptor(settings); // Make sure this is identical as possible between Obsidian and CLI
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/src/Publisher.ts
@@ -1,4 +1,4 @@
import { SettingsLoader } from "./Settings";
import { SettingsLoader } from "./SettingsLoader";
import { traverse, filter } from "@atlaskit/adf-utils/traverse";
import { RequiredConfluenceClient, LoaderAdaptor } from "./adaptors";
import { JSONDocNode } from "@atlaskit/editor-json-transformer";
Expand Down
292 changes: 0 additions & 292 deletions packages/lib/src/Settings.ts
@@ -1,7 +1,3 @@
import fs from "fs";
import path from "path";
import yargs from "yargs";

export type ConfluenceSettings = {
confluenceBaseUrl: string;
confluenceParentId: string;
Expand All @@ -21,291 +17,3 @@ export const DEFAULT_SETTINGS: ConfluenceSettings = {
contentRoot: process.cwd(),
firstHeadingPageTitle: false,
};

export abstract class SettingsLoader {
abstract loadPartial(): Partial<ConfluenceSettings>;

load(): ConfluenceSettings {
const initialSettings = this.loadPartial();
const settings = this.validateSettings(initialSettings);
return settings;
}

protected validateSettings(
settings: Partial<ConfluenceSettings>
): ConfluenceSettings {
if (!settings.confluenceBaseUrl) {
throw new Error("Confluence base URL is required");
}

if (!settings.confluenceParentId) {
throw new Error("Confluence parent ID is required");
}

if (!settings.atlassianUserName) {
throw new Error("Atlassian user name is required");
}

if (!settings.atlassianApiToken) {
throw new Error("Atlassian API token is required");
}

if (!settings.folderToPublish) {
throw new Error("Folder to publish is required");
}

if (!settings.contentRoot) {
throw new Error("Content root is required");
} else {
if (!settings.contentRoot.endsWith("/")) {
settings.contentRoot += "/";
}
}

if (!("firstHeadingPageTitle" in settings)) {
settings.firstHeadingPageTitle = false;
}

return settings as ConfluenceSettings;
}
}

export class DefaultSettingsLoader extends SettingsLoader {
loadPartial(): Partial<ConfluenceSettings> {
return DEFAULT_SETTINGS;
}
}

export class StaticSettingsLoader extends SettingsLoader {
constructor(private settings: Partial<ConfluenceSettings>) {
super();
}

loadPartial(): Partial<ConfluenceSettings> {
return this.settings;
}
}

export class EnvironmentVariableSettingsLoader extends SettingsLoader {
getValue<T extends keyof ConfluenceSettings>(
propertyKey: T,
envVar: string
): Partial<ConfluenceSettings> {
const value = process.env[envVar];
return value ? { [propertyKey]: value } : {};
}

loadPartial(): Partial<ConfluenceSettings> {
return {
...this.getValue("confluenceBaseUrl", "CONFLUENCE_BASE_URL"),
...this.getValue("confluenceParentId", "CONFLUENCE_PARENT_ID"),
...this.getValue("atlassianUserName", "ATLASSIAN_USERNAME"),
...this.getValue("atlassianApiToken", "ATLASSIAN_API_TOKEN"),
...this.getValue("folderToPublish", "FOLDER_TO_PUBLISH"),
...this.getValue("contentRoot", "CONFLUENCE_CONTENT_ROOT"),
firstHeadingPageTitle:
(process.env["CONFLUENCE_FIRST_HEADING_PAGE_TITLE"] ??
"false") === "true",
};
}
}

export class ConfigFileSettingsLoader extends SettingsLoader {
private configPath: string = path.join(
process.cwd() ?? "",
".markdown-confluence.json"
);

constructor(configPath?: string) {
super();

if (configPath) {
this.configPath = configPath;
return;
}

if (
"CONFLUENCE_CONFIG_FILE" in process.env &&
process.env["CONFLUENCE_CONFIG_FILE"]
) {
this.configPath = process.env["CONFLUENCE_CONFIG_FILE"];
}

const options = yargs(process.argv)
.option("config", {
alias: "c",
describe: "Path to the config file",
type: "string",
default: this.configPath,
demandOption: false,
})
.parseSync();

this.configPath = options.config;
}

loadPartial(): Partial<ConfluenceSettings> {
try {
const configData = fs.readFileSync(this.configPath, {
encoding: "utf-8",
});
const config = JSON.parse(configData);

const result: Partial<ConfluenceSettings> = {};

for (const key in DEFAULT_SETTINGS) {
if (Object.prototype.hasOwnProperty.call(config, key)) {
const propertyKey = key as keyof ConfluenceSettings;
const element = config[propertyKey];
if (element) {
result[propertyKey] = element;
}
}
}

return result;
} catch {
return {};
}
}
}

export class CommandLineArgumentSettingsLoader extends SettingsLoader {
getValue<T extends keyof ConfluenceSettings>(
propertyKey: T,
envVar: string
): Partial<ConfluenceSettings> {
const value = process.env[envVar];
return value ? { [propertyKey]: value } : {};
}

loadPartial(): Partial<ConfluenceSettings> {
const options = yargs(process.argv)
.usage("Usage: $0 [options]")
.option("config", {
alias: "c",
describe: "Path to the config file",
type: "string",
default: path.join(
process.env["HOME"] ?? "",
".mermaid-confluence.json"
),
demandOption: false,
})
.option("baseUrl", {
alias: "b",
describe: "Confluence base URL",
type: "string",
demandOption: false,
})
.option("parentId", {
alias: "p",
describe: "Confluence parent ID",
type: "string",
demandOption: false,
})
.option("userName", {
alias: "u",
describe: "Atlassian user name",
type: "string",
demandOption: false,
})
.option("apiToken", {
describe: "Atlassian API token",
type: "string",
demandOption: false,
})
.option("enableFolder", {
alias: "f",
describe: "Folder enable to publish",
type: "string",
demandOption: false,
})
.option("contentRoot", {
alias: "cr",
describe:
"Root to search for files to publish. All files must be part of this directory.",
type: "string",
demandOption: false,
})
.option("firstHeaderPageTitle", {
alias: "fh",
describe:
"Replace page title with first header element when 'connie-title' isn't specified.",
type: "boolean",
demandOption: false,
})
.parseSync();

return {
...(options.baseUrl
? { confluenceBaseUrl: options.baseUrl }
: undefined),
...(options.parentId
? { confluenceParentId: options.parentId }
: undefined),
...(options.userName
? { atlassianUserName: options.userName }
: undefined),
...(options.apiToken
? { atlassianApiToken: options.apiToken }
: undefined),
...(options.enableFolder
? { folderToPublish: options.enableFolder }
: undefined),
...(options.contentRoot
? { contentRoot: options.contentRoot }
: undefined),
...(options.firstHeaderPageTitle
? { firstHeadingPageTitle: options.firstHeaderPageTitle }
: undefined),
};
}
}

export class AutoSettingsLoader extends SettingsLoader {
constructor(private loaders: SettingsLoader[] = []) {
super();

if (loaders.length === 0) {
this.loaders.push(new DefaultSettingsLoader());
this.loaders.push(new ConfigFileSettingsLoader());
this.loaders.push(new EnvironmentVariableSettingsLoader());
this.loaders.push(new CommandLineArgumentSettingsLoader());
}
}

private combineSettings(): ConfluenceSettings {
let settings: Partial<ConfluenceSettings> = {};

for (const loader of this.loaders) {
const partialSettings = loader.loadPartial();
for (const key in partialSettings) {
const propertyKey = key as keyof ConfluenceSettings;
if (
Object.prototype.hasOwnProperty.call(
partialSettings,
propertyKey
)
) {
const element = partialSettings[propertyKey];
if (
element &&
typeof element === typeof DEFAULT_SETTINGS[propertyKey]
) {
settings = {
...settings,
[propertyKey]: element,
};
}
}
}
}

return settings as ConfluenceSettings;
}

loadPartial(): Partial<ConfluenceSettings> {
return this.combineSettings();
}
}
53 changes: 53 additions & 0 deletions packages/lib/src/SettingsLoader/AutoSettingsLoader.ts
@@ -0,0 +1,53 @@
import { ConfluenceSettings, DEFAULT_SETTINGS } from "src/Settings";
import { DefaultSettingsLoader } from "./DefaultSettingsLoader";
import { EnvironmentVariableSettingsLoader } from "./EnvironmentVariableSettingsLoader";
import { ConfigFileSettingsLoader } from "./ConfigFileSettingsLoader";
import { CommandLineArgumentSettingsLoader } from "./CommandLineArgumentSettingsLoader";
import { SettingsLoader } from ".";

export class AutoSettingsLoader extends SettingsLoader {
constructor(private loaders: SettingsLoader[] = []) {
super();

if (loaders.length === 0) {
this.loaders.push(new DefaultSettingsLoader());
this.loaders.push(new ConfigFileSettingsLoader());
this.loaders.push(new EnvironmentVariableSettingsLoader());
this.loaders.push(new CommandLineArgumentSettingsLoader());
}
}

private combineSettings(): ConfluenceSettings {
let settings: Partial<ConfluenceSettings> = {};

for (const loader of this.loaders) {
const partialSettings = loader.loadPartial();
for (const key in partialSettings) {
const propertyKey = key as keyof ConfluenceSettings;
if (
Object.prototype.hasOwnProperty.call(
partialSettings,
propertyKey
)
) {
const element = partialSettings[propertyKey];
if (
element &&
typeof element === typeof DEFAULT_SETTINGS[propertyKey]
) {
settings = {
...settings,
[propertyKey]: element,
};
}
}
}
}

return settings as ConfluenceSettings;
}

loadPartial(): Partial<ConfluenceSettings> {
return this.combineSettings();
}
}

0 comments on commit f241a11

Please sign in to comment.