@nuasite/cms 0.44.2 → 0.45.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/dist/editor.js +1 -1
- package/package.json +9 -7
- package/src/index.ts +11 -0
- package/src/remark-list-directive.ts +121 -0
package/dist/editor.js
CHANGED
|
@@ -386,7 +386,7 @@ function fC(t, e) {
|
|
|
386
386
|
function mC(t, e) {
|
|
387
387
|
return typeof e == "function" ? e(t) : e;
|
|
388
388
|
}
|
|
389
|
-
const W3 = "0.
|
|
389
|
+
const W3 = "0.45.0", K3 = W3, ct = {
|
|
390
390
|
/** Highlight overlay for hovered elements */
|
|
391
391
|
HIGHLIGHT: 2147483644,
|
|
392
392
|
/** Hover outline for elements/components */
|
package/package.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"directory": "packages/astro-cms"
|
|
15
15
|
},
|
|
16
16
|
"license": "Apache-2.0",
|
|
17
|
-
"version": "0.
|
|
17
|
+
"version": "0.45.0",
|
|
18
18
|
"module": "src/index.ts",
|
|
19
19
|
"types": "src/index.ts",
|
|
20
20
|
"type": "module",
|
|
@@ -26,16 +26,17 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@nuasite/cms-core": "0.
|
|
30
|
-
"@nuasite/cms-types": "0.
|
|
29
|
+
"@nuasite/cms-core": "0.45.0",
|
|
30
|
+
"@nuasite/cms-types": "0.45.0",
|
|
31
31
|
"@astrojs/compiler": "^3.0.1",
|
|
32
32
|
"@babel/parser": "^7.29.2",
|
|
33
33
|
"node-html-parser": "^7.1.0",
|
|
34
|
+
"remark-directive": "^3.0.1",
|
|
34
35
|
"yaml": "^2.8.3"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"@nuasite/cms-sidecar": "0.
|
|
38
|
-
"@nuasite/collections-admin": "0.
|
|
38
|
+
"@nuasite/cms-sidecar": "0.45.0",
|
|
39
|
+
"@nuasite/collections-admin": "0.45.0",
|
|
39
40
|
"@babel/types": "^7.29.0",
|
|
40
41
|
"@types/react": "^19.2.7",
|
|
41
42
|
"@types/react-dom": "^19.2.3",
|
|
@@ -50,6 +51,7 @@
|
|
|
50
51
|
"@milkdown/transformer": "^7.20.0",
|
|
51
52
|
"@milkdown/utils": "^7.20.0",
|
|
52
53
|
"remark-mdx": "^3.1.0",
|
|
54
|
+
"unified": "^11.0.5",
|
|
53
55
|
"@preact/signals": "^2.9.0",
|
|
54
56
|
"@tailwindcss/vite": "^4.2.2",
|
|
55
57
|
"@types/bun": "1.3.11",
|
|
@@ -72,8 +74,8 @@
|
|
|
72
74
|
"typescript": "^6.0.2",
|
|
73
75
|
"vite": "^7.0.0",
|
|
74
76
|
"@aws-sdk/client-s3": "^3.0.0",
|
|
75
|
-
"@nuasite/cms-sidecar": "0.
|
|
76
|
-
"@nuasite/collections-admin": "0.
|
|
77
|
+
"@nuasite/cms-sidecar": "0.45.0",
|
|
78
|
+
"@nuasite/collections-admin": "0.45.0",
|
|
77
79
|
"react": "^19.0.0",
|
|
78
80
|
"react-dom": "^19.0.0"
|
|
79
81
|
},
|
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname, join } from 'node:path'
|
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
|
|
7
7
|
import { createLocalStorageAdapter } from '@nuasite/cms-core'
|
|
8
|
+
import remarkDirective from 'remark-directive'
|
|
8
9
|
import { processBuildOutput } from './build-processor'
|
|
9
10
|
import { scanCollections } from './collection-scanner'
|
|
10
11
|
import { ComponentRegistry } from './component-registry'
|
|
@@ -16,10 +17,20 @@ import { ManifestWriter } from './manifest-writer'
|
|
|
16
17
|
import type { MediaStorageAdapter } from './media/types'
|
|
17
18
|
import { type CmsMode, resolveCmsMode } from './mode'
|
|
18
19
|
import { rehypeCmsMarker } from './rehype-cms-marker'
|
|
20
|
+
import { remarkListDirective } from './remark-list-directive'
|
|
19
21
|
import type { CmsFeatures, CmsMarkerOptions, ComponentDefinition } from './types'
|
|
20
22
|
import { createPublicStaticFileChecker } from './utils'
|
|
21
23
|
import { createVitePlugin } from './vite-plugin'
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Remark plugins that make the CMS list-style directive (`:::list{.class}`,
|
|
27
|
+
* emitted by the editor) render as `<ul class="class">`. `remark-directive`
|
|
28
|
+
* also stops MDX from treating the `{.class}` attribute block as a JS
|
|
29
|
+
* expression (which crashes acorn). Wired into the markdown/MDX pipeline by
|
|
30
|
+
* `@nuasite/nua`.
|
|
31
|
+
*/
|
|
32
|
+
export const cmsRemarkPlugins = [remarkDirective, remarkListDirective]
|
|
33
|
+
|
|
23
34
|
export interface NuaCmsOptions extends CmsMarkerOptions {
|
|
24
35
|
/**
|
|
25
36
|
* URL to the CMS editor (inline visual-editing widget) script.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Plugin } from 'unified'
|
|
2
|
+
|
|
3
|
+
// Render-side counterpart of the editor's styled-list plugin
|
|
4
|
+
// (packages/cms-mdx-editor/src/styled-list-plugin.ts). The editor serializes a
|
|
5
|
+
// styled list as the container directive `:::list{.className}`. On the site we
|
|
6
|
+
// need `remark-directive` to claim that syntax before MDX would otherwise treat
|
|
7
|
+
// `{.className}` as a JSX expression and crash acorn. This plugin then turns the
|
|
8
|
+
// `list` directive into a plain `<ul class="className">` and — crucially —
|
|
9
|
+
// converts every OTHER directive node back to its literal source text, so a
|
|
10
|
+
// stray colon in prose (e.g. `klíč:hodnota` → a `:hodnota` text directive) is
|
|
11
|
+
// never silently dropped by remark-rehype.
|
|
12
|
+
|
|
13
|
+
const LIST_DIRECTIVE_NAME = 'list'
|
|
14
|
+
const LIST_STYLE_CLASS_RE = /^[A-Za-z0-9_-]+$/
|
|
15
|
+
const DIRECTIVE_TYPES = new Set(['textDirective', 'leafDirective', 'containerDirective'])
|
|
16
|
+
|
|
17
|
+
interface MdastNode {
|
|
18
|
+
type: string
|
|
19
|
+
name?: unknown
|
|
20
|
+
value?: unknown
|
|
21
|
+
attributes?: Record<string, unknown> | null
|
|
22
|
+
children?: MdastNode[]
|
|
23
|
+
data?: { hName?: string; hProperties?: Record<string, unknown> }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function normalizeListStyleClass(value: unknown): string | null {
|
|
27
|
+
if (typeof value !== 'string') return null
|
|
28
|
+
const [className] = value.trim().split(/\s+/)
|
|
29
|
+
if (!className || !LIST_STYLE_CLASS_RE.test(className)) return null
|
|
30
|
+
return className
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function childrenOf(node: MdastNode): MdastNode[] {
|
|
34
|
+
return Array.isArray(node.children) ? node.children : []
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function directiveMarker(type: string): string {
|
|
38
|
+
if (type === 'containerDirective') return ':::'
|
|
39
|
+
if (type === 'leafDirective') return '::'
|
|
40
|
+
return ':'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function stringifyAttributes(attributes: Record<string, unknown> | null | undefined): string {
|
|
44
|
+
if (!attributes) return ''
|
|
45
|
+
const parts: string[] = []
|
|
46
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
47
|
+
if (key === 'class' && typeof value === 'string') {
|
|
48
|
+
for (const cls of value.split(/\s+/).filter(Boolean)) parts.push(`.${cls}`)
|
|
49
|
+
} else if (key === 'id' && typeof value === 'string') {
|
|
50
|
+
parts.push(`#${value}`)
|
|
51
|
+
} else {
|
|
52
|
+
parts.push(`${key}="${String(value)}"`)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return parts.length > 0 ? `{${parts.join(' ')}}` : ''
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function text(value: string): MdastNode {
|
|
59
|
+
return { type: 'text', value }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Turn a `:::list{.className}` directive into its inner list, tagged with the
|
|
63
|
+
// class so remark-rehype emits `<ul class="className">`. Always unwraps (keeps
|
|
64
|
+
// the list content) even when the class is missing/invalid.
|
|
65
|
+
function unwrapListDirective(node: MdastNode): MdastNode[] {
|
|
66
|
+
const children = childrenOf(node)
|
|
67
|
+
const className = normalizeListStyleClass(node.attributes?.class)
|
|
68
|
+
if (className) {
|
|
69
|
+
const list = children.find(child => child.type === 'list')
|
|
70
|
+
if (list) {
|
|
71
|
+
const data = list.data ?? (list.data = {})
|
|
72
|
+
const properties = data.hProperties ?? (data.hProperties = {})
|
|
73
|
+
properties.className = [className]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return children
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Reconstruct any non-list directive back to literal source so no text is lost.
|
|
80
|
+
function neutralizeDirective(node: MdastNode): MdastNode[] {
|
|
81
|
+
const marker = directiveMarker(node.type)
|
|
82
|
+
const name = typeof node.name === 'string' ? node.name : ''
|
|
83
|
+
const label = childrenOf(node)
|
|
84
|
+
const attributes = stringifyAttributes(node.attributes)
|
|
85
|
+
|
|
86
|
+
if (node.type === 'textDirective') {
|
|
87
|
+
const parts: MdastNode[] = [text(`${marker}${name}`)]
|
|
88
|
+
if (label.length > 0) parts.push(text('['), ...label, text(']'))
|
|
89
|
+
if (attributes) parts.push(text(attributes))
|
|
90
|
+
return parts
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Block-level (leaf/container) false positives are extremely unlikely in
|
|
94
|
+
// prose; preserve the content and prefix it with the literal opening line.
|
|
95
|
+
const opener: MdastNode = { type: 'paragraph', children: [text(`${marker}${name}${attributes}`)] }
|
|
96
|
+
return [opener, ...label]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function transform(node: MdastNode): void {
|
|
100
|
+
const children = node.children
|
|
101
|
+
if (!Array.isArray(children)) return
|
|
102
|
+
|
|
103
|
+
for (let index = 0; index < children.length; index++) {
|
|
104
|
+
const child = children[index]
|
|
105
|
+
if (!child) continue
|
|
106
|
+
transform(child)
|
|
107
|
+
|
|
108
|
+
if (!DIRECTIVE_TYPES.has(child.type)) continue
|
|
109
|
+
|
|
110
|
+
const replacement = child.type === 'containerDirective' && child.name === LIST_DIRECTIVE_NAME
|
|
111
|
+
? unwrapListDirective(child)
|
|
112
|
+
: neutralizeDirective(child)
|
|
113
|
+
|
|
114
|
+
children.splice(index, 1, ...replacement)
|
|
115
|
+
index += replacement.length - 1
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const remarkListDirective: Plugin<[]> = () => (tree) => {
|
|
120
|
+
transform(tree)
|
|
121
|
+
}
|