scoobie 17.0.0 → 17.1.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/README.md CHANGED
@@ -194,6 +194,24 @@ sequenceDiagram
194
194
  ```
195
195
  ````
196
196
 
197
+ Some Mermaid diagrams allow for YAML frontmatter. Scoobie has extended this by allowing a deep merge of the Mermaid configuration
198
+ with a special `overrides` key in the frontmatter. This could be useful for diagram-specific configuration that is not otherwise easy to set.
199
+
200
+ ````markdown
201
+ ```mermaid
202
+ ---
203
+ overrides:
204
+ gantt:
205
+ useWidth: 500
206
+ ---
207
+ gantt
208
+ title A Gantt Diagram
209
+ dateFormat YYYY-MM-DD
210
+ section Section
211
+ A task :a1, 2014-01-01, 30d
212
+ ```
213
+ ````
214
+
197
215
  [mermaid live editor]: https://mermaidjs.github.io/mermaid-live-editor
198
216
 
199
217
  ### Headings
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "sideEffects": false,
7
- "version": "17.0.0",
7
+ "version": "17.1.0",
8
8
  "dependencies": {
9
9
  "@capsizecss/core": "^4.0.0",
10
10
  "@mdx-js/react": "^1.6.22",
@@ -13,6 +13,7 @@
13
13
  "@vanilla-extract/css-utils": "^0.1.1",
14
14
  "babel-loader": "^9.0.0",
15
15
  "clsx": "^2.0.0",
16
+ "deepmerge-ts": "^7.1.0",
16
17
  "find-up": "^5.0.0",
17
18
  "fs-extra": "^11.0.0",
18
19
  "jsonc-parser": "^3.0.0",
@@ -26,7 +27,8 @@
26
27
  "unist-util-visit": "^2.0.3",
27
28
  "unist-util-visit-parents": "^3.1.1",
28
29
  "webpack-merge": "^6.0.0",
29
- "which": "^4.0.0"
30
+ "which": "^4.0.0",
31
+ "yaml": "^2.5.0"
30
32
  },
31
33
  "devDependencies": {
32
34
  "@changesets/cli": "2.27.7",
@@ -4,9 +4,11 @@
4
4
  const crypto = require('crypto');
5
5
  const path = require('path');
6
6
 
7
+ const { deepmerge } = require('deepmerge-ts');
7
8
  const fs = require('fs-extra');
8
9
  const mermaidIsomorphic = require('mermaid-isomorphic');
9
10
  const { optimize } = require('svgo');
11
+ const YAML = require('yaml');
10
12
 
11
13
  const { MERMAID_DIR, PLUGIN_NAME } = require('./constants');
12
14
 
@@ -70,16 +72,28 @@ const generateFilePaths = (rootDir, data) => {
70
72
  function createMermaidRenderer(options) {
71
73
  const renderer = mermaidIsomorphic.createMermaidRenderer(options);
72
74
 
73
- return async (nodes) => {
75
+ async function renderNodes(nodes) {
74
76
  const mapped = nodes.map((node) => {
75
77
  const paths = generateFilePaths(options.rootDir, node.value);
78
+ const frontmatter = node.value.startsWith('---')
79
+ ? YAML.parse(node.value.match(/---([\s\S]*?)---/)[1]) ?? {}
80
+ : {};
81
+
76
82
  return {
77
83
  ...paths,
78
84
  value: node.value,
85
+ frontmatter,
79
86
  exists: fs.existsSync(paths.svgPath),
80
87
  };
81
88
  });
82
89
 
90
+ if (mapped.length > 1 && mapped.some((n) => n.frontmatter.overrides)) {
91
+ // If any node has an override, render all nodes separately because we pass config globally
92
+ return await Promise.all(
93
+ nodes.map(async (node) => (await renderNodes([node]))[0]),
94
+ );
95
+ }
96
+
83
97
  if (mapped.every(({ exists }) => exists)) {
84
98
  return mapped.map(({ svgNodeUrl }) => ({
85
99
  status: 'fulfilled',
@@ -93,7 +107,13 @@ function createMermaidRenderer(options) {
93
107
 
94
108
  const results = await renderer(
95
109
  toRender.map(({ value }) => value),
96
- { ...options, mermaidConfig },
110
+ {
111
+ ...options,
112
+ mermaidConfig: deepmerge(
113
+ mermaidConfig,
114
+ ...toRender.map(({ frontmatter }) => frontmatter.overrides),
115
+ ),
116
+ },
97
117
  );
98
118
 
99
119
  const errorResults = new Map();
@@ -120,12 +140,13 @@ function createMermaidRenderer(options) {
120
140
  }
121
141
  });
122
142
 
123
- return mapped.map(({ svgNodeUrl }, i) =>
124
- errorResults.has(i)
125
- ? errorResults.get(i)
126
- : { status: 'fulfilled', value: { svgNodeUrl } },
143
+ return mapped.map(
144
+ ({ svgNodeUrl }, i) =>
145
+ errorResults.get(i) ?? { status: 'fulfilled', value: { svgNodeUrl } },
127
146
  );
128
- };
147
+ }
148
+
149
+ return renderNodes;
129
150
  }
130
151
 
131
152
  module.exports = {
@@ -1,7 +1,7 @@
1
1
  import { Box, Stack, Text, TextLinkButton } from 'braid-design-system';
2
2
  import { parse } from 'jsonc-parser';
3
3
  import { Highlight } from 'prism-react-renderer';
4
- import React, { useState } from 'react';
4
+ import React, { useEffect, useState } from 'react';
5
5
 
6
6
  import { Prism, themes } from '../private/Prism';
7
7
  import { ScrollableInline } from '../private/ScrollableInline';
@@ -23,6 +23,7 @@ import * as styles from './CodeBlock.css';
23
23
  interface Props {
24
24
  children: readonly CodeChildProps[] | string;
25
25
  graphqlPlayground?: string;
26
+ initialIndex?: number;
26
27
  label?: string;
27
28
  language?: string;
28
29
  size?: Size;
@@ -31,9 +32,10 @@ interface Props {
31
32
 
32
33
  export const CodeBlock = ({
33
34
  children: rawChildren,
35
+ graphqlPlayground,
36
+ initialIndex = 0,
34
37
  label: rawLabel,
35
38
  language: rawLanguage,
36
- graphqlPlayground,
37
39
  size = DEFAULT_SIZE,
38
40
  trim = true,
39
41
  }: Props) => {
@@ -53,9 +55,14 @@ export const CodeBlock = ({
53
55
  const smallerSize = SIZE_TO_SMALLER[size];
54
56
  const tablePadding = SIZE_TO_TABLE_PADDING[size];
55
57
 
56
- const [index, setIndex] = useState(0);
58
+ const [index, setIndex] = useState({ dirty: false, value: initialIndex });
59
+
60
+ useEffect(
61
+ () => setIndex((i) => (i.dirty ? i : { ...i, value: initialIndex })),
62
+ [initialIndex],
63
+ );
57
64
 
58
- const child = children[index];
65
+ const child = children[index.value] ?? children[0];
59
66
 
60
67
  const jsoncVariables =
61
68
  children[0].language === 'graphql' && children[1]?.label === 'Variables'
@@ -90,13 +97,17 @@ export const CodeBlock = ({
90
97
  >
91
98
  <Text
92
99
  size={smallerSize}
93
- tone={index === labelIndex ? 'secondary' : undefined}
100
+ tone={index.value === labelIndex ? 'secondary' : undefined}
94
101
  weight="medium"
95
102
  >
96
- {children.length === 1 || index === labelIndex ? (
103
+ {children.length === 1 || index.value === labelIndex ? (
97
104
  label
98
105
  ) : (
99
- <TextLinkButton onClick={() => setIndex(labelIndex)}>
106
+ <TextLinkButton
107
+ onClick={() =>
108
+ setIndex({ dirty: true, value: labelIndex })
109
+ }
110
+ >
100
111
  {label}
101
112
  </TextLinkButton>
102
113
  )}
@@ -6,7 +6,13 @@ import type { CodeChildProps } from '../components/CodeBlock/CodeChild';
6
6
  import { useGraphQLPlayground } from './hooks/graphqlPlayground';
7
7
  import type { Size } from './size';
8
8
 
9
- const toLabel = (meta?: string) => meta?.match(/label="([^"]+)"/)?.[1];
9
+ const LABEL_REGEX = /label="([^"]+)"/;
10
+
11
+ const toDefault = (meta?: string): boolean =>
12
+ Boolean(meta?.replace(LABEL_REGEX, '').match(/(^|\s)default(\s|$)/));
13
+
14
+ const toLabel = (meta?: string): string | undefined =>
15
+ meta?.match(LABEL_REGEX)?.[1];
10
16
 
11
17
  /**
12
18
  * MDAST node as passed through by the `mergeCodeBlocks` Remark plugin.
@@ -39,8 +45,18 @@ export const createMdxCodeBlock =
39
45
  if (className === 'language-scoobie-merged-code' && metastring) {
40
46
  const data = JSON.parse(metastring) as MdastCode[];
41
47
 
48
+ const firstDefaultIndex = data.findIndex((child) =>
49
+ toDefault(child.meta),
50
+ );
51
+
52
+ const initialIndex = firstDefaultIndex === -1 ? 0 : firstDefaultIndex;
53
+
42
54
  return (
43
- <CodeBlock graphqlPlayground={graphqlPlayground} size={size}>
55
+ <CodeBlock
56
+ graphqlPlayground={graphqlPlayground}
57
+ initialIndex={initialIndex}
58
+ size={size}
59
+ >
44
60
  {data.map(toCodeChildProps)}
45
61
  </CodeBlock>
46
62
  );