Rename project to SSAU Generator and restructure content

- Move projects into content/<project>/{docx,pptx} with shared assets/
- Flatten config paths; output = project name in project folder
- Add pptx --init scaffold; remove docx exe build
- Add feature-complete content/example; ignore content/* except example
- Rewrite README and AGENTS.md
This commit is contained in:
2026-10-11 23:25:22 +04:00
parent 35611e2a91
commit bf7816ab7c
80 changed files with 1166 additions and 3432 deletions

View File

@@ -1,145 +1,123 @@
# AGENTS.md — генератор DocxGenerator
# AGENTS.md — работа с документом DOCX
`DocxGenerator` — автономный инструмент, который собирает отчёт в формате **DOCX** из обычных
**Markdown**-файлов по фиксированному дизайну СТО 02068410-004-2018. Поведение полностью
задаётся файлом `config.yaml`.
Документ создан и для людей, и для ИИ-агентов: он описывает CLI, формат контента, схему
конфига и порядок сборки `.exe`.
---
Генератор `docx/` собирает отчёт в формате **DOCX** из обычных **Markdown**-файлов
по фиксированному дизайну СТО 02068410-004-2018. Этот файл — инструкция агенту по
созданию и правке конкретного документа. Поведение полностью задаётся `config.yaml`
проекта, менять код генератора не нужно.
## 1. Требования
- Windows (собранный `.exe` под `win-x64`).
- Node.js **только для сборки** `.exe` из исходников. Конечному пользователю Node.js не нужен.
- Готовые PNG вставляются без внешних зависимостей. Для рендеринга PlantUML (`.puml`)
нужны установленная Java (JRE 11+) и файл `plantuml.jar` (см. п. 4, `plantuml`).
- Node.js 18+, зависимости ставятся командой `npm install` в папке `docx/`.
- Windows/macOS/Linux.
- Готовые PNG вставляются без внешних зависимостей. Для рендеринга PlantUML
(`.puml`) нужны Java 11+ и файл `plantuml.jar` (см. `plantuml` в конфиге).
## 2. Состав дистрибутива
## 2. Раскладка проекта
Проект лежит в `content/<проект>/docx/` рядом с `config.yaml`:
```
DocxGenerator.exe # исполняемый файл
config.yaml # настройки (создаётся автоматически при первом запуске)
content/ # Markdown-файлы документа и ресурсы
*.md
assets/
images/ # PNG для рисунков (готовятся заранее) и исходники .puml
title-page.docx # титульный лист (по умолчанию content/assets/title-page.docx)
tools/
plantuml.jar # рендерер PlantUML (для файлов .puml)
report.docx # результат сборки (путь задаётся document.output)
content/<проект>/
├── assets/ # общие ресурсы проекта (общие с pptx)
│ ├── title-page.docx # титульный лист
│ └── images/ # PNG-рисунки и исходники .puml
└── docx/
├── config.yaml # настройки документа
├── 01-introduction.md
├── 02-*.md
└── 05-bibliography.md
```
Все относительные пути в `config.yaml` считаются **от каталога самого `config.yaml`**.
Сам `config.yaml` и папки ищутся **рядом с `DocxGenerator.exe`**.
Все относительные пути в `config.yaml` считаются **от каталога самого
`config.yaml`**. Если ресурсы общие с презентацией, указывайте их через `../assets`
(например, `paths.assets: ../assets`, `document.titlePage: ../assets/title-page.docx`).
В монорепозитории общий `plantuml.jar` лежит в корне, в `tools/plantuml.jar`,
поэтому репозиторный `config.yaml` задаёт `plantuml.jar: ../tools/plantuml.jar`.
Для автономного дистрибутива положите `tools/plantuml.jar` рядом с `.exe`
(значение по умолчанию при `--init` — `tools/plantuml.jar`).
Отчёт собирается в папку проекта и называется по имени проекта:
`content/<проект>/<проект>.docx`. Значение ключа `document.output` при такой
раскладке — `../<проект>.docx`.
Структуру проекта создавайте командой `--init`: она формирует `config.yaml`,
примеры `*.md` и общий каталог `content/<проект>/assets/` (титульный лист,
изображения), который используется и генератором `pptx`.
Готовый пример со всеми возможностями — `content/example/docx/`.
## 3. Командная строка
```
DocxGenerator [опции]
Из корня репозитория:
--config <путь> путь к config.yaml (по умолчанию рядом с exe)
--content <dir> каталог с config.yaml, content/ и assets/ (по умолчанию рядом с exe)
```
node docx/src/cli.js [опции]
--config <путь> путь к config.yaml (по умолчанию config.yaml в текущем каталоге)
--content <dir> каталог проекта: config.yaml, *.md и assets/ (по умолчанию текущий)
--output <путь> переопределить путь выходного .docx
--check только проверить конфиг и ссылки, без записи файла
--init создать шаблон config.yaml/content/assets и выйти
--init создать шаблон config.yaml/*.md/assets и выйти
--verbose подробный вывод
--version показать версию
--help справка
```
Поведение:
- Если `config.yaml` не найден, инструмент **сам создаёт** `config.yaml`, примеры `content/*.md`
и `assets/images/placeholder.png`, печатает подсказку и завершается.
- `--check` не пишет DOCX, но сообщает обо всех ошибках (битые ссылки, выход `[@n]` за границы
списка источников, отсутствующие картинки и т.п.).
Типичный цикл работы:
Типичный цикл:
```powershell
.\DocxGenerator.exe --init # первый запуск: получить шаблон
# ... правим config.yaml и content/*.md ...
.\DocxGenerator.exe --check # проверить
.\DocxGenerator.exe # собрать report.docx
node docx/src/cli.js --content content/<проект>/docx --init # создать структуру проекта
node docx/src/cli.js --content content/<проект>/docx --check # проверить
node docx/src/cli.js --content content/<проект>/docx # собрать
```
После любого изменения исходников (`config.yaml`, `*.md`, ресурсов `assets/`)
**пересоберите документ**, чтобы результат соответствовал исходникам.
Если `config.yaml` не найден, инструмент сам создаёт шаблон (`config.yaml`,
примеры Markdown и общий `assets/`) и завершается.
## 4. Схема `config.yaml`
Любой ключ можно опустить — берётся значение по умолчанию (см. `src/config/defaults.js`).
Любой ключ можно опустить — берётся значение по умолчанию (см.
`docx/src/config/defaults.js`).
### document
| Ключ | Тип | Смысл |
|------|-----|-------|
| `output` | string | путь выходного `.docx` |
| `titlePage` | string/null | путь к готовому `title-page.docx` (по умолчанию `content/assets/title-page.docx`); `null` — без титульника |
| `output` | string | путь выходного `.docx` (относительно `config.yaml`) |
| `titlePage` | string/null | путь к готовому `title-page.docx`; `null` — без титульника |
| `features.updateFields` | bool | обновлять поля (в т.ч. оглавление) при открытии в Word |
**Титульный лист.** Берётся готовый DOCX по пути `document.titlePage` и склеивается перед
документом; между титульником и основным текстом вставляется разрыв страницы. При `--init`
создаётся пример `content/assets/title-page.docx` — замените его своим. Значение `null`
отключает титульный лист.
Титульный лист склеивается перед документом, между ним и текстом вставляется
разрыв страницы. При `--init` создаётся пример `assets/title-page.docx`.
### paths
| Ключ | Смысл |
|------|-------|
| `assets` | корень ресурсов; `file=images/x.png` в директивах ищется здесь (по умолчанию `content/assets`) |
| `assets` | корень ресурсов; `file=images/x.png` в директивах ищется здесь |
### output
Путь и имя выходного `.docx` задаются ключом `document.output` (по умолчанию
`report.docx`). Путь считается от каталога `config.yaml` и может быть переопределён
флагом `--output`.
### Структура и оформление
### page
`width`, `height` (DXA, 1/20 pt), `margins: {left,right,top,bottom}` (twips). A4 и поля СТО
заданы по умолчанию.
### typography
| Ключ | Смысл |
|------|-------|
| `font` | гарнитура (по умолчанию Times New Roman) |
| `sizeBody` / `sizeTable` / `sizeSmall` | размеры в half-points (28 = 14 pt) |
| `lineSpacing` | множитель межстрочного интервала (1.5 = полуторный) |
| `color` | цвет текста (hex, без `#`) |
| `useBold` | разрешить полужирный (СТО: `false`) |
| `allowEmphasis` | разрешить курсив (СТО: `false`) |
### headings
Интервалы/выравнивание для `struct`, `section`, `subsection`, `point` (twips).
### captions
`figure`, `table`, `tableContinue` — префиксы и тире подписей (`Рисунок`, `Таблица`, `—`).
### lists
`bulletChar`, `letterSuffix`, `numberSuffix`, `nestedLevelStep`, `forbiddenLetters`.
### numbering
`figures`, `tables`, `formulas`: `global` (сквозная по документу) или `section` (в пределах
раздела, вид `1.1`). Приложения всегда имеют префикс буквы (`А.1`).
### formula
`spacingBeforeAfter`, `whereTabPosition`.
### table
`font`, `size`, `borderColor`, `borderSize`, `cellMargins: {top,bottom,left,right}`,
`headerFill`, `cellFill`.
### figure
`maxWidth` (px; `null` — по ширине полосы), `maxHeight` (px).
| Раздел | Назначение |
|--------|-----------|
| `page` | `width`, `height` (DXA), `margins` (twips) |
| `typography` | `font`, `sizeBody`/`sizeTable`/`sizeSmall`, `lineSpacing`, `color`, `useBold`, `allowEmphasis`, `language` |
| `headings` | интервалы/выравнивание `struct`, `section`, `subsection`, `point` |
| `captions` | префиксы и тире подписей `figure`, `table`, `tableContinue` |
| `lists` | `bulletChar`, `letterSuffix`, `numberSuffix`, `nestedLevelStep`, `forbiddenLetters` |
| `numbering` | `figures`, `tables`, `formulas`: `global` или `section` |
| `formula` | `spacingBeforeAfter`, `whereTabPosition` |
| `table` | `font`, `size`, `borderColor`, `borderSize`, `cellMargins`, `headerFill`, `cellFill` |
| `figure` | `maxWidth` (px, `null` — по ширине), `maxHeight` (px) |
| `pageNumber` | `align`, `format`, `startFrom` |
| `toc` | `enabled`, `title`, `depth` (1..3) |
| `bibliography` | `title` |
| `citation` | `bracketOpen`, `bracketClose`, `pagePrefix` |
| `appendix` | `labelPrefix`, `forbiddenLetters` |
### plantuml
Генерация рисунков из исходников PlantUML (см. п. 5, «Рисунки»).
| Ключ | Тип | Смысл |
|------|-----|-------|
| `enabled` | bool | включить рендеринг `.puml`; `false` — считать ошибкой при наличии `.puml` |
| `java` | string | исполняемый файл Java; `auto` — `%JAVA_HOME%\bin\java`, иначе `java` из PATH |
| `enabled` | bool | включить рендеринг `.puml`; `false` — считать ошибкой |
| `java` | string | `auto` — `%JAVA_HOME%\bin\java`, иначе `java` из PATH |
| `jar` | string | путь к `plantuml.jar` (относительно `config.yaml`) |
| `format` | string | только `png` |
| `charset` | string | кодировка исходника (по умолчанию `UTF-8`) |
@@ -147,43 +125,31 @@ DocxGenerator [опции]
| `cacheDir` | string | каталог кэша PNG (относительно `config.yaml`) |
| `timeoutMs` | int | таймаут рендера одной диаграммы, мс |
Ошибки рендера (нет `jar`, нет Java, синтаксическая ошибка) — **фатальные**: сборка
прекращается с кодом выхода `1`. Требуется установленная Java (JRE 11+) и файл
`plantuml.jar`; для части типов диаграмм нужен Graphviz либо встроенный Smlayout.
### pageNumber
`align`, `format`, `startFrom`.
### toc
`enabled` (bool), `title`, `depth` (1..3). Оглавление вставляется записью
`sections: [{ type: toc }]`.
### bibliography / citation / appendix
`bibliography.title`, `citation: {bracketOpen,bracketClose,pagePrefix}`,
`appendix: {labelPrefix, forbiddenLetters}`.
Общий `plantuml.jar` репозитория лежит в `tools/plantuml.jar`; из проекта путь
вида `../../../tools/plantuml.jar`. Ошибки рендера (нет `jar`, нет Java,
синтаксическая ошибка) — фатальные: сборка останавливается с кодом `1`.
### sections — структура документа
Упорядоченный список частей:
```yaml
sections:
- { type: toc } # поле оглавления
- { file: content/01-introduction.md, type: structural, title: Введение }
- { file: content/02-main.md, type: numbered } # # = 1, ## = 1.1, ### = 1.1.1
- { file: content/03-conclusion.md, type: structural, title: Заключение }
- { file: content/04-bibliography.md, type: bibliography }
- { file: content/05-appendix-a.md, type: appendix, letter: А, title: Приложение }
- { type: toc } # поле оглавления
- { file: 01-introduction.md, type: structural, title: Введение }
- { file: 02-main.md, type: numbered } # # = 1, ## = 1.1, ### = 1.1.1
- { file: 04-conclusion.md, type: structural, title: Заключение }
- { file: 05-bibliography.md, type: bibliography }
- { file: 06-appendix-a.md, type: appendix, letter: А, title: Приложение }
```
Типы частей:
| type | Поведение |
|------|-----------|
| `toc` | вставляет оглавление (не требует `file`) |
| `structural` | ненумерованный раздел (Введение/Заключение): заголовок = `title`, ЗАГЛАВНЫМИ, по центру, с новой страницы |
| `numbered` | нумеруемый раздел; `#` = раздел, `##`/`###` = подраздел/пункт |
| `bibliography` | список источников; нумеруется автоматически |
| `appendix` | приложение: заголовок «ПРИЛОЖЕНИЕ <letter>» + `title`, нумерация рисунков/таблиц с префиксом буквы |
| `appendix` | приложение: заголовок «ПРИЛОЖЕНИЕ <letter>» + `title`, буквенный префикс у рисунков/таблиц |
## 5. Формат контента
@@ -197,12 +163,12 @@ sections:
### Пункт -> 1.1.1 Пункт
```
Номера подставляются автоматически (Word-numbering). В `structural`/`appendix` заголовок
берётся из конфига, а заголовки внутри файла рендерятся как обычные заголовки без номера.
Номера подставляются автоматически (Word-numbering). В `structural`/`appendix`
заголовок берётся из `title`, а заголовки внутри файла рендерятся без номера.
### Абзацы, списки
### Абзацы и списки
Обычные абзацы и списки Markdown. Списки:
Обычные абзацы и списки Markdown. Пример:
```md
- первый пункт;
@@ -211,8 +177,8 @@ sections:
2. ещё один.
```
Маркер вложенности выбирается автоматически: тире → `а)` → `1)`. Полужирный/курсив по
умолчанию игнорируются (СТО).
Маркер вложенности выбирается автоматически: тире → `а)` → `1)`. Полужирный и
курсив по умолчанию игнорируются (СТО).
### Таблицы
@@ -237,13 +203,18 @@ sections:
:::
```
`file` — относительно `config.paths.assets`. Если файла нет — предупреждение, подпись
всё равно выводится.
`file` — относительно `paths.assets`. Если файла нет — предупреждение, подпись
всё равно выводится. Подпись ставится под рисунком по центру:
`Рисунок N — Наименование`. Если пояснять нечего, тело директивы оставляют
пустым:
**PlantUML.** Если `file` оканчивается на `.puml` (`.plantuml`, `.pu`, `.iuml`), то файл
считается исходником PlantUML: инструмент рендерит его в PNG через локальный
`plantuml.jar` (см. `plantuml` в конфиге) и вставляет результат как обычный рисунок.
Нумерация, ссылки `@fig:id` и примечания работают так же. Пример:
```md
::: figure "Общая схема системы" id=common file=images/common.png
:::
```
**PlantUML.** Если `file` оканчивается на `.puml` (`.plantuml`, `.pu`, `.iuml`),
файл рендерится в PNG через `plantuml.jar` и вставляется как обычный рисунок:
```md
::: figure "Диаграмма последовательности" id=seq file=images/seq.puml
@@ -252,18 +223,7 @@ sections:
:::
```
Ошибка рендера — фатальная (код выхода `1`), `--check` тоже рендерит `.puml`.
**Подписи по СТО.** Подпись ставится **под** рисунком по центру: `Рисунок N — Наименование`
(с прописной буквы, без точки в конце). Если на рисунке обозначены отдельные детали,
пояснительные данные размещают **над подписью** и оформляют перечислением `а)`, `б)`, `в)` —
именно так, как показано в примере выше. Если рисунок общий и пояснять нечего, указывают
только подпись `Рисунок N — Наименование`, а тело директивы оставляют пустым:
```md
::: figure "Общая схема системы" id=common file=images/common.png
:::
```
Ошибка рендера фатальна, `--check` тоже рендерит `.puml`.
В приложениях номер рисунка получает префикс буквы: `Рисунок А.1 — Наименование`.
@@ -281,8 +241,8 @@ a — коэффициент.
:::
```
В формулах поддерживается простой синтаксис: `^` (степень), `_` (индекс), `/` (дробь).
Номер формулы ставится автоматически справа.
Поддерживается простой синтаксис: `^` (степень), `_` (индекс), `/` (дробь). Номер
формулы ставится автоматически справа.
### Примечания и термины
@@ -313,56 +273,44 @@ a — коэффициент.
### Ссылки на источники
`[@1]` — ссылка на 1-й источник, `[@1, с. 96]` — с номером страницы. Список источников —
это Markdown-список в файле `type: bibliography`; порядок строк = номера источников.
`[@1]` — ссылка на 1-й источник, `[@1, с. 96]` — с номером страницы. Список
источников — Markdown-список в файле `type: bibliography`; порядок строк = номера.
Ссылка вне диапазона — ошибка в `--check`.
### Перекрёстные ссылки
`@fig:id`, `@tbl:id`, `@eq:id` подставляют актуальный номер рисунка/таблицы/формулы
(например `2`, `1.1`, `А.1`). `id` задаётся в директиве.
`@fig:id`, `@tbl:id`, `@eq:id` подставляют актуальный номер рисунка/таблицы/
формулы (например `2`, `1.1`, `А.1`). `id` задаётся в директиве.
### Блоки кода
Функция листингов удалена: fenced-блоки ``` игнорируются с предупреждением.
Fenced-блоки ``` игнорируются с предупреждением (функция листингов удалена).
## 6. Проверка и ошибки
`DocxGenerator --check` проверяет:
`--check` проверяет:
- корректность `config.yaml` (типы, существование файлов, допустимые буквы приложений);
- корректность `config.yaml` (типы, существование файлов, буквы приложений);
- наличие всех `sections[].file`;
- существование меток в `@fig/@tbl/@eq`;
- диапазон ссылок `[@n]`;
- рендеринг всех `::: figure ... file=*.puml` (PlantUML).
- рендеринг всех `::: figure ... file=*.puml`.
Ошибки печатаются списком и возвращают код выхода `1`. Предупреждения (например,
отсутствующий PNG) не останавливают сборку; ошибки PlantUML — фатальные.
## 7. Сборка `.exe` из исходников
## 7. Подготовка диаграмм
```powershell
npm install
npm run build:exe # -> DocxGenerator.exe в корне (target node24-win-x64)
npm run generate # локальная сборка (node src/cli.js)
npm run check # проверка конфига и ссылок
```
Упаковка выполняется через `@yao-pkg/pkg`. Чтобы уменьшить размер, можно добавить
`--compress Brotli` в команду `build:exe`.
### Подготовка диаграмм
Готовые PNG кладутся в `content/assets/images/`. Для исходников PlantUML (`*.puml`)
инструмент рендерит PNG сам: положите исходник в `content/assets/images/`, укажите его в
`::: figure ... file=images/<имя>.puml` и поместите `plantuml.jar` в `tools/plantuml.jar`
(или задайте путь в `plantuml.jar`). Диаграммы других форматов (Mermaid, draw.io) готовятся
внешними средствами и вставляются как PNG.
Готовые PNG кладутся в общий каталог ресурсов (`content/<проект>/assets/images/`).
Исходники PlantUML (`.puml`) кладутся туда же и указываются в
`::: figure ... file=images/<имя>.puml`. Диаграммы других форматов (Mermaid,
draw.io) готовятся внешними средствами и вставляются как PNG.
## 8. Замечания по дизайну
- Дизайн фиксирован под СТО 02068410-004-2018; меняются только параметры из `config.yaml`.
- Полужирный и курсив по умолчанию отключены (`typography.useBold`, `allowEmphasis`).
- Шрифт, размеры, интервалы, отступы и подписи настраиваются в конфиге.
- Оглавление — поле Word; при открытии нажмите «Обновить поле», либо включено
- Дизайн фиксирован под СТО 02068410-004-2018; меняются только параметры из
`config.yaml`.
- Полужирный и курсив по умолчанию отключены (`typography.useBold`,
`allowEmphasis`).
- Оглавление — поле Word; при открытии нажмите «Обновить поле» либо включите
`document.features.updateFields`.

View File

@@ -1,44 +1,29 @@
# DocxGenerator
# SSAU Generator DOCX
Автономный генератор отчётов **DOCX** из **Markdown** по дизайну
Генератор отчётов **DOCX** из **Markdown** по дизайну
[СТО 02068410-004-2018](documentation/STO_SGAU_02068410-004-2018.md).
Поведение полностью задаётся файлом `config.yaml`; путь выходного файла — ключ
`document.output`.
Содержимое задаётся Markdown-файлами и `config.yaml` проекта; путь выходного
файла — ключ `document.output`.
Полная документация: **[AGENTS.md](AGENTS.md)** — CLI, схема конфига, формат контента,
директивы и сборка `.exe`.
Полная инструкция по работе с документом: **[AGENTS.md](AGENTS.md)** — CLI, схема
конфига, формат контента, директивы и PlantUML.
## Быстрый старт
```powershell
.\DocxGenerator.exe --init # создать config.yaml, примеры content/ и content/assets/
# ... правим config.yaml и content/*.md ...
.\DocxGenerator.exe --check # проверить ссылки и конфиг
.\DocxGenerator.exe # собрать report.docx
node docx/src/cli.js --content content/example/docx --check # проверка
node docx/src/cli.js --content content/example/docx # собрать report.docx
```
## Разработка
```powershell
npm install
npm run generate # локальная сборка report.docx
npm run check # проверка конфига и ссылок
npm run build:exe # собрать DocxGenerator.exe в корне (@yao-pkg/pkg, node24-win-x64)
npm run generate # локальная сборка (node src/cli.js)
npm run check # проверка конфига и ссылок
npm run init # создать шаблон проекта
```
## Структура репозитория
| Путь | Назначение |
|------|------------|
| `src/cli.js` | точка входа, разбор аргументов |
| `src/config/` | дефолты, загрузка и валидация `config.yaml`, шаблон |
| `src/markdown/` | markdown-it, директивы `:::`, инлайн-ссылки `[@n]`, `@fig/@tbl/@eq` |
| `src/render/` | сборка DOCX: стили, примитивы, нумерация, слияние титульника |
| `src/build.js` | оркестрация сборки |
| `content/` | примеры Markdown |
| `content/assets/` | PNG для рисунков, опциональный `title-page.docx` |
| `documentation/` | стандарт СТО |
Готовые PNG кладутся в `content/assets/images/`. Исходники PlantUML (`*.puml`) можно указать
в `::: figure ... file=images/x.puml` — генератор отрендерит их в PNG через локальный
`plantuml.jar` (требуется Java 11+).
Структура кода: `src/cli.js` (точка входа), `src/config/` (дефолты, загрузка и
валидация `config.yaml`, шаблон), `src/markdown/` (markdown-it и директивы),
`src/render/` (сборка DOCX), `src/plantuml/` (рендеринг схем).

View File

@@ -1,145 +0,0 @@
# Конфигурация генератора lab-gen.
# Все относительные пути считаются от каталога этого файла.
# Любой ключ можно опустить — будет использовано значение по умолчанию.
document:
output: report.docx
titlePage: content/assets/title-page.docx
features:
updateFields: true
paths:
assets: content/assets
page:
width: 11906
height: 16838
margins:
left: 1701
right: 851
top: 1134
bottom: 1134
typography:
font: Times New Roman
sizeBody: 28
sizeTable: 28
sizeSmall: 24
lineSpacing: 1.5
indentFirst: 709
color: '000000'
useBold: false
allowEmphasis: false
headings:
struct:
spacingBefore: 360
spacingAfter: 560
align: center
section:
spacingBefore: 920
spacingAfter: 0
align: left
subsection:
spacingBefore: 284
spacingBeforeAfterSection: 240
spacingAfter: 0
align: left
point:
spacingBefore: 360
spacingAfter: 0
captions:
figure:
prefix: Рисунок
dash: —
align: center
table:
prefix: Таблица
dash: —
align: left
tableContinue:
prefix: Продолжение таблицы
lists:
bulletChar: '-'
letterSuffix: )
numberSuffix: )
forbiddenLetters:
- ё
- з
- й
- о
- ч
- ь
- ы
- ъ
nestedLevelStep: 360
numbering:
figures: global
tables: global
formulas: global
formula:
spacingBeforeAfter: 360
whereTabPosition: 720
table:
font: Times New Roman
size: 28
borderColor: '000000'
borderSize: 1
cellMargins:
top: 57
bottom: 57
left: 113
right: 113
headerFill: FFFFFF
cellFill: FFFFFF
figure:
maxWidth: null
maxHeight: 620
plantuml:
enabled: true
java: auto
jar: ../tools/plantuml.jar
format: png
charset: UTF-8
cache: true
cacheDir: .plantuml-cache
timeoutMs: 30000
pageNumber:
align: center
format: decimal
startFrom: 1
toc:
enabled: true
title: Содержание
depth: 3
bibliography:
title: Список использованных источников
citation:
bracketOpen: '['
bracketClose: ']'
pagePrefix: с.
appendix:
labelPrefix: ПРИЛОЖЕНИЕ
forbiddenLetters:
- Ё
- З
- Й
- О
- Ч
- Ъ
- Ы
- Ь
sections:
- type: toc
- file: content/01-introduction.md
type: structural
title: Введение
- file: content/02-method.md
type: numbered
- file: content/03-spheres.md
type: numbered
- file: content/04-conclusion.md
type: structural
title: Заключение
- file: content/05-bibliography.md
type: bibliography
- file: content/06-appendix-a.md
type: appendix
letter: А
title: Сводная таблица применения метода

View File

@@ -1,41 +0,0 @@
Объектом исследования выступает процесс выведения нового товара на рынок,
предметом — выбор метода реализации проекта по выводу новинки и его обоснование
в различных сферах деятельности.
Актуальность работы обусловлена тем, что запуск нового товара сопровождается
высоким риском: значительная доля новинок не достигает устойчивых продаж, а
неудачный запуск приводит к прямым убыткам, потерям доли рынка и подрыву доверия
к бренду. По разным оценкам, до семи из десяти новых товаров уходят с рынка в
первые годы после появления, поэтому цена ошибки при выводе новинки особенно
велика [@1], [@2]. В этих условиях выбор управляемого и повторяемого метода
вывода товара на рынок становится практически значимой задачей: он позволяет
снизить неопределённость, распределить ресурсы проекта и своевременно отказаться
от бесперспективного направления [@7].
Под новым товаром в работе понимается продукт, услуга или их значимая
модификация, ранее не предлагавшиеся компанией целевой аудитории. Выведение
такого товара на рынок охватывает исследование спроса, разработку предложения,
его продвижение и получение обратной связи от клиентов. Именно комплексность
этих этапов и их взаимное влияние порождают основные риски: чем позже компания
узнаёт реакцию рынка, тем дороже исправлять допущенные ошибки [@5], [@7].
Целью работы является выбор метода реализации проекта по выведению нового товара
на рынок и обоснование его применимости в десяти сферах деятельности. Для
достижения цели необходимо решить следующие задачи:
- изучить классические и гибкие методы выведения товара на рынок и их основные
ограничения;
- обосновать выбор метода Agile/Scrum применительно к запуску новинки;
- описать роли, артефакты и события выбранной методологии;
- раскрыть механизм проверки спроса через спринты и инкременты;
- обосновать применимость метода в производстве, связи, на транспорте, в
строительстве, коммерческой деятельности, юридических услугах, организации
праздников, бытовых услугах, IT-услугах и банковской сфере;
- сформулировать выводы об универсальности выбранного метода и условиях его
эффективного применения.
При выполнении работы использованы методы системного анализа, сравнительного
анализа методов управления проектами, проектного моделирования и обобщения
практики [@3], [@4]. Структура работы соответствует поставленным задачам: первый
раздел посвящён обоснованию выбора метода, второй — его проверке в десяти сферах
деятельности, заключение содержит основные результаты и выводы.

View File

@@ -1,154 +0,0 @@
# ВЫБОР МЕТОДА ВЫВЕДЕНИЯ НОВОГО ТОВАРА НА РЫНОК
В разделе рассматриваются основные методы реализации проекта по выведению нового
товара на рынок, формулируются критерии их сравнения и обосновывается выбор
методологии Agile/Scrum [@1], [@5].
## Обзор методов выведения товара на рынок
В практике управления проектами сложились как классические, так и гибкие методы
вывода новинки на рынок. Классические подходы (Waterfall, Stage-Gate) строятся на
последовательном прохождении этапов и предполагают, что требования известны
заранее, а результат оценивается на завершающей стадии. Гибкие подходы
(Design Thinking, Lean Startup, Agile/Scrum) переносят проверку спроса внутрь
процесса и позволяют уточнять требования по мере получения отклика рынка. Краткая
характеристика методов приведена в таблице @tbl:methods.
::: table "Сравнение методов выведения товара на рынок" id=methods
| Метод | Сущность | Основное ограничение |
|---|---|---|
| Waterfall (водопад) | последовательные этапы от идеи до запуска | поздняя обратная связь рынка |
| Stage-Gate | этапы с контрольными «воротами» go/kill | высокая стоимость стадии |
| Design Thinking | старт от потребности клиента, прототип и тест | не задаёт процесс управления |
| Lean Startup | минимальный продукт (MVP) и цикл «создать — измерить — научиться» | требует зрелой аналитики |
| Agile/Scrum | короткие итерации — спринты, готовый инкремент | нужна дисциплина команды |
| 4P (маркетинг-микс) | проработка товара, цены, каналов, продвижения | не покрывает разработку |
:::
Сравнение методов проводится по четырём критериям: скорость получения обратной
связи, способность реагировать на изменения, стоимость проверки спроса и
прозрачность управления. Как видно из таблицы @tbl:methods, классические методы
предполагают длительный цикл и получают обратную связь рынка лишь на финальной
стадии. Для запуска нового товара, спрос на который ещё не подтверждён, это
создаёт повышенный риск: к моменту первых продаж значительная часть ресурсов уже
израсходована, а изменение требований требует дорогостоящей переработки.
Наиболее подходящим признан гибкий подход Agile и его методология Scrum [@2],
[@6]. Ключевые различия классического и гибкого подходов сведены в таблице
@tbl:compare.
::: table "Сравнение классического и гибкого подходов" id=compare
| Критерий | Waterfall | Agile/Scrum |
|---|---|---|
| Обратная связь рынка | в конце проекта | в каждом спринте |
| Реакция на изменения | низкая | высокая |
| Готовый продукт | к концу проекта | в конце каждого спринта |
| Стоимость проверки спроса | высокая | низкая (пилотный инкремент) |
| Требования | фиксируются заранее | уточняются итеративно |
| Риск неудачного запуска | высокий | снижается поэтапно |
:::
## Сущность методологии Agile/Scrum
Agile — это гибкий подход к управлению проектами, закреплённый в «Манифесте
гибкой разработки» 2001 года. Манифест ставит в приоритет людей и взаимодействие
над процессами и инструментами, работающий продукт над исчерпывающей
документацией, сотрудничество с заказчиком над согласованием условий и готовность
к изменениям над следованиям первоначальному плану [@1]. На этих ценностях
строятся двенадцать принципов, важнейшими из которых для вывода нового товара
являются ранняя и частая поставка ценности, неразрывная работа бизнеса и
исполнителей и постоянная адаптация к меняющимся условиям рынка.
Scrum — наиболее распространённая методология Agile: работа разбивается на
короткие итерации — спринты длительностью от одной до четырёх недель, каждая из
которых завершается готовым приростом продукта (инкрементом) [@2], [@3]. В основе
Scrum лежат три идеи: прозрачность (все участники видят состояние работы),
инспекция (результат регулярно проверяется) и адаптация (процесс меняется по
результатам проверки). Такой цикл «планирование — работа — проверка — улучшение»
позволяет проверять спрос небольшими порциями и снижать стоимость ошибки, что
особенно важно при запуске товара на непроверенном рынке [@3], [@4].
## Роли, артефакты и события Scrum
Методология Scrum опирается на три роли, три артефакта и четыре события. Роли
отвечают за постановку целей и организацию работы, артефакты фиксируют требования
и прогресс, а события задают ритм и обеспечивают регулярную обратную связь. Роли
и артефакты приведены в таблице @tbl:scrum.
::: table "Роли и артефакты методологии Scrum" id=scrum
| Элемент | Назначение |
|---|---|
| Product Owner | отвечает за ценность товара и ведёт Product Backlog |
| Scrum Master | убирает препятствия, следит за процессом |
| Команда разработки | 5–9 специалистов, создающих продукт |
| Product Backlog | перечень всех требований к новому товару |
| Sprint Backlog | план работ текущего спринта |
| Increment | готовый к показу прирост продукта |
:::
Product Owner формирует и упорядочивает Product Backlog по ценности, принимает
результат спринта и представляет интересы заказчика. Scrum Master отвечает за
соблюдение правил методологии, помогает команде убирать препятствия и защищает
спринт от незапланированных работ. Команда разработки — самоорганизующаяся
группа из пяти–девяти специалистов, которая самостоятельно определяет, сколько
задач войдёт в спринт, и отвечает за качество инкремента. Каждый артефакт имеет
своё назначение: Product Backlog хранит все требования к товару, Sprint Backlog —
план текущей итерации, а Increment — готовую к показу часть продукта. Единое
понимание готовности фиксируется в критериях Definition of Done, что делает
результат спринта проверяемым.
Цикл работы методологии при выводе товара на рынок приведён на рисунке
@fig:scrum-cycle.
::: figure "Цикл Scrum при выводе нового товара на рынок" id=scrum-cycle file=images/scrum-cycle.puml
а) Product Backlog фиксирует требования к товару;
б) спринт завершается инкрементом;
в) Sprint Review даёт обратную связь рынка;
г) по результатам Review выполняется следующий спринт или масштабирование.
:::
События Scrum задают ритм работы и обеспечивают инспекцию результата. Sprint
Planning открывает спринт и определяет его цель и состав работ. Daily Scrum —
короткая ежедневная встреча для синхронизации команды и выявления препятствий.
Sprint Review завершает спринт демонстрацией инкремента заказчику и рынку, а
Sprint Retrospective позволяет команде улучшить собственный процесс. Поскольку
каждый спринт заканчивается инкрементом, рынок получает способ проверить товар
до полного запуска производства и масштабных вложений.
Результативность спринта оценивается по величине созданного инкремента и
получаемому эффекту. Скорость команды — суммарный объём задач, выполненных за
итерацию, — позволяет прогнозировать сроки вывода товара, а ожидаемый эффект
инкремента помогает сравнивать ценность планируемых улучшений с затратами на
спринт. Ожидаемый эффект рассчитывается по формуле @eq:effect.
::: formula id=effect
E = C * V - Z
:::
::: where
E — ожидаемый эффект спринта;
C — ценность инкремента для клиента;
V — вероятность подтверждения спроса;
Z — затраты на проведение спринта.
:::
Положительный ожидаемый эффект служит основанием для развития товара, а
отрицательный — для корректировки состава требований. Такой расчёт делает
решение о продолжении или изменении проекта более обоснованным [@5].
## Обоснование выбора Scrum
Выбор Scrum определяется рядом преимуществ применительно к выводу нового товара на
рынок [@3], [@4]:
- рынок проверяется дешёвым инкрементом, а не дорогим запуском «с нуля»;
- обратная связь собирается в каждом спринте, что снижает риск ошибки;
- требования уточняются итеративно, поэтому изменения не приводят к переработке
всего проекта;
- прозрачность процесса обеспечивается ролями, артефактами и событиями;
- ранние пилотные продажи формируют выручку ещё до полного запуска;
- метод не зависит от отрасли и применим там, где есть клиент и обратная связь;
- скорость команды и результаты спринтов делают план развития измеримым.
Таким образом, Agile/Scrum выбран в качестве метода реализации проекта по
выведению нового товара на рынок. Его применимость в десяти сферах деятельности
обоснована в следующем разделе.

View File

@@ -1,105 +0,0 @@
# ОБОСНОВАНИЕ МЕТОДА AGILE/SCRUM В СФЕРАХ ДЕЯТЕЛЬНОСТИ
В разделе обосновывается применимость метода Agile/Scrum в десяти сферах
деятельности. В каждой сфере рассматривается пример нового товара и показывается,
как его вывод на рынок организуется через спринты, пилотный инкремент и обратную
связь [@3], [@6]. Общая логика одинакова для всех сфер: сначала создаётся
ограниченный пробный продукт, затем замеряется результат, и только после этого
принимается решение о масштабировании (рисунок @fig:funnel).
::: figure "Воронка вывода нового товара на рынок" id=funnel file=images/product-funnel.puml
а) спринт 0 уточняет требования и сегмент рынка;
б) спринты 1–2 создают минимальную версию товара;
в) спринты 3–4 проверяют спрос на ограниченной аудитории;
г) начиная со спринта 5 товар масштабируется по подтверждённым метрикам.
:::
## Производство
Новым товаром выступает линейка упаковки. Вместо единовременного перезапуска всех
линий выпускается пробная партия на одной линии, которая играет роль инкремента.
В ходе спринта замеряются себестоимость партии, доля брака и скорость переналадки
оборудования. Sprint Review с оптовыми покупателями показывает востребованность
новой упаковки, после чего производство масштабируется на остальные линии, а
замечания клиентов становятся задачами следующего спринта.
## Связь
Новый товар — тариф «Умный дом». Пилотный спринт запускает тариф для ста
абонентов, в ходе Sprint Review анализируются подключения, средний чек и отток.
Обратная связь абонентов позволяет скорректировать состав услуг и условия
подключения. По результатам обратной связи тариф дорабатывается и выводится на
массовый рынок без риска массового оттока при неудачной конфигурации.
## Транспорт
Новым товаром является сервис каршеринга мини-фургонов. Пилот реализуется пятью
машинами в одном районе; метрики загрузки, среднего времени аренды и спроса
оцениваются в конце спринта. Если загрузка подтверждает спрос, инкремент
признаётся успешным, и автопарк расширяется за счёт новых районов. Такой подход
позволяет проверить экономику сервиса до крупных капитальных вложений в технику.
## Строительство
Новый товар — модульные дома. Первый спринт предполагает возведение одного
пилотного модуля и его показ заказчикам. На Sprint Review фиксируются затраты на
монтаж, сроки и замечания по проекту, после чего они учитываются в следующем
инкременте — серийном модуле. Итеративная проверка снижает риск ошибок в типовом
проекте, который затем тиражируется на серию.
## Коммерческая деятельность
Новым товаром выступает подписка на наборы продуктов. Бокс-пилот на пятидесяти
клиентах позволяет измерить конверсию, средний чек и удержание. Обратная связь
покупателей формирует Sprint Backlog для доработки ассортимента и условий
подписки. Если удержание растёт, объём подписки увеличивается, а состав наборов
уточняется по отклику клиентов.
## Предоставление юридических услуг
Новый товар — пакет «юрфирма на аутсорсе». Пробный месяц для пяти клиентов даёт
инкремент услуги; на Sprint Review оцениваются востребованность, трудозатраты и
повторные обращения. На основе отзывов формируются тарифы и состав пакета.
Поэтапная проверка позволяет выстроить услугу под реальные ожидания клиентов, не
вкладываясь сразу в расширение штата юристов.
## Организация праздников
Новым товаром является онлайн-конструктор мероприятий. Один сценарий тестируется
на пробной группе клиентов; метрики вовлечённости, повторных заказов и средней
стоимости мероприятия анализируются в конце спринта. Успешный сценарий
становится основой каталога и автоматизации заказов, а непопулярные сценарии
исключаются без затрат на массовое продвижение.
## Предоставление бытовых услуг
Новый товар — подписка на уборку. Пилот с двадцатью подписчиками в одном районе
позволяет измерить удержание, среднее число заказов и загрузку исполнителей. По
результатам спринта сервис дорабатывается и масштабируется на другие районы.
Пилотный формат помогает наладить логистику и стандарты качества до выхода на
городской уровень.
## IT-услуги
Новым товаром выступает модуль SaaS-платформы. Бета-версия для десяти клиентов
является инкрементом; на Sprint Review собираются метрики использования, частоты
ошибок и NPS. Обратная связь определяет состав следующего релиза и приоритеты
доработки. Такой цикл соответствует самой природе гибкой разработки и позволяет
выпускать ценность небольшими, но регулярными порциями.
## Банковские услуги
Новый товар — вклад «Цель» в мобильном приложении. Тест на ограниченной группе
пользователей позволяет оценить долю открытий, средний срок и удержание. Даже в
регулируемой сфере спринты обеспечивают безопасную проверку спроса до полного
внедрения: пилот проводится в контролируемых условиях, а результат проходит
согласование с подразделениями банка.
## Обобщение по сферам
Рассмотренные примеры показывают, что различия между сферами касаются в первую
очередь содержания товара и применяемых метрик, тогда как структура процесса
остаётся общей. Везде выделяются заказчик, готовый участвовать в проверке
инкремента, и измеримый результат спринта, на основании которого принимается
решение о продолжении работы. Именно эта общность позволяет считать Agile/Scrum
универсальным методом выведения нового товара на рынок [@3], [@6].

View File

@@ -1,37 +0,0 @@
В работе выбран метод реализации проекта по выведению нового товара на рынок и
обоснована его применимость в десяти сферах деятельности.
В результате сравнительного анализа методов установлено, что классические подходы
(Waterfall, Stage-Gate) предполагают длительный цикл и позднюю обратную связь
рынка, что повышает риск запуска новинки. Сравнение по критериям скорости
обратной связи, реакции на изменения, стоимости проверки спроса и прозрачности
управления (таблица @tbl:compare) показало, что для непроверенного рынка
предпочтителен гибкий подход. В качестве метода выбран Agile/Scrum, поскольку он
проверяет спрос дешёвым инкрементом в каждом спринте и позволяет корректировать
товар по отклику рынка.
Показано, что методология Scrum опирается на роли (Product Owner, Scrum Master,
команда разработки), артефакты (Product Backlog, Sprint Backlog, Increment) и
события (Sprint Planning, Daily Scrum, Sprint Review, Sprint Retrospective), что
делает процесс прозрачным и управляемым [@2], [@3]. В работе раскрыт механизм
оценки результативности итераций: ожидаемый эффект спринта рассчитывается по
формуле @eq:effect, а решение о развитии товара принимается по результатам Sprint
Review. Такой подход заменяет один дорогой запуск серией последовательных
проверок и снижает стоимость возможной ошибки.
Обоснована применимость метода в производстве, связи, на транспорте, в
строительстве, коммерческой деятельности, юридических услугах, организации
праздников, бытовых услугах, IT-услугах и банковских услугах. В каждой сфере
вывод товара на рынок строится по единой логике: ограниченный пробный продукт —
замер результата — масштабирование. Различия касаются содержания товара и набора
метрик, тогда как структура процесса и роль обратной связи остаются неизменными.
Практическая значимость работы состоит в том, что предложенный метод можно
применять как типовой алгоритм: выделить сегмент и требования, создать MVP,
провести пилот, измерить результат и принять решение о масштабировании. Это
позволяет компании управлять риском запуска новинки и обосновывать вложения
перед расширением производства или сервиса.
Таким образом, выбранный метод Agile/Scrum является универсальным и позволяет
снизить риск и стоимость ошибки при выведении нового товара на рынок в различных
сферах деятельности.

View File

@@ -1,7 +0,0 @@
- Манифест гибкой разработки программного обеспечения. — 2001. — URL: https://agilemanifesto.org (дата обращения: 01.10.2026).
- Schwaber K., Sutherland J. The Scrum Guide: The Definitive Guide to Scrum. — 2020. — URL: https://scrumguides.org (дата обращения: 01.10.2026).
- Сазерленд Дж. Scrum. Революционный метод управления проектами. — М.: Манн, Иванов и Фербер, 2016. — 288 с.
- ГОСТ Р 54869-2011. Проектный менеджмент. Требования к управлению проектом. — М.: Стандартинформ, 2011. — 9 с.
- Cooper R. G. Perspective: The Stage-Gate Idea-to-Launch Process. — Journal of Product Innovation Management, 2008. — Vol. 25. — P. 213–232.
- Рис Э. Бизнес с нуля. Lean Startup для быстрого тестирования идей. — М.: Альпина Паблишер, 2014. — 253 с.
- Котлер Ф. Основы маркетинга. Краткий курс. — М.: Вильямс, 2019. — 496 с.

View File

@@ -1,17 +0,0 @@
Сводная характеристика применения метода Agile/Scrum в десяти сферах деятельности
приведена в таблице @tbl:sphere-summary.
::: table "Применение метода Agile/Scrum в сферах деятельности" id=sphere-summary
| № | Сфера | Новый товар | Пилотный инкремент спринта |
|---|---|---|---|
| 1 | Производство | новая линейка упаковки | пробная партия на одной линии |
| 2 | Связь | тариф «Умный дом» | пилот на 100 абонентов |
| 3 | Транспорт | каршеринг мини-фургонов | пять машин в одном районе |
| 4 | Строительство | модульные дома | один пилотный модуль |
| 5 | Коммерческая деятельность | подписка на наборы продуктов | бокс-пилот на 50 клиентов |
| 6 | Юридические услуги | пакет «юрфирма на аутсорсе» | пробный месяц для 5 клиентов |
| 7 | Организация праздников | онлайн-конструктор мероприятий | один сценарий на пробной группе |
| 8 | Бытовые услуги | подписка на уборку | 20 подписчиков в одном районе |
| 9 | IT-услуги | модуль SaaS-платформы | бета на 10 клиентов |
| 10 | Банковские услуги | вклад «Цель» в приложении | тест на группе пользователей |
:::

View File

@@ -1,19 +0,0 @@
@startuml
skinparam defaultFontName "Times New Roman"
skinparam defaultFontSize 13
skinparam shadowing false
skinparam ArrowColor #1B87C9
skinparam rectangleBorderColor #1B87C9
skinparam rectangleBackgroundColor #EAF4FB
title Воронка вывода нового товара на рынок
rectangle "Идея и исследование рынка\nСпринт 0" as A
rectangle "MVP — минимальный товар\nСпринты 1–2" as B
rectangle "Пилот на ограниченной аудитории\nСпринты 3–4" as C
rectangle "Масштабирование товара\nСпринты 5 и далее" as D
A --> B : спрос подтверждён
B --> C : инкремент принят
C --> D : метрики достигнуты
@enduml

View File

@@ -1,26 +0,0 @@
@startuml
skinparam defaultFontName "Times New Roman"
skinparam defaultFontSize 13
skinparam shadowing false
skinparam ArrowColor #1B87C9
skinparam rectangleBorderColor #1B87C9
skinparam rectangleBackgroundColor #EAF4FB
title Цикл Scrum при выводе нового товара на рынок
rectangle "Product Backlog — требования к товару" as PB
rectangle "Sprint Planning" as SP
rectangle "Спринт 1–4 недели" as S
rectangle "Increment — пробный продукт" as I
rectangle "Sprint Review — обратная связь рынка" as R
rectangle "Sprint Retrospective" as RET
rectangle "Масштабирование товара" as M
PB --> SP
SP --> S
S --> I
I --> R
R --> RET
RET --> SP : следующий спринт
R --> M : спрос подтверждён
@enduml

Binary file not shown.

1982
docx/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,16 @@
{
"name": "docxgenerator",
"name": "ssau-generator-docx",
"version": "2.0.0",
"private": true,
"description": "DocxGenerator — автономный генератор отчётов DOCX из Markdown по СТО 02068410-004-2018",
"description": "SSAU Generator DOCX — автономный генератор отчётов DOCX из Markdown по СТО 02068410-004-2018",
"bin": {
"docxgenerator": "src/cli.js"
"ssau-generator-docx": "src/cli.js"
},
"main": "src/index.js",
"scripts": {
"generate": "node src/cli.js",
"check": "node src/cli.js --check",
"build:exe": "pkg . --targets node24-win-x64 --compress Brotli --output DocxGenerator.exe"
"init": "node src/cli.js --init"
},
"dependencies": {
"docx": "^9.5.0",
@@ -18,16 +18,5 @@
"js-yaml": "^4.1.0",
"jszip": "^3.10.1",
"markdown-it": "^14.1.0"
},
"devDependencies": {
"@yao-pkg/pkg": "^6.23.0"
},
"pkg": {
"scripts": [
"src/**/*.js"
],
"targets": [
"node24-win-x64"
]
}
}

View File

@@ -14,17 +14,17 @@ const { ConfigError } = require('./util/errors');
const VERSION = '2.0.0';
const HELP = `DocxGenerator ${VERSION} — генератор DOCX из Markdown (СТО 02068410-004-2018)
const HELP = `SSAU Generator DOCX ${VERSION} — генератор DOCX из Markdown (СТО 02068410-004-2018)
Использование:
DocxGenerator.exe [опции]
node docx/src/cli.js [опции]
Опции:
--config <путь> Путь к config.yaml (по умолчанию рядом с exe)
--content <dir> Каталог с config.yaml, content/ и assets/ (по умолчанию рядом с exe)
--config <путь> Путь к config.yaml (по умолчанию config.yaml в текущем каталоге)
--content <dir> Каталог проекта: config.yaml, *.md и assets/ (по умолчанию текущий)
--output <путь> Переопределить путь выходного .docx
--check Только проверить конфиг и ссылки, без записи
--init Создать шаблон config.yaml/content/assets и выйти
--init Создать шаблон config.yaml/*.md/assets и выйти
--verbose Подробный вывод
--version Показать версию
--help Показать эту справку
@@ -70,7 +70,7 @@ function parseArgs(argv) {
}
function defaultBaseDir() {
return process.pkg ? path.dirname(process.execPath) : process.cwd();
return process.cwd();
}
async function main() {
@@ -101,7 +101,7 @@ async function main() {
logger.info('Файлы уже существуют, ничего не создано.');
}
logger.info('');
logger.info('Отредактируйте config.yaml и файлы в content/, затем запустите DocxGenerator.exe снова.');
logger.info('Отредактируйте config.yaml и файлы *.md, затем запустите генератор снова.');
return 0;
}

View File

@@ -7,12 +7,12 @@
const DEFAULT_CONFIG = {
document: {
output: 'report.docx',
titlePage: 'content/assets/title-page.docx',
titlePage: 'assets/title-page.docx',
features: { updateFields: true },
},
paths: {
assets: 'content/assets',
assets: 'assets',
},
page: {
@@ -28,6 +28,7 @@ const DEFAULT_CONFIG = {
sizeSmall: 24,
lineSpacing: 1.5,
indentFirst: 709,
language: 'ru-RU',
color: '000000',
useBold: false,
allowEmphasis: false,
@@ -111,19 +112,19 @@ const DEFAULT_CONFIG = {
sections: [
{ type: 'toc' },
{ file: 'content/01-introduction.md', type: 'structural', title: 'Введение' },
{ file: 'content/02-analysis.md', type: 'numbered' },
{ file: 'content/03-design.md', type: 'numbered' },
{ file: 'content/04-conclusion.md', type: 'structural', title: 'Заключение' },
{ file: 'content/05-bibliography.md', type: 'bibliography' },
{ file: '01-introduction.md', type: 'structural', title: 'Введение' },
{ file: '02-analysis.md', type: 'numbered' },
{ file: '03-design.md', type: 'numbered' },
{ file: '04-conclusion.md', type: 'structural', title: 'Заключение' },
{ file: '05-bibliography.md', type: 'bibliography' },
{
file: 'content/06-appendix-a.md',
file: '06-appendix-a.md',
type: 'appendix',
letter: 'А',
title: 'Сводные таблицы',
},
{
file: 'content/07-appendix-b.md',
file: '07-appendix-b.md',
type: 'appendix',
letter: 'Б',
title: 'Вспомогательные материалы',

View File

@@ -8,21 +8,53 @@ const { DEFAULT_CONFIG } = require('./defaults');
const { diagramPng, chartPng } = require('../util/png');
const { buildSampleTitlePage } = require('../render/sample-titlepage');
const CONFIG_HEADER = `# Конфигурация генератора DocxGenerator.
const CONFIG_HEADER = `# Конфигурация генератора SSAU Generator DOCX.
# Все относительные пути считаются от каталога этого файла.
# Любой ключ можно опустить — будет использовано значение по умолчанию.
`;
function defaultConfigYaml() {
/** Каталог генератора внутри проекта (docx/ или pptx/). */
function isGeneratorDir(baseDir) {
const base = path.basename(baseDir).toLowerCase();
return base === 'docx' || base === 'pptx';
}
/**
* Раскладка проекта: имя проекта = имя папки проекта, общие ресурсы в
* content/<проект>/assets/, результат — content/<проект>/<проект>.docx.
*/
function projectLayout(baseDir) {
const isSub = isGeneratorDir(baseDir);
const projectDir = isSub ? path.dirname(baseDir) : baseDir;
const projectName = path.basename(projectDir) || 'project';
return {
projectDir,
projectName,
assetsRel: isSub ? '../assets' : 'assets',
assetsDir: path.join(projectDir, 'assets'),
outputRel: isSub ? `../${projectName}.docx` : `${projectName}.docx`,
};
}
function defaultConfigYaml(layout) {
const config = {
...DEFAULT_CONFIG,
document: {
...DEFAULT_CONFIG.document,
output: layout.outputRel,
titlePage: `${layout.assetsRel}/title-page.docx`,
},
paths: { assets: layout.assetsRel },
};
return (
CONFIG_HEADER +
yaml.dump(DEFAULT_CONFIG, { noRefs: true, lineWidth: -1, sortKeys: false })
yaml.dump(config, { noRefs: true, lineWidth: -1, sortKeys: false })
);
}
const SAMPLE = {
'content/01-introduction.md': `Целью работы является разработка информационной системы учёта
'01-introduction.md': `Целью работы является разработка информационной системы учёта
заявок для отдела технической поддержки. Актуальность темы обусловлена ростом числа обращений и
необходимостью их оперативной обработки [@1].
@@ -38,7 +70,7 @@ const SAMPLE = {
действующими стандартами [@3], [@4].
`,
'content/02-analysis.md': `# АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ
'02-analysis.md': `# АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ
Раздел посвящён анализу процесса обработки обращений и обоснованию требований к информационной
системе. Рассматриваются участники процесса, информационные потоки и ограничения предметной
@@ -147,7 +179,7 @@ T_new — среднее время обработки заявки после
:::
`,
'content/03-design.md': `# ПРОЕКТИРОВАНИЕ ИНФОРМАЦИОННОЙ СИСТЕМЫ
'03-design.md': `# ПРОЕКТИРОВАНИЕ ИНФОРМАЦИОННОЙ СИСТЕМЫ
В разделе описаны архитектура, модель данных и пользовательские сценарии разрабатываемой
информационной системы.
@@ -275,7 +307,7 @@ N — число обращений в месяц.
:::
`,
'content/04-conclusion.md': `В ходе работы проанализирована предметная область, спроектирована архитектура информационной
'04-conclusion.md': `В ходе работы проанализирована предметная область, спроектирована архитектура информационной
системы и определены показатели эффективности [@6], [@7].
Основные результаты работы:
@@ -289,7 +321,7 @@ N — число обращений в месяц.
соответствии с действующим стандартом [@8].
`,
'content/05-bibliography.md': `- ГОСТ 7.32-2017. Система стандартов по информации, библиотечному и издательскому делу. Отчёт о научно-исследовательской работе. Структура и правила оформления. — М. : Стандартинформ, 2017. — 32 с.
'05-bibliography.md': `- ГОСТ 7.32-2017. Система стандартов по информации, библиотечному и издательскому делу. Отчёт о научно-исследовательской работе. Структура и правила оформления. — М. : Стандартинформ, 2017. — 32 с.
- ГОСТ Р 7.0.5-2008. Библиографическая ссылка. Общие требования и правила составления. — М. : Стандартинформ, 2008. — 23 с.
- Иванов, И. И. Управление ИТ-сервисами : учебное пособие / И. И. Иванов. — Самара : Изд-во Самарского университета, 2019. — 184 с.
- Петров, П. П. Обзор систем Service Desk / П. П. Петров // Информационные технологии. — 2020. — № 4. — С. 25–33.
@@ -299,7 +331,7 @@ N — число обращений в месяц.
- ГОСТ 8.417-2002. Государственная система обеспечения единства измерений. Единицы величин. — М. : Стандартинформ, 2002. — 32 с.
`,
'content/06-appendix-a.md': `Сводные данные по обращениям за отчётный период приведены в таблице @tbl:app-a1.
'06-appendix-a.md': `Сводные данные по обращениям за отчётный период приведены в таблице @tbl:app-a1.
::: table "Сводные данные по обращениям" id=app-a1
| Номер | Дата регистрации | Категория | Приоритет | Исполнитель | Статус | Время, мин |
@@ -339,7 +371,7 @@ N — число обращений в месяц.
:::
`,
'content/07-appendix-b.md': `Схема размещения компонентов системы приведена на рисунке @fig:app-b1.
'07-appendix-b.md': `Схема размещения компонентов системы приведена на рисунке @fig:app-b1.
::: figure "Схема размещения компонентов" id=app-b1 file=images/appendix-scheme.png
а) узлы развёртывания;
@@ -361,29 +393,22 @@ N — число обращений в месяц.
};
const IMAGES = {
'content/assets/images/architecture.png': () => diagramPng(900, 420, [46, 94, 170]),
'content/assets/images/data-flow.png': () => diagramPng(900, 380, [28, 140, 96]),
'content/assets/images/interface.png': () => diagramPng(820, 480, [196, 120, 32]),
'content/assets/images/chart.png': () => chartPng(820, 420, [122, 70, 190]),
'content/assets/images/appendix-scheme.png': () => diagramPng(900, 520, [176, 48, 64]),
'images/architecture.png': () => diagramPng(900, 420, [46, 94, 170]),
'images/data-flow.png': () => diagramPng(900, 380, [28, 140, 96]),
'images/interface.png': () => diagramPng(820, 480, [196, 120, 32]),
'images/chart.png': () => chartPng(820, 420, [122, 70, 190]),
'images/appendix-scheme.png': () => diagramPng(900, 520, [176, 48, 64]),
};
function scaffoldFiles() {
const files = { 'config.yaml': defaultConfigYaml() };
for (const [name, content] of Object.entries(SAMPLE)) files[name] = content;
for (const [name, make] of Object.entries(IMAGES)) {
files[name] = { binary: make() };
}
return files;
}
/** Создаёт config.yaml, примеры content/ и ассеты, если их ещё нет. */
/**
* Создаёт config.yaml, примеры Markdown и общие ресурсы проекта
* (content/<проект>/assets/), если их ещё нет.
*/
async function ensureScaffold(baseDir) {
const files = scaffoldFiles();
const layout = projectLayout(baseDir);
const created = [];
for (const [name, content] of Object.entries(files)) {
const target = path.join(baseDir, name);
if (fs.existsSync(target)) continue;
const write = (target, content) => {
if (fs.existsSync(target)) return;
fs.mkdirSync(path.dirname(target), { recursive: true });
if (content && typeof content === 'object' && content.binary) {
fs.writeFileSync(target, content.binary);
@@ -391,15 +416,17 @@ async function ensureScaffold(baseDir) {
fs.writeFileSync(target, content, 'utf8');
}
created.push(target);
}
};
const titlePage = path.join(baseDir, 'content', 'assets', 'title-page.docx');
if (!fs.existsSync(titlePage)) {
fs.mkdirSync(path.dirname(titlePage), { recursive: true });
fs.writeFileSync(titlePage, await buildSampleTitlePage());
created.push(titlePage);
write(path.join(baseDir, 'config.yaml'), defaultConfigYaml(layout));
for (const [name, content] of Object.entries(SAMPLE)) {
write(path.join(baseDir, name), content);
}
for (const [name, make] of Object.entries(IMAGES)) {
write(path.join(layout.assetsDir, name), { binary: make() });
}
write(path.join(layout.assetsDir, 'title-page.docx'), await buildSampleTitlePage());
return created;
}
module.exports = { defaultConfigYaml, scaffoldFiles, ensureScaffold, SAMPLE };
module.exports = { defaultConfigYaml, projectLayout, ensureScaffold, SAMPLE };

View File

@@ -69,6 +69,9 @@ function validateConfig(config) {
if (!(typeof typography.lineSpacing === 'number' && typography.lineSpacing > 0)) {
errors.push('typography.lineSpacing: ожидалось положительное число');
}
if (typography.language != null && typeof typography.language !== 'string') {
errors.push('typography.language: ожидалась строка (например, ru-RU)');
}
for (const [key, value] of Object.entries(page.margins)) {
if (!Number.isInteger(value) || value < 0) {

View File

@@ -104,7 +104,14 @@ function renderInline(ctx, inlineToken, state) {
push(textRun(ctx, ' '));
break;
case 'hardbreak':
push(new TextRun({ break: 1, font: ctx.typography.font, size: ctx.typography.sizeBody }));
push(
new TextRun({
break: 1,
font: ctx.typography.font,
size: ctx.typography.sizeBody,
...(ctx.typography.language ? { language: { value: ctx.typography.language } } : {}),
})
);
break;
case 'strong_open':
flags.bold = true;

View File

@@ -46,7 +46,7 @@ function alignmentOf(name) {
function textRun(ctx, text, opts = {}) {
const t = ctx.typography;
return new TextRun({
const run = {
text: text == null ? '' : String(text),
font: t.font,
size: opts.size || t.sizeBody,
@@ -54,7 +54,9 @@ function textRun(ctx, text, opts = {}) {
italics: t.allowEmphasis ? Boolean(opts.italics) : false,
subScript: opts.subScript || false,
superScript: opts.superScript || false,
});
};
if (t.language) run.language = { value: t.language };
return new TextRun(run);
}
function flatten(...blocks) {
@@ -325,7 +327,7 @@ function tableRow(ctx, cells, isHeader = false) {
children: [textRun(ctx, line, { size: t.size })],
spacing: { ...ctx.typography.lineSpacingSingle, before: 0, after: 0 },
alignment: AlignmentType.LEFT,
indent: {},
indent: { left: 0, firstLine: 0, hanging: 0 },
})
),
})
@@ -388,12 +390,22 @@ function parseSimpleFormula(text) {
let last = 0;
let match;
while ((match = re.exec(trimmed)) !== null) {
if (match.index > last) children.push(new MathRun(trimmed.slice(last, match.index)));
const run = new MathRun(match[2]);
let prefix = trimmed.slice(last, match.index);
const token = match[2];
// Основание — завершающий идентификатор текста перед оператором.
const tail = /^([\s\S]*?)([A-Za-zА-Яа-я0-9]+)$/.exec(prefix);
let base = '';
if (tail) {
base = tail[2];
prefix = tail[1];
}
if (prefix) children.push(new MathRun(prefix));
const baseRuns = base ? [new MathRun(base)] : [];
const scriptRun = new MathRun(token);
children.push(
match[1] === '_'
? new MathSubScript({ children: [run] })
: new MathSuperScript({ children: [run] })
? new MathSubScript({ children: baseRuns, subScript: [scriptRun] })
: new MathSuperScript({ children: baseRuns, superScript: [scriptRun] })
);
last = re.lastIndex;
}
@@ -580,6 +592,7 @@ function pageNumberFooter(ctx) {
children: [PageNumber.CURRENT],
font: ctx.typography.font,
size: ctx.typography.sizeBody,
...(ctx.typography.language ? { language: { value: ctx.typography.language } } : {}),
}),
],
}),

View File

@@ -36,6 +36,7 @@ function buildContext(config) {
lineSpacing,
lineSpacingSingle,
indentFirst: typography.indentFirst,
language: typography.language,
},
figureMaxWidthPx:
figure.maxWidth != null