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