Browse Source

chore: removes js-service as redundant dependency

e22m4u 3 months ago
parent
commit
c94bc4a7a4

+ 166 - 3
README.md

@@ -10,6 +10,7 @@
   * [Node.js: Переменная окружения DEBUG](#nodejs-переменная-окружения-debug)
   * [Браузер: localStorage.debug](#браузер-localstoragedebug)
   * [Синтаксис паттернов](#синтаксис-паттернов)
+* [Класс Debuggable](#класс-debuggable)
 * [Тесты](#тесты)
 * [Лицензия](#лицензия)
 
@@ -53,7 +54,7 @@ debug('Got values %l.', ['foo', 10, true]);
 import {createDebugger} from '@e22m4u/js-debug';
 
 const debug = createDebugger();
-// используется метод inspect
+// (!) используется метод inspect
 debug.inspect({
   email: 'john.doe@example.com',
   phone: {
@@ -100,15 +101,17 @@ import {createDebugger} from '@e22m4u/js-debug';
 // вызов createDebugger() без аргументов создает
 // отладчик с пустым пространством имен
 const debug1 = createDebugger();
+debug1('Hello world');
+// Hello world
 
 // пространство имен можно передать в первом
 // аргументе фабрики, как это показано ниже
 const debug2 = createDebugger('myApp');
 const debug3 = createDebugger('myApp', 'myService');
 const debug4 = createDebugger('myApp:myService');
-debug1('Hello world');
 debug2('Hello world');
 debug3('Hello world');
+debug4('Hello world');
 // myApp Hello world
 // myApp:myService Hello world
 // myApp:myService Hello world
@@ -299,7 +302,7 @@ localStorage.debug = '';
 
 - Точное совпадение (например, `myApp:myService`);
 - Wildcard (\*): `myApp*` соответствует `myApp`, `myApp:myService` и т.д.;
-- Несколько паттернов можно указать разделив из запятой или пробелом;
+- Несколько паттернов можно указать разделив их запятой или пробелом;
 - Включить всё: `*` включает вывод для всех пространств имен;
 
 Примеры шаблонов в окружении Node.js:
@@ -312,6 +315,166 @@ DEBUG=app:* node main.js # пространства имен с префиксо
 DEBUG=app:worker,legacy node main.js # только app:worker и legacy
 ```
 
+## Класс Debuggable
+
+Класс реализует метод `getDebuggerFor(method: Function)`, позволяющий
+связывать сообщения отладки по названию метода и идентифицировать вызовы
+с помощью хэша.
+
+```
+┌────────────────────────────────────┐
+│ класс      | метод    | хэш вызова │
+" calculator : multiply : ds83       "
+└────────────────────────────────────┘
+```
+
+Создание экземпляра автоматически логируется.
+
+```js
+import {Debuggable} from '@e22m4u/js-debug';
+
+class MyClass extends Debuggable {}
+
+new MyClass();
+// myClass:constructor:f12s Instantiated.
+```
+
+Пример вывода сообщений отладки двух методов одного класса.
+
+```js
+import {Debuggable} from '@e22m4u/js-debug';
+
+process.env['DEBUG'] = '*';
+
+class Calculator extends Debuggable {
+  multiply(a, b) {
+    const debug = this.getDebuggerFor(this.multiply); // <=
+    debug('Multiplying %v by %v.');
+    const res = a * b;
+    debug('Result %v.');
+    return res;
+  }
+
+  divide(a, b) {
+    const debug = this.getDebuggerFor(this.divide); // <=
+    debug('Dividing %v by %v.');
+    const res = a / b;
+    debug('Result %v.');
+    return res;
+  }
+}
+
+const calculator = new Calculator();
+calculator.multiply(4, 8);
+calculator.multiply(6, 10);
+calculator.divide(32, 8);
+calculator.divide(60, 10);
+// calculator:constructor:ds83 Instantiated.
+// calculator:multiply:4d8w Multiplying 4 by 8.
+// calculator:multiply:4d8w Result 32.
+// calculator:multiply:v54w Multiplying 6 by 10.
+// calculator:multiply:v54w Result 60.
+// calculator:divide:c9ew Dividing 32 by 8.
+// calculator:divide:c9ew Result 4.
+// calculator:divide:twq2 Dividing 60 by 10.
+// calculator:divide:twq2 Result 6.
+```
+
+Использование переменной окружения `DEBUGGER_NAMESPACE`.
+
+```js
+import {Debuggable} from '@e22m4u/js-debug';
+
+process.env['DEBUGGER_NAMESPACE'] = 'myApp'; // <=
+process.env['DEBUG'] = 'myApp*';
+
+class Calculator extends Debuggable {
+  multiply(a, b) {
+    const debug = this.getDebuggerFor(this.multiply);
+    debug('Multiplying %v by %v.');
+    const res = a * b;
+    debug('Result %v.');
+    return res;
+  }
+}
+
+const calculator = new Calculator();
+calculator.multiply(4, 8);
+// myApp:calculator:constructor:ds83 Instantiated.
+// myApp:calculator:multiply:4d8w Multiplying 4 by 8.
+// myApp:calculator:multiply:4d8w Result 32.
+```
+
+### DebuggableOptions
+
+Первый аргумент класса `Debuggable` принимает объект со следующими свойствами.
+
+- `namespace?: string` - префиксное пространство имен;
+- `noEnvNs?: boolean` - игнорировать переменную `DEBUGGER_NAMESPACE`;
+
+#### DebuggableOptions.namespace
+
+Значение опции `namespace` добавляет префиксное пространство имен.
+
+```js
+import {Debuggable} from '@e22m4u/js-debug';
+
+process.env['DEBUG'] = 'myApp*';
+
+class Calculator extends Debuggable {
+  constructor() {
+    super({namespace: 'myApp'}); // <=
+  }
+
+  multiply(a, b) {
+    const debug = this.getDebuggerFor(this.multiply);
+    debug('Multiplying %v by %v.');
+    const res = a * b;
+    debug('Result %v.');
+    return res;
+  }
+}
+
+const calculator = new Calculator();
+calculator.multiply(4, 8);
+// myApp:calculator:constructor:ds83 Instantiated.
+// myApp:calculator:multiply:4d8w Multiplying 4 by 8.
+// myApp:calculator:multiply:4d8w Result 32.
+```
+
+#### DebuggableOptions.noEnvNs
+
+Значение `true` опции `noEnvNs` позволяет игнорировать переменную
+окружения `DEBUGGER_NAMESPACE`, устанавливающую префиксное пространство
+имен.
+
+```js
+import {Debuggable} from '@e22m4u/js-debug';
+
+process.env['DEBUGGER_NAMESPACE'] = 'myApp'; // <=
+process.env['DEBUG'] = '*';
+
+class Calculator extends Debuggable {
+  constructor() {
+    super({noEnvNs: true}); // <=
+  }
+
+  multiply(a, b) {
+    const debug = this.getDebuggerFor(this.multiply);
+    debug('Multiplying %v by %v.');
+    const res = a * b;
+    debug('Result %v.');
+    return res;
+  }
+}
+
+const calculator = new Calculator();
+calculator.multiply(4, 8);
+// calculator:constructor:ds83 Instantiated.
+// calculator:multiply:4d8w Multiplying 4 by 8.
+// calculator:multiply:4d8w Result 32.
+```
+
 ## Тесты
 
 ```bash

+ 27 - 16
dist/cjs/index.cjs

@@ -23,16 +23,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
 var index_exports = {};
 __export(index_exports, {
   DEFAULT_OFFSET_STEP_SPACES: () => DEFAULT_OFFSET_STEP_SPACES,
-  DebuggableService: () => DebuggableService,
+  Debuggable: () => Debuggable,
   INSPECT_OPTIONS: () => INSPECT_OPTIONS,
   createColorizedDump: () => createColorizedDump,
   createDebugger: () => createDebugger
 });
 module.exports = __toCommonJS(index_exports);
 
-// src/services/debuggable-service.js
-var import_js_service = require("@e22m4u/js-service");
-
 // src/utils/to-camel-case.js
 function toCamelCase(input) {
   return input.replace(/(^\w|[A-Z]|\b\w)/g, (c) => c.toUpperCase()).replace(/\W+/g, "").replace(/(^\w)/g, (c) => c.toLowerCase());
@@ -63,14 +60,20 @@ function generateRandomHex(length = 4) {
 }
 __name(generateRandomHex, "generateRandomHex");
 
-// src/services/debuggable-service.js
-var _DebuggableService = class _DebuggableService extends import_js_service.Service {
+// src/debuggable.js
+var _Debuggable = class _Debuggable {
   /**
    * Debug.
    *
    * @type {Function}
    */
   debug;
+  /**
+   * Ctor Debug.
+   *
+   * @type {Function}
+   */
+  ctorDebug;
   /**
    * Возвращает функцию-отладчик с сегментом пространства имен
    * указанного в параметре метода.
@@ -85,23 +88,31 @@ var _DebuggableService = class _DebuggableService extends import_js_service.Serv
    * Constructor.
    *
    * @param {object|undefined} container
+   * @param {DebuggableOptions|undefined} options
    */
-  constructor(container) {
-    super(container);
-    const serviceName = toCamelCase(this.constructor.name);
-    this.debug = createDebugger(serviceName);
-    const debug = this.debug.withNs("constructor").withHash();
-    debug(_DebuggableService.INSTANTIATION_MESSAGE);
+  constructor(options = void 0) {
+    const className = toCamelCase(this.constructor.name);
+    options = typeof options === "object" && options || {};
+    const namespace = options.namespace && String(options.namespace) || void 0;
+    if (namespace) {
+      this.debug = createDebugger(namespace, className);
+    } else {
+      this.debug = createDebugger(className);
+    }
+    const noEnvNs = Boolean(options.noEnvNs);
+    if (noEnvNs) this.debug = this.debug.withoutEnvNs();
+    this.ctorDebug = this.debug.withNs("constructor").withHash();
+    this.ctorDebug(_Debuggable.INSTANTIATION_MESSAGE);
   }
 };
-__name(_DebuggableService, "DebuggableService");
+__name(_Debuggable, "Debuggable");
 /**
  * Instantiation message;
  *
  * @type {string}
  */
-__publicField(_DebuggableService, "INSTANTIATION_MESSAGE", "Instantiated.");
-var DebuggableService = _DebuggableService;
+__publicField(_Debuggable, "INSTANTIATION_MESSAGE", "Instantiated.");
+var Debuggable = _Debuggable;
 
 // src/create-debugger.js
 var import_js_format = require("@e22m4u/js-format");
@@ -381,7 +392,7 @@ __name(createDebugger, "createDebugger");
 // Annotate the CommonJS export names for ESM import in node:
 0 && (module.exports = {
   DEFAULT_OFFSET_STEP_SPACES,
-  DebuggableService,
+  Debuggable,
   INSPECT_OPTIONS,
   createColorizedDump,
   createDebugger

+ 1 - 2
package.json

@@ -38,8 +38,7 @@
     "prepare": "husky"
   },
   "dependencies": {
-    "@e22m4u/js-format": "~0.1.8",
-    "@e22m4u/js-service": "~0.3.3"
+    "@e22m4u/js-format": "~0.1.8"
   },
   "devDependencies": {
     "@commitlint/cli": "~19.8.1",

+ 19 - 7
src/services/debuggable-service.d.ts → src/debuggable.d.ts

@@ -1,17 +1,28 @@
-import {Callable} from '../types.js';
-import {Service} from '@e22m4u/js-service';
-import {Debugger} from '../create-debugger.js';
-import {ServiceContainer} from '@e22m4u/js-service';
+import {Callable} from './types.js';
+import {Debugger} from './create-debugger.js';
 
 /**
- * Debuggable Service.
+ * Debuggable options.
  */
-export class DebuggableService extends Service {
+export type DebuggableOptions = {
+  namespace: string,
+  noEnvNs: string,
+}
+
+/**
+ * Debuggable.
+ */
+export class Debuggable {
   /**
    * Debug.
    */
   debug: Debugger;
 
+  /**
+   * Debug.
+   */
+  ctorDebug: Debugger;
+
   /**
    * Возвращает функцию-отладчик с сегментом пространства имен
    * указанного в параметре метода.
@@ -25,6 +36,7 @@ export class DebuggableService extends Service {
    * Constructor.
    *
    * @param container
+   * @param options
    */
-  constructor(container?: ServiceContainer);
+  constructor(options?: DebuggableOptions);
 }

+ 73 - 0
src/debuggable.js

@@ -0,0 +1,73 @@
+import {toCamelCase} from './utils/index.js';
+import {createDebugger} from '@e22m4u/js-debug';
+
+/**
+ * @typedef {{
+ *   namespace: string,
+ *   noEnvNs: string,
+ * }} DebuggableOptions
+ */
+
+/**
+ * Debuggable.
+ */
+export class Debuggable {
+  /**
+   * Instantiation message;
+   *
+   * @type {string}
+   */
+  static INSTANTIATION_MESSAGE = 'Instantiated.';
+
+  /**
+   * Debug.
+   *
+   * @type {Function}
+   */
+  debug;
+
+  /**
+   * Ctor Debug.
+   *
+   * @type {Function}
+   */
+  ctorDebug;
+
+  /**
+   * Возвращает функцию-отладчик с сегментом пространства имен
+   * указанного в параметре метода.
+   *
+   * @param {Function} method
+   * @returns {Function}
+   */
+  getDebuggerFor(method) {
+    return this.debug.withHash().withNs(method.name);
+  }
+
+  /**
+   * Constructor.
+   *
+   * @param {object|undefined} container
+   * @param {DebuggableOptions|undefined} options
+   */
+  constructor(options = undefined) {
+    const className = toCamelCase(this.constructor.name);
+    options = (typeof options === 'object' && options) || {};
+    // кроме переменной окружения DEBUGGER_NAMESPACE,
+    // пространство имен можно определить опцией "namespace"
+    const namespace =
+      (options.namespace && String(options.namespace)) || undefined;
+    if (namespace) {
+      this.debug = createDebugger(namespace, className);
+    } else {
+      this.debug = createDebugger(className);
+    }
+    // опция "noEnvNs" отключает пространство имен
+    // из переменной окружения DEBUGGER_NAMESPACE
+    const noEnvNs = Boolean(options.noEnvNs);
+    if (noEnvNs) this.debug = this.debug.withoutEnvNs();
+
+    this.ctorDebug = this.debug.withNs('constructor').withHash();
+    this.ctorDebug(Debuggable.INSTANTIATION_MESSAGE);
+  }
+}

+ 131 - 0
src/debuggable.spec.js

@@ -0,0 +1,131 @@
+import {expect} from 'chai';
+import {createSpy} from '@e22m4u/js-spy';
+import {Debuggable} from './debuggable.js';
+import {stripAnsi} from './utils/index.js';
+import {escapeRegexp} from './utils/index.js';
+
+describe('Debuggable', function () {
+  let consoleLogSpy;
+  let originalDebugEnv;
+  let originalDebuggerNamespaceEnv;
+
+  beforeEach(function () {
+    // шпионим за console.log перед каждым тестом
+    consoleLogSpy = createSpy(console, 'log');
+    // сохраняем исходные переменные окружения
+    originalDebugEnv = process.env.DEBUG;
+    originalDebuggerNamespaceEnv = process.env.DEBUGGER_NAMESPACE;
+    // сбрасываем переменные перед тестом
+    delete process.env.DEBUG;
+    delete process.env.DEBUGGER_NAMESPACE;
+  });
+
+  afterEach(function () {
+    // восстанавливаем console.log
+    consoleLogSpy.restore();
+    // восстанавливаем переменные окружения
+    if (originalDebugEnv === undefined) {
+      delete process.env.DEBUG;
+    } else {
+      process.env.DEBUG = originalDebugEnv;
+    }
+    if (originalDebuggerNamespaceEnv === undefined) {
+      delete process.env.DEBUGGER_NAMESPACE;
+    } else {
+      process.env.DEBUGGER_NAMESPACE = originalDebuggerNamespaceEnv;
+    }
+  });
+
+  it('has the debug method', function () {
+    const res = new Debuggable();
+    expect(typeof res.debug).to.be.eq('function');
+  });
+
+  describe('constructor', function () {
+    it('should output the specific message when instantiated', function () {
+      process.env.DEBUG = '*';
+      new Debuggable();
+      expect(consoleLogSpy.callCount).to.equal(1);
+      const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+      expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+        new RegExp(`^debuggable:constructor:[a-f0-9]{4} ${msg}$`),
+      );
+    });
+
+    it('should use DEBUGGER_NAMESPACE environment variable', function () {
+      process.env.DEBUGGER_NAMESPACE = 'myApp';
+      process.env.DEBUG = '*';
+      new Debuggable();
+      expect(consoleLogSpy.callCount).to.equal(1);
+      const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+      expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+        new RegExp(`^myApp:debuggable:constructor:[a-f0-9]{4} ${msg}$`),
+      );
+    });
+
+    it('uses extended class name as namespace', function () {
+      process.env.DEBUG = '*';
+      class MyService extends Debuggable {}
+      new MyService();
+      expect(consoleLogSpy.callCount).to.equal(1);
+      const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+      expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+        new RegExp(`^myService:constructor:[a-f0-9]{4} ${msg}$`),
+      );
+    });
+
+    describe('"namespace" option', function () {
+      it('should use "namespace" option as the first namespace segment', function () {
+        process.env.DEBUG = '*';
+        new Debuggable({namespace: 'myApp'});
+        expect(consoleLogSpy.callCount).to.equal(1);
+        const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+        expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+          new RegExp(`^myApp:debuggable:constructor:[a-f0-9]{4} ${msg}$`),
+        );
+      });
+    });
+
+    describe('"noEnvNs" option', function () {
+      it('should use DEBUGGER_NAMESPACE when the option "noEnvNs" is false', function () {
+        process.env.DEBUGGER_NAMESPACE = 'myApp';
+        process.env.DEBUG = '*';
+        new Debuggable({noEnvNs: false});
+        expect(consoleLogSpy.callCount).to.equal(1);
+        const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+        expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+          new RegExp(`^myApp:debuggable:constructor:[a-f0-9]{4} ${msg}$`),
+        );
+      });
+
+      it('should skip DEBUGGER_NAMESPACE when the option "noEnvNs" is true', function () {
+        process.env.DEBUGGER_NAMESPACE = 'myApp';
+        process.env.DEBUG = '*';
+        new Debuggable({noEnvNs: true});
+        expect(consoleLogSpy.callCount).to.equal(1);
+        const msg = escapeRegexp(Debuggable.INSTANTIATION_MESSAGE);
+        expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
+          new RegExp(`^debuggable:constructor:[a-f0-9]{4} ${msg}$`),
+        );
+      });
+    });
+  });
+
+  describe('getDebuggerFor', function () {
+    it('returns a new debugger with method name segment and hash', function () {
+      process.env.DEBUG = '*';
+      class MyService extends Debuggable {
+        myMethod() {
+          const debug = this.getDebuggerFor(this.myMethod);
+          debug('Message');
+        }
+      }
+      const myService = new MyService();
+      myService.myMethod();
+      expect(consoleLogSpy.callCount).to.equal(2);
+      expect(stripAnsi(consoleLogSpy.getCall(1).args[0])).to.match(
+        new RegExp(`^myService:myMethod:[a-f0-9]{4} Message$`),
+      );
+    });
+  });
+});

+ 1 - 0
src/index.d.ts

@@ -1,2 +1,3 @@
+export * from './debuggable.js';
 export * from './create-debugger.js';
 export * from './create-colorized-dump.js';

+ 1 - 1
src/index.js

@@ -1,3 +1,3 @@
-export * from './services/index.js';
+export * from './debuggable.js';
 export * from './create-debugger.js';
 export * from './create-colorized-dump.js';

+ 0 - 46
src/services/debuggable-service.js

@@ -1,46 +0,0 @@
-import {Service} from '@e22m4u/js-service';
-import {toCamelCase} from '../utils/index.js';
-import {createDebugger} from '@e22m4u/js-debug';
-
-/**
- * Debuggable Service.
- */
-export class DebuggableService extends Service {
-  /**
-   * Instantiation message;
-   *
-   * @type {string}
-   */
-  static INSTANTIATION_MESSAGE = 'Instantiated.';
-
-  /**
-   * Debug.
-   *
-   * @type {Function}
-   */
-  debug;
-
-  /**
-   * Возвращает функцию-отладчик с сегментом пространства имен
-   * указанного в параметре метода.
-   *
-   * @param {Function} method
-   * @returns {Function}
-   */
-  getDebuggerFor(method) {
-    return this.debug.withHash().withNs(method.name);
-  }
-
-  /**
-   * Constructor.
-   *
-   * @param {object|undefined} container
-   */
-  constructor(container) {
-    super(container);
-    const serviceName = toCamelCase(this.constructor.name);
-    this.debug = createDebugger(serviceName);
-    const debug = this.debug.withNs('constructor').withHash();
-    debug(DebuggableService.INSTANTIATION_MESSAGE);
-  }
-}

+ 0 - 60
src/services/debuggable-service.spec.js

@@ -1,60 +0,0 @@
-import {expect} from 'chai';
-import {createSpy} from '@e22m4u/js-spy';
-import {Service} from '@e22m4u/js-service';
-import {escapeRegexp, stripAnsi} from '../utils/index.js';
-import {DebuggableService} from './debuggable-service.js';
-
-describe('DebuggableService', function () {
-  let consoleLogSpy;
-  let originalDebugEnv;
-  let originalDebuggerNamespaceEnv;
-
-  beforeEach(function () {
-    // шпионим за console.log перед каждым тестом
-    consoleLogSpy = createSpy(console, 'log');
-    // сохраняем исходные переменные окружения
-    originalDebugEnv = process.env.DEBUG;
-    originalDebuggerNamespaceEnv = process.env.DEBUGGER_NAMESPACE;
-    // сбрасываем переменные перед тестом
-    delete process.env.DEBUG;
-    delete process.env.DEBUGGER_NAMESPACE;
-  });
-
-  afterEach(function () {
-    // восстанавливаем console.log
-    consoleLogSpy.restore();
-    // восстанавливаем переменные окружения
-    if (originalDebugEnv === undefined) {
-      delete process.env.DEBUG;
-    } else {
-      process.env.DEBUG = originalDebugEnv;
-    }
-    if (originalDebuggerNamespaceEnv === undefined) {
-      delete process.env.DEBUGGER_NAMESPACE;
-    } else {
-      process.env.DEBUGGER_NAMESPACE = originalDebuggerNamespaceEnv;
-    }
-  });
-
-  it('has the debug method', function () {
-    const res = new DebuggableService();
-    expect(typeof res.debug).to.be.eq('function');
-  });
-
-  describe('constructor', function () {
-    it('extends the Service class', function () {
-      const res = new DebuggableService();
-      expect(res).to.be.instanceof(Service);
-    });
-
-    it('should output the specific message when instantiated', function () {
-      process.env.DEBUG = '*';
-      new DebuggableService();
-      expect(consoleLogSpy.callCount).to.equal(1);
-      const msg = escapeRegexp(DebuggableService.INSTANTIATION_MESSAGE);
-      expect(stripAnsi(consoleLogSpy.getCall(0).args[0])).to.match(
-        new RegExp(`debuggableService:constructor:[a-f0-9]{4} ${msg}`),
-      );
-    });
-  });
-});

+ 0 - 1
src/services/index.d.ts

@@ -1 +0,0 @@
-export * from './debuggable-service.js';

+ 0 - 1
src/services/index.js

@@ -1 +0,0 @@
-export * from './debuggable-service.js';