@ingcreators/annot-product-docs-xlsx 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +104 -0
- package/README.md +13 -13
- package/dist/cli.d.ts +11 -0
- package/dist/default-layout.d.ts +28 -0
- package/dist/extract.d.ts +48 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +616 -0
- package/dist/named-range.d.ts +18 -0
- package/dist/placeholder.d.ts +34 -0
- package/dist/template.d.ts +28 -0
- package/dist/workbook.d.ts +28 -0
- package/package.json +5 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @ingcreators/annot-product-docs-xlsx
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2e92c97: First publish of the living-product-docs package family. Phases
|
|
8
|
+
1-5 + 7 of `docs/plans/living-product-docs.md` landed across
|
|
9
|
+
PRs 876-899; this entry flips the three packages from
|
|
10
|
+
`private: true` to publishable and stamps `0.1.0`.
|
|
11
|
+
|
|
12
|
+
### `@ingcreators/annot-product-docs`
|
|
13
|
+
- MDX parser (`parseMdx` / `parseMdxFile`) — Remark / unified
|
|
14
|
+
pipeline that walks `.mdx` files with `annot:` frontmatter
|
|
15
|
+
and extracts `<Screen>` / `<Overlay>` / `<Transition>` /
|
|
16
|
+
`<HistoryEntry>` / `<ScreenList>` JSX components.
|
|
17
|
+
- Match resolver (`parseSnapshot` / `resolveMatch` /
|
|
18
|
+
`resolveOverlays`) for the Playwright `aria-snapshot`
|
|
19
|
+
YAML, honouring `match.under` disambiguation and emitting
|
|
20
|
+
`not-found` / `ambiguous` / `renamed` / `role-changed` /
|
|
21
|
+
`live-mismatch` diagnostics.
|
|
22
|
+
- `screen` fixture extending `@ingcreators/annot-playwright`
|
|
23
|
+
with `screen.capture({ id, mdxPath })` that re-syncs
|
|
24
|
+
`annot:snapshot` + `annot:attributes` MDX comment blocks
|
|
25
|
+
in place.
|
|
26
|
+
- Drift detector (`detectDrift` / `detectDriftFromYaml`) — six
|
|
27
|
+
finding kinds (added / removed / renamed / role-changed /
|
|
28
|
+
duplicated / attribute-drift) with severity buckets.
|
|
29
|
+
- `annot-docs` CLI (`init` / `sync` / `lint`) with `--json`
|
|
30
|
+
/ `--ci` / `--fix` flags + a sample GitHub Actions workflow
|
|
31
|
+
emitting GitHub annotations on PR diff views.
|
|
32
|
+
|
|
33
|
+
### `@ingcreators/annot-product-docs-astro`
|
|
34
|
+
- `productDocsIntegration()` Astro 5.x integration factory.
|
|
35
|
+
- 7 docs components: `<Screen>`, `<Overlay>`, `<Transition>`,
|
|
36
|
+
`<TransitionTable>`, `<HistoryEntry>`, `<ScreenList>`,
|
|
37
|
+
`<TransitionGraph>`. Shipped as `.astro` source under
|
|
38
|
+
`./components/*.astro` exports.
|
|
39
|
+
- Image Service (`renderAnnotatedScreen` + SHA-keyed
|
|
40
|
+
`createFileCache` / `createMemoryCache`) that composes the
|
|
41
|
+
base screenshot with overlay callouts at build time.
|
|
42
|
+
|
|
43
|
+
### `@ingcreators/annot-product-docs-xlsx`
|
|
44
|
+
- MDX → normalised bundle extractor; per-role default layout
|
|
45
|
+
(cover / history / list / screen / reference); customer-
|
|
46
|
+
template support with `{var}` placeholder substitution
|
|
47
|
+
(including `{annot:date}` special vars + `{name:format}`
|
|
48
|
+
date formatting); Excel Named Range writers
|
|
49
|
+
(`annotImage` / `annotItemTable` / `annotHistory` /
|
|
50
|
+
`annotList` / `annotSnapshot` / `annotAttributes`).
|
|
51
|
+
- `annot-docs-xlsx render` CLI with multi-book emit + per-book
|
|
52
|
+
template config.
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- 657a685: **Republish with `dist/` included.** The `0.1.0` tarballs of all
|
|
57
|
+
three packages shipped to npm without their `dist/` directory —
|
|
58
|
+
the `publish.yml` workflow's pre-pack `pnpm build` step had only
|
|
59
|
+
filtered four other packages, so `pnpm pack` packed the three
|
|
60
|
+
`product-docs*` packages against empty `dist/`s. The
|
|
61
|
+
`publishConfig.main` (`./dist/index.js`) consequently pointed at
|
|
62
|
+
a missing file, breaking `npm install` for every consumer.
|
|
63
|
+
|
|
64
|
+
The source fix landed in
|
|
65
|
+
[#947](https://github.com/ingcreators/annot/pull/947) with two
|
|
66
|
+
defences:
|
|
67
|
+
1. Three new `--filter` lines in the workflow's build step so
|
|
68
|
+
all seven publishable packages get built before pack.
|
|
69
|
+
2. A per-package `prepack` script (`pnpm run build`) so even a
|
|
70
|
+
misconfigured workflow (or a manual `pnpm pack` / `pnpm
|
|
71
|
+
publish`) refreshes `dist/` before packing.
|
|
72
|
+
|
|
73
|
+
No source-code changes in any of the three packages — only the
|
|
74
|
+
packaging is fixed. This patch publish exists solely to deliver
|
|
75
|
+
working tarballs to the registry; the public API surface is
|
|
76
|
+
byte-identical to `0.1.0`.
|
|
77
|
+
|
|
78
|
+
Verified locally:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
$ pnpm --filter @ingcreators/annot-product-docs pack --dry-run
|
|
82
|
+
Tarball Contents
|
|
83
|
+
bin/annot-docs.mjs
|
|
84
|
+
dist/cli.d.ts
|
|
85
|
+
dist/config.d.ts
|
|
86
|
+
dist/drift.d.ts
|
|
87
|
+
dist/fixture.d.ts
|
|
88
|
+
dist/index.d.ts
|
|
89
|
+
dist/index.js
|
|
90
|
+
dist/mdx.d.ts
|
|
91
|
+
dist/resolver.d.ts
|
|
92
|
+
dist/types-config.d.ts
|
|
93
|
+
dist/types.d.ts
|
|
94
|
+
LICENSE
|
|
95
|
+
package.json
|
|
96
|
+
README.md
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Before the fix the same command produced 4 files (LICENSE +
|
|
100
|
+
README + package.json + bin/annot-docs.mjs), no compiled code.
|
|
101
|
+
|
|
102
|
+
- Updated dependencies [2e92c97]
|
|
103
|
+
- Updated dependencies [657a685]
|
|
104
|
+
- @ingcreators/annot-product-docs@0.2.0
|
package/README.md
CHANGED
|
@@ -9,12 +9,12 @@ files with `annot:` frontmatter, fills customer-supplied Excel
|
|
|
9
9
|
templates via `{var}` placeholders + Excel Named Ranges, and
|
|
10
10
|
emits one `.xlsx` per book.
|
|
11
11
|
|
|
12
|
-
Targets the
|
|
13
|
-
expect a specific
|
|
12
|
+
Targets the corporate screen-specifications use case where
|
|
13
|
+
customers expect a specific Excel template populated from a
|
|
14
14
|
code-driven source of truth.
|
|
15
15
|
|
|
16
16
|
Phase 3 of
|
|
17
|
-
[`docs/plans/living-product-docs.md`](https://github.com/ingcreators/annot/blob/main/docs/plans/living-product-docs.md).
|
|
17
|
+
[`docs/plans/_done/living-product-docs.md`](https://github.com/ingcreators/annot/blob/main/docs/plans/_done/living-product-docs.md).
|
|
18
18
|
|
|
19
19
|
## Install
|
|
20
20
|
|
|
@@ -26,7 +26,7 @@ pnpm add @ingcreators/annot-product-docs-xlsx
|
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
28
|
annot-docs-xlsx render --root docs --out dist/xlsx
|
|
29
|
-
annot-docs-xlsx render --book "Screen
|
|
29
|
+
annot-docs-xlsx render --book "Screen specifications"
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Reads `annot-docs.config.ts` from the project root; uses a
|
|
@@ -41,19 +41,19 @@ import { defineConfig } from "@ingcreators/annot-product-docs";
|
|
|
41
41
|
|
|
42
42
|
export default defineConfig({
|
|
43
43
|
meta: {
|
|
44
|
-
projectName: "
|
|
45
|
-
customerName: "
|
|
44
|
+
projectName: "Customer management system",
|
|
45
|
+
customerName: "Acme Corp.",
|
|
46
46
|
},
|
|
47
47
|
xlsx: {
|
|
48
|
-
defaultBook: "
|
|
48
|
+
defaultBook: "Screen specifications",
|
|
49
49
|
books: {
|
|
50
|
-
"
|
|
50
|
+
"Screen specifications": {
|
|
51
51
|
template: "./templates/customer-screen-spec.xlsx",
|
|
52
52
|
templateSheets: {
|
|
53
|
-
cover: "
|
|
54
|
-
history: "
|
|
55
|
-
list: "
|
|
56
|
-
screen: "
|
|
53
|
+
cover: "Cover template",
|
|
54
|
+
history: "History template",
|
|
55
|
+
list: "Screen list template",
|
|
56
|
+
screen: "Per-screen template",
|
|
57
57
|
},
|
|
58
58
|
},
|
|
59
59
|
},
|
|
@@ -71,7 +71,7 @@ In template cells:
|
|
|
71
71
|
{meta.author} → frontmatter meta.author
|
|
72
72
|
{projectName} → project meta.projectName
|
|
73
73
|
{annot:date} → "2026-05-21"
|
|
74
|
-
{annot:date:yyyy
|
|
74
|
+
{annot:date:yyyy-MM-dd} → "2026-05-21"
|
|
75
75
|
{meta.createdDate:yyyy/MM/dd} → "2026/05/21"
|
|
76
76
|
{annot:sheetIndex}/{annot:totalSheets} → "3/12"
|
|
77
77
|
```
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface CliOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
stdout?: (line: string) => void;
|
|
4
|
+
stderr?: (line: string) => void;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* `main(argv)` entrypoint. Returns the process exit code so the
|
|
8
|
+
* bin script can `process.exit(...)`. Also driveable from
|
|
9
|
+
* vitest via custom `stdout` / `stderr` / `cwd`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function main(argv: string[], options?: CliOptions): Promise<number>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { default as ExcelJS } from 'exceljs';
|
|
2
|
+
import { ExcelMdxBundle } from './extract.js';
|
|
3
|
+
export interface ApplyDefaultLayoutInput {
|
|
4
|
+
workbook: ExcelJS.Workbook;
|
|
5
|
+
bundles: ExcelMdxBundle[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Apply the default no-template layout. The workbook is
|
|
9
|
+
* mutated in place — every bundle contributes one sheet,
|
|
10
|
+
* ordered by role then by `xlsx.order` then by file path.
|
|
11
|
+
*/
|
|
12
|
+
export declare function applyDefaultLayout(input: ApplyDefaultLayoutInput): void;
|
|
13
|
+
/**
|
|
14
|
+
* Sort bundles in the order they appear in the workbook.
|
|
15
|
+
* Documented in plan section "Open question 7":
|
|
16
|
+
* 1. Role-default order (cover → history → list → screen → reference)
|
|
17
|
+
* 2. Within same role: `order` field (default 100)
|
|
18
|
+
* 3. Tie-break: alphabetical by MDX file path.
|
|
19
|
+
*
|
|
20
|
+
* Exported so the template-path branch (PR 3) can reuse the
|
|
21
|
+
* same sort.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sortBundlesForLayout(bundles: ExcelMdxBundle[]): ExcelMdxBundle[];
|
|
24
|
+
/**
|
|
25
|
+
* Excel limits sheet names to 31 chars and forbids `: \ / ? * [ ]`.
|
|
26
|
+
* This is shared with the future template-path branch.
|
|
27
|
+
*/
|
|
28
|
+
export declare function sanitiseSheetName(name: string): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AnnotFrontmatter, HistoryEntrySpec, OverlaySpec, ParsedMdx } from '@ingcreators/annot-product-docs';
|
|
2
|
+
export interface ExcelMdxBundle {
|
|
3
|
+
/** Absolute or cwd-relative path to the source `.mdx` file. */
|
|
4
|
+
mdxPath: string;
|
|
5
|
+
/** Verbatim source bytes — kept so downstream tools can hash. */
|
|
6
|
+
source: string;
|
|
7
|
+
/** The `annot:` frontmatter block. */
|
|
8
|
+
frontmatter: AnnotFrontmatter;
|
|
9
|
+
/** Flat per-screen rows (one entry per `<Screen>`). */
|
|
10
|
+
screens: NormalisedScreen[];
|
|
11
|
+
/** Flat per-overlay rows (one entry per `<Overlay>`, including its
|
|
12
|
+
* parent screen's id + screen-relative number). */
|
|
13
|
+
overlays: NormalisedOverlay[];
|
|
14
|
+
/** Flat per-history-entry rows (only populated for `role: history` MDXs). */
|
|
15
|
+
history: HistoryEntrySpec[];
|
|
16
|
+
/** Verbatim aria-snapshot block as captured by the fixture. */
|
|
17
|
+
snapshotYaml: string;
|
|
18
|
+
/** Verbatim attributes block as captured by the fixture. */
|
|
19
|
+
attributesYaml: string;
|
|
20
|
+
}
|
|
21
|
+
export interface NormalisedScreen {
|
|
22
|
+
id: string;
|
|
23
|
+
src: string | undefined;
|
|
24
|
+
overlayCount: number;
|
|
25
|
+
}
|
|
26
|
+
export interface NormalisedOverlay {
|
|
27
|
+
screenId: string;
|
|
28
|
+
number: number;
|
|
29
|
+
intent: OverlaySpec["intent"];
|
|
30
|
+
/** `role + name` joined for one-cell rendering. */
|
|
31
|
+
matchLabel: string;
|
|
32
|
+
matchRole: string;
|
|
33
|
+
matchName: string;
|
|
34
|
+
/** Markdown body — caller renders to rich text. */
|
|
35
|
+
body: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract a normalised bundle from a parsed MDX. Used directly
|
|
39
|
+
* by `extractMdxFile` and re-exposed so tests can drive it
|
|
40
|
+
* without filesystem I/O.
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractFromParsed(parsed: ParsedMdx, mdxPath: string): ExcelMdxBundle;
|
|
43
|
+
/**
|
|
44
|
+
* Convenience: parse the MDX file and normalise. Returns `null`
|
|
45
|
+
* for files without `annot:` frontmatter so the bulk-extract
|
|
46
|
+
* caller can keep its glob noise-free.
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractMdxFile(mdxPath: string): Promise<ExcelMdxBundle | null>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type { CliOptions } from './cli.js';
|
|
2
|
+
export { main, main as cliMain } from './cli.js';
|
|
3
|
+
export type { ApplyDefaultLayoutInput } from './default-layout.js';
|
|
4
|
+
export { applyDefaultLayout, sanitiseSheetName, sortBundlesForLayout, } from './default-layout.js';
|
|
5
|
+
export type { ExcelMdxBundle, NormalisedOverlay, NormalisedScreen, } from './extract.js';
|
|
6
|
+
export { extractFromParsed, extractMdxFile } from './extract.js';
|
|
7
|
+
export type { NamedRangeWriteInput } from './named-range.js';
|
|
8
|
+
export { applyNamedRanges } from './named-range.js';
|
|
9
|
+
export type { SubstituteOptions } from './placeholder.js';
|
|
10
|
+
export { applyPlaceholders, applyPlaceholdersToSheet, resolvePlaceholders, } from './placeholder.js';
|
|
11
|
+
export type { ApplyTemplateInput } from './template.js';
|
|
12
|
+
export { applyTemplateLayout, loadTemplateWorkbook } from './template.js';
|
|
13
|
+
export type { BuildWorkbookInput } from './workbook.js';
|
|
14
|
+
export { buildEmptyWorkbook, groupBundlesByBook, writeWorkbookToBytes, } from './workbook.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
import { mkdir as e, readdir as t, writeFile as n } from "node:fs/promises";
|
|
2
|
+
import { join as r, resolve as i } from "node:path";
|
|
3
|
+
import { pathToFileURL as a } from "node:url";
|
|
4
|
+
import { parseMdxFile as o } from "@ingcreators/annot-product-docs";
|
|
5
|
+
import s from "exceljs";
|
|
6
|
+
//#region src/default-layout.ts
|
|
7
|
+
var c = {
|
|
8
|
+
cover: 1,
|
|
9
|
+
history: 2,
|
|
10
|
+
list: 3,
|
|
11
|
+
screen: 4,
|
|
12
|
+
reference: 5
|
|
13
|
+
};
|
|
14
|
+
function l(e) {
|
|
15
|
+
let t = u(e.bundles);
|
|
16
|
+
for (let n of t) {
|
|
17
|
+
let t = n.frontmatter.xlsx?.role ?? "screen", r = g(n, t), i = e.workbook.addWorksheet(r);
|
|
18
|
+
switch (t) {
|
|
19
|
+
case "cover":
|
|
20
|
+
d(i, n);
|
|
21
|
+
break;
|
|
22
|
+
case "history":
|
|
23
|
+
f(i, n);
|
|
24
|
+
break;
|
|
25
|
+
case "list":
|
|
26
|
+
p(i, e.bundles);
|
|
27
|
+
break;
|
|
28
|
+
case "reference":
|
|
29
|
+
h(i, n);
|
|
30
|
+
break;
|
|
31
|
+
default: m(i, n);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function u(e) {
|
|
36
|
+
return [...e].sort((e, t) => {
|
|
37
|
+
let n = c[e.frontmatter.xlsx?.role ?? "screen"] ?? 99, r = c[t.frontmatter.xlsx?.role ?? "screen"] ?? 99;
|
|
38
|
+
if (n !== r) return n - r;
|
|
39
|
+
let i = e.frontmatter.xlsx?.order ?? 100, a = t.frontmatter.xlsx?.order ?? 100;
|
|
40
|
+
return i === a ? e.mdxPath.localeCompare(t.mdxPath) : i - a;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function d(e, t) {
|
|
44
|
+
let n = t.frontmatter;
|
|
45
|
+
if (e.columns = [{
|
|
46
|
+
key: "label",
|
|
47
|
+
width: 24
|
|
48
|
+
}, {
|
|
49
|
+
key: "value",
|
|
50
|
+
width: 60
|
|
51
|
+
}], e.addRow({
|
|
52
|
+
label: "Title",
|
|
53
|
+
value: n.title ?? n.id
|
|
54
|
+
}), e.addRow({
|
|
55
|
+
label: "Document ID",
|
|
56
|
+
value: n.id
|
|
57
|
+
}), n.purpose && e.addRow({
|
|
58
|
+
label: "Purpose",
|
|
59
|
+
value: n.purpose
|
|
60
|
+
}), n.meta) for (let [t, r] of Object.entries(n.meta)) e.addRow({
|
|
61
|
+
label: `meta.${t}`,
|
|
62
|
+
value: String(r)
|
|
63
|
+
});
|
|
64
|
+
e.getColumn(1).font = { bold: !0 };
|
|
65
|
+
}
|
|
66
|
+
function f(e, t) {
|
|
67
|
+
e.columns = [
|
|
68
|
+
{
|
|
69
|
+
header: "Version",
|
|
70
|
+
key: "version",
|
|
71
|
+
width: 12
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
header: "Date",
|
|
75
|
+
key: "date",
|
|
76
|
+
width: 14
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
header: "Author",
|
|
80
|
+
key: "author",
|
|
81
|
+
width: 18
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
header: "Notes",
|
|
85
|
+
key: "body",
|
|
86
|
+
width: 60
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
for (let n of t.history) e.addRow({
|
|
90
|
+
version: n.version,
|
|
91
|
+
date: n.date,
|
|
92
|
+
author: n.author,
|
|
93
|
+
body: n.body
|
|
94
|
+
});
|
|
95
|
+
e.getRow(1).font = { bold: !0 };
|
|
96
|
+
}
|
|
97
|
+
function p(e, t) {
|
|
98
|
+
e.columns = [
|
|
99
|
+
{
|
|
100
|
+
header: "ID",
|
|
101
|
+
key: "id",
|
|
102
|
+
width: 14
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
header: "Title",
|
|
106
|
+
key: "title",
|
|
107
|
+
width: 36
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
header: "Screens",
|
|
111
|
+
key: "screens",
|
|
112
|
+
width: 10
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
header: "Overlays",
|
|
116
|
+
key: "overlays",
|
|
117
|
+
width: 10
|
|
118
|
+
}
|
|
119
|
+
];
|
|
120
|
+
for (let n of t) (n.frontmatter.xlsx?.role ?? "screen") === "screen" && e.addRow({
|
|
121
|
+
id: n.frontmatter.id,
|
|
122
|
+
title: n.frontmatter.title ?? n.frontmatter.id,
|
|
123
|
+
screens: n.screens.length,
|
|
124
|
+
overlays: n.overlays.length
|
|
125
|
+
});
|
|
126
|
+
e.getRow(1).font = { bold: !0 };
|
|
127
|
+
}
|
|
128
|
+
function m(e, t) {
|
|
129
|
+
let n = t.frontmatter;
|
|
130
|
+
e.addRow(["ID", n.id]), e.addRow(["Title", n.title ?? ""]), n.purpose && e.addRow(["Purpose", n.purpose]), e.addRow([]), e.addRow([
|
|
131
|
+
"#",
|
|
132
|
+
"Role",
|
|
133
|
+
"Name",
|
|
134
|
+
"Intent",
|
|
135
|
+
"Notes"
|
|
136
|
+
]);
|
|
137
|
+
let r = e.lastRow;
|
|
138
|
+
r.font = { bold: !0 };
|
|
139
|
+
for (let n of t.overlays) e.addRow([
|
|
140
|
+
n.number,
|
|
141
|
+
n.matchRole,
|
|
142
|
+
n.matchName,
|
|
143
|
+
n.intent ?? "",
|
|
144
|
+
n.body
|
|
145
|
+
]);
|
|
146
|
+
e.getColumn(1).width = 6, e.getColumn(2).width = 14, e.getColumn(3).width = 24, e.getColumn(4).width = 12, e.getColumn(5).width = 60, e.getColumn(1).font = { bold: !0 };
|
|
147
|
+
}
|
|
148
|
+
function h(e, t) {
|
|
149
|
+
e.addRow(["ID", t.frontmatter.id]), e.addRow(["Title", t.frontmatter.title ?? ""]), t.frontmatter.purpose && e.addRow(["Purpose", t.frontmatter.purpose]);
|
|
150
|
+
}
|
|
151
|
+
function g(e, t) {
|
|
152
|
+
let n = e.frontmatter.xlsx;
|
|
153
|
+
return typeof n?.sheet == "string" && n.sheet.length > 0 ? _(n.sheet) : t === "cover" ? "Cover" : t === "history" ? "Revision history" : t === "list" ? "Screen list" : _(t === "reference" ? e.frontmatter.id : e.frontmatter.title ?? e.frontmatter.id);
|
|
154
|
+
}
|
|
155
|
+
function _(e) {
|
|
156
|
+
let t = e.replace(/[\\/?*[\]:]/g, "_").slice(0, 31);
|
|
157
|
+
return t.length > 0 ? t : "Sheet";
|
|
158
|
+
}
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/extract.ts
|
|
161
|
+
function v(e, t) {
|
|
162
|
+
let n = e.screens.map((e) => ({
|
|
163
|
+
id: e.id,
|
|
164
|
+
src: e.src,
|
|
165
|
+
overlayCount: e.overlays.length
|
|
166
|
+
})), r = [];
|
|
167
|
+
for (let t of e.screens) {
|
|
168
|
+
let e = 1;
|
|
169
|
+
for (let n of t.overlays) {
|
|
170
|
+
let i = n.number ?? e++;
|
|
171
|
+
r.push({
|
|
172
|
+
screenId: t.id,
|
|
173
|
+
number: i,
|
|
174
|
+
intent: n.intent,
|
|
175
|
+
matchLabel: ee(n),
|
|
176
|
+
matchRole: n.match.role,
|
|
177
|
+
matchName: n.match.name,
|
|
178
|
+
body: n.body
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
mdxPath: t,
|
|
184
|
+
source: e.source,
|
|
185
|
+
frontmatter: e.frontmatter,
|
|
186
|
+
screens: n,
|
|
187
|
+
overlays: r,
|
|
188
|
+
history: e.history,
|
|
189
|
+
snapshotYaml: e.commentBlocks.snapshot ?? "",
|
|
190
|
+
attributesYaml: e.commentBlocks.attributes ?? ""
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function y(e) {
|
|
194
|
+
let t = await o(e);
|
|
195
|
+
return t ? v(t, e) : null;
|
|
196
|
+
}
|
|
197
|
+
function ee(e) {
|
|
198
|
+
let t = `${e.match.role} "${e.match.name}"`;
|
|
199
|
+
return e.match.under ? `${t} under ${e.match.under.role} "${e.match.under.name}"` : t;
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/placeholder.ts
|
|
203
|
+
var te = /\{([^{}]+)\}/g, b = "annot:";
|
|
204
|
+
function x(e, t, n = {}) {
|
|
205
|
+
let r = n.totalSheets ?? e.worksheets.length;
|
|
206
|
+
e.worksheets.forEach((e, i) => {
|
|
207
|
+
S(e, t, {
|
|
208
|
+
...n,
|
|
209
|
+
sheetIndex: n.sheetIndex ?? i + 1,
|
|
210
|
+
totalSheets: r
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function S(e, t, n = {}) {
|
|
215
|
+
e.eachRow({ includeEmpty: !1 }, (e) => {
|
|
216
|
+
e.eachCell({ includeEmpty: !1 }, (e) => {
|
|
217
|
+
let r = e.value;
|
|
218
|
+
if (typeof r != "string") return;
|
|
219
|
+
let i = C(r, t, n);
|
|
220
|
+
i !== r && (e.value = i);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function C(e, t, n = {}) {
|
|
225
|
+
return e.replace(te, (e, r) => {
|
|
226
|
+
let { key: i, format: a } = w(r), o = T(i, t, n);
|
|
227
|
+
return o === void 0 ? e : a ? E(o, a) : o;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function w(e) {
|
|
231
|
+
let t = e.trim();
|
|
232
|
+
if (t.startsWith(b)) {
|
|
233
|
+
let e = t.slice(6), n = e.indexOf(":");
|
|
234
|
+
return n < 0 ? {
|
|
235
|
+
key: t,
|
|
236
|
+
format: void 0
|
|
237
|
+
} : {
|
|
238
|
+
key: b + e.slice(0, n),
|
|
239
|
+
format: e.slice(n + 1)
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
let n = t.indexOf(":");
|
|
243
|
+
return n < 0 ? {
|
|
244
|
+
key: t,
|
|
245
|
+
format: void 0
|
|
246
|
+
} : {
|
|
247
|
+
key: t.slice(0, n),
|
|
248
|
+
format: t.slice(n + 1)
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function T(e, t, n) {
|
|
252
|
+
if (e.startsWith(b)) {
|
|
253
|
+
let t = e.slice(6), r = n.renderTime ?? /* @__PURE__ */ new Date();
|
|
254
|
+
switch (t) {
|
|
255
|
+
case "date": return O(r, "yyyy-MM-dd");
|
|
256
|
+
case "datetime": return r.toISOString();
|
|
257
|
+
case "sheetIndex": return n.sheetIndex === void 0 ? void 0 : String(n.sheetIndex);
|
|
258
|
+
case "totalSheets": return n.totalSheets === void 0 ? void 0 : String(n.totalSheets);
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (e.startsWith("meta.")) {
|
|
263
|
+
let r = e.slice(5), i = t.frontmatter.meta?.[r] ?? n.projectMeta?.[r];
|
|
264
|
+
return i === void 0 ? void 0 : String(i);
|
|
265
|
+
}
|
|
266
|
+
if (e === "id") return t.frontmatter.id;
|
|
267
|
+
if (e === "title") return t.frontmatter.title;
|
|
268
|
+
if (e === "purpose") return t.frontmatter.purpose;
|
|
269
|
+
let r = n.projectMeta?.[e];
|
|
270
|
+
if (r !== void 0) return String(r);
|
|
271
|
+
}
|
|
272
|
+
function E(e, t) {
|
|
273
|
+
let n = D(e);
|
|
274
|
+
return n ? O(n, t) : e;
|
|
275
|
+
}
|
|
276
|
+
function D(e) {
|
|
277
|
+
let t = e.trim(), n = t.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
278
|
+
if (n) return new Date(Number.parseInt(n[1], 10), Number.parseInt(n[2], 10) - 1, Number.parseInt(n[3], 10));
|
|
279
|
+
let r = new Date(t);
|
|
280
|
+
return Number.isNaN(r.getTime()) ? null : r;
|
|
281
|
+
}
|
|
282
|
+
function O(e, t) {
|
|
283
|
+
let n = String(e.getFullYear()).padStart(4, "0"), r = String(e.getMonth() + 1).padStart(2, "0"), i = String(e.getDate()).padStart(2, "0"), a = String(e.getHours()).padStart(2, "0"), o = String(e.getMinutes()).padStart(2, "0"), s = String(e.getSeconds()).padStart(2, "0");
|
|
284
|
+
return t.replace(/yyyy/g, n).replace(/MM/g, r).replace(/dd/g, i).replace(/HH/g, a).replace(/mm/g, o).replace(/ss/g, s);
|
|
285
|
+
}
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/template.ts
|
|
288
|
+
function k(e) {
|
|
289
|
+
let t = u(e.bundles);
|
|
290
|
+
for (let n of t) {
|
|
291
|
+
let t = n.frontmatter.xlsx?.role ?? "screen", r = e.bookConfig.templateSheets?.[t];
|
|
292
|
+
if (!r) continue;
|
|
293
|
+
let i = e.template.getWorksheet(r);
|
|
294
|
+
if (!i) throw Error(`Template sheet "${r}" not found for role "${t}" in book template.`);
|
|
295
|
+
let a = n.frontmatter.xlsx?.sheets;
|
|
296
|
+
if (a) {
|
|
297
|
+
let t = Object.entries(a);
|
|
298
|
+
for (let [, r] of t) S(j(e.workbook, i, _(r)), n, e.substitute ?? {});
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
let o = _(n.frontmatter.xlsx?.sheet ?? n.frontmatter.title ?? n.frontmatter.id);
|
|
302
|
+
S(j(e.workbook, i, o), n, e.substitute ?? {});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function A(e) {
|
|
306
|
+
let t = new s.Workbook();
|
|
307
|
+
return await t.xlsx.readFile(e), t;
|
|
308
|
+
}
|
|
309
|
+
function j(e, t, n) {
|
|
310
|
+
let r = e.addWorksheet(n);
|
|
311
|
+
r.columns = t.columns.map((e) => ({
|
|
312
|
+
header: e.header,
|
|
313
|
+
key: e.key,
|
|
314
|
+
width: e.width,
|
|
315
|
+
style: e.style
|
|
316
|
+
})), t.eachRow({ includeEmpty: !0 }, (e, t) => {
|
|
317
|
+
let n = r.getRow(t);
|
|
318
|
+
e.eachCell({ includeEmpty: !0 }, (e, t) => {
|
|
319
|
+
let r = n.getCell(t);
|
|
320
|
+
r.value = e.value, e.style && (r.style = e.style);
|
|
321
|
+
}), e.height !== void 0 && (n.height = e.height), n.commit();
|
|
322
|
+
});
|
|
323
|
+
for (let e of Object.values(M(t))) r.mergeCells(e);
|
|
324
|
+
return r;
|
|
325
|
+
}
|
|
326
|
+
function M(e) {
|
|
327
|
+
return e._merges ?? {};
|
|
328
|
+
}
|
|
329
|
+
//#endregion
|
|
330
|
+
//#region src/workbook.ts
|
|
331
|
+
function N(e) {
|
|
332
|
+
let t = new s.Workbook();
|
|
333
|
+
return t.creator = "@ingcreators/annot-product-docs-xlsx", t.created = /* @__PURE__ */ new Date(), t.title = e.book, t;
|
|
334
|
+
}
|
|
335
|
+
async function P(e) {
|
|
336
|
+
let t = await e.xlsx.writeBuffer();
|
|
337
|
+
return t instanceof Buffer ? new Uint8Array(t.buffer, t.byteOffset, t.byteLength) : new Uint8Array(t);
|
|
338
|
+
}
|
|
339
|
+
function F(e, t) {
|
|
340
|
+
let n = /* @__PURE__ */ new Map();
|
|
341
|
+
for (let r of e) {
|
|
342
|
+
let e = r.frontmatter.xlsx?.book ?? t;
|
|
343
|
+
if (!e) continue;
|
|
344
|
+
let i = n.get(e);
|
|
345
|
+
i || (i = [], n.set(e, i)), i.push(r);
|
|
346
|
+
}
|
|
347
|
+
return n;
|
|
348
|
+
}
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/cli.ts
|
|
351
|
+
var I = [
|
|
352
|
+
"annot-docs-xlsx <command> [options]",
|
|
353
|
+
"",
|
|
354
|
+
"Commands:",
|
|
355
|
+
" render Walk MDXs and emit one .xlsx per book",
|
|
356
|
+
"",
|
|
357
|
+
"Options:",
|
|
358
|
+
" --root <dir> MDX search root (default: docs)",
|
|
359
|
+
" --out <dir> Output dir for .xlsx files (default: dist/xlsx)",
|
|
360
|
+
" --book <name> Restrict to a single book name",
|
|
361
|
+
" --config <file> Path to annot-docs.config.ts (default: ./annot-docs.config.ts)",
|
|
362
|
+
" --help Show this help"
|
|
363
|
+
].join("\n");
|
|
364
|
+
async function L(e, t = {}) {
|
|
365
|
+
let n = t.stdout ?? ((e) => process.stdout.write(`${e}\n`)), r = t.stderr ?? ((e) => process.stderr.write(`${e}\n`)), i = t.cwd ?? process.cwd(), [, , a, ...o] = e;
|
|
366
|
+
if (a !== "render") return r(I), +!!a;
|
|
367
|
+
let s = R(o);
|
|
368
|
+
try {
|
|
369
|
+
return await z(i, s, n);
|
|
370
|
+
} catch (e) {
|
|
371
|
+
return r(`annot-docs-xlsx render: ${e.message}`), 1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function R(e) {
|
|
375
|
+
let t = "docs", n = "dist/xlsx", r, i = "annot-docs.config.ts";
|
|
376
|
+
for (let a = 0; a < e.length; a++) {
|
|
377
|
+
let o = e[a];
|
|
378
|
+
o === "--root" ? t = e[++a] ?? t : o === "--out" ? n = e[++a] ?? n : o === "--book" ? r = e[++a] : o === "--config" && (i = e[++a] ?? i);
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
root: t,
|
|
382
|
+
out: n,
|
|
383
|
+
book: r,
|
|
384
|
+
config: i
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async function z(t, a, o) {
|
|
388
|
+
let s = await B(i(t, a.config), o), c = await H(await V(i(t, a.root)));
|
|
389
|
+
if (c.length === 0) return o(`annot-docs-xlsx render: no MDX files with \`annot:\` frontmatter under ${a.root}.`), 0;
|
|
390
|
+
let u = F(c, s.xlsx?.defaultBook), d = i(t, a.out);
|
|
391
|
+
await e(d, { recursive: !0 });
|
|
392
|
+
let f = 0;
|
|
393
|
+
for (let [e, c] of u.entries()) {
|
|
394
|
+
if (a.book && e !== a.book) continue;
|
|
395
|
+
let u = N({
|
|
396
|
+
book: e,
|
|
397
|
+
bundles: c
|
|
398
|
+
}), p = s.xlsx?.books?.[e], m = p?.template;
|
|
399
|
+
m ? k({
|
|
400
|
+
workbook: u,
|
|
401
|
+
template: await A(i(t, m)),
|
|
402
|
+
bookConfig: p,
|
|
403
|
+
bundles: c,
|
|
404
|
+
substitute: {
|
|
405
|
+
projectMeta: s.meta,
|
|
406
|
+
renderTime: /* @__PURE__ */ new Date()
|
|
407
|
+
}
|
|
408
|
+
}) : l({
|
|
409
|
+
workbook: u,
|
|
410
|
+
bundles: c
|
|
411
|
+
});
|
|
412
|
+
let h = await P(u), g = r(d, `${U(e)}.xlsx`);
|
|
413
|
+
await n(g, h), o(` wrote ${g} (${c.length} MDX file(s))`), f++;
|
|
414
|
+
}
|
|
415
|
+
return a.book && f === 0 ? (o(`annot-docs-xlsx render: book "${a.book}" not found (had: ${[...u.keys()].join(", ")}).`), 1) : (o(`annot-docs-xlsx render: emitted ${f} workbook(s) to ${a.out}`), 0);
|
|
416
|
+
}
|
|
417
|
+
async function B(e, t) {
|
|
418
|
+
try {
|
|
419
|
+
let t = await import(a(e).href);
|
|
420
|
+
return t.default ?? t;
|
|
421
|
+
} catch (n) {
|
|
422
|
+
if (n.code === "ERR_MODULE_NOT_FOUND") return t(`annot-docs-xlsx render: no config at ${e} — using defaults.`), {};
|
|
423
|
+
throw n;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
async function V(e) {
|
|
427
|
+
let n = [], i = [e];
|
|
428
|
+
for (; i.length > 0;) {
|
|
429
|
+
let e = i.pop(), a;
|
|
430
|
+
try {
|
|
431
|
+
a = await t(e, { withFileTypes: !0 });
|
|
432
|
+
} catch (e) {
|
|
433
|
+
if (e.code === "ENOENT") continue;
|
|
434
|
+
throw e;
|
|
435
|
+
}
|
|
436
|
+
for (let t of a) {
|
|
437
|
+
let a = r(e, t.name);
|
|
438
|
+
if (t.isDirectory()) {
|
|
439
|
+
if (t.name === "node_modules" || t.name.startsWith(".")) continue;
|
|
440
|
+
i.push(a);
|
|
441
|
+
} else t.isFile() && t.name.endsWith(".mdx") && n.push(a);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return n.sort();
|
|
445
|
+
}
|
|
446
|
+
async function H(e) {
|
|
447
|
+
let t = [];
|
|
448
|
+
for (let n of e) try {
|
|
449
|
+
let e = await o(n);
|
|
450
|
+
e && t.push(v(e, n));
|
|
451
|
+
} catch {}
|
|
452
|
+
return t;
|
|
453
|
+
}
|
|
454
|
+
function U(e) {
|
|
455
|
+
return e.replace(/[\\/?*[\]:<>|"]/g, "_");
|
|
456
|
+
}
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/named-range.ts
|
|
459
|
+
var W = "annot";
|
|
460
|
+
function G(e) {
|
|
461
|
+
for (let t of ie(e.workbook)) K(e, t);
|
|
462
|
+
}
|
|
463
|
+
function K(e, t) {
|
|
464
|
+
let [n, r] = q(t.name.replace(/^annot/, ""));
|
|
465
|
+
switch (n) {
|
|
466
|
+
case "Image":
|
|
467
|
+
J(e, t, r);
|
|
468
|
+
return;
|
|
469
|
+
case "ItemTable":
|
|
470
|
+
X(t, e.bundle);
|
|
471
|
+
return;
|
|
472
|
+
case "Transitions":
|
|
473
|
+
Z(t, e.bundle);
|
|
474
|
+
return;
|
|
475
|
+
case "History":
|
|
476
|
+
ne(t, e.bundle);
|
|
477
|
+
return;
|
|
478
|
+
case "List":
|
|
479
|
+
re(t, e.allBundles ?? [e.bundle]);
|
|
480
|
+
return;
|
|
481
|
+
case "Snapshot":
|
|
482
|
+
Q(t, e.bundle.snapshotYaml);
|
|
483
|
+
return;
|
|
484
|
+
case "Attributes":
|
|
485
|
+
Q(t, e.bundle.attributesYaml);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
function q(e) {
|
|
490
|
+
let t = e.indexOf("_");
|
|
491
|
+
return t < 0 ? [e, void 0] : [e.slice(0, t), e.slice(t + 1)];
|
|
492
|
+
}
|
|
493
|
+
function J(e, t, n) {
|
|
494
|
+
let r = n ? e.imagesByScreenId?.[n] : e.defaultImage ?? e.imagesByScreenId?.[e.bundle.screens[0]?.id ?? ""];
|
|
495
|
+
if (!r) return;
|
|
496
|
+
let i = e.workbook.addImage({
|
|
497
|
+
buffer: Buffer.from(r),
|
|
498
|
+
extension: "png"
|
|
499
|
+
});
|
|
500
|
+
t.sheet.addImage(i, t.address);
|
|
501
|
+
}
|
|
502
|
+
function Y(e, t, n) {
|
|
503
|
+
let { sheet: r, box: i } = e;
|
|
504
|
+
for (let e = 0; e < t.length; e++) {
|
|
505
|
+
let n = r.getCell(i.startRow, i.startCol + e);
|
|
506
|
+
n.value = t[e] ?? "", n.font = { bold: !0 };
|
|
507
|
+
}
|
|
508
|
+
for (let e = 0; e < n.length; e++) {
|
|
509
|
+
let t = n[e], a = i.startRow + 1 + e;
|
|
510
|
+
if (a > i.endRow) break;
|
|
511
|
+
for (let e = 0; e < t.length; e++) r.getCell(a, i.startCol + e).value = t[e] ?? "";
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function X(e, t) {
|
|
515
|
+
Y(e, [
|
|
516
|
+
"#",
|
|
517
|
+
"Role",
|
|
518
|
+
"Name",
|
|
519
|
+
"Intent",
|
|
520
|
+
"Notes"
|
|
521
|
+
], t.overlays.map((e) => [
|
|
522
|
+
e.number,
|
|
523
|
+
e.matchRole,
|
|
524
|
+
e.matchName,
|
|
525
|
+
e.intent ?? "",
|
|
526
|
+
e.body
|
|
527
|
+
]));
|
|
528
|
+
}
|
|
529
|
+
function Z(e, t) {
|
|
530
|
+
Y(e, [
|
|
531
|
+
"Trigger",
|
|
532
|
+
"Event",
|
|
533
|
+
"Target",
|
|
534
|
+
"Notes"
|
|
535
|
+
], []);
|
|
536
|
+
}
|
|
537
|
+
function ne(e, t) {
|
|
538
|
+
Y(e, [
|
|
539
|
+
"Version",
|
|
540
|
+
"Date",
|
|
541
|
+
"Author",
|
|
542
|
+
"Notes"
|
|
543
|
+
], t.history.map((e) => [
|
|
544
|
+
e.version,
|
|
545
|
+
e.date,
|
|
546
|
+
e.author,
|
|
547
|
+
e.body
|
|
548
|
+
]));
|
|
549
|
+
}
|
|
550
|
+
function re(e, t) {
|
|
551
|
+
Y(e, [
|
|
552
|
+
"ID",
|
|
553
|
+
"Title",
|
|
554
|
+
"Screens",
|
|
555
|
+
"Overlays"
|
|
556
|
+
], t.filter((e) => (e.frontmatter.xlsx?.role ?? "screen") === "screen").map((e) => [
|
|
557
|
+
e.frontmatter.id,
|
|
558
|
+
e.frontmatter.title ?? e.frontmatter.id,
|
|
559
|
+
e.screens.length,
|
|
560
|
+
e.overlays.length
|
|
561
|
+
]));
|
|
562
|
+
}
|
|
563
|
+
function Q(e, t) {
|
|
564
|
+
let n = e.sheet.getCell(e.box.startRow, e.box.startCol);
|
|
565
|
+
n.value = t, n.alignment = {
|
|
566
|
+
wrapText: !0,
|
|
567
|
+
vertical: "top"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
function ie(e) {
|
|
571
|
+
let t = /* @__PURE__ */ new Map();
|
|
572
|
+
for (let n of e.worksheets) n.eachRow({ includeEmpty: !0 }, (e, r) => {
|
|
573
|
+
e.eachCell({ includeEmpty: !0 }, (e, i) => {
|
|
574
|
+
let a = ae(e);
|
|
575
|
+
for (let e of a) {
|
|
576
|
+
if (!e.startsWith(W)) continue;
|
|
577
|
+
let a = `${n.name}|${e}`, o = t.get(a);
|
|
578
|
+
o || (o = {
|
|
579
|
+
sheet: n,
|
|
580
|
+
name: e,
|
|
581
|
+
cells: []
|
|
582
|
+
}, t.set(a, o)), o.cells.push({
|
|
583
|
+
row: r,
|
|
584
|
+
col: i
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
return [...t.values()].map((e) => {
|
|
590
|
+
let t = e.cells.map((e) => e.row), n = e.cells.map((e) => e.col), r = {
|
|
591
|
+
startRow: Math.min(...t),
|
|
592
|
+
startCol: Math.min(...n),
|
|
593
|
+
endRow: Math.max(...t),
|
|
594
|
+
endCol: Math.max(...n)
|
|
595
|
+
};
|
|
596
|
+
return {
|
|
597
|
+
name: e.name,
|
|
598
|
+
sheet: e.sheet,
|
|
599
|
+
box: r,
|
|
600
|
+
address: `${$(r.startCol)}${r.startRow}:${$(r.endCol)}${r.endRow}`
|
|
601
|
+
};
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
function ae(e) {
|
|
605
|
+
return e.names ?? [];
|
|
606
|
+
}
|
|
607
|
+
function $(e) {
|
|
608
|
+
let t = e, n = "";
|
|
609
|
+
for (; t > 0;) {
|
|
610
|
+
let e = (t - 1) % 26;
|
|
611
|
+
n = String.fromCharCode(65 + e) + n, t = Math.floor((t - 1) / 26);
|
|
612
|
+
}
|
|
613
|
+
return n;
|
|
614
|
+
}
|
|
615
|
+
//#endregion
|
|
616
|
+
export { l as applyDefaultLayout, G as applyNamedRanges, x as applyPlaceholders, S as applyPlaceholdersToSheet, k as applyTemplateLayout, N as buildEmptyWorkbook, L as cliMain, L as main, v as extractFromParsed, y as extractMdxFile, F as groupBundlesByBook, A as loadTemplateWorkbook, C as resolvePlaceholders, _ as sanitiseSheetName, u as sortBundlesForLayout, P as writeWorkbookToBytes };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { default as ExcelJS } from 'exceljs';
|
|
2
|
+
import { ExcelMdxBundle } from './extract.js';
|
|
3
|
+
export interface NamedRangeWriteInput {
|
|
4
|
+
workbook: ExcelJS.Workbook;
|
|
5
|
+
bundle: ExcelMdxBundle;
|
|
6
|
+
/** PNG bytes per `<Screen id>` (Phase 2 PR 2 Image Service). */
|
|
7
|
+
imagesByScreenId?: Record<string, Uint8Array>;
|
|
8
|
+
/** Fallback PNG when an `annotImage` (un-suffixed) range is
|
|
9
|
+
* used and the MDX has exactly one screen. */
|
|
10
|
+
defaultImage?: Uint8Array;
|
|
11
|
+
/** All bundles in the same book — used by `annotList`. */
|
|
12
|
+
allBundles?: ExcelMdxBundle[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Apply every recognised `annot*` named range in the workbook
|
|
16
|
+
* for one MDX bundle. Mutates the workbook in place.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyNamedRanges(input: NamedRangeWriteInput): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { default as ExcelJS } from 'exceljs';
|
|
2
|
+
import { ExcelMdxBundle } from './extract.js';
|
|
3
|
+
export interface SubstituteOptions {
|
|
4
|
+
/** Project-level `meta` from `annot-docs.config.ts`. */
|
|
5
|
+
projectMeta?: Record<string, unknown>;
|
|
6
|
+
/** Used by `{annot:date}` / `{annot:datetime}`. Defaults to
|
|
7
|
+
* `new Date()` per call but the pipeline can pin a single
|
|
8
|
+
* `renderTime` across all substitutions so the workbook
|
|
9
|
+
* has consistent timestamps. */
|
|
10
|
+
renderTime?: Date;
|
|
11
|
+
/** Used by `{annot:sheetIndex}` / `{annot:totalSheets}`. The
|
|
12
|
+
* per-sheet wrapper passes the index of the sheet being
|
|
13
|
+
* rendered + the total. */
|
|
14
|
+
sheetIndex?: number;
|
|
15
|
+
totalSheets?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Walk every cell on every worksheet in the workbook and
|
|
19
|
+
* substitute `{var}` placeholders. Mutates in place.
|
|
20
|
+
*/
|
|
21
|
+
export declare function applyPlaceholders(workbook: ExcelJS.Workbook, bundle: ExcelMdxBundle, options?: SubstituteOptions): void;
|
|
22
|
+
/**
|
|
23
|
+
* Single-sheet version — exposed so the template-driven path
|
|
24
|
+
* (which clones one template sheet per `<Screen>`) can call it
|
|
25
|
+
* after the clone.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyPlaceholdersToSheet(sheet: ExcelJS.Worksheet, bundle: ExcelMdxBundle, options?: SubstituteOptions): void;
|
|
28
|
+
/**
|
|
29
|
+
* Pure string transform — replace every `{name}` or
|
|
30
|
+
* `{name:format}` token in a source string with its resolved
|
|
31
|
+
* value. Exposed for unit tests and for callers (sheet-name
|
|
32
|
+
* substitution, log messages, …).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolvePlaceholders(source: string, bundle: ExcelMdxBundle, options?: SubstituteOptions): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BookConfig } from '@ingcreators/annot-product-docs';
|
|
2
|
+
import { default as ExcelJS } from 'exceljs';
|
|
3
|
+
import { ExcelMdxBundle } from './extract.js';
|
|
4
|
+
import { SubstituteOptions } from './placeholder.js';
|
|
5
|
+
export interface ApplyTemplateInput {
|
|
6
|
+
/** The empty workbook to populate. */
|
|
7
|
+
workbook: ExcelJS.Workbook;
|
|
8
|
+
/** Pre-loaded ExcelJS workbook from the customer template. */
|
|
9
|
+
template: ExcelJS.Workbook;
|
|
10
|
+
/** Per-book config from `annot-docs.config.ts`. */
|
|
11
|
+
bookConfig: BookConfig;
|
|
12
|
+
/** All bundles assigned to this book. */
|
|
13
|
+
bundles: ExcelMdxBundle[];
|
|
14
|
+
/** Substitution options forwarded to `applyPlaceholdersToSheet`. */
|
|
15
|
+
substitute?: SubstituteOptions;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Apply the customer template path. Mutates `workbook` in
|
|
19
|
+
* place: clones the configured template sheet for each role
|
|
20
|
+
* once per matching bundle, renames each clone, and runs
|
|
21
|
+
* placeholder substitution on every cloned cell.
|
|
22
|
+
*/
|
|
23
|
+
export declare function applyTemplateLayout(input: ApplyTemplateInput): void;
|
|
24
|
+
/**
|
|
25
|
+
* Load a template workbook from disk. Thin wrapper around
|
|
26
|
+
* ExcelJS so the caller doesn't have to import it directly.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loadTemplateWorkbook(path: string): Promise<ExcelJS.Workbook>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { default as ExcelJS } from 'exceljs';
|
|
2
|
+
import { ExcelMdxBundle } from './extract.js';
|
|
3
|
+
export interface BuildWorkbookInput {
|
|
4
|
+
/** Book name from MDX frontmatter (`xlsx.book`). All MDXs for
|
|
5
|
+
* this book end up in the same workbook. */
|
|
6
|
+
book: string;
|
|
7
|
+
/** All MDX bundles assigned to this book. */
|
|
8
|
+
bundles: ExcelMdxBundle[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Produce an `ExcelJS.Workbook` with the book name set as the
|
|
12
|
+
* workbook title but no sheets yet. PR 2 of Phase 3 fills it
|
|
13
|
+
* with the default layout; PR 3 adds template support.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildEmptyWorkbook(input: BuildWorkbookInput): ExcelJS.Workbook;
|
|
16
|
+
/**
|
|
17
|
+
* Serialise a workbook to PNG bytes. Wrapper around ExcelJS's
|
|
18
|
+
* `xlsx.writeBuffer` that normalises the return type to
|
|
19
|
+
* `Uint8Array` — ExcelJS uses Node `Buffer` natively.
|
|
20
|
+
*/
|
|
21
|
+
export declare function writeWorkbookToBytes(wb: ExcelJS.Workbook): Promise<Uint8Array>;
|
|
22
|
+
/**
|
|
23
|
+
* Group a list of `ExcelMdxBundle`s by their `xlsx.book` value.
|
|
24
|
+
* MDXs without a book fall under the optional `defaultBook`
|
|
25
|
+
* key (typically wired from the project config); MDXs without
|
|
26
|
+
* either get filtered out.
|
|
27
|
+
*/
|
|
28
|
+
export declare function groupBundlesByBook(bundles: ExcelMdxBundle[], defaultBook?: string): Map<string, ExcelMdxBundle[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingcreators/annot-product-docs-xlsx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Excel adapter for `@ingcreators/annot-product-docs`. Walks MDX files with `annot:` frontmatter, fills customer-supplied Excel templates via `{var}` placeholders + named ranges, and emits one `.xlsx` per book. Phase 3 of docs/plans/living-product-docs.md.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "ingcreators",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"mdx",
|
|
23
23
|
"docs",
|
|
24
24
|
"screen-spec",
|
|
25
|
-
"
|
|
25
|
+
"screen-specifications"
|
|
26
26
|
],
|
|
27
27
|
"type": "module",
|
|
28
28
|
"main": "./dist/index.js",
|
|
@@ -47,12 +47,12 @@
|
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"typescript": "^6.0.3",
|
|
50
|
-
"vite": "^
|
|
51
|
-
"vite-plugin-dts": "^5.0.
|
|
50
|
+
"vite": "^8.0.13",
|
|
51
|
+
"vite-plugin-dts": "^5.0.1"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"exceljs": "^4.4.0",
|
|
55
|
-
"@ingcreators/annot-product-docs": "0.
|
|
55
|
+
"@ingcreators/annot-product-docs": "0.2.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "vite build",
|