Типовой ES-модуль в TeqFW или «сборник вредных советов»
- вторник, 25 марта 2025 г. в 00:00:04
Я ранее описал принципы, которыми руководствуюсь при разработке веб-приложений, а также требования, предъявляемые со стороны платформы TeqFW к JS-коду. В этой публикации я покажу, как выглядит код типового модуля платформы, где не используется статический импорт. Хочу сразу отметить, что кажущаяся сложность материалов обусловлена непривычностью представленных концепций. Наработанный опыт и инерция мышления — сильные вещи! Тем, кто имеет ограниченный опыт в JS-разработке, этот материал будет проще для восприятия, в то время как опытным разработчикам предстоит преодолеть барьер устоявшихся привычек. На мой взгляд, несмотря на то что "TypeScript — это суперсет JavaScript", самыми сложными концепции платформы станут именно для TS-разработчиков.
Ну, вот - я предупредил, дальнейшее чтение - на ваш страх и риск.
Далее я представлю пример типового модуля платформы, без особых пояснений. Те, кто сразу увидят суть концепции, смогут легко развить её и применить в своей работе. Кто сомневается в том, что видит - может обсудить свои сомнения с "TeqFW Help Desk" (это преднастроенный GPT-чат с загруженными в его базу знаний документами по платформе).
Разработать ES-модуль, который рекурсивно обходит указанный каталог и возвращает объект с количеством файлов и папок в каждом подкаталоге.
/**
* 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, обеспечивая стандарты, улучшая читаемость и
тестируемость.
Спасибо тем, кто читал, и всем приятного кодинга!! 🧂🥃🍋🟩