@riebeckite/plugin-shortcodes 0.0.19 → 0.0.20

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
@@ -6,13 +6,15 @@ A generic shortcode system for Markdown, built on `remark-directive`.
6
6
 
7
7
  ## Overview
8
8
 
9
- `shortcodes()` turns `remark-directive` syntax into HTML at Markdown time.
10
- It supports leaf shortcodes (`::name[label]{key=value}`) and container
11
- shortcodes (`:::name[label]{attrs}` … `:::`), and ships an extensible registry
12
- so projects can add their own renderers on top of the built-ins.
9
+ `shortcodes()` turns `remark-directive` syntax into HTML at Markdown time. It
10
+ supports inline shortcodes (`:name[label]{key=value}`), block leaf shortcodes
11
+ (`::name[label]{key=value}`), and container shortcodes
12
+ (`:::name[label]{attrs}` … `:::`), and ships an extensible registry so projects
13
+ can add their own renderers on top of the built-ins.
13
14
 
14
- Output is rendered into `mdast` `html` nodes and wrapped in a `span` (leaf) or
15
- `div` (container) carrying `rb-shortcode rb-shortcode--<name>`.
15
+ Inline shortcodes render as a `span` inside the surrounding paragraph; block and
16
+ container shortcodes render as a `div`. Every wrapper carries
17
+ `rb-shortcode rb-shortcode--<name>`.
16
18
 
17
19
  ## Usage
18
20
 
@@ -31,6 +33,8 @@ Styles ship in `style.css`.
31
33
  ## Syntax
32
34
 
33
35
  ```
36
+ :badge[New]{variant=success}
37
+
34
38
  ::kbd[Ctrl+S]
35
39
 
36
40
  ::youtube[id=dQw4w9WgXcQ]
@@ -40,30 +44,34 @@ Container bodies support **Markdown**.
40
44
  :::
41
45
  ```
42
46
 
43
- - `::name[label]{key=value}` — leaf shortcode, rendered inline.
47
+ - `:name[label]{key=value}` — inline shortcode, rendered as a `span` inside the
48
+ current paragraph. Only names in `inlineShortcodes` can be used inline;
49
+ built-ins opt in with `badge`, `kbd`, `link-card`, and `file`.
50
+ - `::name[label]{key=value}` — block leaf shortcode, rendered as a `div`.
44
51
  - `:::name[label]{key=value}` … `:::` — container shortcode; the body is
45
52
  rendered by the normal Markdown pipeline.
46
53
 
47
- Unknown shortcodes emit a `shortcodes-unknown` diagnostic and stay on the page
48
- as escaped text. Malformed attributes emit `shortcodes-invalid` and are
49
- ignored.
54
+ A shortcode that is only defined as a block emits a
55
+ `shortcodes-inline-unsupported` diagnostic when written with `:` and stays on
56
+ the page as escaped text. Unknown shortcodes emit a `shortcodes-unknown`
57
+ diagnostic, and malformed attributes emit `shortcodes-invalid` and are ignored.
50
58
 
51
59
  ## Built-in shortcodes
52
60
 
53
- | Name | Kind | Attributes | Output |
54
- | ---- | ---- | ---------- | ------ |
55
- | `figure` | leaf / container | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | `<figure>` with image and caption |
56
- | `youtube` | leaf | `id`/`video` or `url`/`src`, `title` | Privacy-friendly `youtube-nocookie.com` embed |
57
- | `vimeo` | leaf | `id`/`video` or `url`/`src`, `title` | `player.vimeo.com` embed (`dnt=1`) |
58
- | `gist` | leaf | `user` + `id`, or `url`, `file` | GitHub Gist embed with `<noscript>` link |
59
- | `kbd` | leaf | label or `keys` | `<kbd>` elements split on `+` |
60
- | `badge` | leaf | label or `text`, `variant`/`type`/`color`, `title` | Inline badge |
61
- | `details` | container | label or `summary`, `open` | `<details>` disclosure |
62
- | `spoiler` | container | label or `summary`, `open` | `details` alias with a different class |
63
- | `note` | leaf / container | label or `title`, `type`/`variant` | Callout box |
64
- | `callout` | leaf / container | label or `title`, `type`/`variant` | `note` alias with a different class |
65
- | `link-card` | leaf | `url`/`href`, `title`, `description`, `image`/`icon` | Link preview card |
66
- | `file` | leaf | `url`/`src`/`path`, `name`/`label`, `size` | Download link |
61
+ | Name | Kind | Inline | Attributes | Output |
62
+ | ---- | ---- | ------ | ---------- | ------ |
63
+ | `figure` | leaf / container | – | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | `<figure>` with image and caption |
64
+ | `youtube` | leaf | – | `id`/`video` or `url`/`src`, `title` | Privacy-friendly `youtube-nocookie.com` embed |
65
+ | `vimeo` | leaf | – | `id`/`video` or `url`/`src`, `title` | `player.vimeo.com` embed (`dnt=1`) |
66
+ | `gist` | leaf | – | `user` + `id`, or `url`, `file` | GitHub Gist embed with `<noscript>` link |
67
+ | `kbd` | leaf | yes | label or `keys` | `<kbd>` elements split on `+` |
68
+ | `badge` | leaf | yes | label or `text`, `variant`/`type`/`color`, `title` | Inline badge |
69
+ | `details` | container | – | label or `summary`, `open` | `<details>` disclosure |
70
+ | `spoiler` | container | – | label or `summary`, `open` | `details` alias with a different class |
71
+ | `note` | leaf / container | – | label or `title`, `type`/`variant` | Callout box |
72
+ | `callout` | leaf / container | – | label or `title`, `type`/`variant` | `note` alias with a different class |
73
+ | `link-card` | leaf | yes | `url`/`href`, `title`, `description`, `image`/`icon` | Link preview card |
74
+ | `file` | leaf | yes | `url`/`src`/`path`, `name`/`label`, `size` | Download link |
67
75
 
68
76
  ## Options
69
77
 
@@ -73,6 +81,7 @@ ignored.
73
81
  | `language` | `string` | – | BCP-47 tag applied to wrappers as `lang` |
74
82
  | `builtins` | `boolean` | `true` | Register the built-in renderers |
75
83
  | `shortcodes` | `Record<string, ShortcodeRenderer>` | `{}` | Custom renderers, merged over the built-ins |
84
+ | `inlineShortcodes` | `readonly string[]` | `[]` | Extra names allowed in the inline `:` form |
76
85
 
77
86
  ## Custom renderers
78
87
 
@@ -83,16 +92,19 @@ shortcodes({
83
92
  shortcodes: {
84
93
  mark: ({ label, attributes }) => `<mark>${label}</mark>`,
85
94
  },
95
+ inlineShortcodes: ["mark"],
86
96
  });
87
97
  ```
88
98
 
89
99
  A `ShortcodeRenderer` receives `{ name, label, attributes, childrenHtml,
90
- context, container }` and returns a string. For container shortcodes the plugin
91
- wraps the result in `<div class="rb-shortcode rb-shortcode--<name>">` and
92
- replaces `childrenHtml` with the rendered body, so container renderers must echo
93
- `input.childrenHtml` where the body belongs. Renderers are synchronous and must
94
- escape any user input themselves — `escapeHtml` and `escapeHtmlAttribute` are
95
- re-exported from `@riebeckite/core`.
100
+ context, container }` and returns a string. A custom renderer can be used with
101
+ the inline `:` form only when its name is listed in `inlineShortcodes`. For
102
+ container shortcodes the plugin wraps the result in
103
+ `<div class="rb-shortcode rb-shortcode--<name>">` and replaces `childrenHtml`
104
+ with the rendered body, so container renderers must echo `input.childrenHtml`
105
+ where the body belongs. Renderers are synchronous and must escape any user
106
+ input themselves — `escapeHtml` and `escapeHtmlAttribute` are re-exported from
107
+ `@riebeckite/core`.
96
108
 
97
109
  ## Exports
98
110
 
@@ -101,11 +113,12 @@ re-exported from `@riebeckite/core`.
101
113
  - `renderShortcode(request, options)` — render a single shortcode
102
114
  - `resolveShortcodeOptions(options?)` — normalize options
103
115
  - `builtinShortcodes`, `builtinShortcodeNames` — the built-in registry
116
+ - `builtinInlineShortcodes` — built-in names allowed in the inline `:` form
104
117
  - Types: `ShortcodeRenderer`, `ShortcodeOptions`, `ResolvedShortcodeOptions`,
105
118
  `ShortcodeRenderInput`, `ShortcodeRenderRequest`, `ShortcodeAttributes`,
106
119
  `RemarkShortcodesOptions`
107
120
 
108
121
  ## See also
109
122
 
110
- - [Plugin guide](../../../docs/docs/reference/plugin-api.en.md)
123
+ - [Plugin guide](../../../docs/docs/reference/plugin-api.md)
111
124
 
package/README_ja.md CHANGED
@@ -7,12 +7,13 @@
7
7
  ## 概要
8
8
 
9
9
  `shortcodes()` は `remark-directive` の記法を Markdown 処理時に HTML へ変換
10
- します。リーフ型(`::name[label]{key=value}`)とコンテナ型
11
- (`:::name[label]{attrs}` … `:::`)の両方に対応し、組み込みレンダラーに
12
- 独自のレンダラーを重ねられる拡張可能なレジストリを備えます。
10
+ します。インライン型(`:name[label]{key=value}`)、ブロックのリーフ型
11
+ (`::name[label]{key=value}`)、コンテナ型(`:::name[label]{attrs}` … `:::`)
12
+ に対応し、組み込みレンダラーに独自のレンダラーを重ねられる拡張可能な
13
+ レジストリを備えます。
13
14
 
14
- 出力は `mdast` の `html` ノードとして書き出され、リーフは `span`、コンテナは
15
- `div` で `rb-shortcode rb-shortcode--<name>` を付けてラップされます。
15
+ インライン型は段落の中の `span` として、リーフ型とコンテナ型は `div` として
16
+ 描画されます。ラッパーには `rb-shortcode rb-shortcode--<name>` が付きます。
16
17
 
17
18
  ## 使い方
18
19
 
@@ -31,6 +32,8 @@ export default defineConfig({
31
32
  ## 記法
32
33
 
33
34
  ```
35
+ :badge[新着]{variant=success}
36
+
34
37
  ::kbd[Ctrl+S]
35
38
 
36
39
  ::youtube[id=dQw4w9WgXcQ]
@@ -40,30 +43,34 @@ export default defineConfig({
40
43
  :::
41
44
  ```
42
45
 
43
- - `::name[label]{key=value}` — リーフ型。インラインで描画されます。
46
+ - `:name[label]{key=value}` — インライン型。現在の段落の中の `span` として
47
+ 描画されます。インラインで使えるのは `inlineShortcodes` に含まれる名前だけ
48
+ で、組み込みでは `badge`、`kbd`、`link-card`、`file` が該当します。
49
+ - `::name[label]{key=value}` — ブロックのリーフ型。`div` として描画されます。
44
50
  - `:::name[label]{key=value}` … `:::` — コンテナ型。本文は通常の Markdown
45
51
  パイプラインで描画されます。
46
52
 
47
- 未知のショートコードは `shortcodes-unknown` 診断を出し、エスケープした
48
- テキストとしてページに残します。不正な属性は `shortcodes-invalid` を出して
49
- 無視します。
53
+ ブロック専用のショートコードを `:` で書くと
54
+ `shortcodes-inline-unsupported` 診断を出し、エスケープしたテキストとして
55
+ ページに残します。未知のショートコードは `shortcodes-unknown` 診断を出し、
56
+ 不正な属性は `shortcodes-invalid` を出して無視します。
50
57
 
51
58
  ## 組み込みショートコード
52
59
 
53
- | 名前 | 種類 | 属性 | 出力 |
54
- | ---- | ---- | ---- | ---- |
55
- | `figure` | リーフ / コンテナ | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | 画像とキャプションの `<figure>` |
56
- | `youtube` | リーフ | `id`/`video` または `url`/`src`, `title` | `youtube-nocookie.com` のプライバシー配慮埋め込み |
57
- | `vimeo` | リーフ | `id`/`video` または `url`/`src`, `title` | `player.vimeo.com` 埋め込み(`dnt=1`) |
58
- | `gist` | リーフ | `user` + `id`、または `url`, `file` | `<noscript>` リンク付きの Gist 埋め込み |
59
- | `kbd` | リーフ | ラベルまたは `keys` | `+` で分割した `<kbd>` |
60
- | `badge` | リーフ | ラベルまたは `text`, `variant`/`type`/`color`, `title` | インラインバッジ |
61
- | `details` | コンテナ | ラベルまたは `summary`, `open` | `<details>` 折りたたみ |
62
- | `spoiler` | コンテナ | ラベルまたは `summary`, `open` | クラス違いの `details` エイリアス |
63
- | `note` | リーフ / コンテナ | ラベルまたは `title`, `type`/`variant` | コールアウト |
64
- | `callout` | リーフ / コンテナ | ラベルまたは `title`, `type`/`variant` | クラス違いの `note` エイリアス |
65
- | `link-card` | リーフ | `url`/`href`, `title`, `description`, `image`/`icon` | リンクプレビューカード |
66
- | `file` | リーフ | `url`/`src`/`path`, `name`/`label`, `size` | ダウンロードリンク |
60
+ | 名前 | 種類 | インライン | 属性 | 出力 |
61
+ | ---- | ---- | ---------- | ---- | ---- |
62
+ | `figure` | リーフ / コンテナ | – | `src`/`url`/`image`, `alt`, `caption`, `width`, `height` | 画像とキャプションの `<figure>` |
63
+ | `youtube` | リーフ | – | `id`/`video` または `url`/`src`, `title` | `youtube-nocookie.com` のプライバシー配慮埋め込み |
64
+ | `vimeo` | リーフ | – | `id`/`video` または `url`/`src`, `title` | `player.vimeo.com` 埋め込み(`dnt=1`) |
65
+ | `gist` | リーフ | – | `user` + `id`、または `url`, `file` | `<noscript>` リンク付きの Gist 埋め込み |
66
+ | `kbd` | リーフ | 可 | ラベルまたは `keys` | `+` で分割した `<kbd>` |
67
+ | `badge` | リーフ | 可 | ラベルまたは `text`, `variant`/`type`/`color`, `title` | インラインバッジ |
68
+ | `details` | コンテナ | – | ラベルまたは `summary`, `open` | `<details>` 折りたたみ |
69
+ | `spoiler` | コンテナ | – | ラベルまたは `summary`, `open` | クラス違いの `details` エイリアス |
70
+ | `note` | リーフ / コンテナ | – | ラベルまたは `title`, `type`/`variant` | コールアウト |
71
+ | `callout` | リーフ / コンテナ | – | ラベルまたは `title`, `type`/`variant` | クラス違いの `note` エイリアス |
72
+ | `link-card` | リーフ | 可 | `url`/`href`, `title`, `description`, `image`/`icon` | リンクプレビューカード |
73
+ | `file` | リーフ | 可 | `url`/`src`/`path`, `name`/`label`, `size` | ダウンロードリンク |
67
74
 
68
75
  ## オプション
69
76
 
@@ -73,6 +80,7 @@ export default defineConfig({
73
80
  | `language` | `string` | – | ラッパーの `lang` に付与する BCP-47 タグ |
74
81
  | `builtins` | `boolean` | `true` | 組み込みレンダラーを登録する |
75
82
  | `shortcodes` | `Record<string, ShortcodeRenderer>` | `{}` | 組み込みに重ねる独自レンダラー |
83
+ | `inlineShortcodes` | `readonly string[]` | `[]` | インラインの `:` 形式で使える名前に追加する |
76
84
 
77
85
  ## 独自レンダラー
78
86
 
@@ -83,12 +91,14 @@ shortcodes({
83
91
  shortcodes: {
84
92
  mark: ({ label, attributes }) => `<mark>${label}</mark>`,
85
93
  },
94
+ inlineShortcodes: ["mark"],
86
95
  });
87
96
  ```
88
97
 
89
98
  `ShortcodeRenderer` は `{ name, label, attributes, childrenHtml, context,
90
- container }` を受け取り、文字列を返します。コンテナ型では結果が
91
- `<div class="rb-shortcode rb-shortcode--<name>">` でラップされ、
99
+ container }` を受け取り、文字列を返します。独自レンダラーをインラインの `:`
100
+ 形式で使うには、その名前を `inlineShortcodes` に追加します。コンテナ型では
101
+ 結果が `<div class="rb-shortcode rb-shortcode--<name>">` でラップされ、
92
102
  `childrenHtml` の位置に本文が差し込まれます。コンテナ用レンダラーは本文を
93
103
  置きたい位置で `input.childrenHtml` をそのまま出力してください。レンダラーは
94
104
  同期関数で、ユーザー入力のエスケープは各自で行う必要があります
@@ -102,11 +112,12 @@ container }` を受け取り、文字列を返します。コンテナ型では
102
112
  - `renderShortcode(request, options)` — 単一ショートコードの描画
103
113
  - `resolveShortcodeOptions(options?)` — オプションの正規化
104
114
  - `builtinShortcodes`, `builtinShortcodeNames` — 組み込みレジストリ
115
+ - `builtinInlineShortcodes` — インラインの `:` 形式で使える組み込み名
105
116
  - 型: `ShortcodeRenderer`, `ShortcodeOptions`, `ResolvedShortcodeOptions`,
106
117
  `ShortcodeRenderInput`, `ShortcodeRenderRequest`, `ShortcodeAttributes`,
107
118
  `RemarkShortcodesOptions`
108
119
 
109
120
  ## 関連
110
121
 
111
- - [プラグインガイド](../../../docs/docs/reference/plugin-api.md)
122
+ - [プラグインガイド](../../../docs/docs/reference/plugin-api.ja.md)
112
123
 
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { builtinShortcodeNames, builtinShortcodes } from "./src/builtins.js";
2
- import { DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, SHORTCODES_SOURCE } from "./src/remark.js";
3
- import { createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER } from "./src/render.js";
1
+ import { builtinInlineShortcodes, builtinShortcodeNames, builtinShortcodes } from "./src/builtins.js";
2
+ import { DIAGNOSTIC_INLINE_UNSUPPORTED, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, SHORTCODES_SOURCE } from "./src/remark.js";
3
+ import { createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, isInlineShortcode, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER } from "./src/render.js";
4
4
  import type { ShortcodeOptions } from "./src/types.js";
5
5
  export type { DirectiveNode, RemarkShortcodesOptions, ResolvedShortcodeOptions, ShortcodeAttributes, ShortcodeOptions, ShortcodeRenderer, ShortcodeRenderInput, ShortcodeRenderRequest, } from "./src/types.js";
6
- export { builtinShortcodeNames, builtinShortcodes, createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, remarkShortcodes, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER, SHORTCODES_SOURCE, };
6
+ export { builtinInlineShortcodes, builtinShortcodeNames, builtinShortcodes, createShortcodeRenderContext, DEFAULT_SHORTCODE_CLASS_NAME, DIAGNOSTIC_INLINE_UNSUPPORTED, DIAGNOSTIC_INVALID, DIAGNOSTIC_UNKNOWN, isInlineShortcode, remarkShortcodes, renderShortcode, resolveShortcodeOptions, SHORTCODE_CHILDREN_MARKER, SHORTCODES_SOURCE, };
7
7
  export declare function shortcodes(options?: ShortcodeOptions): import("@riebeckite/core").RiebeckitePlugin<ShortcodeOptions>;
8
8
  export declare const shortcodesPlugin: typeof shortcodes;
package/dist/index.js CHANGED
@@ -206,6 +206,12 @@ var builtinShortcodes = {
206
206
  "link-card": renderLinkCard,
207
207
  file: renderFile
208
208
  };
209
+ var builtinInlineShortcodes = [
210
+ "badge",
211
+ "kbd",
212
+ "link-card",
213
+ "file"
214
+ ];
209
215
  var builtinShortcodeNames = Object.keys(builtinShortcodes);
210
216
 
211
217
  // src/remark.ts
@@ -236,14 +242,24 @@ function resolveShortcodeOptions(options = {}) {
236
242
  const builtins = options.builtins !== false;
237
243
  const custom = options.shortcodes ?? {};
238
244
  const shortcodes2 = builtins ? { ...builtinShortcodes, ...custom } : { ...custom };
245
+ const inlineShortcodes = new Set(
246
+ builtins ? builtinInlineShortcodes : []
247
+ );
248
+ for (const name of options.inlineShortcodes ?? []) {
249
+ inlineShortcodes.add(name);
250
+ }
239
251
  const resolved = {
240
252
  className: options.className ?? DEFAULT_SHORTCODE_CLASS_NAME,
241
253
  builtins,
242
- shortcodes: shortcodes2
254
+ shortcodes: shortcodes2,
255
+ inlineShortcodes
243
256
  };
244
257
  if (options.language !== void 0) resolved.language = options.language;
245
258
  return resolved;
246
259
  }
260
+ function isInlineShortcode(name, options) {
261
+ return options.inlineShortcodes.has(name);
262
+ }
247
263
  function createShortcodeRenderContext(input) {
248
264
  return {
249
265
  contentIndex: input.contentIndex ?? /* @__PURE__ */ new Map(),
@@ -272,6 +288,7 @@ function renderShortcode(request, options) {
272
288
  url: firstUrl(attributes)
273
289
  });
274
290
  const container = request.container ?? false;
291
+ const block = request.block ?? container;
275
292
  const input = {
276
293
  name: request.name,
277
294
  label: request.label ?? "",
@@ -283,10 +300,10 @@ function renderShortcode(request, options) {
283
300
  input.childrenHtml = request.childrenHtml;
284
301
  }
285
302
  const body = renderer(input);
286
- return wrapShortcode(body, request.name, container, options);
303
+ return wrapShortcode(body, request.name, block, options);
287
304
  }
288
- function wrapShortcode(body, name, container, options) {
289
- const tag = container ? "div" : "span";
305
+ function wrapShortcode(body, name, block, options) {
306
+ const tag = block ? "div" : "span";
290
307
  const classes = `${options.className} ${options.className}--${name}`;
291
308
  const languageAttribute = options.language === void 0 ? "" : ` lang="${escapeHtmlAttribute2(options.language)}"`;
292
309
  return `<${tag} class="${escapeHtmlAttribute2(
@@ -305,6 +322,7 @@ function firstUrl(attributes) {
305
322
  var SHORTCODES_SOURCE = "@riebeckite/plugin-shortcodes";
306
323
  var DIAGNOSTIC_UNKNOWN = "shortcodes-unknown";
307
324
  var DIAGNOSTIC_INVALID = "shortcodes-invalid";
325
+ var DIAGNOSTIC_INLINE_UNSUPPORTED = "shortcodes-inline-unsupported";
308
326
  var ATTRIBUTE_NAME_PATTERN = /^[A-Za-z_][\w-]*$/;
309
327
  function remarkShortcodes(options = {}) {
310
328
  const resolved = resolveShortcodeOptions(options);
@@ -312,7 +330,7 @@ function remarkShortcodes(options = {}) {
312
330
  const targets = [];
313
331
  visit(
314
332
  tree,
315
- ["containerDirective", "leafDirective"],
333
+ ["containerDirective", "leafDirective", "textDirective"],
316
334
  (node, index, parent) => {
317
335
  if (parent === void 0 || index === void 0) return;
318
336
  targets.push({ node, index, parent });
@@ -326,10 +344,37 @@ function remarkShortcodes(options = {}) {
326
344
  function transformDirective(target, resolved, options, file) {
327
345
  const { node, index, parent } = target;
328
346
  const name = node.name;
347
+ const inline = node.type === "textDirective";
329
348
  const container = node.type === "containerDirective";
330
349
  const { label, children } = extractLabel(node);
331
350
  const attributes = normalizeAttributes(node, file);
332
351
  const childrenHtml = container ? SHORTCODE_CHILDREN_MARKER : void 0;
352
+ if (inline && !isInlineShortcode(name, resolved)) {
353
+ const known = Object.hasOwn(resolved.shortcodes, name);
354
+ report(
355
+ file,
356
+ known ? DIAGNOSTIC_INLINE_UNSUPPORTED : DIAGNOSTIC_UNKNOWN,
357
+ known ? `Shortcode ":${name}" is block-only and cannot be used inline.` : `Unknown shortcode ":${name}" was left as escaped text.`,
358
+ node
359
+ );
360
+ parent.children.splice(
361
+ index,
362
+ 1,
363
+ ...createFallback(
364
+ resolved.className,
365
+ name,
366
+ label,
367
+ attributes,
368
+ {
369
+ container: false,
370
+ block: false,
371
+ modifier: known ? "inline-unsupported" : "unknown"
372
+ },
373
+ children
374
+ )
375
+ );
376
+ return;
377
+ }
333
378
  const html = renderShortcode(
334
379
  {
335
380
  name,
@@ -337,6 +382,7 @@ function transformDirective(target, resolved, options, file) {
337
382
  attributes,
338
383
  childrenHtml,
339
384
  container,
385
+ block: !inline,
340
386
  context: createShortcodeRenderContext({
341
387
  name,
342
388
  label,
@@ -363,7 +409,7 @@ function transformDirective(target, resolved, options, file) {
363
409
  name,
364
410
  label,
365
411
  attributes,
366
- container,
412
+ { container, block: !inline, modifier: "unknown" },
367
413
  children
368
414
  )
369
415
  );
@@ -431,11 +477,14 @@ function normalizeAttributes(node, file) {
431
477
  }
432
478
  return attributes;
433
479
  }
434
- function createFallback(className, name, label, attributes, container, children) {
435
- const classes = `${className} ${className}--unknown`;
480
+ function createFallback(className, name, label, attributes, shape, children) {
481
+ const classes = `${className} ${className}--${shape.modifier}`;
436
482
  const dataName = ` data-shortcode-name="${escapeHtmlAttribute3(name)}"`;
437
- const directive = escapeHtml2(sourceText(name, label, attributes, container));
438
- if (container) {
483
+ const inline = !shape.container && !shape.block;
484
+ const directive = escapeHtml2(
485
+ sourceText(name, label, attributes, shape.container, inline)
486
+ );
487
+ if (shape.container) {
439
488
  return [
440
489
  createHtml(
441
490
  `<div class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}`
@@ -444,14 +493,15 @@ function createFallback(className, name, label, attributes, container, children)
444
493
  createHtml("</div>")
445
494
  ];
446
495
  }
496
+ const tag = shape.block ? "div" : "span";
447
497
  return [
448
498
  createHtml(
449
- `<span class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}</span>`
499
+ `<${tag} class="${escapeHtmlAttribute3(classes)}"${dataName}>${directive}</${tag}>`
450
500
  )
451
501
  ];
452
502
  }
453
- function sourceText(name, label, attributes, container) {
454
- const fence = container ? ":::" : "::";
503
+ function sourceText(name, label, attributes, container, inline) {
504
+ const fence = container ? ":::" : inline ? ":" : "::";
455
505
  const labelPart = label ? `[${label}]` : "";
456
506
  return `${fence}${name}${labelPart}${attributesText(attributes)}`;
457
507
  }
@@ -503,13 +553,16 @@ function shortcodes(options = {}) {
503
553
  var shortcodesPlugin = shortcodes;
504
554
  export {
505
555
  DEFAULT_SHORTCODE_CLASS_NAME,
556
+ DIAGNOSTIC_INLINE_UNSUPPORTED,
506
557
  DIAGNOSTIC_INVALID,
507
558
  DIAGNOSTIC_UNKNOWN,
508
559
  SHORTCODES_SOURCE,
509
560
  SHORTCODE_CHILDREN_MARKER,
561
+ builtinInlineShortcodes,
510
562
  builtinShortcodeNames,
511
563
  builtinShortcodes,
512
564
  createShortcodeRenderContext,
565
+ isInlineShortcode,
513
566
  remarkShortcodes,
514
567
  renderShortcode,
515
568
  resolveShortcodeOptions,
@@ -1,4 +1,5 @@
1
1
  import type { ShortcodeRenderInput } from "./types.js";
2
2
  /** Built-in renderers keyed by directive name. */
3
3
  export declare const builtinShortcodes: Record<string, (input: ShortcodeRenderInput) => string>;
4
+ export declare const builtinInlineShortcodes: readonly string[];
4
5
  export declare const builtinShortcodeNames: readonly string[];
@@ -3,4 +3,5 @@ import type { RemarkShortcodesOptions } from "./types.js";
3
3
  export declare const SHORTCODES_SOURCE = "@riebeckite/plugin-shortcodes";
4
4
  export declare const DIAGNOSTIC_UNKNOWN = "shortcodes-unknown";
5
5
  export declare const DIAGNOSTIC_INVALID = "shortcodes-invalid";
6
+ export declare const DIAGNOSTIC_INLINE_UNSUPPORTED = "shortcodes-inline-unsupported";
6
7
  export declare function remarkShortcodes(options?: RemarkShortcodesOptions): (tree: Root, file: unknown) => void;
@@ -8,6 +8,7 @@ export declare const DEFAULT_SHORTCODE_CLASS_NAME = "rb-shortcode";
8
8
  */
9
9
  export declare const SHORTCODE_CHILDREN_MARKER = "\0rb-shortcodes:children\0";
10
10
  export declare function resolveShortcodeOptions(options?: ShortcodeOptions): ResolvedShortcodeOptions;
11
+ export declare function isInlineShortcode(name: string, options: ResolvedShortcodeOptions): boolean;
11
12
  export declare function createShortcodeRenderContext(input: {
12
13
  name: string;
13
14
  label?: string;
@@ -31,12 +31,14 @@ export type ShortcodeOptions = {
31
31
  builtins?: boolean;
32
32
  /** Custom renderers, merged on top of the built-ins by name. */
33
33
  shortcodes?: Record<string, ShortcodeRenderer>;
34
+ inlineShortcodes?: readonly string[];
34
35
  };
35
36
  export type ResolvedShortcodeOptions = {
36
37
  className: string;
37
38
  language?: string;
38
39
  builtins: boolean;
39
40
  shortcodes: Record<string, ShortcodeRenderer>;
41
+ inlineShortcodes: ReadonlySet<string>;
40
42
  };
41
43
  /** Input accepted by {@link renderShortcode}. */
42
44
  export type ShortcodeRenderRequest = {
@@ -45,6 +47,7 @@ export type ShortcodeRenderRequest = {
45
47
  attributes?: ShortcodeAttributes;
46
48
  childrenHtml?: string;
47
49
  container?: boolean;
50
+ block?: boolean;
48
51
  context?: PluginRenderContext;
49
52
  };
50
53
  /**
@@ -56,7 +59,7 @@ export type RemarkShortcodesOptions = ShortcodeOptions & {
56
59
  contentIndex?: Map<string, string>;
57
60
  contentSource?: ContentSource;
58
61
  };
59
- export type DirectiveName = "leafDirective" | "containerDirective";
62
+ export type DirectiveName = "textDirective" | "leafDirective" | "containerDirective";
60
63
  export type DirectiveNode = Parent & {
61
64
  type: DirectiveName;
62
65
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riebeckite/plugin-shortcodes",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -17,11 +17,11 @@
17
17
  "dependencies": {
18
18
  "@types/mdast": "^4.0.4",
19
19
  "unist-util-visit": "^5.1.0",
20
- "@riebeckite/core": "0.0.19"
20
+ "@riebeckite/core": "0.0.20"
21
21
  },
22
22
  "devDependencies": {
23
23
  "tsx": "^4.22.4",
24
- "@riebeckite/test": "0.0.19"
24
+ "@riebeckite/test": "0.0.20"
25
25
  },
26
26
  "publishConfig": {
27
27
  "access": "public"
package/style.css CHANGED
@@ -245,7 +245,8 @@ div.rb-shortcode {
245
245
 
246
246
  /* fallback ---------------------------------------------------------------- */
247
247
 
248
- .rb-shortcode--unknown {
248
+ .rb-shortcode--unknown,
249
+ .rb-shortcode--inline-unsupported {
249
250
  color: var(--rb-shortcode-accent);
250
251
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
251
252
  font-size: 0.875em;