Refactor code to comply with ESLint rules
Major refactoring using ESLint with rules from AirBnb and Vue. Enable most of the ESLint rules and do necessary linting in the code. Also add more information for rules that are disabled to describe what they are and why they are disabled. Allow logging (`console.log`) in test files, and in development mode (e.g. when working with `npm run serve`), but disable it when environment is production (as pre-configured by Vue). Also add flag (`--mode production`) in `lint:eslint` command so production linting is executed earlier in lifecycle. Disable rules that requires a separate work. Such as ESLint rules that are broken in TypeScript: no-useless-constructor (eslint/eslint#14118) and no-shadow (eslint/eslint#13014).
This commit is contained in:
@@ -1,13 +1,14 @@
|
||||
import { IFunctionCallArgument } from './IFunctionCallArgument';
|
||||
import { ensureValidParameterName } from '../../Shared/ParameterNameValidator';
|
||||
import { IFunctionCallArgument } from './IFunctionCallArgument';
|
||||
|
||||
export class FunctionCallArgument implements IFunctionCallArgument {
|
||||
constructor(
|
||||
public readonly parameterName: string,
|
||||
public readonly argumentValue: string) {
|
||||
ensureValidParameterName(parameterName);
|
||||
if (!argumentValue) {
|
||||
throw new Error(`undefined argument value for "${parameterName}"`);
|
||||
}
|
||||
constructor(
|
||||
public readonly parameterName: string,
|
||||
public readonly argumentValue: string,
|
||||
) {
|
||||
ensureValidParameterName(parameterName);
|
||||
if (!argumentValue) {
|
||||
throw new Error(`undefined argument value for "${parameterName}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,33 +2,37 @@ import { IFunctionCallArgument } from './IFunctionCallArgument';
|
||||
import { IFunctionCallArgumentCollection } from './IFunctionCallArgumentCollection';
|
||||
|
||||
export class FunctionCallArgumentCollection implements IFunctionCallArgumentCollection {
|
||||
private readonly arguments = new Map<string, IFunctionCallArgument>();
|
||||
public addArgument(argument: IFunctionCallArgument): void {
|
||||
if (!argument) {
|
||||
throw new Error('undefined argument');
|
||||
}
|
||||
if (this.hasArgument(argument.parameterName)) {
|
||||
throw new Error(`argument value for parameter ${argument.parameterName} is already provided`);
|
||||
}
|
||||
this.arguments.set(argument.parameterName, argument);
|
||||
private readonly arguments = new Map<string, IFunctionCallArgument>();
|
||||
|
||||
public addArgument(argument: IFunctionCallArgument): void {
|
||||
if (!argument) {
|
||||
throw new Error('undefined argument');
|
||||
}
|
||||
public getAllParameterNames(): string[] {
|
||||
return Array.from(this.arguments.keys());
|
||||
if (this.hasArgument(argument.parameterName)) {
|
||||
throw new Error(`argument value for parameter ${argument.parameterName} is already provided`);
|
||||
}
|
||||
public hasArgument(parameterName: string): boolean {
|
||||
if (!parameterName) {
|
||||
throw new Error('undefined parameter name');
|
||||
}
|
||||
return this.arguments.has(parameterName);
|
||||
this.arguments.set(argument.parameterName, argument);
|
||||
}
|
||||
|
||||
public getAllParameterNames(): string[] {
|
||||
return Array.from(this.arguments.keys());
|
||||
}
|
||||
|
||||
public hasArgument(parameterName: string): boolean {
|
||||
if (!parameterName) {
|
||||
throw new Error('undefined parameter name');
|
||||
}
|
||||
public getArgument(parameterName: string): IFunctionCallArgument {
|
||||
if (!parameterName) {
|
||||
throw new Error('undefined parameter name');
|
||||
}
|
||||
const arg = this.arguments.get(parameterName);
|
||||
if (!arg) {
|
||||
throw new Error(`parameter does not exist: ${parameterName}`);
|
||||
}
|
||||
return arg;
|
||||
return this.arguments.has(parameterName);
|
||||
}
|
||||
|
||||
public getArgument(parameterName: string): IFunctionCallArgument {
|
||||
if (!parameterName) {
|
||||
throw new Error('undefined parameter name');
|
||||
}
|
||||
const arg = this.arguments.get(parameterName);
|
||||
if (!arg) {
|
||||
throw new Error(`parameter does not exist: ${parameterName}`);
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface IFunctionCallArgument {
|
||||
readonly parameterName: string;
|
||||
readonly argumentValue: string;
|
||||
readonly parameterName: string;
|
||||
readonly argumentValue: string;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IFunctionCallArgument } from './IFunctionCallArgument';
|
||||
|
||||
export interface IReadOnlyFunctionCallArgumentCollection {
|
||||
getArgument(parameterName: string): IFunctionCallArgument;
|
||||
getAllParameterNames(): string[];
|
||||
hasArgument(parameterName: string): boolean;
|
||||
getArgument(parameterName: string): IFunctionCallArgument;
|
||||
getAllParameterNames(): string[];
|
||||
hasArgument(parameterName: string): boolean;
|
||||
}
|
||||
|
||||
export interface IFunctionCallArgumentCollection extends IReadOnlyFunctionCallArgumentCollection {
|
||||
addArgument(argument: IFunctionCallArgument): void;
|
||||
addArgument(argument: IFunctionCallArgument): void;
|
||||
}
|
||||
|
||||
@@ -1,139 +1,149 @@
|
||||
import { IReadOnlyFunctionCallArgumentCollection } from '@/application/Parser/Script/Compiler/Function/Call/Argument/IFunctionCallArgumentCollection';
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
import { IFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/IFunctionCall';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import { ISharedFunctionCollection } from '../../ISharedFunctionCollection';
|
||||
import { IFunctionCallCompiler } from './IFunctionCallCompiler';
|
||||
import { IExpressionsCompiler } from '../../../Expressions/IExpressionsCompiler';
|
||||
import { ExpressionsCompiler } from '../../../Expressions/ExpressionsCompiler';
|
||||
import { ISharedFunction, IFunctionCode } from '../../ISharedFunction';
|
||||
import { IFunctionCall } from '@/application/Parser/Script/Compiler/Function/Call/IFunctionCall';
|
||||
import { FunctionCall } from '../FunctionCall';
|
||||
import { FunctionCallArgumentCollection } from '../Argument/FunctionCallArgumentCollection';
|
||||
import { FunctionCallArgument } from '@/application/Parser/Script/Compiler/Function/Call/Argument/FunctionCallArgument';
|
||||
import { IFunctionCallCompiler } from './IFunctionCallCompiler';
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
|
||||
export class FunctionCallCompiler implements IFunctionCallCompiler {
|
||||
public static readonly instance: IFunctionCallCompiler = new FunctionCallCompiler();
|
||||
public static readonly instance: IFunctionCallCompiler = new FunctionCallCompiler();
|
||||
|
||||
protected constructor(
|
||||
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler()) {
|
||||
}
|
||||
protected constructor(
|
||||
private readonly expressionsCompiler: IExpressionsCompiler = new ExpressionsCompiler(),
|
||||
) {
|
||||
}
|
||||
|
||||
public compileCall(
|
||||
calls: IFunctionCall[],
|
||||
functions: ISharedFunctionCollection): ICompiledCode {
|
||||
if (!functions) { throw new Error('undefined functions'); }
|
||||
if (!calls) { throw new Error('undefined calls'); }
|
||||
if (calls.some((f) => !f)) { throw new Error('undefined function call'); }
|
||||
const context: ICompilationContext = {
|
||||
allFunctions: functions,
|
||||
callSequence: calls,
|
||||
expressionsCompiler: this.expressionsCompiler,
|
||||
};
|
||||
const code = compileCallSequence(context);
|
||||
return code;
|
||||
}
|
||||
public compileCall(
|
||||
calls: IFunctionCall[],
|
||||
functions: ISharedFunctionCollection,
|
||||
): ICompiledCode {
|
||||
if (!functions) { throw new Error('undefined functions'); }
|
||||
if (!calls) { throw new Error('undefined calls'); }
|
||||
if (calls.some((f) => !f)) { throw new Error('undefined function call'); }
|
||||
const context: ICompilationContext = {
|
||||
allFunctions: functions,
|
||||
callSequence: calls,
|
||||
expressionsCompiler: this.expressionsCompiler,
|
||||
};
|
||||
const code = compileCallSequence(context);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
interface ICompilationContext {
|
||||
allFunctions: ISharedFunctionCollection;
|
||||
callSequence: readonly IFunctionCall[];
|
||||
expressionsCompiler: IExpressionsCompiler;
|
||||
allFunctions: ISharedFunctionCollection;
|
||||
callSequence: readonly IFunctionCall[];
|
||||
expressionsCompiler: IExpressionsCompiler;
|
||||
}
|
||||
|
||||
interface ICompiledFunctionCall {
|
||||
readonly code: string;
|
||||
readonly revertCode: string;
|
||||
readonly code: string;
|
||||
readonly revertCode: string;
|
||||
}
|
||||
|
||||
function compileCallSequence(context: ICompilationContext): ICompiledFunctionCall {
|
||||
const compiledFunctions = new Array<ICompiledFunctionCall>();
|
||||
for (const call of context.callSequence) {
|
||||
const compiledCode = compileSingleCall(call, context);
|
||||
compiledFunctions.push(...compiledCode);
|
||||
}
|
||||
return {
|
||||
code: merge(compiledFunctions.map((f) => f.code)),
|
||||
revertCode: merge(compiledFunctions.map((f) => f.revertCode)),
|
||||
};
|
||||
const compiledFunctions = new Array<ICompiledFunctionCall>();
|
||||
for (const call of context.callSequence) {
|
||||
const compiledCode = compileSingleCall(call, context);
|
||||
compiledFunctions.push(...compiledCode);
|
||||
}
|
||||
return {
|
||||
code: merge(compiledFunctions.map((f) => f.code)),
|
||||
revertCode: merge(compiledFunctions.map((f) => f.revertCode)),
|
||||
};
|
||||
}
|
||||
|
||||
function compileSingleCall(call: IFunctionCall, context: ICompilationContext): ICompiledFunctionCall[] {
|
||||
const func = context.allFunctions.getFunctionByName(call.functionName);
|
||||
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
|
||||
if (func.body.code) { // Function with inline code
|
||||
const compiledCode = compileCode(func.body.code, call.args, context.expressionsCompiler);
|
||||
return [ compiledCode ];
|
||||
} else { // Function with inner calls
|
||||
return func.body.calls
|
||||
.map((innerCall) => {
|
||||
const compiledArgs = compileArgs(innerCall.args, call.args, context.expressionsCompiler);
|
||||
const compiledCall = new FunctionCall(innerCall.functionName, compiledArgs);
|
||||
return compileSingleCall(compiledCall, context);
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
function compileSingleCall(
|
||||
call: IFunctionCall,
|
||||
context: ICompilationContext,
|
||||
): ICompiledFunctionCall[] {
|
||||
const func = context.allFunctions.getFunctionByName(call.functionName);
|
||||
ensureThatCallArgumentsExistInParameterDefinition(func, call.args);
|
||||
if (func.body.code) { // Function with inline code
|
||||
const compiledCode = compileCode(func.body.code, call.args, context.expressionsCompiler);
|
||||
return [compiledCode];
|
||||
}
|
||||
// Function with inner calls
|
||||
return func.body.calls
|
||||
.map((innerCall) => {
|
||||
const compiledArgs = compileArgs(innerCall.args, call.args, context.expressionsCompiler);
|
||||
const compiledCall = new FunctionCall(innerCall.functionName, compiledArgs);
|
||||
return compileSingleCall(compiledCall, context);
|
||||
})
|
||||
.flat();
|
||||
}
|
||||
|
||||
function compileCode(
|
||||
code: IFunctionCode,
|
||||
args: IReadOnlyFunctionCallArgumentCollection,
|
||||
compiler: IExpressionsCompiler): ICompiledFunctionCall {
|
||||
return {
|
||||
code: compiler.compileExpressions(code.do, args),
|
||||
revertCode: compiler.compileExpressions(code.revert, args),
|
||||
};
|
||||
code: IFunctionCode,
|
||||
args: IReadOnlyFunctionCallArgumentCollection,
|
||||
compiler: IExpressionsCompiler,
|
||||
): ICompiledFunctionCall {
|
||||
return {
|
||||
code: compiler.compileExpressions(code.do, args),
|
||||
revertCode: compiler.compileExpressions(code.revert, args),
|
||||
};
|
||||
}
|
||||
|
||||
function compileArgs(
|
||||
argsToCompile: IReadOnlyFunctionCallArgumentCollection,
|
||||
args: IReadOnlyFunctionCallArgumentCollection,
|
||||
compiler: IExpressionsCompiler,
|
||||
): IReadOnlyFunctionCallArgumentCollection {
|
||||
const compiledArgs = new FunctionCallArgumentCollection();
|
||||
for (const parameterName of argsToCompile.getAllParameterNames()) {
|
||||
const argumentValue = argsToCompile.getArgument(parameterName).argumentValue;
|
||||
const compiledValue = compiler.compileExpressions(argumentValue, args);
|
||||
const newArgument = new FunctionCallArgument(parameterName, compiledValue);
|
||||
compiledArgs.addArgument(newArgument);
|
||||
}
|
||||
return compiledArgs;
|
||||
argsToCompile: IReadOnlyFunctionCallArgumentCollection,
|
||||
args: IReadOnlyFunctionCallArgumentCollection,
|
||||
compiler: IExpressionsCompiler,
|
||||
): IReadOnlyFunctionCallArgumentCollection {
|
||||
const compiledArgs = new FunctionCallArgumentCollection();
|
||||
for (const parameterName of argsToCompile.getAllParameterNames()) {
|
||||
const { argumentValue } = argsToCompile.getArgument(parameterName);
|
||||
const compiledValue = compiler.compileExpressions(argumentValue, args);
|
||||
const newArgument = new FunctionCallArgument(parameterName, compiledValue);
|
||||
compiledArgs.addArgument(newArgument);
|
||||
}
|
||||
return compiledArgs;
|
||||
}
|
||||
|
||||
function merge(codeParts: readonly string[]): string {
|
||||
return codeParts
|
||||
.filter((part) => part?.length > 0)
|
||||
.join('\n');
|
||||
return codeParts
|
||||
.filter((part) => part?.length > 0)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
function ensureThatCallArgumentsExistInParameterDefinition(
|
||||
func: ISharedFunction,
|
||||
args: IReadOnlyFunctionCallArgumentCollection): void {
|
||||
const callArgumentNames = args.getAllParameterNames();
|
||||
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
|
||||
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
|
||||
throwIfNotEmpty(func.name, unexpectedParameters, functionParameterNames);
|
||||
func: ISharedFunction,
|
||||
args: IReadOnlyFunctionCallArgumentCollection,
|
||||
): void {
|
||||
const callArgumentNames = args.getAllParameterNames();
|
||||
const functionParameterNames = func.parameters.all.map((param) => param.name) || [];
|
||||
const unexpectedParameters = findUnexpectedParameters(callArgumentNames, functionParameterNames);
|
||||
throwIfNotEmpty(func.name, unexpectedParameters, functionParameterNames);
|
||||
}
|
||||
|
||||
function findUnexpectedParameters(
|
||||
callArgumentNames: string[],
|
||||
functionParameterNames: string[]): string[] {
|
||||
if (!callArgumentNames.length && !functionParameterNames.length) {
|
||||
return [];
|
||||
}
|
||||
return callArgumentNames
|
||||
.filter((callParam) => !functionParameterNames.includes(callParam));
|
||||
callArgumentNames: string[],
|
||||
functionParameterNames: string[],
|
||||
): string[] {
|
||||
if (!callArgumentNames.length && !functionParameterNames.length) {
|
||||
return [];
|
||||
}
|
||||
return callArgumentNames
|
||||
.filter((callParam) => !functionParameterNames.includes(callParam));
|
||||
}
|
||||
|
||||
function throwIfNotEmpty(
|
||||
functionName: string,
|
||||
unexpectedParameters: string[],
|
||||
expectedParameters: string[]) {
|
||||
if (!unexpectedParameters.length) {
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
`Function "${functionName}" has unexpected parameter(s) provided: ` +
|
||||
`"${unexpectedParameters.join('", "')}"` +
|
||||
'. Expected parameter(s): ' +
|
||||
(expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
|
||||
);
|
||||
functionName: string,
|
||||
unexpectedParameters: string[],
|
||||
expectedParameters: string[],
|
||||
) {
|
||||
if (!unexpectedParameters.length) {
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
// eslint-disable-next-line prefer-template
|
||||
`Function "${functionName}" has unexpected parameter(s) provided: `
|
||||
+ `"${unexpectedParameters.join('", "')}"`
|
||||
+ '. Expected parameter(s): '
|
||||
+ (expectedParameters.length ? `"${expectedParameters.join('", "')}"` : 'none'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface ICompiledCode {
|
||||
readonly code: string;
|
||||
readonly revertCode?: string;
|
||||
readonly code: string;
|
||||
readonly revertCode?: string;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
import { ISharedFunctionCollection } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionCollection';
|
||||
import { IFunctionCall } from '../IFunctionCall';
|
||||
import { ICompiledCode } from './ICompiledCode';
|
||||
|
||||
export interface IFunctionCallCompiler {
|
||||
compileCall(
|
||||
calls: IFunctionCall[],
|
||||
functions: ISharedFunctionCollection): ICompiledCode;
|
||||
compileCall(
|
||||
calls: IFunctionCall[],
|
||||
functions: ISharedFunctionCollection): ICompiledCode;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCal
|
||||
import { IFunctionCall } from './IFunctionCall';
|
||||
|
||||
export class FunctionCall implements IFunctionCall {
|
||||
constructor(
|
||||
public readonly functionName: string,
|
||||
public readonly args: IReadOnlyFunctionCallArgumentCollection) {
|
||||
if (!functionName) {
|
||||
throw new Error('empty function name in function call');
|
||||
}
|
||||
if (!args) {
|
||||
throw new Error('undefined args');
|
||||
}
|
||||
constructor(
|
||||
public readonly functionName: string,
|
||||
public readonly args: IReadOnlyFunctionCallArgumentCollection,
|
||||
) {
|
||||
if (!functionName) {
|
||||
throw new Error('empty function name in function call');
|
||||
}
|
||||
if (!args) {
|
||||
throw new Error('undefined args');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,31 +5,31 @@ import { FunctionCallArgument } from './Argument/FunctionCallArgument';
|
||||
import { FunctionCall } from './FunctionCall';
|
||||
|
||||
export function parseFunctionCalls(calls: FunctionCallsData): IFunctionCall[] {
|
||||
if (!calls) {
|
||||
throw new Error('undefined call data');
|
||||
}
|
||||
const sequence = getCallSequence(calls);
|
||||
return sequence.map((call) => parseFunctionCall(call));
|
||||
if (calls === undefined) {
|
||||
throw new Error('undefined call data');
|
||||
}
|
||||
const sequence = getCallSequence(calls);
|
||||
return sequence.map((call) => parseFunctionCall(call));
|
||||
}
|
||||
|
||||
function getCallSequence(calls: FunctionCallsData): FunctionCallData[] {
|
||||
if (typeof calls !== 'object') {
|
||||
throw new Error('called function(s) must be an object');
|
||||
}
|
||||
if (calls instanceof Array) {
|
||||
return calls as FunctionCallData[];
|
||||
}
|
||||
return [ calls as FunctionCallData ];
|
||||
if (typeof calls !== 'object') {
|
||||
throw new Error('called function(s) must be an object');
|
||||
}
|
||||
if (calls instanceof Array) {
|
||||
return calls as FunctionCallData[];
|
||||
}
|
||||
return [calls as FunctionCallData];
|
||||
}
|
||||
|
||||
function parseFunctionCall(call: FunctionCallData): IFunctionCall {
|
||||
if (!call) {
|
||||
throw new Error(`undefined function call`);
|
||||
}
|
||||
const args = new FunctionCallArgumentCollection();
|
||||
for (const parameterName of Object.keys(call.parameters || {})) {
|
||||
const arg = new FunctionCallArgument(parameterName, call.parameters[parameterName]);
|
||||
args.addArgument(arg);
|
||||
}
|
||||
return new FunctionCall(call.function, args);
|
||||
if (!call) {
|
||||
throw new Error('undefined function call');
|
||||
}
|
||||
const args = new FunctionCallArgumentCollection();
|
||||
for (const parameterName of Object.keys(call.parameters || {})) {
|
||||
const arg = new FunctionCallArgument(parameterName, call.parameters[parameterName]);
|
||||
args.addArgument(arg);
|
||||
}
|
||||
return new FunctionCall(call.function, args);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IReadOnlyFunctionCallArgumentCollection } from './Argument/IFunctionCallArgumentCollection';
|
||||
|
||||
export interface IFunctionCall {
|
||||
readonly functionName: string;
|
||||
readonly args: IReadOnlyFunctionCallArgumentCollection;
|
||||
readonly functionName: string;
|
||||
readonly args: IReadOnlyFunctionCallArgumentCollection;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user