diff --git a/packages/cli/src/embed.ts b/packages/cli/src/embed.ts
index 3fcd553..5117838 100644
--- a/packages/cli/src/embed.ts
+++ b/packages/cli/src/embed.ts
@@ -2,6 +2,9 @@ import type { LaidOutDiagram } from '@stackmap/core';
/** The block the viewer template ships empty; the viewer reads it back by id. */
export const EMPTY_DATA_BLOCK = '';
+/** The template carries every brand mark as data (~170 KB); a delivered file keeps the ones it draws. */
+export const BRANDS_BLOCK_ID = 'stackmap-brands';
+const BRANDS_OPEN = `', from);
+ const all = JSON.parse(html.slice(from, end)) as Record;
+ const used = new Set(diagram.draft.nodes.flatMap((n) => (n.card.brand ? [n.card.brand] : [])));
+ const kept = Object.fromEntries(Object.entries(all).filter(([slug]) => used.has(slug)));
+ return html.slice(0, from) + toScriptJson(kept) + html.slice(end);
}
diff --git a/packages/cli/src/io.ts b/packages/cli/src/io.ts
index b459d63..80d8fe2 100644
--- a/packages/cli/src/io.ts
+++ b/packages/cli/src/io.ts
@@ -1,5 +1,5 @@
import { randomBytes } from 'node:crypto';
-import { readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
+import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
import { basename, dirname, join } from 'node:path';
export class CliError extends Error {}
@@ -19,6 +19,7 @@ export function readText(path: string): string {
export function writeAtomic(path: string, data: string): void {
const tmp = join(dirname(path), `.${basename(path)}.${randomBytes(4).toString('hex')}.tmp`);
try {
+ mkdirSync(dirname(path), { recursive: true });
writeFileSync(tmp, data);
renameSync(tmp, path);
} catch (e) {
diff --git a/packages/cli/test/commands.test.ts b/packages/cli/test/commands.test.ts
index 02695f4..f170b7a 100644
--- a/packages/cli/test/commands.test.ts
+++ b/packages/cli/test/commands.test.ts
@@ -121,6 +121,13 @@ describe('deliver', () => {
expect(r.stderr).toMatch(/^stackmap: internal error: viewer template must contain/);
});
+ it('creates the folders -o names', async () => {
+ const out = join(dir, 'new', 'deeper', 'd.html');
+ const r = await deliverCommand(file('d.json', commerceApi), { template: TEMPLATE, out });
+ expect(r.code).toBe(0);
+ expect(readFileSync(out, 'utf8')).toContain('stackmap-data');
+ });
+
it('keeps the previous file intact and leaves no temp file when the write fails', async () => {
const input = file('d.json', commerceApi);
const outDir = join(dir, 'out');
diff --git a/packages/cli/test/embed.test.ts b/packages/cli/test/embed.test.ts
index 7112804..635d9fd 100644
--- a/packages/cli/test/embed.test.ts
+++ b/packages/cli/test/embed.test.ts
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest';
import type { LaidOutDiagram } from '@stackmap/core';
import { commerceApi } from '@stackmap/core/samples';
-import { embedDiagram, EMPTY_DATA_BLOCK } from '../src/embed';
+import { BRANDS_BLOCK_ID, embedDiagram, EMPTY_DATA_BLOCK } from '../src/embed';
const template = `stackmap${EMPTY_DATA_BLOCK}`;
const laidOut = (title: string): LaidOutDiagram => ({ draft: { ...commerceApi, title }, nodes: {}, groups: {}, edges: {}, bounds: { width: 0, height: 0 } });
@@ -38,4 +38,14 @@ describe('embedDiagram', () => {
expect(() => embedDiagram(template + EMPTY_DATA_BLOCK, laidOut('T'))).toThrow(/template/);
expect(() => embedDiagram(`${EMPTY_DATA_BLOCK}`, laidOut('T'))).toThrow(//);
});
+
+ it('keeps only the brand marks the diagram uses', () => {
+ const marks = { postgresql: { title: 'PostgreSQL', path: 'M1' }, redis: { title: 'Redis', path: 'M2' }, nginx: { title: 'NGINX', path: 'M3' } };
+ const withBrands = template.replace('