Refactor to unify scripts/categories as Executable
This commit consolidates scripts and categories under a unified 'Executable' concept. This simplifies the architecture and improves code readability. - Introduce subfolders within `src/domain` to segregate domain elements. - Update class and interface names by removing the 'I' prefix in alignment with new coding standards. - Replace 'Node' with 'Executable' to clarify usage; reserve 'Node' exclusively for the UI's tree component.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
import type { SelectedScript } from '@/application/Context/State/Selection/Script/SelectedScript';
|
||||
import type { ICodeChangedEvent } from './ICodeChangedEvent';
|
||||
@@ -6,13 +6,13 @@ import type { ICodeChangedEvent } from './ICodeChangedEvent';
|
||||
export class CodeChangedEvent implements ICodeChangedEvent {
|
||||
public readonly code: string;
|
||||
|
||||
public readonly addedScripts: ReadonlyArray<IScript>;
|
||||
public readonly addedScripts: ReadonlyArray<Script>;
|
||||
|
||||
public readonly removedScripts: ReadonlyArray<IScript>;
|
||||
public readonly removedScripts: ReadonlyArray<Script>;
|
||||
|
||||
public readonly changedScripts: ReadonlyArray<IScript>;
|
||||
public readonly changedScripts: ReadonlyArray<Script>;
|
||||
|
||||
private readonly scripts: Map<IScript, ICodePosition>;
|
||||
private readonly scripts: Map<Script, ICodePosition>;
|
||||
|
||||
constructor(
|
||||
code: string,
|
||||
@@ -25,7 +25,7 @@ export class CodeChangedEvent implements ICodeChangedEvent {
|
||||
this.addedScripts = selectIfNotExists(newScripts, oldScripts);
|
||||
this.removedScripts = selectIfNotExists(oldScripts, newScripts);
|
||||
this.changedScripts = getChangedScripts(oldScripts, newScripts);
|
||||
this.scripts = new Map<IScript, ICodePosition>();
|
||||
this.scripts = new Map<Script, ICodePosition>();
|
||||
scripts.forEach((position, selection) => {
|
||||
this.scripts.set(selection.script, position);
|
||||
});
|
||||
@@ -35,7 +35,7 @@ export class CodeChangedEvent implements ICodeChangedEvent {
|
||||
return this.scripts.size === 0;
|
||||
}
|
||||
|
||||
public getScriptPositionInCode(script: IScript): ICodePosition {
|
||||
public getScriptPositionInCode(script: Script): ICodePosition {
|
||||
return this.getPositionById(script.id);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ function ensureAllPositionsExist(script: string, positions: ReadonlyArray<ICodeP
|
||||
function getChangedScripts(
|
||||
oldScripts: ReadonlyArray<SelectedScript>,
|
||||
newScripts: ReadonlyArray<SelectedScript>,
|
||||
): ReadonlyArray<IScript> {
|
||||
): ReadonlyArray<Script> {
|
||||
return newScripts
|
||||
.filter((newScript) => oldScripts.find((oldScript) => oldScript.id === newScript.id
|
||||
&& oldScript.revert !== newScript.revert))
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { ICodePosition } from '@/application/Context/State/Code/Position/ICodePosition';
|
||||
|
||||
export interface ICodeChangedEvent {
|
||||
readonly code: string;
|
||||
readonly addedScripts: ReadonlyArray<IScript>;
|
||||
readonly removedScripts: ReadonlyArray<IScript>;
|
||||
readonly changedScripts: ReadonlyArray<IScript>;
|
||||
readonly addedScripts: ReadonlyArray<Script>;
|
||||
readonly removedScripts: ReadonlyArray<Script>;
|
||||
readonly changedScripts: ReadonlyArray<Script>;
|
||||
isEmpty(): boolean;
|
||||
getScriptPositionInCode(script: IScript): ICodePosition;
|
||||
getScriptPositionInCode(script: Script): ICodePosition;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { ICategory } from '@/domain/ICategory';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { FilterResult } from './FilterResult';
|
||||
|
||||
export class AppliedFilterResult implements FilterResult {
|
||||
constructor(
|
||||
public readonly scriptMatches: ReadonlyArray<IScript>,
|
||||
public readonly categoryMatches: ReadonlyArray<ICategory>,
|
||||
public readonly scriptMatches: ReadonlyArray<Script>,
|
||||
public readonly categoryMatches: ReadonlyArray<Category>,
|
||||
public readonly query: string,
|
||||
) {
|
||||
if (!query) { throw new Error('Query is empty or undefined'); }
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { IScript, ICategory } from '@/domain/ICategory';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
|
||||
export interface FilterResult {
|
||||
readonly categoryMatches: ReadonlyArray<ICategory>;
|
||||
readonly scriptMatches: ReadonlyArray<IScript>;
|
||||
readonly categoryMatches: ReadonlyArray<Category>;
|
||||
readonly scriptMatches: ReadonlyArray<Script>;
|
||||
readonly query: string;
|
||||
hasAnyMatches(): boolean;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { ICategory, IScript } from '@/domain/ICategory';
|
||||
import type { IScriptCode } from '@/domain/IScriptCode';
|
||||
import type { IDocumentable } from '@/domain/IDocumentable';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode';
|
||||
import type { Documentable } from '@/domain/Executables/Documentable';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { AppliedFilterResult } from '../Result/AppliedFilterResult';
|
||||
import type { FilterStrategy } from './FilterStrategy';
|
||||
import type { FilterResult } from '../Result/FilterResult';
|
||||
@@ -24,7 +25,7 @@ export class LinearFilterStrategy implements FilterStrategy {
|
||||
}
|
||||
|
||||
function matchesCategory(
|
||||
category: ICategory,
|
||||
category: Category,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return matchesAny(
|
||||
@@ -34,7 +35,7 @@ function matchesCategory(
|
||||
}
|
||||
|
||||
function matchesScript(
|
||||
script: IScript,
|
||||
script: Script,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return matchesAny(
|
||||
@@ -58,7 +59,7 @@ function matchName(
|
||||
}
|
||||
|
||||
function matchCode(
|
||||
code: IScriptCode,
|
||||
code: ScriptCode,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
if (code.execute.toLowerCase().includes(filterLowercase)) {
|
||||
@@ -71,7 +72,7 @@ function matchCode(
|
||||
}
|
||||
|
||||
function matchDocumentation(
|
||||
documentable: IDocumentable,
|
||||
documentable: Documentable,
|
||||
filterLowercase: string,
|
||||
): boolean {
|
||||
return documentable.docs.some(
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ICategory } from '@/domain/ICategory';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { CategorySelectionChangeCommand } from './CategorySelectionChange';
|
||||
|
||||
export interface ReadonlyCategorySelection {
|
||||
areAllScriptsSelected(category: ICategory): boolean;
|
||||
isAnyScriptSelected(category: ICategory): boolean;
|
||||
areAllScriptsSelected(category: Category): boolean;
|
||||
isAnyScriptSelected(category: Category): boolean;
|
||||
}
|
||||
|
||||
export interface CategorySelection extends ReadonlyCategorySelection {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ICategory } from '@/domain/ICategory';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import type { CategorySelectionChange, CategorySelectionChangeCommand } from './CategorySelectionChange';
|
||||
import type { CategorySelection } from './CategorySelection';
|
||||
@@ -13,7 +13,7 @@ export class ScriptToCategorySelectionMapper implements CategorySelection {
|
||||
|
||||
}
|
||||
|
||||
public areAllScriptsSelected(category: ICategory): boolean {
|
||||
public areAllScriptsSelected(category: Category): boolean {
|
||||
const { selectedScripts } = this.scriptSelection;
|
||||
if (selectedScripts.length === 0) {
|
||||
return false;
|
||||
@@ -27,7 +27,7 @@ export class ScriptToCategorySelectionMapper implements CategorySelection {
|
||||
);
|
||||
}
|
||||
|
||||
public isAnyScriptSelected(category: ICategory): boolean {
|
||||
public isAnyScriptSelected(category: Category): boolean {
|
||||
const { selectedScripts } = this.scriptSelection;
|
||||
if (selectedScripts.length === 0) {
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { InMemoryRepository } from '@/infrastructure/Repository/InMemoryRepository';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { EventSource } from '@/infrastructure/Events/EventSource';
|
||||
import type { ReadonlyRepository, Repository } from '@/application/Repository/Repository';
|
||||
import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
@@ -80,7 +80,7 @@ export class DebouncedScriptSelection implements ScriptSelection {
|
||||
});
|
||||
}
|
||||
|
||||
public selectOnly(scripts: readonly IScript[]): void {
|
||||
public selectOnly(scripts: readonly Script[]): void {
|
||||
assertNonEmptyScriptSelection(scripts);
|
||||
this.processChanges({
|
||||
changes: [
|
||||
@@ -145,7 +145,7 @@ export class DebouncedScriptSelection implements ScriptSelection {
|
||||
}
|
||||
}
|
||||
|
||||
function assertNonEmptyScriptSelection(selectedItems: readonly IScript[]) {
|
||||
function assertNonEmptyScriptSelection(selectedItems: readonly Script[]) {
|
||||
if (selectedItems.length === 0) {
|
||||
throw new Error('Provided script array is empty. To deselect all scripts, please use the deselectAll() method instead.');
|
||||
}
|
||||
@@ -153,7 +153,7 @@ function assertNonEmptyScriptSelection(selectedItems: readonly IScript[]) {
|
||||
|
||||
function getScriptIdsToBeSelected(
|
||||
existingItems: ReadonlyRepository<string, SelectedScript>,
|
||||
desiredScripts: readonly IScript[],
|
||||
desiredScripts: readonly Script[],
|
||||
): string[] {
|
||||
return desiredScripts
|
||||
.filter((script) => !existingItems.exists(script.id))
|
||||
@@ -162,7 +162,7 @@ function getScriptIdsToBeSelected(
|
||||
|
||||
function getScriptIdsToBeDeselected(
|
||||
existingItems: ReadonlyRepository<string, SelectedScript>,
|
||||
desiredScripts: readonly IScript[],
|
||||
desiredScripts: readonly Script[],
|
||||
): string[] {
|
||||
return existingItems
|
||||
.getItems()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { IEventSource } from '@/infrastructure/Events/IEventSource';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { SelectedScript } from './SelectedScript';
|
||||
import type { ScriptSelectionChangeCommand } from './ScriptSelectionChange';
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface ReadonlyScriptSelection {
|
||||
}
|
||||
|
||||
export interface ScriptSelection extends ReadonlyScriptSelection {
|
||||
selectOnly(scripts: readonly IScript[]): void;
|
||||
selectOnly(scripts: readonly Script[]): void;
|
||||
selectAll(): void;
|
||||
deselectAll(): void;
|
||||
processChanges(action: ScriptSelectionChangeCommand): void;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { IEntity } from '@/infrastructure/Entity/IEntity';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
|
||||
type ScriptId = IScript['id'];
|
||||
type ScriptId = Script['id'];
|
||||
|
||||
export interface SelectedScript extends IEntity<ScriptId> {
|
||||
readonly script: IScript;
|
||||
readonly script: Script;
|
||||
readonly revert: boolean;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { BaseEntity } from '@/infrastructure/Entity/BaseEntity';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import type { SelectedScript } from './SelectedScript';
|
||||
|
||||
type SelectedScriptId = SelectedScript['id'];
|
||||
|
||||
export class UserSelectedScript extends BaseEntity<SelectedScriptId> {
|
||||
constructor(
|
||||
public readonly script: IScript,
|
||||
public readonly script: Script,
|
||||
public readonly revert: boolean,
|
||||
) {
|
||||
super(script.id);
|
||||
|
||||
@@ -4,20 +4,21 @@ import type { ICategoryCollection } from '@/domain/ICategoryCollection';
|
||||
import { CategoryCollection } from '@/domain/CategoryCollection';
|
||||
import type { ProjectDetails } from '@/domain/Project/ProjectDetails';
|
||||
import { createEnumParser } from '../Common/Enum';
|
||||
import { parseCategory } from './CategoryParser';
|
||||
import { CategoryCollectionParseContext } from './Script/CategoryCollectionParseContext';
|
||||
import { parseCategory } from './Executable/CategoryParser';
|
||||
import { ScriptingDefinitionParser } from './ScriptingDefinition/ScriptingDefinitionParser';
|
||||
import { createCollectionUtilities, type CategoryCollectionSpecificUtilitiesFactory } from './Executable/CategoryCollectionSpecificUtilities';
|
||||
|
||||
export function parseCategoryCollection(
|
||||
content: CollectionData,
|
||||
projectDetails: ProjectDetails,
|
||||
osParser = createEnumParser(OperatingSystem),
|
||||
createUtilities: CategoryCollectionSpecificUtilitiesFactory = createCollectionUtilities,
|
||||
): ICategoryCollection {
|
||||
validate(content);
|
||||
const scripting = new ScriptingDefinitionParser()
|
||||
.parse(content.scripting, projectDetails);
|
||||
const context = new CategoryCollectionParseContext(content.functions, scripting);
|
||||
const categories = content.actions.map((action) => parseCategory(action, context));
|
||||
const utilities = createUtilities(content.functions, scripting);
|
||||
const categories = content.actions.map((action) => parseCategory(action, utilities));
|
||||
const os = osParser.parseEnum(content.os, 'os');
|
||||
const collection = new CategoryCollection(
|
||||
os,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import type { FunctionData } from '@/application/collections/';
|
||||
import { ScriptCompiler } from './Script/Compiler/ScriptCompiler';
|
||||
import { SyntaxFactory } from './Script/Validation/Syntax/SyntaxFactory';
|
||||
import type { IScriptCompiler } from './Script/Compiler/IScriptCompiler';
|
||||
import type { ILanguageSyntax } from './Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ISyntaxFactory } from './Script/Validation/Syntax/ISyntaxFactory';
|
||||
|
||||
export interface CategoryCollectionSpecificUtilities {
|
||||
readonly compiler: IScriptCompiler;
|
||||
readonly syntax: ILanguageSyntax;
|
||||
}
|
||||
|
||||
export const createCollectionUtilities: CategoryCollectionSpecificUtilitiesFactory = (
|
||||
functionsData: ReadonlyArray<FunctionData> | undefined,
|
||||
scripting: IScriptingDefinition,
|
||||
syntaxFactory: ISyntaxFactory = new SyntaxFactory(),
|
||||
) => {
|
||||
const syntax = syntaxFactory.create(scripting.language);
|
||||
return {
|
||||
compiler: new ScriptCompiler(functionsData ?? [], syntax),
|
||||
syntax,
|
||||
};
|
||||
};
|
||||
|
||||
export interface CategoryCollectionSpecificUtilitiesFactory {
|
||||
(
|
||||
functionsData: ReadonlyArray<FunctionData> | undefined,
|
||||
scripting: IScriptingDefinition,
|
||||
syntaxFactory?: ISyntaxFactory,
|
||||
): CategoryCollectionSpecificUtilities;
|
||||
}
|
||||
@@ -1,35 +1,35 @@
|
||||
import type {
|
||||
CategoryData, ScriptData, CategoryOrScriptData,
|
||||
CategoryData, ScriptData, ExecutableData,
|
||||
} from '@/application/collections/';
|
||||
import { Script } from '@/domain/Script';
|
||||
import { Category } from '@/domain/Category';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import type { ICategory } from '@/domain/ICategory';
|
||||
import type { Category } from '@/domain/Executables/Category/Category';
|
||||
import { CollectionCategory } from '@/domain/Executables/Category/CollectionCategory';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { parseDocs, type DocsParser } from './DocumentationParser';
|
||||
import { parseScript, type ScriptParser } from './Script/ScriptParser';
|
||||
import { createNodeDataValidator, type NodeDataValidator, type NodeDataValidatorFactory } from './NodeValidation/NodeDataValidator';
|
||||
import { NodeDataType } from './NodeValidation/NodeDataType';
|
||||
import type { ICategoryCollectionParseContext } from './Script/ICategoryCollectionParseContext';
|
||||
import { createExecutableDataValidator, type ExecutableValidator, type ExecutableValidatorFactory } from './Validation/ExecutableValidator';
|
||||
import { ExecutableType } from './Validation/ExecutableType';
|
||||
import type { CategoryCollectionSpecificUtilities } from './CategoryCollectionSpecificUtilities';
|
||||
|
||||
let categoryIdCounter = 0;
|
||||
|
||||
export function parseCategory(
|
||||
category: CategoryData,
|
||||
context: ICategoryCollectionParseContext,
|
||||
utilities: CategoryParserUtilities = DefaultCategoryParserUtilities,
|
||||
collectionUtilities: CategoryCollectionSpecificUtilities,
|
||||
categoryUtilities: CategoryParserUtilities = DefaultCategoryParserUtilities,
|
||||
): Category {
|
||||
return parseCategoryRecursively({
|
||||
categoryData: category,
|
||||
context,
|
||||
utilities,
|
||||
collectionUtilities,
|
||||
categoryUtilities,
|
||||
});
|
||||
}
|
||||
|
||||
interface CategoryParseContext {
|
||||
readonly categoryData: CategoryData;
|
||||
readonly context: ICategoryCollectionParseContext;
|
||||
readonly collectionUtilities: CategoryCollectionSpecificUtilities;
|
||||
readonly parentCategory?: CategoryData;
|
||||
readonly utilities: CategoryParserUtilities;
|
||||
readonly categoryUtilities: CategoryParserUtilities;
|
||||
}
|
||||
|
||||
function parseCategoryRecursively(
|
||||
@@ -41,24 +41,24 @@ function parseCategoryRecursively(
|
||||
subscripts: new Array<Script>(),
|
||||
};
|
||||
for (const data of context.categoryData.children) {
|
||||
parseNode({
|
||||
nodeData: data,
|
||||
parseExecutable({
|
||||
data,
|
||||
children,
|
||||
parent: context.categoryData,
|
||||
utilities: context.utilities,
|
||||
context: context.context,
|
||||
categoryUtilities: context.categoryUtilities,
|
||||
collectionUtilities: context.collectionUtilities,
|
||||
});
|
||||
}
|
||||
try {
|
||||
return context.utilities.createCategory({
|
||||
return context.categoryUtilities.createCategory({
|
||||
id: categoryIdCounter++,
|
||||
name: context.categoryData.category,
|
||||
docs: context.utilities.parseDocs(context.categoryData),
|
||||
docs: context.categoryUtilities.parseDocs(context.categoryData),
|
||||
subcategories: children.subcategories,
|
||||
scripts: children.subscripts,
|
||||
});
|
||||
} catch (error) {
|
||||
throw context.utilities.wrapError(
|
||||
throw context.categoryUtilities.wrapError(
|
||||
error,
|
||||
validator.createContextualErrorMessage('Failed to parse category.'),
|
||||
);
|
||||
@@ -67,12 +67,12 @@ function parseCategoryRecursively(
|
||||
|
||||
function ensureValidCategory(
|
||||
context: CategoryParseContext,
|
||||
): NodeDataValidator {
|
||||
): ExecutableValidator {
|
||||
const category = context.categoryData;
|
||||
const validator: NodeDataValidator = context.utilities.createValidator({
|
||||
type: NodeDataType.Category,
|
||||
selfNode: context.categoryData,
|
||||
parentNode: context.parentCategory,
|
||||
const validator: ExecutableValidator = context.categoryUtilities.createValidator({
|
||||
type: ExecutableType.Category,
|
||||
self: context.categoryData,
|
||||
parentCategory: context.parentCategory,
|
||||
});
|
||||
validator.assertDefined(category);
|
||||
validator.assertValidName(category.category);
|
||||
@@ -88,44 +88,43 @@ interface CategoryChildren {
|
||||
readonly subscripts: Script[];
|
||||
}
|
||||
|
||||
interface NodeParseContext {
|
||||
readonly nodeData: CategoryOrScriptData;
|
||||
interface ExecutableParseContext {
|
||||
readonly data: ExecutableData;
|
||||
readonly children: CategoryChildren;
|
||||
readonly parent: CategoryData;
|
||||
readonly context: ICategoryCollectionParseContext;
|
||||
|
||||
readonly utilities: CategoryParserUtilities;
|
||||
readonly collectionUtilities: CategoryCollectionSpecificUtilities;
|
||||
readonly categoryUtilities: CategoryParserUtilities;
|
||||
}
|
||||
|
||||
function parseNode(context: NodeParseContext) {
|
||||
const validator: NodeDataValidator = context.utilities.createValidator({
|
||||
selfNode: context.nodeData,
|
||||
parentNode: context.parent,
|
||||
function parseExecutable(context: ExecutableParseContext) {
|
||||
const validator: ExecutableValidator = context.categoryUtilities.createValidator({
|
||||
self: context.data,
|
||||
parentCategory: context.parent,
|
||||
});
|
||||
validator.assertDefined(context.nodeData);
|
||||
validator.assertDefined(context.data);
|
||||
validator.assert(
|
||||
() => isCategory(context.nodeData) || isScript(context.nodeData),
|
||||
'Node is neither a category or a script.',
|
||||
() => isCategory(context.data) || isScript(context.data),
|
||||
'Executable is neither a category or a script.',
|
||||
);
|
||||
if (isCategory(context.nodeData)) {
|
||||
if (isCategory(context.data)) {
|
||||
const subCategory = parseCategoryRecursively({
|
||||
categoryData: context.nodeData,
|
||||
context: context.context,
|
||||
categoryData: context.data,
|
||||
collectionUtilities: context.collectionUtilities,
|
||||
parentCategory: context.parent,
|
||||
utilities: context.utilities,
|
||||
categoryUtilities: context.categoryUtilities,
|
||||
});
|
||||
context.children.subcategories.push(subCategory);
|
||||
} else { // A script
|
||||
const script = context.utilities.parseScript(context.nodeData, context.context);
|
||||
const script = context.categoryUtilities.parseScript(context.data, context.collectionUtilities);
|
||||
context.children.subscripts.push(script);
|
||||
}
|
||||
}
|
||||
|
||||
function isScript(data: CategoryOrScriptData): data is ScriptData {
|
||||
function isScript(data: ExecutableData): data is ScriptData {
|
||||
return hasCode(data) || hasCall(data);
|
||||
}
|
||||
|
||||
function isCategory(data: CategoryOrScriptData): data is CategoryData {
|
||||
function isCategory(data: ExecutableData): data is CategoryData {
|
||||
return hasProperty(data, 'category');
|
||||
}
|
||||
|
||||
@@ -151,21 +150,21 @@ function hasProperty(
|
||||
}
|
||||
|
||||
export type CategoryFactory = (
|
||||
...parameters: ConstructorParameters<typeof Category>
|
||||
) => ICategory;
|
||||
...parameters: ConstructorParameters<typeof CollectionCategory>
|
||||
) => Category;
|
||||
|
||||
interface CategoryParserUtilities {
|
||||
readonly createCategory: CategoryFactory;
|
||||
readonly wrapError: ErrorWithContextWrapper;
|
||||
readonly createValidator: NodeDataValidatorFactory;
|
||||
readonly createValidator: ExecutableValidatorFactory;
|
||||
readonly parseScript: ScriptParser;
|
||||
readonly parseDocs: DocsParser;
|
||||
}
|
||||
|
||||
const DefaultCategoryParserUtilities: CategoryParserUtilities = {
|
||||
createCategory: (...parameters) => new Category(...parameters),
|
||||
createCategory: (...parameters) => new CollectionCategory(...parameters),
|
||||
wrapError: wrapErrorWithAdditionalContext,
|
||||
createValidator: createNodeDataValidator,
|
||||
createValidator: createExecutableDataValidator,
|
||||
parseScript,
|
||||
parseDocs,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FunctionParameterCollection } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
||||
import { FunctionParameterCollection } from '@/application/Parser/Executable/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
||||
import { FunctionCallArgumentCollection } from '../../Function/Call/Argument/FunctionCallArgumentCollection';
|
||||
import { ExpressionEvaluationContext, type IExpressionEvaluationContext } from './ExpressionEvaluationContext';
|
||||
import { ExpressionPosition } from './ExpressionPosition';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type IExpressionEvaluationContext, ExpressionEvaluationContext } from '@/application/Parser/Script/Compiler/Expressions/Expression/ExpressionEvaluationContext';
|
||||
import { type IExpressionEvaluationContext, ExpressionEvaluationContext } from '@/application/Parser/Executable/Script/Compiler/Expressions/Expression/ExpressionEvaluationContext';
|
||||
import { CompositeExpressionParser } from './Parser/CompositeExpressionParser';
|
||||
import type { IReadOnlyFunctionCallArgumentCollection } from '../Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||
import type { IExpressionsCompiler } from './IExpressionsCompiler';
|
||||
@@ -1,4 +1,4 @@
|
||||
import { FunctionParameter } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameter';
|
||||
import { FunctionParameter } from '@/application/Parser/Executable/Script/Compiler/Function/Parameter/FunctionParameter';
|
||||
import { RegexParser, type PrimitiveExpression } from '../Parser/Regex/RegexParser';
|
||||
import { ExpressionRegexBuilder } from '../Parser/Regex/ExpressionRegexBuilder';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// eslint-disable-next-line max-classes-per-file
|
||||
import type { IExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import { FunctionParameterCollection } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
||||
import { FunctionParameter } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameter';
|
||||
import type { IExpressionParser } from '@/application/Parser/Executable/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import { FunctionParameterCollection } from '@/application/Parser/Executable/Script/Compiler/Function/Parameter/FunctionParameterCollection';
|
||||
import { FunctionParameter } from '@/application/Parser/Executable/Script/Compiler/Function/Parameter/FunctionParameter';
|
||||
import { ExpressionPosition } from '../Expression/ExpressionPosition';
|
||||
import { ExpressionRegexBuilder } from '../Parser/Regex/ExpressionRegexBuilder';
|
||||
import { createPositionFromRegexFullMatch } from '../Expression/ExpressionPositionFactory';
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import type { ISharedFunctionCollection } from '@/application/Parser/Executable/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import type { FunctionCall } from '../FunctionCall';
|
||||
import type { SingleCallCompiler } from './SingleCall/SingleCallCompiler';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import type { ISharedFunctionCollection } from '@/application/Parser/Executable/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import type { CompiledCode } from './CompiledCode';
|
||||
import type { FunctionCall } from '../FunctionCall';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import { NewlineCodeSegmentMerger } from './CodeSegmentJoin/NewlineCodeSegmentMerger';
|
||||
import { AdaptiveFunctionCallCompiler } from './SingleCall/AdaptiveFunctionCallCompiler';
|
||||
import type { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { ISharedFunction } from '@/application/Parser/Executable/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { CompiledCode } from '../CompiledCode';
|
||||
import type { FunctionCallCompilationContext } from '../FunctionCallCompilationContext';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
|
||||
export interface ArgumentCompiler {
|
||||
createCompiledNestedCall(
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import { FunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import { ParsedFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/ParsedFunctionCall';
|
||||
import type { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import { FunctionCallArgumentCollection } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import { ParsedFunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/ParsedFunctionCall';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import type { ArgumentCompiler } from './ArgumentCompiler';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { FunctionBodyType, type ISharedFunction } from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { FunctionBodyType, type ISharedFunction } from '@/application/Parser/Executable/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { CompiledCode } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||
import type { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
|
||||
|
||||
export class InlineFunctionCallCompiler implements SingleCallCompilerStrategy {
|
||||
@@ -1,10 +1,10 @@
|
||||
import {
|
||||
type CallFunctionBody, FunctionBodyType,
|
||||
type ISharedFunction,
|
||||
} from '@/application/Parser/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import type { CompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||
} from '@/application/Parser/Executable/Script/Compiler/Function/ISharedFunction';
|
||||
import type { FunctionCall } from '@/application/Parser/Executable/Script/Compiler/Function/Call/FunctionCall';
|
||||
import type { FunctionCallCompilationContext } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/FunctionCallCompilationContext';
|
||||
import type { CompiledCode } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Compiler/CompiledCode';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { NestedFunctionArgumentCompiler } from './Argument/NestedFunctionArgumentCompiler';
|
||||
import type { SingleCallCompilerStrategy } from '../SingleCallCompilerStrategy';
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FunctionData } from '@/application/collections/';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ISharedFunctionCollection } from './ISharedFunctionCollection';
|
||||
|
||||
export interface ISharedFunctionsParser {
|
||||
@@ -2,11 +2,11 @@ import type {
|
||||
FunctionData, CodeInstruction, CodeFunctionData, CallFunctionData,
|
||||
CallInstruction, ParameterDefinitionData,
|
||||
} from '@/application/collections/';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CodeValidator } from '@/application/Parser/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmptyLines';
|
||||
import { NoDuplicatedLines } from '@/application/Parser/Script/Validation/Rules/NoDuplicatedLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CodeValidator } from '@/application/Parser/Executable/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoEmptyLines';
|
||||
import { NoDuplicatedLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoDuplicatedLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { isArray, isNullOrUndefined, isPlainObject } from '@/TypeHelpers';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { createFunctionWithInlineCode, createCallerFunction } from './SharedFunction';
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ScriptData } from '@/application/collections/';
|
||||
import type { IScriptCode } from '@/domain/IScriptCode';
|
||||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode';
|
||||
|
||||
export interface IScriptCompiler {
|
||||
canCompile(script: ScriptData): boolean;
|
||||
compile(script: ScriptData): IScriptCode;
|
||||
compile(script: ScriptData): ScriptCode;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { FunctionData, ScriptData, CallInstruction } from '@/application/collections/';
|
||||
import type { IScriptCode } from '@/domain/IScriptCode';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CodeValidator } from '@/application/Parser/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmptyLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
||||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CodeValidator } from '@/application/Parser/Executable/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoEmptyLines';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import { createScriptCode, type ScriptCodeFactory } from '@/domain/ScriptCodeFactory';
|
||||
import { createScriptCode, type ScriptCodeFactory } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import { SharedFunctionsParser } from './Function/SharedFunctionsParser';
|
||||
import { FunctionCallSequenceCompiler } from './Function/Call/Compiler/FunctionCallSequenceCompiler';
|
||||
import { parseFunctionCalls } from './Function/Call/FunctionCallParser';
|
||||
@@ -34,7 +34,7 @@ export class ScriptCompiler implements IScriptCompiler {
|
||||
return hasCall(script);
|
||||
}
|
||||
|
||||
public compile(script: ScriptData): IScriptCode {
|
||||
public compile(script: ScriptData): ScriptCode {
|
||||
try {
|
||||
if (!hasCall(script)) {
|
||||
throw new Error('Script does include any calls.');
|
||||
@@ -1,44 +1,44 @@
|
||||
import type { ScriptData, CodeScriptData, CallScriptData } from '@/application/collections/';
|
||||
import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmptyLines';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { Script } from '@/domain/Script';
|
||||
import { RecommendationLevel } from '@/domain/RecommendationLevel';
|
||||
import type { IScriptCode } from '@/domain/IScriptCode';
|
||||
import type { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Executable/Script/Validation/Rules/NoEmptyLines';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CollectionScript } from '@/domain/Executables/Script/CollectionScript';
|
||||
import { RecommendationLevel } from '@/domain/Executables/Script/RecommendationLevel';
|
||||
import type { ScriptCode } from '@/domain/Executables/Script/Code/ScriptCode';
|
||||
import type { ICodeValidator } from '@/application/Parser/Executable/Script/Validation/ICodeValidator';
|
||||
import { wrapErrorWithAdditionalContext, type ErrorWithContextWrapper } from '@/application/Parser/ContextualError';
|
||||
import type { ScriptCodeFactory } from '@/domain/ScriptCodeFactory';
|
||||
import { createScriptCode } from '@/domain/ScriptCodeFactory';
|
||||
import type { IScript } from '@/domain/IScript';
|
||||
import type { ScriptCodeFactory } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import { createScriptCode } from '@/domain/Executables/Script/Code/ScriptCodeFactory';
|
||||
import type { Script } from '@/domain/Executables/Script/Script';
|
||||
import { createEnumParser, type IEnumParser } from '@/application/Common/Enum';
|
||||
import { parseDocs, type DocsParser } from '../DocumentationParser';
|
||||
import { createEnumParser, type IEnumParser } from '../../Common/Enum';
|
||||
import { NodeDataType } from '../NodeValidation/NodeDataType';
|
||||
import { createNodeDataValidator, type NodeDataValidator, type NodeDataValidatorFactory } from '../NodeValidation/NodeDataValidator';
|
||||
import { ExecutableType } from '../Validation/ExecutableType';
|
||||
import { createExecutableDataValidator, type ExecutableValidator, type ExecutableValidatorFactory } from '../Validation/ExecutableValidator';
|
||||
import { CodeValidator } from './Validation/CodeValidator';
|
||||
import { NoDuplicatedLines } from './Validation/Rules/NoDuplicatedLines';
|
||||
import type { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||
import type { CategoryCollectionSpecificUtilities } from '../CategoryCollectionSpecificUtilities';
|
||||
|
||||
export interface ScriptParser {
|
||||
(
|
||||
data: ScriptData,
|
||||
context: ICategoryCollectionParseContext,
|
||||
utilities?: ScriptParserUtilities,
|
||||
): IScript;
|
||||
collectionUtilities: CategoryCollectionSpecificUtilities,
|
||||
scriptUtilities?: ScriptParserUtilities,
|
||||
): Script;
|
||||
}
|
||||
|
||||
export const parseScript: ScriptParser = (
|
||||
data,
|
||||
context,
|
||||
collectionUtilities,
|
||||
utilities = DefaultScriptParserUtilities,
|
||||
) => {
|
||||
const validator = utilities.createValidator({
|
||||
type: NodeDataType.Script,
|
||||
selfNode: data,
|
||||
type: ExecutableType.Script,
|
||||
self: data,
|
||||
});
|
||||
validateScript(data, validator);
|
||||
try {
|
||||
const script = utilities.createScript({
|
||||
name: data.name,
|
||||
code: parseCode(data, context, utilities.codeValidator, utilities.createCode),
|
||||
code: parseCode(data, collectionUtilities, utilities.codeValidator, utilities.createCode),
|
||||
docs: utilities.parseDocs(data),
|
||||
level: parseLevel(data.recommend, utilities.levelParser),
|
||||
});
|
||||
@@ -63,21 +63,21 @@ function parseLevel(
|
||||
|
||||
function parseCode(
|
||||
script: ScriptData,
|
||||
context: ICategoryCollectionParseContext,
|
||||
collectionUtilities: CategoryCollectionSpecificUtilities,
|
||||
codeValidator: ICodeValidator,
|
||||
createCode: ScriptCodeFactory,
|
||||
): IScriptCode {
|
||||
if (context.compiler.canCompile(script)) {
|
||||
return context.compiler.compile(script);
|
||||
): ScriptCode {
|
||||
if (collectionUtilities.compiler.canCompile(script)) {
|
||||
return collectionUtilities.compiler.compile(script);
|
||||
}
|
||||
const codeScript = script as CodeScriptData; // Must be inline code if it cannot be compiled
|
||||
const code = createCode(codeScript.code, codeScript.revertCode);
|
||||
validateHardcodedCodeWithoutCalls(code, codeValidator, context.syntax);
|
||||
validateHardcodedCodeWithoutCalls(code, codeValidator, collectionUtilities.syntax);
|
||||
return code;
|
||||
}
|
||||
|
||||
function validateHardcodedCodeWithoutCalls(
|
||||
scriptCode: IScriptCode,
|
||||
scriptCode: ScriptCode,
|
||||
validator: ICodeValidator,
|
||||
syntax: ILanguageSyntax,
|
||||
) {
|
||||
@@ -93,7 +93,7 @@ function validateHardcodedCodeWithoutCalls(
|
||||
|
||||
function validateScript(
|
||||
script: ScriptData,
|
||||
validator: NodeDataValidator,
|
||||
validator: ExecutableValidator,
|
||||
): asserts script is NonNullable<ScriptData> {
|
||||
validator.assertDefined(script);
|
||||
validator.assertValidName(script.name);
|
||||
@@ -116,17 +116,17 @@ interface ScriptParserUtilities {
|
||||
readonly createScript: ScriptFactory;
|
||||
readonly codeValidator: ICodeValidator;
|
||||
readonly wrapError: ErrorWithContextWrapper;
|
||||
readonly createValidator: NodeDataValidatorFactory;
|
||||
readonly createValidator: ExecutableValidatorFactory;
|
||||
readonly createCode: ScriptCodeFactory;
|
||||
readonly parseDocs: DocsParser;
|
||||
}
|
||||
|
||||
export type ScriptFactory = (
|
||||
...parameters: ConstructorParameters<typeof Script>
|
||||
) => IScript;
|
||||
...parameters: ConstructorParameters<typeof CollectionScript>
|
||||
) => Script;
|
||||
|
||||
const createScript: ScriptFactory = (...parameters) => {
|
||||
return new Script(...parameters);
|
||||
return new CollectionScript(...parameters);
|
||||
};
|
||||
|
||||
const DefaultScriptParserUtilities: ScriptParserUtilities = {
|
||||
@@ -134,7 +134,7 @@ const DefaultScriptParserUtilities: ScriptParserUtilities = {
|
||||
createScript,
|
||||
codeValidator: CodeValidator.instance,
|
||||
wrapError: wrapErrorWithAdditionalContext,
|
||||
createValidator: createNodeDataValidator,
|
||||
createValidator: createExecutableDataValidator,
|
||||
createCode: createScriptCode,
|
||||
parseDocs,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ICodeLine } from '../ICodeLine';
|
||||
import type { ICodeValidationRule, IInvalidCodeLine } from '../ICodeValidationRule';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
|
||||
const BatchFileCommonCodeParts = ['(', ')', 'else', '||'];
|
||||
const PowerShellCommonCodeParts = ['{', '}'];
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
|
||||
export class ShellScriptSyntax implements ILanguageSyntax {
|
||||
public readonly commentDelimiters = ['#'];
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
||||
import { ScriptingLanguageFactory } from '@/application/Common/ScriptingLanguage/ScriptingLanguageFactory';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import type { ILanguageSyntax } from '@/application/Parser/Executable/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { BatchFileSyntax } from './BatchFileSyntax';
|
||||
import { ShellScriptSyntax } from './ShellScriptSyntax';
|
||||
import type { ISyntaxFactory } from './ISyntaxFactory';
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { CategoryData, ScriptData, ExecutableData } from '@/application/collections/';
|
||||
import { ExecutableType } from './ExecutableType';
|
||||
|
||||
export type ExecutableErrorContext = {
|
||||
readonly parentCategory?: CategoryData;
|
||||
} & (CategoryErrorContext | ScriptErrorContext | UnknownExecutableErrorContext);
|
||||
|
||||
export type CategoryErrorContext = {
|
||||
readonly type: ExecutableType.Category;
|
||||
readonly self: CategoryData;
|
||||
readonly parentCategory?: CategoryData;
|
||||
};
|
||||
|
||||
export type ScriptErrorContext = {
|
||||
readonly type: ExecutableType.Script;
|
||||
readonly self: ScriptData;
|
||||
readonly parentCategory?: CategoryData;
|
||||
};
|
||||
|
||||
export type UnknownExecutableErrorContext = {
|
||||
readonly type?: undefined;
|
||||
readonly self: ExecutableData;
|
||||
readonly parentCategory?: CategoryData;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { ExecutableData } from '@/application/collections/';
|
||||
import { ExecutableType } from './ExecutableType';
|
||||
import type { ExecutableErrorContext } from './ExecutableErrorContext';
|
||||
|
||||
export interface ExecutableContextErrorMessageCreator {
|
||||
(
|
||||
errorMessage: string,
|
||||
context: ExecutableErrorContext,
|
||||
): string;
|
||||
}
|
||||
|
||||
export const createExecutableContextErrorMessage: ExecutableContextErrorMessageCreator = (
|
||||
errorMessage,
|
||||
context,
|
||||
) => {
|
||||
let message = '';
|
||||
if (context.type !== undefined) {
|
||||
message += `${ExecutableType[context.type]}: `;
|
||||
}
|
||||
message += errorMessage;
|
||||
message += `\n${getErrorContextDetails(context)}`;
|
||||
return message;
|
||||
};
|
||||
|
||||
function getErrorContextDetails(context: ExecutableErrorContext): string {
|
||||
let output = `Self: ${printExecutable(context.self)}`;
|
||||
if (context.parentCategory) {
|
||||
output += `\nParent: ${printExecutable(context.parentCategory)}`;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function printExecutable(executable: ExecutableData): string {
|
||||
return JSON.stringify(executable, undefined, 2);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum ExecutableType {
|
||||
Script,
|
||||
Category,
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import { isString } from '@/TypeHelpers';
|
||||
import { type NodeDataErrorContext } from './NodeDataErrorContext';
|
||||
import { createNodeContextErrorMessage, type NodeContextErrorMessageCreator } from './NodeDataErrorContextMessage';
|
||||
import type { NodeData } from './NodeData';
|
||||
import type { ExecutableData } from '@/application/collections/';
|
||||
import { type ExecutableErrorContext } from './ExecutableErrorContext';
|
||||
import { createExecutableContextErrorMessage, type ExecutableContextErrorMessageCreator } from './ExecutableErrorContextMessage';
|
||||
|
||||
export interface NodeDataValidatorFactory {
|
||||
(context: NodeDataErrorContext): NodeDataValidator;
|
||||
export interface ExecutableValidatorFactory {
|
||||
(context: ExecutableErrorContext): ExecutableValidator;
|
||||
}
|
||||
|
||||
export interface NodeDataValidator {
|
||||
export interface ExecutableValidator {
|
||||
assertValidName(nameValue: string): void;
|
||||
assertDefined(
|
||||
node: NodeData | undefined,
|
||||
): asserts node is NonNullable<NodeData> & void;
|
||||
data: ExecutableData | undefined,
|
||||
): asserts data is NonNullable<ExecutableData> & void;
|
||||
assert(
|
||||
validationPredicate: () => boolean,
|
||||
errorMessage: string,
|
||||
@@ -19,14 +19,14 @@ export interface NodeDataValidator {
|
||||
createContextualErrorMessage(errorMessage: string): string;
|
||||
}
|
||||
|
||||
export const createNodeDataValidator
|
||||
: NodeDataValidatorFactory = (context) => new ContextualNodeDataValidator(context);
|
||||
export const createExecutableDataValidator
|
||||
: ExecutableValidatorFactory = (context) => new ContextualExecutableValidator(context);
|
||||
|
||||
export class ContextualNodeDataValidator implements NodeDataValidator {
|
||||
export class ContextualExecutableValidator implements ExecutableValidator {
|
||||
constructor(
|
||||
private readonly context: NodeDataErrorContext,
|
||||
private readonly context: ExecutableErrorContext,
|
||||
private readonly createErrorMessage
|
||||
: NodeContextErrorMessageCreator = createNodeContextErrorMessage,
|
||||
: ExecutableContextErrorMessageCreator = createExecutableContextErrorMessage,
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -40,11 +40,11 @@ export class ContextualNodeDataValidator implements NodeDataValidator {
|
||||
}
|
||||
|
||||
public assertDefined(
|
||||
node: NodeData,
|
||||
): asserts node is NonNullable<NodeData> {
|
||||
data: ExecutableData,
|
||||
): asserts data is NonNullable<ExecutableData> {
|
||||
this.assert(
|
||||
() => node !== undefined && node !== null && Object.keys(node).length > 0,
|
||||
'missing node data',
|
||||
() => data !== undefined && data !== null && Object.keys(data).length > 0,
|
||||
'missing executable data',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import type { ScriptData, CategoryData } from '@/application/collections/';
|
||||
|
||||
export type NodeData = CategoryData | ScriptData;
|
||||
@@ -1,25 +0,0 @@
|
||||
import type { CategoryData, ScriptData } from '@/application/collections/';
|
||||
import { NodeDataType } from './NodeDataType';
|
||||
import type { NodeData } from './NodeData';
|
||||
|
||||
export type NodeDataErrorContext = {
|
||||
readonly parentNode?: CategoryData;
|
||||
} & (CategoryNodeErrorContext | ScriptNodeErrorContext | UnknownNodeErrorContext);
|
||||
|
||||
export type CategoryNodeErrorContext = {
|
||||
readonly type: NodeDataType.Category;
|
||||
readonly selfNode: CategoryData;
|
||||
readonly parentNode?: CategoryData;
|
||||
};
|
||||
|
||||
export type ScriptNodeErrorContext = {
|
||||
readonly type: NodeDataType.Script;
|
||||
readonly selfNode: ScriptData;
|
||||
readonly parentNode?: CategoryData;
|
||||
};
|
||||
|
||||
export type UnknownNodeErrorContext = {
|
||||
readonly type?: undefined;
|
||||
readonly selfNode: NodeData;
|
||||
readonly parentNode?: CategoryData;
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
import { NodeDataType } from './NodeDataType';
|
||||
import type { NodeDataErrorContext } from './NodeDataErrorContext';
|
||||
import type { NodeData } from './NodeData';
|
||||
|
||||
export interface NodeContextErrorMessageCreator {
|
||||
(
|
||||
errorMessage: string,
|
||||
context: NodeDataErrorContext,
|
||||
): string;
|
||||
}
|
||||
|
||||
export const createNodeContextErrorMessage: NodeContextErrorMessageCreator = (
|
||||
errorMessage,
|
||||
context,
|
||||
) => {
|
||||
let message = '';
|
||||
if (context.type !== undefined) {
|
||||
message += `${NodeDataType[context.type]}: `;
|
||||
}
|
||||
message += errorMessage;
|
||||
message += `\n${getErrorContextDetails(context)}`;
|
||||
return message;
|
||||
};
|
||||
|
||||
function getErrorContextDetails(context: NodeDataErrorContext): string {
|
||||
let output = `Self: ${printNodeDataAsJson(context.selfNode)}`;
|
||||
if (context.parentNode) {
|
||||
output += `\nParent: ${printNodeDataAsJson(context.parentNode)}`;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function printNodeDataAsJson(node: NodeData): string {
|
||||
return JSON.stringify(node, undefined, 2);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum NodeDataType {
|
||||
Script,
|
||||
Category,
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { FunctionData } from '@/application/collections/';
|
||||
import type { IScriptingDefinition } from '@/domain/IScriptingDefinition';
|
||||
import { ScriptCompiler } from './Compiler/ScriptCompiler';
|
||||
import { SyntaxFactory } from './Validation/Syntax/SyntaxFactory';
|
||||
import type { ILanguageSyntax } from './Validation/Syntax/ILanguageSyntax';
|
||||
import type { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import type { ICategoryCollectionParseContext } from './ICategoryCollectionParseContext';
|
||||
import type { ISyntaxFactory } from './Validation/Syntax/ISyntaxFactory';
|
||||
|
||||
export class CategoryCollectionParseContext implements ICategoryCollectionParseContext {
|
||||
public readonly compiler: IScriptCompiler;
|
||||
|
||||
public readonly syntax: ILanguageSyntax;
|
||||
|
||||
constructor(
|
||||
functionsData: ReadonlyArray<FunctionData> | undefined,
|
||||
scripting: IScriptingDefinition,
|
||||
syntaxFactory: ISyntaxFactory = new SyntaxFactory(),
|
||||
) {
|
||||
this.syntax = syntaxFactory.create(scripting.language);
|
||||
this.compiler = new ScriptCompiler(functionsData ?? [], this.syntax);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { IScriptCompiler } from './Compiler/IScriptCompiler';
|
||||
import type { ILanguageSyntax } from './Validation/Syntax/ILanguageSyntax';
|
||||
|
||||
export interface ICategoryCollectionParseContext {
|
||||
readonly compiler: IScriptCompiler;
|
||||
readonly syntax: ILanguageSyntax;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { ParameterSubstitutionParser } from '@/application/Parser/Script/Compiler/Expressions/SyntaxParsers/ParameterSubstitutionParser';
|
||||
import { CompositeExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/CompositeExpressionParser';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { IExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/IExpressionsCompiler';
|
||||
import { ParameterSubstitutionParser } from '@/application/Parser/Executable/Script/Compiler/Expressions/SyntaxParsers/ParameterSubstitutionParser';
|
||||
import { CompositeExpressionParser } from '@/application/Parser/Executable/Script/Compiler/Expressions/Parser/CompositeExpressionParser';
|
||||
import { ExpressionsCompiler } from '@/application/Parser/Executable/Script/Compiler/Expressions/ExpressionsCompiler';
|
||||
import type { ProjectDetails } from '@/domain/Project/ProjectDetails';
|
||||
import { FunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import type { IExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import { FunctionCallArgumentCollection } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Argument/FunctionCallArgumentCollection';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Executable/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import type { IExpressionParser } from '@/application/Parser/Executable/Script/Compiler/Expressions/Parser/IExpressionParser';
|
||||
import type { ICodeSubstituter } from './ICodeSubstituter';
|
||||
|
||||
export class CodeSubstituter implements ICodeSubstituter {
|
||||
|
||||
12
src/application/collections/collection.yaml.d.ts
vendored
12
src/application/collections/collection.yaml.d.ts
vendored
@@ -1,4 +1,8 @@
|
||||
declare module '@/application/collections/*' {
|
||||
export interface ExecutableDefinition extends DocumentableData {
|
||||
|
||||
}
|
||||
|
||||
export interface CollectionData {
|
||||
readonly os: string;
|
||||
readonly scripting: ScriptingDefinitionData;
|
||||
@@ -6,12 +10,12 @@ declare module '@/application/collections/*' {
|
||||
readonly functions?: ReadonlyArray<FunctionData>;
|
||||
}
|
||||
|
||||
export interface CategoryData extends DocumentableData {
|
||||
readonly children: ReadonlyArray<CategoryOrScriptData>;
|
||||
export interface CategoryData extends ExecutableDefinition {
|
||||
readonly children: ReadonlyArray<ExecutableData>;
|
||||
readonly category: string;
|
||||
}
|
||||
|
||||
export type CategoryOrScriptData = CategoryData | ScriptData;
|
||||
export type ExecutableData = CategoryData | ScriptData;
|
||||
export type DocumentationData = ReadonlyArray<string> | string | undefined;
|
||||
|
||||
export interface DocumentableData {
|
||||
@@ -56,7 +60,7 @@ declare module '@/application/collections/*' {
|
||||
|
||||
export type FunctionCallsData = readonly FunctionCallData[] | FunctionCallData | undefined;
|
||||
|
||||
export type ScriptDefinition = DocumentableData & {
|
||||
export type ScriptDefinition = ExecutableDefinition & {
|
||||
readonly name: string;
|
||||
readonly recommend?: string;
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getEnumValues, assertInRange } from '@/application/Common/Enum';
|
||||
import { RecommendationLevel } from './RecommendationLevel';
|
||||
import { RecommendationLevel } from './Executables/Script/RecommendationLevel';
|
||||
import { OperatingSystem } from './OperatingSystem';
|
||||
import type { IEntity } from '../infrastructure/Entity/IEntity';
|
||||
import type { ICategory } from './ICategory';
|
||||
import type { IScript } from './IScript';
|
||||
import type { Category } from './Executables/Category/Category';
|
||||
import type { Script } from './Executables/Script/Script';
|
||||
import type { IScriptingDefinition } from './IScriptingDefinition';
|
||||
import type { ICategoryCollection } from './ICategoryCollection';
|
||||
|
||||
@@ -16,7 +16,7 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
|
||||
constructor(
|
||||
public readonly os: OperatingSystem,
|
||||
public readonly actions: ReadonlyArray<ICategory>,
|
||||
public readonly actions: ReadonlyArray<Category>,
|
||||
public readonly scripting: IScriptingDefinition,
|
||||
) {
|
||||
this.queryable = makeQueryable(actions);
|
||||
@@ -26,7 +26,7 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
ensureNoDuplicates(this.queryable.allScripts);
|
||||
}
|
||||
|
||||
public getCategory(categoryId: number): ICategory {
|
||||
public getCategory(categoryId: number): Category {
|
||||
const category = this.queryable.allCategories.find((c) => c.id === categoryId);
|
||||
if (!category) {
|
||||
throw new Error(`Missing category with ID: "${categoryId}"`);
|
||||
@@ -34,13 +34,13 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
return category;
|
||||
}
|
||||
|
||||
public getScriptsByLevel(level: RecommendationLevel): readonly IScript[] {
|
||||
public getScriptsByLevel(level: RecommendationLevel): readonly Script[] {
|
||||
assertInRange(level, RecommendationLevel);
|
||||
const scripts = this.queryable.scriptsByLevel.get(level);
|
||||
return scripts ?? [];
|
||||
}
|
||||
|
||||
public getScript(scriptId: string): IScript {
|
||||
public getScript(scriptId: string): Script {
|
||||
const script = this.queryable.allScripts.find((s) => s.id === scriptId);
|
||||
if (!script) {
|
||||
throw new Error(`missing script: ${scriptId}`);
|
||||
@@ -48,11 +48,11 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
return script;
|
||||
}
|
||||
|
||||
public getAllScripts(): IScript[] {
|
||||
public getAllScripts(): Script[] {
|
||||
return this.queryable.allScripts;
|
||||
}
|
||||
|
||||
public getAllCategories(): ICategory[] {
|
||||
public getAllCategories(): Category[] {
|
||||
return this.queryable.allCategories;
|
||||
}
|
||||
}
|
||||
@@ -73,9 +73,9 @@ function ensureNoDuplicates<TKey>(entities: ReadonlyArray<IEntity<TKey>>) {
|
||||
}
|
||||
|
||||
interface IQueryableCollection {
|
||||
allCategories: ICategory[];
|
||||
allScripts: IScript[];
|
||||
scriptsByLevel: Map<RecommendationLevel, readonly IScript[]>;
|
||||
allCategories: Category[];
|
||||
allScripts: Script[];
|
||||
scriptsByLevel: Map<RecommendationLevel, readonly Script[]>;
|
||||
}
|
||||
|
||||
function ensureValid(application: IQueryableCollection) {
|
||||
@@ -83,13 +83,13 @@ function ensureValid(application: IQueryableCollection) {
|
||||
ensureValidScripts(application.allScripts);
|
||||
}
|
||||
|
||||
function ensureValidCategories(allCategories: readonly ICategory[]) {
|
||||
function ensureValidCategories(allCategories: readonly Category[]) {
|
||||
if (!allCategories.length) {
|
||||
throw new Error('must consist of at least one category');
|
||||
}
|
||||
}
|
||||
|
||||
function ensureValidScripts(allScripts: readonly IScript[]) {
|
||||
function ensureValidScripts(allScripts: readonly Script[]) {
|
||||
if (!allScripts.length) {
|
||||
throw new Error('must consist of at least one script');
|
||||
}
|
||||
@@ -102,8 +102,8 @@ function ensureValidScripts(allScripts: readonly IScript[]) {
|
||||
}
|
||||
|
||||
function flattenApplication(
|
||||
categories: ReadonlyArray<ICategory>,
|
||||
): [ICategory[], IScript[]] {
|
||||
categories: ReadonlyArray<Category>,
|
||||
): [Category[], Script[]] {
|
||||
const [subCategories, subScripts] = (categories || [])
|
||||
// Parse children
|
||||
.map((category) => flattenApplication(category.subCategories))
|
||||
@@ -113,7 +113,7 @@ function flattenApplication(
|
||||
[...previousCategories, ...currentCategories],
|
||||
[...previousScripts, ...currentScripts],
|
||||
];
|
||||
}, [new Array<ICategory>(), new Array<IScript>()]);
|
||||
}, [new Array<Category>(), new Array<Script>()]);
|
||||
return [
|
||||
[
|
||||
...(categories || []),
|
||||
@@ -127,7 +127,7 @@ function flattenApplication(
|
||||
}
|
||||
|
||||
function makeQueryable(
|
||||
actions: ReadonlyArray<ICategory>,
|
||||
actions: ReadonlyArray<Category>,
|
||||
): IQueryableCollection {
|
||||
const flattened = flattenApplication(actions);
|
||||
return {
|
||||
@@ -138,8 +138,8 @@ function makeQueryable(
|
||||
}
|
||||
|
||||
function groupByLevel(
|
||||
allScripts: readonly IScript[],
|
||||
): Map<RecommendationLevel, readonly IScript[]> {
|
||||
allScripts: readonly Script[],
|
||||
): Map<RecommendationLevel, readonly Script[]> {
|
||||
return getEnumValues(RecommendationLevel)
|
||||
.map((level) => ({
|
||||
level,
|
||||
@@ -150,5 +150,5 @@ function groupByLevel(
|
||||
.reduce((map, group) => {
|
||||
map.set(group.level, group.scripts);
|
||||
return map;
|
||||
}, new Map<RecommendationLevel, readonly IScript[]>());
|
||||
}, new Map<RecommendationLevel, readonly Script[]>());
|
||||
}
|
||||
|
||||
11
src/domain/Executables/Category/Category.ts
Normal file
11
src/domain/Executables/Category/Category.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { Script } from '../Script/Script';
|
||||
import type { Executable } from '../Executable';
|
||||
|
||||
export interface Category extends Executable<number> {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
readonly subCategories: ReadonlyArray<Category>;
|
||||
readonly scripts: ReadonlyArray<Script>;
|
||||
includes(script: Script): boolean;
|
||||
getAllScriptsRecursively(): ReadonlyArray<Script>;
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
import { BaseEntity } from '../infrastructure/Entity/BaseEntity';
|
||||
import type { ICategory } from './ICategory';
|
||||
import type { IScript } from './IScript';
|
||||
import { BaseEntity } from '@/infrastructure/Entity/BaseEntity';
|
||||
import type { Category } from './Category';
|
||||
import type { Script } from '../Script/Script';
|
||||
|
||||
export class Category extends BaseEntity<number> implements ICategory {
|
||||
private allSubScripts?: ReadonlyArray<IScript> = undefined;
|
||||
export class CollectionCategory extends BaseEntity<number> implements Category {
|
||||
private allSubScripts?: ReadonlyArray<Script> = undefined;
|
||||
|
||||
public readonly name: string;
|
||||
|
||||
public readonly docs: ReadonlyArray<string>;
|
||||
|
||||
public readonly subCategories: ReadonlyArray<ICategory>;
|
||||
public readonly subCategories: ReadonlyArray<Category>;
|
||||
|
||||
public readonly scripts: ReadonlyArray<IScript>;
|
||||
public readonly scripts: ReadonlyArray<Script>;
|
||||
|
||||
constructor(parameters: CategoryInitParameters) {
|
||||
super(parameters.id);
|
||||
@@ -22,11 +22,11 @@ export class Category extends BaseEntity<number> implements ICategory {
|
||||
this.scripts = parameters.scripts;
|
||||
}
|
||||
|
||||
public includes(script: IScript): boolean {
|
||||
public includes(script: Script): boolean {
|
||||
return this.getAllScriptsRecursively().some((childScript) => childScript.id === script.id);
|
||||
}
|
||||
|
||||
public getAllScriptsRecursively(): readonly IScript[] {
|
||||
public getAllScriptsRecursively(): readonly Script[] {
|
||||
if (!this.allSubScripts) {
|
||||
this.allSubScripts = parseScriptsRecursively(this);
|
||||
}
|
||||
@@ -38,11 +38,11 @@ export interface CategoryInitParameters {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
readonly docs: ReadonlyArray<string>;
|
||||
readonly subcategories: ReadonlyArray<ICategory>;
|
||||
readonly scripts: ReadonlyArray<IScript>;
|
||||
readonly subcategories: ReadonlyArray<Category>;
|
||||
readonly scripts: ReadonlyArray<Script>;
|
||||
}
|
||||
|
||||
function parseScriptsRecursively(category: ICategory): ReadonlyArray<IScript> {
|
||||
function parseScriptsRecursively(category: Category): ReadonlyArray<Script> {
|
||||
return [
|
||||
...category.scripts,
|
||||
...category.subCategories.flatMap((c) => c.getAllScriptsRecursively()),
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface IDocumentable {
|
||||
export interface Documentable {
|
||||
readonly docs: ReadonlyArray<string>;
|
||||
}
|
||||
6
src/domain/Executables/Executable.ts
Normal file
6
src/domain/Executables/Executable.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import type { IEntity } from '@/infrastructure/Entity/IEntity';
|
||||
import type { Documentable } from './Documentable';
|
||||
|
||||
export interface Executable<TExecutableKey>
|
||||
extends Documentable, IEntity<TExecutableKey> {
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { IScriptCode } from './IScriptCode';
|
||||
import type { ScriptCode } from './ScriptCode';
|
||||
|
||||
export class ScriptCode implements IScriptCode {
|
||||
export class DistinctReversibleScriptCode implements ScriptCode {
|
||||
constructor(
|
||||
public readonly execute: string,
|
||||
public readonly revert: string | undefined,
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface IScriptCode {
|
||||
export interface ScriptCode {
|
||||
readonly execute: string;
|
||||
readonly revert?: string;
|
||||
}
|
||||
12
src/domain/Executables/Script/Code/ScriptCodeFactory.ts
Normal file
12
src/domain/Executables/Script/Code/ScriptCodeFactory.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { DistinctReversibleScriptCode } from './DistinctReversibleScriptCode';
|
||||
import type { ScriptCode } from './ScriptCode';
|
||||
|
||||
export interface ScriptCodeFactory {
|
||||
(
|
||||
...args: ConstructorParameters<typeof DistinctReversibleScriptCode>
|
||||
): ScriptCode;
|
||||
}
|
||||
|
||||
export const createScriptCode: ScriptCodeFactory = (
|
||||
...args
|
||||
) => new DistinctReversibleScriptCode(...args);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user