From 5f8cfb49ab56a791786fd3aa82687ea84884077e Mon Sep 17 00:00:00 2001 From: USERSATOSHI Date: Thu, 25 Jul 2024 00:35:35 +0530 Subject: [PATCH] refactor: refactored more old codebase --- eslint.config.mjs | 9 ++ lib/aoi.js/src/core/AoiReader.ts | 74 +++++++++ lib/aoi.js/src/core/Error.ts | 4 + lib/aoi.js/src/core/Transpiler.ts | 15 +- lib/aoi.js/src/core/builders/Condition.ts | 142 ++++++++++++++++++ lib/aoi.js/src/core/builders/Function.ts | 4 +- lib/aoi.js/src/core/builders/Scope.ts | 6 +- lib/aoi.js/src/core/parsers/condition.ts | 62 ++++++++ lib/aoi.js/src/core/parsers/object.ts | 104 +++++++++++++ lib/aoi.js/src/functions/js/misc/$let.test.ts | 73 +++++++++ lib/aoi.js/src/functions/js/misc/$let.ts | 80 ++++++++++ lib/aoi.js/src/functions/js/process/$cpu.ts | 13 ++ .../src/functions/js/process/$procenv.test.ts | 55 +++++++ .../src/functions/js/process/$procenv.ts | 57 +++++++ lib/aoi.js/src/testing/testClient.ts | 0 lib/aoi.js/src/typings/enum.ts | 20 +-- lib/aoi.js/src/utils/helpers.ts | 77 ++++++++++ 17 files changed, 769 insertions(+), 26 deletions(-) create mode 100644 lib/aoi.js/src/core/AoiReader.ts create mode 100644 lib/aoi.js/src/core/builders/Condition.ts create mode 100644 lib/aoi.js/src/core/parsers/condition.ts create mode 100644 lib/aoi.js/src/core/parsers/object.ts create mode 100644 lib/aoi.js/src/functions/js/misc/$let.test.ts create mode 100644 lib/aoi.js/src/functions/js/misc/$let.ts create mode 100644 lib/aoi.js/src/functions/js/process/$procenv.test.ts create mode 100644 lib/aoi.js/src/functions/js/process/$procenv.ts create mode 100644 lib/aoi.js/src/testing/testClient.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index a7ccd192f..3560914e6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -50,6 +50,15 @@ export default [ { selector: 'parameter', format: ['camelCase'] }, { selector: 'typeLike', format: ['PascalCase'] }, ], + '@typescript-eslint/prefer-literal-enum-member': [ + 'error', + [ + 'error', + { + allowBitwiseExpressions: true, + }, + ], + ], }, }, { diff --git a/lib/aoi.js/src/core/AoiReader.ts b/lib/aoi.js/src/core/AoiReader.ts new file mode 100644 index 000000000..50636577c --- /dev/null +++ b/lib/aoi.js/src/core/AoiReader.ts @@ -0,0 +1,74 @@ +import { BundlerCustoms } from "@aoi.js/typings/enum"; +import { TranspilerError } from "./Error"; +import Transpiler from "./Transpiler.js"; + +export default class AoiReader { + + _parseEmbeddedJS(code: string) { + let cntr = 0, isPotentialStart = false, tmp = ''; + const embeds = []; + + for (let i = 0; i < code.length; i++) { + const char = code[i]; + + if ( char === '$') { + if (cntr) { + tmp += char; + } + + if (!isPotentialStart) { + isPotentialStart = true; + } + } else if (char === '{') { + if (isPotentialStart && !cntr) { + cntr++; + } else if (cntr) { + tmp += char; + cntr++; + } + } else if (char === '}') { + if (cntr) { + cntr--; + + if (!cntr) { + embeds.push(tmp); + tmp = ''; + isPotentialStart = false; + } else { + tmp += char; + } + } else if (isPotentialStart) { + isPotentialStart = false; + } + } else { + if (cntr) { + tmp += char; + } + } + } + + if (cntr) { + throw TranspilerError.AoiReaderError('Invalid embedded JS', { code }); + } + + return embeds; + } + + _updateEmbedJs(compiled: string, embeds: string[]) { + while (embeds.length) { + const embed = embeds.pop()!; + compiled = this._replaceLast(compiled, BundlerCustoms.EJS, embed); + } + + return compiled; + } + + _replaceLast(str: string, find: string, replace: string) { + const index = str.lastIndexOf(find); + if (index === -1) { + return str; + } + + return str.slice(0, index) + replace + str.slice(index + find.length); + } +} \ No newline at end of file diff --git a/lib/aoi.js/src/core/Error.ts b/lib/aoi.js/src/core/Error.ts index 7045b3112..73543bf88 100644 --- a/lib/aoi.js/src/core/Error.ts +++ b/lib/aoi.js/src/core/Error.ts @@ -8,6 +8,10 @@ export class TranspilerError extends Error { return new TranspilerError(`RunTimeError: ${msg}`, data); } + static AoiReaderError(msg: string, data?: { function?: { name: string; code: string }; cmd?: string; path?: string; code?: string }) { + return new TranspilerError(`AoiReaderError: ${msg}`, data); + } + cause: TranspilerError; function?: { name: string; code: string }; cmd?: string; diff --git a/lib/aoi.js/src/core/Transpiler.ts b/lib/aoi.js/src/core/Transpiler.ts index 3f3b3f277..362ac0614 100644 --- a/lib/aoi.js/src/core/Transpiler.ts +++ b/lib/aoi.js/src/core/Transpiler.ts @@ -128,7 +128,7 @@ export default class Transpiler { ast, ); - scope.addVariables(this.scopeData.vars ?? []); + scope.addVariables( ...(this.scopeData.vars ?? []) ); scope.addEmbeds(this.scopeData.embeds ?? []); scope.env.push(...(this.scopeData.env ?? [])); scope.objects = { ...scope.objects, ...this.scopeData.object }; @@ -381,17 +381,4 @@ export default class Transpiler { return scope.generate(ast.executed); } - - // updateEmbedJs() { - // const embeds = [...this.embeddedJS.reverse()]; - // for (const embed of embeds) { - // const old = this.rest; - // this.rest = this.replaceLast(this.rest, BundlerCustoms.EJS, embed); - // if (this.rest === old) { - // this.packages = this.embeddedJS.shift() + '\n' + this.packages; - // } else { - // this.embeddedJS.shift(); - // } - // } - // } } \ No newline at end of file diff --git a/lib/aoi.js/src/core/builders/Condition.ts b/lib/aoi.js/src/core/builders/Condition.ts new file mode 100644 index 000000000..bbb09c316 --- /dev/null +++ b/lib/aoi.js/src/core/builders/Condition.ts @@ -0,0 +1,142 @@ +import { inspect } from 'util'; +import { parseData } from '@aoi.js/utils/helpers.js'; +import { parseString } from '../parsers/string.js'; +import { BundlerCustoms, TranspilerCustoms } from '@aoi.js/typings/enum.js'; + +export const OPERATORS = ['==', '!=', '>=', '<=', '>', '<', '===', '!=='] as const; + +export default class Condition { + condition: string; + children: Condition[] = []; + parent: Condition | undefined; + + constructor(condition: string, parent?: Condition) { + this.condition = condition; + this.parent = parent; + } + + _handlePart(part: string) { + let result; + if (part.split(' ').length === 1) { + result = parseData(part); + + if (typeof result === 'object') { + try { + result = JSON.stringify(result); + } catch { + result = inspect(result, { depth: null }); + } + } else if (typeof result === 'string') { + if ( + !( + (result.startsWith(TranspilerCustoms.FS) && + result.endsWith(TranspilerCustoms.FE)) || + result.startsWith('__$DISCORD_DATA$__') || + result.trim() === (BundlerCustoms.EJS as string) + ) + ) { + result = parseString(result); + + if (typeof parseData(result.slice(1, -1)) !== 'string') { + result = parseData(result.slice(1, -1)); + } + } + } else if (typeof result === 'bigint') { + result = result.toString() + 'n'; + } + } else { + result = parseString(part); + } + + return result; + } + + _handleConditionWithOperator(condition: string, op: string) { + let [left, right] = condition.split(op); + left = left.trim(); + right = right.trim(); + + const leftResult = this._handlePart(left) as string; + const rightResult = this._handlePart(right) as string; + + return `${leftResult}${op}${rightResult}`; + } + + _solve(condition: string) { + condition = condition + .replaceAll(TranspilerCustoms.SBL, '(') + .replaceAll(TranspilerCustoms.SBR, ')'); + + if (this.children.length) { + for (const child of this.children) { + const solved = child.solve(); + condition = condition.replace('#CONDITION#', `(${solved})`); + } + + return condition; + } + + const op = OPERATORS.find((op) => condition.includes(op)); + let result; + + if (op) { + result = this._handleConditionWithOperator(condition, op); + } else { + result = parseData(condition); + + if ( + typeof result === 'string' && + (!result.endsWith(TranspilerCustoms.FE) || + result.trim().split(' ').length > 1) && + !result.startsWith(TranspilerCustoms.FS) && + result.trim() !== (BundlerCustoms.EJS as string) + ) { + result = parseString(result); + } + } + + return result as string; + } + + _solveOr(condition: string) { + const subConditions = condition.split('||'); + const results = []; + + for (const subCondition of subConditions) { + results.push(this._solve(subCondition)); + } + + return results.join(' || '); + } + + _solveAnd(condition: string) { + const subConditions = condition.split('&&'); + const results = []; + + for (const subCondition of subConditions) { + if (subCondition.includes('||')) { + results.push(this._solveOr(subCondition)); + } else { + results.push(this._solve(subCondition)); + } + } + + return results.join(' && '); + } + + solve() { + if (this.children.length) { + return this._solve(this.condition); + } + + return this._solveAnd(this.condition); + } + + add(part: string) { + this.condition += part; + } + + addChild(child: Condition) { + this.children.push(child); + } +} diff --git a/lib/aoi.js/src/core/builders/Function.ts b/lib/aoi.js/src/core/builders/Function.ts index 9f7f91fc0..5d7eed3e4 100644 --- a/lib/aoi.js/src/core/builders/Function.ts +++ b/lib/aoi.js/src/core/builders/Function.ts @@ -193,9 +193,9 @@ export default class FunctionBuilder implements IFunctionData { defineVar(name: string, value: string, redefined = false) { if (redefined) { - return `${escapeVars(name)} = ${value};`; + return `${escapeVars(name)} = ${value};\n`; } else { - return `let ${escapeVars(name)} = ${value}`; + return `let ${escapeVars(name)} = ${value};\n`; } } diff --git a/lib/aoi.js/src/core/builders/Scope.ts b/lib/aoi.js/src/core/builders/Scope.ts index a10fa56c2..619644829 100644 --- a/lib/aoi.js/src/core/builders/Scope.ts +++ b/lib/aoi.js/src/core/builders/Scope.ts @@ -93,10 +93,14 @@ export default class Scope { ); } - addVariables(scopeVars: string[]) { + addVariables(...scopeVars: string[]) { this.variables.push(...scopeVars); } + hasVariable(name: string) { + return this.variables.includes(name); + } + addEmbeds(embeds: unknown[]) { this.embeds.push(...embeds); } diff --git a/lib/aoi.js/src/core/parsers/condition.ts b/lib/aoi.js/src/core/parsers/condition.ts new file mode 100644 index 000000000..e1aabe836 --- /dev/null +++ b/lib/aoi.js/src/core/parsers/condition.ts @@ -0,0 +1,62 @@ +import { TranspilerCustoms } from '@aoi.js/typings/enum.js'; +import Condition, { OPERATORS } from '../builders/Condition.js'; + +export function _countSmoothBrackets(condition: string) { + const start = condition.split('(').length - 1; + const end = condition.split(')').length - 1; + + return { + start, + end, + }; +} + +export function _areSmoothBracketsBalanced(condition: string) { + const { start, end } = _countSmoothBrackets(condition); + + return start === end; +} + +export function parseCondition(condition: string) { + if (condition.includes(TranspilerCustoms.FS)) { + const matches = condition.match( + /((#FUNCTION_START#([$a-z.0-9\s?(){}[\]._:'"`;=><,!-]|\n)+#FUNCTION_END#)|(__\$[a-z_?.()]+\$__))/gim, + ); + + if (matches) { + for (const match of matches) { + const updated = match + .replaceAll('(', TranspilerCustoms.SBL) + .replaceAll(')', TranspilerCustoms.SBR); + + condition = condition.replace(match, updated); + } + } + } + + let i = 0, + starter = new Condition(''); + + while (i < condition.length) { + const char = condition[i]; + + if (char === '(') { + const child = new Condition('', starter); + starter.add('#CONDITION#'); + starter.addChild(child); + starter = child; + } else if (char === ')') { + if (starter.parent) { + starter = starter.parent; + } else { + break; + } + } else { + starter.add(char); + } + + i++; + } + + return starter; +} diff --git a/lib/aoi.js/src/core/parsers/object.ts b/lib/aoi.js/src/core/parsers/object.ts new file mode 100644 index 000000000..ec48623c0 --- /dev/null +++ b/lib/aoi.js/src/core/parsers/object.ts @@ -0,0 +1,104 @@ +import { parseData } from '@aoi.js/utils/helpers.js'; +import StringObject from '../builders/StringObject.js'; +import { parseString } from './string.js'; +import { TranspilerCustoms } from '@aoi.js/typings/enum.js'; + +export const OBJECT_QUOTE_REGEX = /".*?"/g; + +export function _handleStringData(text: string, object: StringObject) { + if (text.startsWith('\'') || text.startsWith('"') || text.startsWith('`')) { + text = text.slice(1, text.length - 1); + text = parseString(text); + } else if (text.includes(TranspilerCustoms.FS)) { + if ( + text + .replaceAll(/#FUNCTION_START#(.+?)#FUNCTION_END#/g, '') + .trim() !== '' + ) { + text = parseString(text); + } + } else { + text = parseString(text); + } + + object.addValue(text); +} + +export function _getObjectAst( + objectString: string, + currentObject: StringObject, +) { + let i = 0, + text = '', + arrayEnded = false; + + while (i < objectString.length) { + const char = objectString[i]; + if (char === '{' || char === '[') { + const newObject = new StringObject(char, currentObject); + currentObject.addValue(`#StringObject_${newObject.name}#`); + currentObject = newObject; + } else if (char === '}' || char === ']') { + currentObject.addEnd(char); + + if (text.trim() !== '') { + const t = parseData(text.trim()); + if (typeof t === 'string') { + _handleStringData(t, currentObject); + text = ''; + } + } + + currentObject.parent?.pushChild(currentObject); + currentObject = currentObject.parent!; + } else if (char === ':') { + currentObject.addKey(text.trim()); + text = ''; + } else if (char === ',') { + if (arrayEnded) { + i++; + arrayEnded = false; + continue; + } + + if (currentObject.start === '[' || currentObject.start === '{') { + const t = parseData(text.trim()); + if (typeof t === 'string') { + _handleStringData(t, currentObject); + } + + text = ''; + } + } else { + text += char; + } + } + + while (currentObject.parent) { + currentObject = currentObject.parent; + } + + return currentObject; +} + +export function _escapeObjectSymbols(text: string) { + return text + .replaceAll(':', TranspilerCustoms.OSEP) + .replaceAll('{', TranspilerCustoms.OS) + .replaceAll('}', TranspilerCustoms.OE) + .replaceAll('[', TranspilerCustoms.AS) + .replaceAll(']', TranspilerCustoms.AE) + .replaceAll(',', TranspilerCustoms.ASEP); +} + +export function parseStringObject(text: string, stringObject: StringObject) { + const quotes = text.match(OBJECT_QUOTE_REGEX); + + if (quotes) { + for (const quote of quotes) { + text = text.replace(quote, _escapeObjectSymbols(quote)); + } + } + + return _getObjectAst(text.slice(1, text.length - 1), stringObject); +} diff --git a/lib/aoi.js/src/functions/js/misc/$let.test.ts b/lib/aoi.js/src/functions/js/misc/$let.test.ts new file mode 100644 index 000000000..b0afbfbca --- /dev/null +++ b/lib/aoi.js/src/functions/js/misc/$let.test.ts @@ -0,0 +1,73 @@ +import Transpiler from '@aoi.js/core/Transpiler.js'; +import AoiClient from '@aoi.js/classes/AoiClient.js'; + +import { $let } from './$let.js'; +import { type AsyncFunction } from '@aoi.js/typings/type.js'; + +const client = new AoiClient(); + +const transpiler = new Transpiler( + { + client: client, + scopeData: { + name: 'global', + vars: [], + embeds: [], + env: [], + object: {}, + embeddedJS: [], + sendFunction: 'send', + }, + customFunctions: { $let }, + sendMessage: true, + }, + client, +); + +describe('$let', () => { + + it('should compile successfully with arg', () => { + const code = `${transpiler.mainFunction}[$let[hi;1] $let[hi;hello]]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + const result = transpiler._compile(ast, [globalScope]); + console.log(result); + expect(result).toBeDefined(); + + const func = new Function(`return ${result}`)() as AsyncFunction; + expect(func).toBeInstanceOf(Function); + }); + + it('should fail to compile without arg', () => { + const code = `${transpiler.mainFunction}[$let]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + expect(() => transpiler._compile(ast, [globalScope])).toThrow(); + }); + + it('should fail to compile with no variable', () => { + const code = `${transpiler.mainFunction}[$let[]]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + expect(() => transpiler._compile(ast, [globalScope])).toThrow(); + }); + + it('should fail to compile with no value', () => { + const code = `${transpiler.mainFunction}[$let[hi]]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + expect(() => transpiler._compile(ast, [globalScope])).toThrow(); + }); +}); \ No newline at end of file diff --git a/lib/aoi.js/src/functions/js/misc/$let.ts b/lib/aoi.js/src/functions/js/misc/$let.ts new file mode 100644 index 000000000..e790f96cb --- /dev/null +++ b/lib/aoi.js/src/functions/js/misc/$let.ts @@ -0,0 +1,80 @@ +import FunctionBuilder from "@aoi.js/core/builders/Function.js"; +import { TranspilerError } from "@aoi.js/core/Error"; +import { parseString } from "@aoi.js/core/parsers/string"; +import { FunctionType, ReturnType } from "@aoi.js/typings/enum.js"; +import { escapeResult, parseData, stringify } from "@aoi.js/utils/helpers"; + +/** + * define a variable with a value + * @example + * ```aoi + * --- + * name: let + * type: basic + * --- + * + * $let[variable;value] + * $get[variable] // value + * ``` + */ +const $let = new FunctionBuilder() + .setName("$let") + .setBrackets(true) + .setOptional(true) + .setFields([ + { + name: 'variable', + type: ReturnType.String, + required: true, + description: "The variable name to store the value in." + }, + { + name: 'value', + type: ReturnType.Any, + required: true, + description: "The value to store." + } + ]) + .setReturns(ReturnType.Void) + .setType(FunctionType.Setter) + .setCode((data, scopes, thisArg) => { + const currentScope = thisArg.getCurrentScope(scopes); + const [variable, value] = data.splits(); + + if (!variable && !thisArg.canSuppressAtComp(data,currentScope)) { + throw TranspilerError.CompileError("Variable name not provided.", { + function: { name: "$let", code: data.total }, + }); + } + + if (!value && !thisArg.canSuppressAtComp(data,currentScope)) { + throw TranspilerError.CompileError("Value not provided.", { + function: { name: "$let", code: data.total }, + }); + } + + let parsedValue = parseData(value); + + if (typeof parsedValue === "string") { + parsedValue = parseString(parsedValue); + } else { + parsedValue = stringify(parsedValue); + } + + const result = thisArg.defineVar(variable, parsedValue, currentScope.hasVariable(variable)); + + if (!currentScope.hasVariable(variable)) { + currentScope.addVariables(variable); + } + + const escaped = escapeResult(result); + + return { + code: escaped, + scope: scopes + }; + }) + .build(); + +export { $let }; + \ No newline at end of file diff --git a/lib/aoi.js/src/functions/js/process/$cpu.ts b/lib/aoi.js/src/functions/js/process/$cpu.ts index c40839019..f0348f4a0 100644 --- a/lib/aoi.js/src/functions/js/process/$cpu.ts +++ b/lib/aoi.js/src/functions/js/process/$cpu.ts @@ -4,6 +4,19 @@ import { FunctionType, ReturnType } from '@aoi.js/typings/enum.js'; import { escapeResult } from '@aoi.js/utils/helpers.js'; import type os from 'os'; +/** + * Returns the CPU usage of the process or the OS. + * @example + * ```aoi + * --- + * name: cpu + * type: basic + * --- + * + * $cpu // returns the CPU usage of the process + * $cpu[os] // returns the CPU usage of the OS + * ``` + */ const $cpu = new FunctionBuilder() .setName('$cpu') .setBrackets(true) diff --git a/lib/aoi.js/src/functions/js/process/$procenv.test.ts b/lib/aoi.js/src/functions/js/process/$procenv.test.ts new file mode 100644 index 000000000..718a58b6f --- /dev/null +++ b/lib/aoi.js/src/functions/js/process/$procenv.test.ts @@ -0,0 +1,55 @@ +import Transpiler from '@aoi.js/core/Transpiler.js'; +import AoiClient from '@aoi.js/classes/AoiClient.js'; + +import { $procenv } from './$procenv.js'; +import { type AsyncFunction } from '@aoi.js/typings/type.js'; + +const client = new AoiClient(); + +const transpiler = new Transpiler( + { + client: client, + scopeData: { + name: 'global', + vars: [], + embeds: [], + env: [], + object: {}, + embeddedJS: [], + sendFunction: 'send', + }, + customFunctions: { $procenv }, + sendMessage: true, + }, + client, +); + +describe('$procenv', () => { + it('should compile successfully without arg', () => { + const code = `${transpiler.mainFunction}[$procenv]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + const result = transpiler._compile(ast, [globalScope]); + expect(result).toBeDefined(); + + const func = new Function(`return ${result}`)() as AsyncFunction; + expect(func).toBeInstanceOf(Function); + }); + + it('should compile successfully with arg', () => { + const code = `${transpiler.mainFunction}[$procenv[PWD]]`; + const ast = transpiler._getFunctionData(code, transpiler.mainFunction, Object.keys(transpiler.functions)); + + expect(ast).toBeDefined(); + + const globalScope = transpiler._createGlobalScope(ast); + const result = transpiler._compile(ast, [globalScope]); + expect(result).toBeDefined(); + + const func = new Function(`return ${result}`)() as AsyncFunction; + expect(func).toBeInstanceOf(Function); + }); +}); \ No newline at end of file diff --git a/lib/aoi.js/src/functions/js/process/$procenv.ts b/lib/aoi.js/src/functions/js/process/$procenv.ts new file mode 100644 index 000000000..91b2efbf1 --- /dev/null +++ b/lib/aoi.js/src/functions/js/process/$procenv.ts @@ -0,0 +1,57 @@ +import FunctionBuilder from '@aoi.js/core/builders/Function.js'; +import { FunctionType, ReturnType } from '@aoi.js/typings/enum.js'; +import { escapeResult } from '@aoi.js/utils/helpers.js'; + +/** + * Returns the environment variable of the process for given key. + * @example + * ```aoi + * --- + * name: procenv + * type: basic + * --- + * + * $procenv // returns all environment variables as an object + * $procenv[KEY] // returns the value of the environment variable + * ``` + */ +const $procenv = new FunctionBuilder() + .setName('$procenv') + .setBrackets(true) + .setOptional(true) + .setType(FunctionType.Getter) + .setFields([ + { + name: 'key', + type: ReturnType.String, + required: false, + description: 'The key of the environment variable to get.', + }, + ]) + .setReturns(ReturnType.String | ReturnType.Object) + .setCode((data, scopes, thisArg) => { + let [key] = thisArg.getParams(data); + + if (!key) { + const result = thisArg.getResultString(() => process.env); + + const escaped = escapeResult(result); + + return { + code: escaped, + scope: scopes, + }; + } + + const result = thisArg.getResultString(() => process.env['$0'], [key]); + + const escaped = escapeResult(result); + + return { + code: escaped, + scope: scopes, + }; + }) + .build(); + +export { $procenv }; diff --git a/lib/aoi.js/src/testing/testClient.ts b/lib/aoi.js/src/testing/testClient.ts new file mode 100644 index 000000000..e69de29bb diff --git a/lib/aoi.js/src/typings/enum.ts b/lib/aoi.js/src/typings/enum.ts index 778936126..a6fe1454a 100644 --- a/lib/aoi.js/src/typings/enum.ts +++ b/lib/aoi.js/src/typings/enum.ts @@ -8,13 +8,13 @@ export enum FunctionType { } export enum ReturnType { - Void, - Any, - Number, - String, - Boolean, - Object, - Array, + Void = 1 << 0, + Any = 1 << 1, + Number = 1 << 2, + String = 1 << 3, + Boolean = 1 << 4, + Object = 1 << 5, + Array = 1 << 6, } export enum TranspilerCustoms { @@ -41,9 +41,11 @@ export enum TranspilerCustoms { AS = '#ARRAY_STARTER#', AE = '#ARRAY_ENDER#', ASEP = '#ARRAY_SEPARATOR#', + SBL = '#SMOOTH_BRACKET_LEFT#', + SBR = '#SMOOTH_BRACKET_RIGHT#', } -export enum BundlerCustoms { +export enum BundlerCustoms { LB = '#LEFT_BRACKET#', RB = '#RIGHT_BRACKET#', EJS = '#EMBEDDED_JS#', @@ -51,4 +53,4 @@ export enum BundlerCustoms { export enum AoiClientEvents { Error = 'error', -} \ No newline at end of file +} diff --git a/lib/aoi.js/src/utils/helpers.ts b/lib/aoi.js/src/utils/helpers.ts index 29e237a6a..0942befb0 100644 --- a/lib/aoi.js/src/utils/helpers.ts +++ b/lib/aoi.js/src/utils/helpers.ts @@ -1,3 +1,5 @@ +import StringObject from '@aoi.js/core/builders/StringObject'; +import { parseStringObject } from '@aoi.js/core/parsers/object'; import { TranspilerCustoms } from '@aoi.js/typings/enum.js'; /** @@ -91,4 +93,79 @@ export function removeSetFunc(code: string) { */ export function removeMultiLineComments(code: string) { return code.replace(/\/\*[\s\S]*?\*\//g, ''); +} + +/** + * parse data to its actual type + * @param text - The string to check. + * @returns - Returns the parsed data. + * @example + * ```js + * parseData("1") // 1 + * parseData("1n") // 1n + * parseData("null") // null + * // and so on... + * ``` + */ +export function parseData(text: string) { + if (text === '') return text; + else if (!isNaN(Number(text)) && Number.isSafeInteger(Number(text))) + return Number(text); + else if ( + (!isNaN(Number(text)) && !Number.isSafeInteger(text)) || + isBigInt(text) + ) + return BigInt(text.replace('n', '')); + else if (text === 'null') return null; + else if (text === 'undefined') return undefined; + else if (text === 'true' || text === 'false') return text === 'true'; + else { + try { + return JSON.parse(text) as Record; + } catch { + if (text.startsWith('{') && text.endsWith('}')) { + const so = new StringObject('{'); + so.addEnd('}'); + let e: Record; + eval(`e = ${parseStringObject(text, so).solve()}`); + // @ts-expect-error - we use eval here + return e; + } else if (text.startsWith('[') && text.endsWith(']')) { + const so = new StringObject('['); + so.addEnd(']'); + let e: unknown[]; + eval(`e = ${parseStringObject(text, so).solve()}`); + // @ts-expect-error - we use eval here + return e; + } else return text; + } + } +} + +/** + * Checks if the given string is a bigint. + * @param string - The string to check. + * @returns - Returns true if the string is a bigint, false otherwise. + * @example + * ```js + * isBigInt("1n") // true + * isBigInt("1") // false + * ``` + */ + +export function isBigInt(string: string) { + return (/^-?\d+n$/.exec(string)) !== null; +} + +export function stringify(data: any): string { + switch (typeof data) { + case 'bigint': + return data + 'n'; + case 'object': + return JSON.stringify(data); + case 'undefined': + return 'undefined'; + default: + return data.toString(); + } } \ No newline at end of file