refactorings
This commit is contained in:
@@ -8,19 +8,11 @@ export class DetectorBuilder {
|
|||||||
constructor(private readonly os: OperatingSystem) { }
|
constructor(private readonly os: OperatingSystem) { }
|
||||||
|
|
||||||
public mustInclude(str: string): DetectorBuilder {
|
public mustInclude(str: string): DetectorBuilder {
|
||||||
if (!str) {
|
return this.add(str, this.existingPartsInUserAgent);
|
||||||
throw new Error('part to include is empty or undefined');
|
|
||||||
}
|
|
||||||
this.existingPartsInUserAgent.push(str);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public mustNotInclude(str: string): DetectorBuilder {
|
public mustNotInclude(str: string): DetectorBuilder {
|
||||||
if (!str) {
|
return this.add(str, this.notExistingPartsInUserAgent);
|
||||||
throw new Error('part to not include is empty or undefined');
|
|
||||||
}
|
|
||||||
this.notExistingPartsInUserAgent.push(str);
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public build(): IBrowserOsDetector {
|
public build(): IBrowserOsDetector {
|
||||||
@@ -28,22 +20,34 @@ export class DetectorBuilder {
|
|||||||
throw new Error('Must include at least a part');
|
throw new Error('Must include at least a part');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
detect: (userAgent) => {
|
detect: (agent) => this.detect(agent),
|
||||||
if (!userAgent) {
|
|
||||||
throw new Error('User agent is null or undefined');
|
|
||||||
}
|
|
||||||
for (const exitingPart of this.existingPartsInUserAgent) {
|
|
||||||
if (!userAgent.includes(exitingPart)) {
|
|
||||||
return OperatingSystem.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const notExistingPart of this.notExistingPartsInUserAgent) {
|
|
||||||
if (userAgent.includes(notExistingPart)) {
|
|
||||||
return OperatingSystem.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.os;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private detect(userAgent: string): OperatingSystem {
|
||||||
|
if (!userAgent) {
|
||||||
|
throw new Error('User agent is null or undefined');
|
||||||
|
}
|
||||||
|
if (this.existingPartsInUserAgent.some((part) => !userAgent.includes(part))) {
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.some((part) => userAgent.includes(part))) {
|
||||||
|
return OperatingSystem.Unknown;
|
||||||
|
}
|
||||||
|
return this.os;
|
||||||
|
}
|
||||||
|
|
||||||
|
private add(part: string, array: string[]): DetectorBuilder {
|
||||||
|
if (!part) {
|
||||||
|
throw new Error('part is empty or undefined');
|
||||||
|
}
|
||||||
|
if (this.existingPartsInUserAgent.includes(part)) {
|
||||||
|
throw new Error(`part ${part} is already included as existing part`);
|
||||||
|
}
|
||||||
|
if (this.notExistingPartsInUserAgent.includes(part)) {
|
||||||
|
throw new Error(`part ${part} is already included as not existing part`);
|
||||||
|
}
|
||||||
|
array.push(part);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,46 @@
|
|||||||
import { YamlDocumentable } from 'js-yaml-loader!./application.yaml';
|
import { YamlDocumentable, DocumentationUrls } from 'js-yaml-loader!./application.yaml';
|
||||||
|
|
||||||
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
||||||
if (!documentable) {
|
if (!documentable) {
|
||||||
throw new Error('documentable is null or undefined');
|
throw new Error('documentable is null or undefined');
|
||||||
}
|
}
|
||||||
const docs = documentable.docs;
|
const docs = documentable.docs;
|
||||||
if (!docs) {
|
if (!docs || !docs.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const result = new DocumentationUrls();
|
let result = new DocumentationUrlContainer();
|
||||||
if (docs instanceof Array) {
|
result = addDocs(docs, result);
|
||||||
for (const doc of docs) {
|
|
||||||
if (typeof doc !== 'string') {
|
|
||||||
throw new Error('Docs field (documentation url) must be an array of strings');
|
|
||||||
}
|
|
||||||
result.add(doc);
|
|
||||||
}
|
|
||||||
} else if (typeof docs === 'string') {
|
|
||||||
result.add(docs);
|
|
||||||
} else {
|
|
||||||
throw new Error('Docs field (documentation url) must a string or array of strings');
|
|
||||||
}
|
|
||||||
return result.getAll();
|
return result.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
class DocumentationUrls {
|
function addDocs(docs: DocumentationUrls, urls: DocumentationUrlContainer): DocumentationUrlContainer {
|
||||||
|
if (docs instanceof Array) {
|
||||||
|
urls.addUrls(docs);
|
||||||
|
} else if (typeof docs === 'string') {
|
||||||
|
urls.addUrl(docs);
|
||||||
|
} else {
|
||||||
|
throw new Error('Docs field (documentation url) must a string or array of strings');
|
||||||
|
}
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentationUrlContainer {
|
||||||
private readonly urls = new Array<string>();
|
private readonly urls = new Array<string>();
|
||||||
|
|
||||||
public add(url: string) {
|
public addUrl(url: string) {
|
||||||
validateUrl(url);
|
validateUrl(url);
|
||||||
this.urls.push(url);
|
this.urls.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addUrls(urls: any[]) {
|
||||||
|
for (const url of urls) {
|
||||||
|
if (typeof url !== 'string') {
|
||||||
|
throw new Error('Docs field (documentation url) must be an array of strings');
|
||||||
|
}
|
||||||
|
this.addUrl(url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public getAll(): ReadonlyArray<string> {
|
public getAll(): ReadonlyArray<string> {
|
||||||
return this.urls;
|
return this.urls;
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/application/application.yaml.d.ts
vendored
2
src/application/application.yaml.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
declare module 'js-yaml-loader!*' {
|
declare module 'js-yaml-loader!*' {
|
||||||
export type CategoryOrScript = YamlCategory | YamlScript;
|
export type CategoryOrScript = YamlCategory | YamlScript;
|
||||||
type DocumentationUrls = ReadonlyArray<string> | string;
|
export type DocumentationUrls = ReadonlyArray<string> | string;
|
||||||
|
|
||||||
export interface YamlDocumentable {
|
export interface YamlDocumentable {
|
||||||
docs?: DocumentationUrls;
|
docs?: DocumentationUrls;
|
||||||
|
|||||||
@@ -18,15 +18,7 @@ export class Application implements IApplication {
|
|||||||
if (!repositoryUrl) { throw Error('Application has no repository url'); }
|
if (!repositoryUrl) { throw Error('Application has no repository url'); }
|
||||||
if (!version) { throw Error('Version cannot be empty'); }
|
if (!version) { throw Error('Version cannot be empty'); }
|
||||||
this.flattened = flatten(actions);
|
this.flattened = flatten(actions);
|
||||||
if (this.flattened.allCategories.length === 0) {
|
ensureValid(this.flattened);
|
||||||
throw new Error('Application must consist of at least one category');
|
|
||||||
}
|
|
||||||
if (this.flattened.allScripts.length === 0) {
|
|
||||||
throw new Error('Application must consist of at least one script');
|
|
||||||
}
|
|
||||||
if (this.flattened.allScripts.filter((script) => script.isRecommended).length === 0) {
|
|
||||||
throw new Error('Application must consist of at least one recommended script');
|
|
||||||
}
|
|
||||||
ensureNoDuplicates(this.flattened.allCategories);
|
ensureNoDuplicates(this.flattened.allCategories);
|
||||||
ensureNoDuplicates(this.flattened.allScripts);
|
ensureNoDuplicates(this.flattened.allScripts);
|
||||||
}
|
}
|
||||||
@@ -75,30 +67,50 @@ interface IFlattenedApplication {
|
|||||||
allScripts: IScript[];
|
allScripts: IScript[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function flattenRecursive(
|
function ensureValid(application: IFlattenedApplication) {
|
||||||
|
if (!application.allCategories || application.allCategories.length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one category');
|
||||||
|
}
|
||||||
|
if (!application.allScripts || application.allScripts.length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one script');
|
||||||
|
}
|
||||||
|
if (application.allScripts.filter((script) => script.isRecommended).length === 0) {
|
||||||
|
throw new Error('Application must consist of at least one recommended script');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenCategories(
|
||||||
categories: ReadonlyArray<ICategory>,
|
categories: ReadonlyArray<ICategory>,
|
||||||
flattened: IFlattenedApplication) {
|
flattened: IFlattenedApplication): IFlattenedApplication {
|
||||||
|
if (!categories || categories.length === 0) {
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
for (const category of categories) {
|
for (const category of categories) {
|
||||||
flattened.allCategories.push(category);
|
flattened.allCategories.push(category);
|
||||||
if (category.scripts) {
|
flattened = flattenScripts(category.scripts, flattened);
|
||||||
for (const script of category.scripts) {
|
flattened = flattenCategories(category.subCategories, flattened);
|
||||||
flattened.allScripts.push(script);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (category.subCategories && category.subCategories.length > 0) {
|
|
||||||
flattenRecursive(
|
|
||||||
category.subCategories as ReadonlyArray<ICategory>,
|
|
||||||
flattened);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenScripts(
|
||||||
|
scripts: ReadonlyArray<IScript>,
|
||||||
|
flattened: IFlattenedApplication): IFlattenedApplication {
|
||||||
|
if (!scripts) {
|
||||||
|
return flattened;
|
||||||
|
}
|
||||||
|
for (const script of scripts) {
|
||||||
|
flattened.allScripts.push(script);
|
||||||
|
}
|
||||||
|
return flattened;
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten(
|
function flatten(
|
||||||
categories: ReadonlyArray<ICategory>): IFlattenedApplication {
|
categories: ReadonlyArray<ICategory>): IFlattenedApplication {
|
||||||
const flattened: IFlattenedApplication = {
|
let flattened: IFlattenedApplication = {
|
||||||
allCategories: new Array<ICategory>(),
|
allCategories: new Array<ICategory>(),
|
||||||
allScripts: new Array<IScript>(),
|
allScripts: new Array<IScript>(),
|
||||||
};
|
};
|
||||||
flattenRecursive(categories, flattened);
|
flattened = flattenCategories(categories, flattened);
|
||||||
return flattened;
|
return flattened;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { faGithub } from '@fortawesome/free-brands-svg-icons';
|
|||||||
/** BRAND ICONS (PREFIX: fab) */
|
/** BRAND ICONS (PREFIX: fab) */
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||||
/** REGULAR ICONS (PREFIX: far) */
|
/** REGULAR ICONS (PREFIX: far) */
|
||||||
import { faFolderOpen, faFolder, faComment, faSmile } from '@fortawesome/free-regular-svg-icons';
|
import { faFolderOpen, faFolder, faSmile } from '@fortawesome/free-regular-svg-icons';
|
||||||
/** SOLID ICONS (PREFIX: fas (default)) */
|
/** SOLID ICONS (PREFIX: fas (default)) */
|
||||||
import { faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe } from '@fortawesome/free-solid-svg-icons';
|
import { faTimes, faFileDownload, faCopy, faSearch, faInfoCircle, faUserSecret, faDesktop, faTag, faGlobe } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
|||||||
@@ -30,19 +30,32 @@ export function getCategoryNodeId(category: ICategory): string {
|
|||||||
|
|
||||||
function parseCategoryRecursively(
|
function parseCategoryRecursively(
|
||||||
parentCategory: ICategory): INode[] {
|
parentCategory: ICategory): INode[] {
|
||||||
if (!parentCategory) { throw new Error('parentCategory is undefined'); }
|
if (!parentCategory) {
|
||||||
|
throw new Error('parentCategory is undefined');
|
||||||
const nodes = new Array<INode>();
|
|
||||||
if (parentCategory.subCategories && parentCategory.subCategories.length > 0) {
|
|
||||||
for (const subCategory of parentCategory.subCategories) {
|
|
||||||
const subCategoryNodes = parseCategoryRecursively(subCategory);
|
|
||||||
nodes.push(convertCategoryToNode(subCategory, subCategoryNodes));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (parentCategory.scripts && parentCategory.scripts.length > 0) {
|
let nodes = new Array<INode>();
|
||||||
for (const script of parentCategory.scripts) {
|
nodes = addCategories(parentCategory.subCategories, nodes);
|
||||||
nodes.push(convertScriptToNode(script));
|
nodes = addScripts(parentCategory.scripts, nodes);
|
||||||
}
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addScripts(scripts: ReadonlyArray<IScript>, nodes: INode[]): INode[] {
|
||||||
|
if (!scripts || scripts.length === 0) {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
for (const script of scripts) {
|
||||||
|
nodes.push(convertScriptToNode(script));
|
||||||
|
}
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCategories(categories: ReadonlyArray<ICategory>, nodes: INode[]): INode[] {
|
||||||
|
if (!categories || categories.length === 0) {
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
for (const category of categories) {
|
||||||
|
const subCategoryNodes = parseCategoryRecursively(category);
|
||||||
|
nodes.push(convertCategoryToNode(category, subCategoryNodes));
|
||||||
}
|
}
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ export function convertExistingToNode(liquorTreeNode: ILiquorTreeExistingNode):
|
|||||||
id: liquorTreeNode.id,
|
id: liquorTreeNode.id,
|
||||||
text: liquorTreeNode.data.text,
|
text: liquorTreeNode.data.text,
|
||||||
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
// selected: liquorTreeNode.states && liquorTreeNode.states.checked,
|
||||||
children: (!liquorTreeNode.children || liquorTreeNode.children.length === 0)
|
children: convertChildren(liquorTreeNode.children, convertExistingToNode),
|
||||||
? [] : liquorTreeNode.children.map((childNode) => convertExistingToNode(childNode)),
|
|
||||||
documentationUrls: liquorTreeNode.data.documentationUrls,
|
documentationUrls: liquorTreeNode.data.documentationUrls,
|
||||||
isReversible : liquorTreeNode.data.isReversible,
|
isReversible : liquorTreeNode.data.isReversible,
|
||||||
};
|
};
|
||||||
@@ -24,11 +23,19 @@ export function toNewLiquorTreeNode(node: INode): ILiquorTreeNewNode {
|
|||||||
state: {
|
state: {
|
||||||
checked: false,
|
checked: false,
|
||||||
},
|
},
|
||||||
children: (!node.children || node.children.length === 0) ? [] :
|
children: convertChildren(node.children, toNewLiquorTreeNode),
|
||||||
node.children.map((childNode) => toNewLiquorTreeNode(childNode)),
|
|
||||||
data: {
|
data: {
|
||||||
documentationUrls: node.documentationUrls,
|
documentationUrls: node.documentationUrls,
|
||||||
isReversible: node.isReversible,
|
isReversible: node.isReversible,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertChildren<TOldNode, TNewNode>(
|
||||||
|
oldChildren: readonly TOldNode[],
|
||||||
|
callback: (value: TOldNode) => TNewNode): TNewNode[] {
|
||||||
|
if (!oldChildren || oldChildren.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return oldChildren.map((childNode) => callback(childNode));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user