From 4f0638662395e65cfec6e88a9dbaadcd05c69491 Mon Sep 17 00:00:00 2001 From: 3y3 <3y3@ya.ru> Date: Wed, 18 Dec 2024 17:10:46 +0300 Subject: [PATCH] fix: Bound class methods with decorator --- src/commands/build/run.ts | 40 ++++++++++++++++++++++++++------------- src/utils/decorators.ts | 12 ++++++++++++ src/utils/index.ts | 1 + tsconfig.json | 1 + 4 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 src/utils/decorators.ts diff --git a/src/commands/build/run.ts b/src/commands/build/run.ts index 425b1a91..25d1be57 100644 --- a/src/commands/build/run.ts +++ b/src/commands/build/run.ts @@ -6,7 +6,7 @@ import {dirname, join, relative, resolve} from 'node:path'; import {access, link, mkdir, readFile, realpath, stat, unlink, writeFile} from 'node:fs/promises'; import {glob} from 'glob'; -import {normalizePath} from '~/utils'; +import {bounded, normalizePath} from '~/utils'; import {configPath} from '~/config'; import { BUNDLE_FOLDER, @@ -112,11 +112,11 @@ export class Run { * * @returns {Promise} */ - read = async (path: AbsolutePath) => { + @bounded async read(path: AbsolutePath) { this.assertProjectScope(path); return this.fs.readFile(path, 'utf8'); - }; + } /** * Run.input bounded write helper. @@ -130,15 +130,26 @@ export class Run { * * @returns {Promise} */ - write = async (path: AbsolutePath, content: string) => { + @bounded async write(path: AbsolutePath, content: string) { this.assertProjectScope(path); await this.fs.mkdir(dirname(path), {recursive: true}); await this.fs.unlink(path).catch(() => {}); await this.fs.writeFile(path, content, 'utf8'); - }; + } - glob = async (pattern: string | string[], options: GlobOptions): Promise => { + /** + * Glob wrapper with some default settings + * + * @param {string | string[]} pattern + * @param {GlobOptions} options + * + * @returns {NormalizedPath[]} + */ + @bounded async glob( + pattern: string | string[], + options: GlobOptions, + ): Promise { const paths = await glob(pattern, { dot: true, nodir: true, @@ -147,13 +158,13 @@ export class Run { }); return paths.map(normalizePath); - }; + } - copy = async ( + @bounded async copy( from: AbsolutePath, to: AbsolutePath, options: CopyOptions | CopyOptions['ignore'] = {}, - ) => { + ) { if (Array.isArray(options)) { options = {ignore: options}; } @@ -205,9 +216,12 @@ export class Run { await hardlink(join(from, file), join(to, file)); } } - }; + } - realpath = async (path: AbsolutePath): Promise => { + realpath(path: AbsolutePath): Promise; + realpath(path: AbsolutePath, withStack: true): Promise; + realpath(path: AbsolutePath, withStack: false): Promise; + @bounded async realpath(path: AbsolutePath, withStack = true) { const stack = [path]; while (this._copyMap[path]) { path = this._copyMap[path]; @@ -222,8 +236,8 @@ export class Run { } } catch {} - return stack; - }; + return withStack ? stack : stack[0]; + } private async assertProjectScope(path: AbsolutePath) { const realpath = await this.realpath(path); diff --git a/src/utils/decorators.ts b/src/utils/decorators.ts new file mode 100644 index 00000000..dea73f1c --- /dev/null +++ b/src/utils/decorators.ts @@ -0,0 +1,12 @@ +export function bounded(_originalMethod: unknown, context: ClassMethodDecoratorContext) { + const methodName = context.name; + + if (context.private) { + throw new Error(`'bound' cannot decorate private properties like ${methodName as string}.`); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + context.addInitializer(function (this: any) { + this[methodName] = this[methodName].bind(this); + }); +} diff --git a/src/utils/index.ts b/src/utils/index.ts index a495e0b1..87876f31 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -6,3 +6,4 @@ export * from './path'; export * from './toc'; export * from './presets'; export * from './file'; +export * from './decorators'; diff --git a/tsconfig.json b/tsconfig.json index 3bc49dd1..7a503038 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,6 +6,7 @@ "outDir": "build", "module": "es2022", "moduleResolution": "bundler", + "experimentalDecorators": false, "paths": { "~/*": ["./src/*"] }