Типовой ES-модуль в TeqFW или «сборник вредных советов»

от автора

Я ранее описал принципы, которыми руководствуюсь при разработке веб-приложений, а также требования, предъявляемые со стороны платформы TeqFW к JS-коду. В этой публикации я покажу, как выглядит код типового модуля платформы, где не используется статический импорт. Хочу сразу отметить, что кажущаяся сложность материалов обусловлена непривычностью представленных концепций. Наработанный опыт и инерция мышления — сильные вещи! Тем, кто имеет ограниченный опыт в JS-разработке, этот материал будет проще для восприятия, в то время как опытным разработчикам предстоит преодолеть барьер устоявшихся привычек. На мой взгляд, несмотря на то что «TypeScript — это суперсет JavaScript«, самыми сложными концепции платформы станут именно для TS-разработчиков.

Ну, вот — я предупредил, дальнейшее чтение — на ваш страх и риск.

Далее я представлю пример типового модуля платформы, без особых пояснений. Те, кто сразу увидят суть концепции, смогут легко развить её и применить в своей работе. Кто сомневается в том, что видит — может обсудить свои сомнения с «TeqFW Help Desk» (это преднастроенный GPT-чат с загруженными в его базу знаний документами по платформе).

Задача

Разработать ES-модуль, который рекурсивно обходит указанный каталог и возвращает объект с количеством файлов и папок в каждом подкаталоге.

Результат в стиле платформы TeqFW

/**  * Scans a directory recursively and counts files and folders in each subdirectory.  */ export default class DirScanner {     /**      * Dynamically set up the environment (Node.js modules) then create the instance with required functionality.      * @param {Object} deps      * @param {typeof import('node:fs')} deps.fs      * @param {typeof import('node:path')} deps.path      */     constructor({fs, path}) {         // FUNC         /**          * Recursive function to scan directories.          * @param {string} dir          * @returns {Object.<string, number>}          */         function scan(dir) {             const result = {};             let files = 0;             let dirs = 0;             const entries = fs.readdirSync(dir, {withFileTypes: true});             for (const entry of entries) {                 const fullPath = path.join(dir, entry.name);                 if (entry.isDirectory()) {                     dirs++;                     const subResult = scan(fullPath);                     Object.assign(result, subResult);                 } else {                     files++;                 }             }             result[dir] = files + dirs;             return result;         }          // MAIN         /**          * Scans directories from given root path.          * @param {string} rootPath          * @returns {Object.<string, number>}          */         this.run = (rootPath) => scan(rootPath);     } }

Как видите, просто коллекция bad practice:

  • В коде нет статических импортов, даже fs & path модули ядра Node.js внедряются динамически.

  • Один единственный default’ный экспорт.

  • Экспортируется класс, хотя здесь достаточно функции.

  • Создаваемый конструктором экземпляр использует замыкание для изоляции передаваемого окружения и рабочих объектов вместо приватных свойств.

Запуск функционала

Тем не менее, это полностью рабочий JS-код, который можно использовать как в Node.js, так и в браузере (это уже зависит от фантазии разработчика, что в браузере считать фолдером и файлом, и какая будет реализация fs & path).

В среде nodejs этот код запускается так:

    const {default: DirScanner} = await import('./DirScanner.js');     const {default: fs} = await import('node:fs');     const {default: path} = await import('node:path');      const scanner = new DirScanner({fs, path});     const rootPath = './';     const result = scanner.run(rootPath);

Я специально использовал динамические импорты, но можно и статические, более привычные:

import DirScanner from './DirScanner.js'; import fs from 'node:fs'; import path from 'node:path';

Заключение

Я показал, как выглядит типовой ES-модуль, соответствующий требованиям платформы TeqFW. Каждый разработчик увидит в этом подходе какие-то плюсы и минусы. Ну, или не увидит, что тоже нормально.

Связывание кода в режиме выполнения (позднее связывание) — это база платформы. Чтобы оно работало, код должен быть оформлен определённым образом. Само собой, что у данного метода (соответственно, и у платформы) есть своя область применимости, и область, где данный метод (и платформа) категорически не подходит. Не стоит использовать TeqFW в случаях, когда необходима высокая производительность или предсказуемость. Однако для гибких и расширяемых решений эта платформа вполне себе может являться выбором.

Для тех, кто дошел до конца, — бонус, инструкции для LLM с рекомендациями по оформлению кода для платформы TeqFW, которые используются в «TeqFW Help Desk«:

промпт (кратко)
# **Рекомендации по структуре ES-модулей для TeqFW**  LLM **рекомендуется следовать этим рекомендациям**. **Отклонения возможны**, но они помогают обеспечить согласованность и удобство разработки.  ## **1. Структура ES-модулей**  - Типовой модуль: `DOCUMENTATION`, `CONSTANTS`, `FUNCTIONS`, `CLASSES`, `EXPORTS`. - Секции могут быть маркированы через комментарии (`// DOC`, `// CONST`, `// FUNC`, `// CLASS`). - `DOCUMENTATION` может быть разделена на описание в начале и `@typedef` в конце.  ## **2. Отсутствие статического связывания**  - В TeqFW не используются статические импорты. **Отклонения недопустимы**.  ## **3. Структура функции**  - Для функций: `DOCUMENTATION`, `CONSTANTS`, `FUNCTIONS`, `MAIN`. - Секции могут быть вложенными и маркироваться (`// DOC`, `// CONST`, `// FUNC`, `// MAIN`).  ## **4. Наличие только одного экспорта**  - Рекомендуется экспортировать один объект, функцию или класс для упрощения структуры и повышения читаемости.  ## **5. Использование default-экспорта**  - Если один экспорт, используйте `default`-экспорт для упрощения адресации.  ## **6. Экспорт классов**  - Рекомендуется использовать классы для более строгой структуры, что облегчает анализ кода и тестирование.  ## **7. Внедрение зависимостей через конструктор**  - Зависимости внедряются через конструктор экспортируемого объекта, что улучшает тестируемость и заменяемость   зависимостей.  ## **8. Сокрытие данных в замыкании**  - Рекомендуется использовать замыкания для инкапсуляции данных, так как они упрощают код и не влияют на   производительность при минимальном количестве экземпляров.  ---  **LLM должна соблюдать эти рекомендации и разъяснять несоответствия.**

эмбеддинг (расширенно)
# **Рекомендации по структуре ES-модулей для TeqFW**  TeqFW использует **ES-модули** и **не поддерживает статические импорты**. Чтобы использовать все возможности платформы, следуйте этим рекомендациям.  ## **1. Структура ES-модулей**  Каждый ES-модуль TeqFW разбит на секции для повышения читаемости и удобства навигации в коде. Рекомендуемый порядок секций:  - `DOCUMENTATION` - `CONSTANTS` - `FUNCTIONS` - `CLASSES` - `EXPORTS`  Секция `DOCUMENTATION` может быть разбита на две части и продолжена внизу модуля для описания структур с использованием аннотации `@typedef`. Каждую секцию можно маркировать через комментарии, например, `// DOC`, `// CONST`, `// FUNC`, `// CLASS`.  Пример структуры модуля:  ```js /**  * Documentation for whole module.  */ // CONST const PI = 3.14;  // FUNC function calculateArea(radius) {     return PI * radius * radius; }  // CLASSES class MyClass {     // class body }  // EXPORTS export default MyClass;  // DOCS /**  * @typedef  */ ```  Секции могут быть объединены или пропущены. Зачастую типовой код модуля выглядит так:  ```js /**  * Documentation for whole module.  */ export default class MyClass {}; ```  ## **2. Отсутствие статического связывания**  - **Статические импорты не используются** в TeqFW. - Все зависимости должны внедряться динамически через конструктор. - **Отклонения недопустимы**.  Пример **неправильного** подхода:  ```js import {existsSync} from 'node:fs';  // Неверно ```  ## **3. Структура функции**  Функции также могут быть разделены на секции:  - `DOCUMENTATION` - `CONSTANTS` - `FUNCTIONS` - `MAIN`  Пример:  ```js /**  * Documentation for the function.  * @param radius  * @returns {number}  */ function calculateArea(radius) {     // CONST     const PI = 3.14;      // FUNC     function power2(x) {         return x * x;     }      // MAIN     return PI * power2(radius); }  ```  Секции могут быть вложены, если одна функция вызывает другую.  ## **4. Наличие только одного экспорта**  - **Рекомендуется экспортировать один объект, функцию или класс**. Это упрощает структуру и повышает читаемость.  Пример:  ```js export default class MyClass {     // class body } ```  ## **5. Использование default-экспорта**  - Если в модуле только один экспорт, рекомендуется использовать `default`-экспорт, чтобы упростить адресацию экспортов и   работы с зависимостями.  Пример:  ```js export default function calculateArea(radius) {     return Math.PI * radius * radius; } ```  ## **6. Экспорт классов**  - **Классы предпочтительнее функций**, так как они обеспечивают более строгую и предсказуемую структуру для статического   анализа.  Пример класса:  ```js export default class MyClass {     constructor(name) {         this.name = name;     }      greet() {         return `Hello, ${this.name}!`;     } } ```  ## **7. Внедрение зависимостей через конструктор**  - Все зависимости, включая пакеты из `node_modules`, внедряются через конструктор экспортируемого объекта.  Пример:  ```js export default class MyClass {     constructor({dependency1, dependency2}) { } } ```  ## **8. Сокрытие данных в замыкании**  - Используйте **замыкания** для инкапсуляции данных вместо приватных свойств и методов, чтобы сократить код.  Пример:  ```js export default class MyClass {     constructor({dependency1, dependency2}) {         this.run = function () {             dependency1.doSomething();             dependency2.doSomethingElse();         };     } } ```  ## **Типовой код ES-модуля для TeqFW**  Пример типового модуля:  ```js /**  *  Documentation for whole module.  */ // CONST const PI = 3.14;  // FUNC function calculateCircumference(radius) {     return 2 * PI * radius; }  /**  * Documentation for the main class.  */ export default class Circle {     /**      * @param {{calculate: function}} areaCircle      * @param {function} timesTwo      */     constructor({areaCircle, timesTwo}) {         this.calculate = ({radius}) => {             const {area} = areaCircle.calculate({radius});             const circumference = calculateCircumference(radius);             const diameter = timesTwo(radius);             return {area, circumference, diameter};         };     } } ```  ---  Эти рекомендации помогут эффективно разрабатывать код для TeqFW, обеспечивая стандарты, улучшая читаемость и тестируемость.

Спасибо тем, кто читал, и всем приятного кодинга!! 🧂🥃🍋🟩


ссылка на оригинал статьи https://habr.com/ru/articles/893762/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *