@contentrain/astro-kit 0.12.1 → 0.14.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/catalog.json CHANGED
@@ -1606,6 +1606,22 @@
1606
1606
  ],
1607
1607
  "default": "inherit",
1608
1608
  "description": "The lead's alignment. inherit: as the layout sets it. start: at the start edge and across the column, under a centred heading (a centred heading widget over a left-aligned text editor in one container)."
1609
+ },
1610
+ "actionsAlign": {
1611
+ "options": [
1612
+ "inherit",
1613
+ "start"
1614
+ ],
1615
+ "default": "inherit",
1616
+ "description": "The buttons' alignment. inherit: as the layout sets it. start: at the start edge, under centred text (a WordPress buttons block with no justification inside a centred cover)."
1617
+ },
1618
+ "width": {
1619
+ "options": [
1620
+ "full",
1621
+ "content"
1622
+ ],
1623
+ "default": "full",
1624
+ "description": "Layout cover only. full: the image runs the full window width. content: the cover sits in the reading column, as a WordPress cover block that is not alignfull does under the page title."
1609
1625
  }
1610
1626
  },
1611
1627
  "props": {
@@ -30,13 +30,22 @@ const hero = tv({
30
30
  // The lead's own alignment: `start` keeps it at the start edge, across the column, under a centred heading
31
31
  // (Hello centres the heading widget and leaves the text editor left in the same container).
32
32
  leadAlign: { inherit: {}, start: {} },
33
+ // The buttons' own alignment: `start` keeps them at the start edge under centred text (a WordPress buttons block
34
+ // with no justification sits left inside a cover whose heading and paragraph are centred).
35
+ actionsAlign: { inherit: {}, start: {} },
36
+ // How wide a cover runs: the full window, or the reading column — a WordPress cover block that is not
37
+ // alignfull sits in the content column, the page title above it.
38
+ width: { full: {}, content: {} },
33
39
  },
34
40
  compoundVariants: [
41
+ // After the layout's own heights, so a boxed cover keeps WordPress's 430px default.
42
+ { layout: 'cover', width: 'content', class: { grid: 'min-h-[20rem] md:min-h-[26.875rem]' } },
35
43
  { layout: 'cover', align: 'center', class: { grid: 'justify-items-center', copy: 'mx-auto items-center text-center', lead: 'mx-auto', actions: 'justify-center' } },
36
44
  // After the centring rules, so the lead leaves them.
37
45
  { leadAlign: 'start', class: { lead: 'mx-0 max-w-none self-stretch text-start' } },
46
+ { actionsAlign: 'start', class: { actions: 'justify-start self-stretch' } },
38
47
  ],
39
- defaultVariants: { layout: 'split', tone: 'default', align: 'center', leadAlign: 'inherit' },
48
+ defaultVariants: { layout: 'split', tone: 'default', align: 'center', leadAlign: 'inherit', actionsAlign: 'inherit', width: 'full' },
40
49
  })
41
50
 
42
51
  interface Props {
@@ -52,17 +61,40 @@ interface Props {
52
61
  align?: 'start' | 'center' | undefined
53
62
  /** The lead's alignment: as the layout sets it, or at the start edge under a centred heading. */
54
63
  leadAlign?: 'inherit' | 'start' | undefined
64
+ /** The buttons' alignment: as the layout sets it, or at the start edge under centred text. */
65
+ actionsAlign?: 'inherit' | 'start' | undefined
66
+ /** A cover's width: the full window (the default) or the reading column; other layouts ignore it. */
67
+ width?: 'full' | 'content' | undefined
55
68
  tone?: 'default' | 'muted' | 'inverse' | 'accent' | undefined
56
69
  id?: string | undefined
57
70
  }
58
71
 
59
- const { heading, level = 1, eyebrow, lead, actions = [], image, layout = 'split', align, leadAlign, tone, id } = Astro.props
60
- const s = hero({ layout, align, leadAlign, tone })
72
+ const { heading, level = 1, eyebrow, lead, actions = [], image, layout = 'split', align, leadAlign, actionsAlign, width = 'full', tone, id } = Astro.props
73
+ const s = hero({ layout, align, leadAlign, actionsAlign, width, tone })
61
74
  const Heading = level === 1 ? 'h1' : 'h2'
62
75
  const headingId = id ? `${id}-title` : undefined
63
76
  const cover = layout === 'cover' && image
64
77
  ---
65
- {cover
78
+ {cover && width === 'content'
79
+ ? (
80
+ <Section tone="none" spacing="sm" width="prose" id={id} aria-labelledby={headingId}>
81
+ <div class="relative isolate overflow-hidden bg-ink text-surface">
82
+ <KitImage image={image} priority sizes="(min-width: 768px) 46rem, 100vw" class="absolute inset-0 -z-10 h-full w-full object-cover" />
83
+ <div class="absolute inset-0 -z-10 bg-ink/60" aria-hidden="true" />
84
+ <div class="px-6 py-10 md:px-10">
85
+ <div class={s.grid()}>
86
+ <div class={s.copy()}>
87
+ {eyebrow && <p class={s.eyebrow()}>{eyebrow}</p>}
88
+ <Heading id={headingId} class={s.heading()}>{heading}</Heading>
89
+ {lead && <p class={s.lead()} data-kit-text>{lead}</p>}
90
+ {actions.length > 0 && <div class={s.actions()}>{actions.map(action => <Button action={action} size="lg" />)}</div>}
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </Section>
96
+ )
97
+ : cover
66
98
  ? (
67
99
  <Section tone="inverse" spacing="none" width="none" id={id} aria-labelledby={headingId} class="relative isolate overflow-hidden">
68
100
  <KitImage image={image} priority sizes="100vw" class="absolute inset-0 -z-10 h-full w-full object-cover" />
@@ -122,5 +122,50 @@
122
122
  "leadAlign": "start",
123
123
  "level": 2
124
124
  }
125
+ },
126
+ {
127
+ "name": "cover-content",
128
+ "props": {
129
+ "heading": "The long way round",
130
+ "lead": "A year of trains across Europe, one station at a time.",
131
+ "layout": "cover",
132
+ "actions": [
133
+ {
134
+ "label": "Read the series",
135
+ "href": "/series/long-way/"
136
+ }
137
+ ],
138
+ "image": {
139
+ "src": "/kit-fixtures/photo-3.svg",
140
+ "alt": "",
141
+ "width": 1600,
142
+ "height": 900
143
+ },
144
+ "width": "content",
145
+ "level": 2
146
+ }
147
+ },
148
+ {
149
+ "name": "cover-content-actions-start",
150
+ "props": {
151
+ "heading": "The long way round",
152
+ "lead": "A year of trains across Europe, one station at a time.",
153
+ "layout": "cover",
154
+ "actions": [
155
+ {
156
+ "label": "Read the series",
157
+ "href": "/series/long-way/"
158
+ }
159
+ ],
160
+ "image": {
161
+ "src": "/kit-fixtures/photo-3.svg",
162
+ "alt": "",
163
+ "width": 1600,
164
+ "height": 900
165
+ },
166
+ "width": "content",
167
+ "level": 2,
168
+ "actionsAlign": "start"
169
+ }
125
170
  }
126
171
  ]
@@ -45,6 +45,22 @@
45
45
  ],
46
46
  "default": "inherit",
47
47
  "description": "The lead's alignment. inherit: as the layout sets it. start: at the start edge and across the column, under a centred heading (a centred heading widget over a left-aligned text editor in one container)."
48
+ },
49
+ "actionsAlign": {
50
+ "options": [
51
+ "inherit",
52
+ "start"
53
+ ],
54
+ "default": "inherit",
55
+ "description": "The buttons' alignment. inherit: as the layout sets it. start: at the start edge, under centred text (a WordPress buttons block with no justification inside a centred cover)."
56
+ },
57
+ "width": {
58
+ "options": [
59
+ "full",
60
+ "content"
61
+ ],
62
+ "default": "full",
63
+ "description": "Layout cover only. full: the image runs the full window width. content: the cover sits in the reading column, as a WordPress cover block that is not alignfull does under the page title."
48
64
  }
49
65
  },
50
66
  "props": {
package/dist/index.d.mts CHANGED
@@ -190,6 +190,13 @@ interface SectionRule {
190
190
  */
191
191
  repeat?: 'columns'; /** The section's root element(s): `core/cover`, `core/media-text` (a loose run of blocks is `run`). */
192
192
  root?: string | string[];
193
+ /**
194
+ * Two slots in the order the source writes them: where both hold a leaf (in each column, with `repeat`), the first
195
+ * leaf of the one must come before the first of the other. `['figure', 'label']` is a figure written above its
196
+ * label, so a rule can pick the variant the source's reading order needs (G-text) and leave the other order to the
197
+ * next rule.
198
+ */
199
+ order?: [string, string];
193
200
  };
194
201
  /**
195
202
  * Named leaf groups. Each leaf, in document order, goes to the first declared slot that matches it and
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/catalog.ts","../src/mapping.ts","../src/copy.ts","../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/catalog.ts","../src/mapping.ts","../src/copy.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -174,6 +174,13 @@ function claimLeaves(rule, leaves, defaults, scope = "all") {
174
174
  else if (rule.when?.only !== false) return null;
175
175
  }
176
176
  for (const [name, def] of slots) if (claimed[name].length < (def.min ?? 0)) return null;
177
+ const [first, then] = rule.when?.order ?? [];
178
+ const a = first ? claimed[first]?.[0] : void 0;
179
+ const b = then ? claimed[then]?.[0] : void 0;
180
+ if (a && b) {
181
+ const at = (path) => leaves.findIndex((l) => l.path === path);
182
+ if (at(a) > at(b)) return null;
183
+ }
177
184
  return claimed;
178
185
  }
179
186
  /**
@@ -298,6 +305,8 @@ function validateSections(table, components) {
298
305
  if (rule.when?.repeat === "columns" && rule.each !== "column") problems.push(`${at}: repeat columns needs each: column`);
299
306
  if (rule.each === "column" && rule.when?.repeat !== "columns") problems.push(`${at}: each column needs when.repeat columns`);
300
307
  for (const [name, slot] of Object.entries(rule.slots)) if (slot.scope === "intro" && rule.when?.repeat !== "columns") problems.push(`${at}: slot ${name} is scope intro, which needs when.repeat columns`);
308
+ for (const name of rule.when?.order ?? []) if (!rule.slots[name]) problems.push(`${at}: when.order names ${name}, which is not a slot`);
309
+ if (rule.when?.order && rule.slots[rule.when.order[0]]?.scope === "intro" !== (rule.slots[rule.when.order[1]]?.scope === "intro")) problems.push(`${at}: when.order compares an intro slot with a column slot`);
301
310
  const readsSlots = (value, where, type, slotted, item = false) => {
302
311
  const slots = sectionValueSlots(value);
303
312
  if (!slots) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/catalog.ts","../src/copy.ts","../src/mapping.ts","../src/index.ts"],"sourcesContent":["// The kit catalog: what each component is, what it takes, how it varies and\n// which WordPress builder elements it stands in for. Machine-readable, so the\n// migration writer chooses components by querying it instead of reading\n// source, and the builder mapping tables (mapping/*.json) are checked against\n// it.\n//\n// Props are described with Contentrain's own `FieldDef`, so a model field and\n// the prop it feeds are compared type to type.\n\nimport type { FieldDef } from '@contentrain/types'\nimport { fieldDepth, sectionFieldName } from '@contentrain/types'\n\nexport const CATALOG_FORMAT = 'astro-kit-catalog@1'\n\n/** The WordPress builders whose elements a component is fed from. */\nexport type KitBuilder = 'gutenberg' | 'elementor' | 'divi' | 'classic'\nexport const KIT_BUILDERS: readonly KitBuilder[] = ['gutenberg', 'elementor', 'divi', 'classic']\n\n/** Element-name prefix per builder, as `facts.json` names elements (`core/cover`, `elementor/heading`, `divi/et_pb_blurb`). */\nexport const BUILDER_PREFIX: Record<KitBuilder, string> = {\n gutenberg: 'core/',\n elementor: 'elementor/',\n divi: 'divi/',\n classic: 'classic/',\n}\n\n/**\n * Whether an element name belongs to a builder. Gutenberg blocks are namespaced by their plugin\n * (`core/cover`, `yoast-seo/breadcrumbs`, `uagb/container`), so any `<ns>/<name>` that is not another\n * builder's is a Gutenberg block.\n */\nexport function isBuilderElement(builder: KitBuilder, name: string): boolean {\n if (builder !== 'gutenberg') return name.startsWith(BUILDER_PREFIX[builder])\n return /^[a-z0-9-]+\\/[a-z0-9-]+(:[\\w-]+)?$/.test(name) && !KIT_BUILDERS.some(b => b !== 'gutenberg' && name.startsWith(BUILDER_PREFIX[b]))\n}\n\nexport type KitCategory = 'chrome' | 'section' | 'content' | 'primitive'\n\n/** Client JavaScript a component ships: none, a few lines of dependency-free script (`vanilla`), Embla (carousel), Zag.js (accessible widget) or the Studio runtime. */\nexport type KitJs = 'none' | 'vanilla' | 'embla' | 'zag' | 'studio'\n\nexport type KitProp = FieldDef & {\n description: string\n /**\n * `false`: a setting the plan or writer passes (heading level, a label from `ui:`, an anchor id), not page\n * content. A section's page-singleton field holds only the other props, its content props.\n */\n content?: false\n}\n\n/**\n * A content prop's name: camelCase, a capital only to start a word (`imageAlt`, not `imageURL`), so it\n * turns into exactly one Contentrain field name.\n */\nexport const CONTENT_PROP_NAME = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/\n\n/**\n * The page-singleton field a content prop is stored in (DECISIONS §7b): the prop name in snake_case, as\n * Contentrain field names are (`ctaLabel` → `cta_label`). The view passes each prop from its field.\n */\nexport const contentFieldName: (prop: string) => string = sectionFieldName\n\n/** The props a section's page-singleton field holds: all but the settings. */\nexport function contentProps(component: KitComponentMeta): Record<string, KitProp> {\n return Object.fromEntries(Object.entries(component.props).filter(([, prop]) => prop.content !== false))\n}\n\n/** Fields under a field, by dotted path (`items[].imageAlt`). */\nfunction fieldsByPath(fields: Record<string, FieldDef>, prefix = ''): Array<[string, FieldDef]> {\n const out: Array<[string, FieldDef]> = []\n for (const [name, field] of Object.entries(fields)) {\n out.push([prefix + name, field], ...fieldsByPath(field.fields ?? {}, `${prefix}${name}.`))\n if (typeof field.items === 'object') out.push(...fieldsByPath(field.items.fields ?? {}, `${prefix}${name}[].`))\n }\n return out\n}\n\n/** A content prop path as its page-singleton field path: every name in it through `contentFieldName`. */\nexport function contentFieldPath(path: string): string {\n return path.replace(/[A-Za-z0-9]+/g, contentFieldName)\n}\n\nexport interface KitVariant {\n options: string[]\n default: string\n description: string\n}\n\n/** One component as written in `components/<id>/meta.json`. */\nexport interface KitComponentMeta {\n id: string\n name: string\n description: string\n category: KitCategory\n /** Kit components this one renders. */\n uses: string[]\n /** Files it imports from `components/_shared/`. */\n shared: string[]\n variants: Record<string, KitVariant>\n props: Record<string, KitProp>\n /** Named slots → what goes in them. `default` is the unnamed slot. */\n slots: Record<string, string>\n js: KitJs\n /** npm packages the site needs for this component, name → range. */\n dependencies: Record<string, string>\n /** Builder elements this component replaces, by builder — what the mapping tables map from. */\n sources: Partial<Record<KitBuilder, string[]>>\n /** How it meets its accessibility pattern. */\n a11y: string\n /**\n * Sections only: the section's shape in a few words and the signals that show it in a source page, a\n * closed-set label for the classifier and the migration architect (`image beside heading + text`).\n */\n pattern?: { summary: string, signals: string[] }\n /**\n * Sections only, written by the catalog build: each content prop path (`slides[].ctaLabel`) → the\n * page-singleton field path that holds it (`slides[].cta_label`).\n */\n contentFields?: Record<string, string>\n}\n\n/** A catalog entry: the meta plus the files that make the component. */\nexport interface KitComponent extends KitComponentMeta {\n /** Paths relative to `components/<id>/`. */\n files: string[]\n}\n\nexport interface KitCatalog {\n format: typeof CATALOG_FORMAT\n /** Design tokens (Tailwind @theme names) the components read; the site must define them. */\n tokens: string[]\n /** npm packages every kit component needs. */\n dependencies: Record<string, string>\n components: KitComponent[]\n}\n\n/** Problems with a catalog, one line each; empty when it is sound. */\nexport function validateCatalog(catalog: KitCatalog): string[] {\n const problems: string[] = []\n const ids = new Set(catalog.components.map(c => c.id))\n for (const c of catalog.components) {\n const at = `component ${c.id}`\n if (!/^[a-z][a-z0-9-]*$/.test(c.id)) problems.push(`${at}: id must be kebab-case`)\n if (!c.files.length) problems.push(`${at}: no files`)\n for (const used of c.uses) if (!ids.has(used)) problems.push(`${at}: uses unknown component \"${used}\"`)\n for (const [axis, variant] of Object.entries(c.variants)) {\n if (!variant.options.includes(variant.default)) problems.push(`${at}: variant ${axis} default \"${variant.default}\" is not an option`)\n }\n for (const [name, prop] of Object.entries(c.props)) {\n if (!prop.description) problems.push(`${at}: prop ${name} has no description`)\n if (prop.type === 'select' && !prop.options?.length) problems.push(`${at}: select prop ${name} has no options`)\n }\n if (c.category === 'section') {\n // A section is stored as one object field of its page singleton: each content prop needs one field\n // name there, and that field must stay within two object levels.\n const content = contentProps(c)\n const paths = fieldsByPath(content)\n for (const [path, field] of paths) {\n if (!CONTENT_PROP_NAME.test(path.split(/\\.|\\[\\]\\./).at(-1)!)) problems.push(`${at}: content prop ${path} must be camelCase with one capital per word (or content: false)`)\n // Studio shows both to editors, and the plan reads them as the field's label and help text.\n if (!field.label) problems.push(`${at}: content prop ${path} has no label`)\n if (!field.description) problems.push(`${at}: content prop ${path} has no description`)\n }\n if (c.contentFields) {\n const expected = Object.fromEntries(paths.map(([path]) => [path, contentFieldPath(path)]))\n if (JSON.stringify(c.contentFields) !== JSON.stringify(expected)) problems.push(`${at}: contentFields is stale — rebuild the catalog`)\n }\n for (const [name, prop] of Object.entries(content)) {\n if (1 + fieldDepth(prop) > 2) problems.push(`${at}: content prop ${name} nests objects deeper than a page singleton allows`)\n }\n }\n for (const [builder, names] of Object.entries(c.sources) as Array<[KitBuilder, string[]]>) {\n const prefix = BUILDER_PREFIX[builder]\n for (const name of names) if (!isBuilderElement(builder, name)) problems.push(`${at}: source \"${name}\" is not a ${builder} element (${prefix}…)`)\n }\n }\n const seen = new Set<string>()\n for (const c of catalog.components) {\n if (seen.has(c.id)) problems.push(`component ${c.id}: listed twice`)\n seen.add(c.id)\n }\n return problems\n}\n\n/** Components that replace a builder element, e.g. `core/cover` → hero. */\nexport function componentsForSource(catalog: KitCatalog, element: string): KitComponent[] {\n return catalog.components.filter(c => Object.values(c.sources).some(names => names?.includes(element)))\n}\n","// Copying components into a site — the shadcn model: the site owns the\n// source, there is no runtime dependency on this package. A component is\n// copied with every kit component it renders and every shared file it\n// imports, under `src/components/kit/`, keeping the relative layout its\n// imports expect.\n\nimport { copyFile, mkdir } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { KitCatalog } from './catalog.js'\n\n/** Where kit files land in a site, relative to the site root. */\nexport const KIT_DIR = 'src/components/kit'\n\nexport interface CopyPlan {\n /** Components copied, the requested ones and everything they render, sorted. */\n components: string[]\n /** Site-relative destination → path relative to the kit's `components/` directory. */\n files: Record<string, string>\n /** npm packages the site needs, name → range. */\n dependencies: Record<string, string>\n}\n\nexport function planCopy(catalog: KitCatalog, ids: readonly string[]): CopyPlan {\n const byId = new Map(catalog.components.map(c => [c.id, c]))\n const wanted = new Set<string>()\n const visit = (id: string) => {\n if (wanted.has(id)) return\n const component = byId.get(id)\n if (!component) throw new Error(`astro-kit has no component \"${id}\". Known: ${[...byId.keys()].join(', ')}.`)\n wanted.add(id)\n for (const used of component.uses) visit(used)\n }\n for (const id of ids) visit(id)\n\n const files: Record<string, string> = {}\n const dependencies: Record<string, string> = { ...catalog.dependencies }\n for (const id of [...wanted].toSorted()) {\n const component = byId.get(id)!\n for (const file of component.files) files[`${KIT_DIR}/${id}/${file}`] = `${id}/${file}`\n for (const file of component.shared) files[`${KIT_DIR}/_shared/${file}`] = `_shared/${file}`\n Object.assign(dependencies, component.dependencies)\n }\n return { components: [...wanted].toSorted(), files, dependencies }\n}\n\n/** Carry out a plan: `kitRoot` is the kit's `components/` directory, `siteRoot` the site. */\nexport async function copyComponents(plan: CopyPlan, kitRoot: string, siteRoot: string): Promise<void> {\n await Promise.all(Object.entries(plan.files).map(async ([dest, source]) => {\n const target = join(siteRoot, dest)\n await mkdir(dirname(target), { recursive: true })\n await copyFile(join(kitRoot, source), target)\n }))\n}\n","// Builder mapping tables: which kit component a WordPress builder element\n// becomes, and where each of its props comes from. One versioned table per\n// builder (`mapping/<builder>.json`), written once and applied\n// deterministically to every site — the migration model is asked only about\n// elements no rule covers (Migrate's `unmapped_element` decision).\n//\n// A prop's source is one expression from a small closed set. The first six are\n// the project plan's values (`@contentrain/types` `PlanValue`) and bind\n// content; the rest read the builder element itself or the page it is on:\n//\n// field:<name> · media:<field> · ref:<field>.<field> · href:self|<field> · term:<field> · ui:<key> · const:<value>\n// site:<field> a field of the `site` singleton (`site:title`, `site:logo`)\n// menu:primary|footer the menu the starter shows in that area\n// page:<value> what the route knows: `page:base`, `page:current`, `page:total`, `page:breadcrumb`\n// attr:<name> a builder attribute (block attribute, Elementor setting, Divi shortcode attribute)\n// dom:<selector> text of the first match inside the element (empty selector: the element)\n// dom:<selector>@<attr> an attribute of it (`dom:a@href`)\n// img:<selector> an image as the kit's `ImageInput` (src, alt, width, height)\n// img:<selector>@src|@alt one part of that image, for a list item's flat `image` + `imageAlt` fields\n// link:<selector> a link as `{ label, href }`\n// html:<selector> inner markup, kept as Markdown (rich text)\n// class:<cls>=<a>|<b> <a> when the element, or an ancestor inside the matched element, has class\n// <cls> (`*` matches any run of characters); <b> otherwise. An empty branch\n// reads nothing and the prop keeps its default. `class:is-style-outline=ghost|primary`\n// is WordPress's outline button as the kit's ghost action.\n//\n// Section rules (`sections`) classify a whole page section by the leaves it holds and fill the component's\n// props from them: the same expressions, read from a named slot's leaf (`@title attr:title`).\n\nimport type { KitBuilder, KitCatalog, KitComponent } from './catalog.js'\nimport { isBuilderElement, KIT_BUILDERS } from './catalog.js'\n\nexport const MAPPING_FORMAT = 'astro-kit-mapping@1'\n\nexport const MAPPING_VALUE_PATTERN = /^(?:field:[\\w-]+|media:[\\w-]+|ref:[\\w-]+\\.[\\w-]+|href:(?:self|[\\w-]+)|term:[\\w-]+|ui:[\\w.-]+|const:(?:true|false|-?\\d+(?:\\.\\d+)?|[a-z][a-z0-9_-]{0,31})|site:[\\w-]+|menu:(?:primary|footer)|page:(?:base|current|total|breadcrumb)|attr:[\\w.-]+|class:[\\w*-]+=(?:[a-z][\\w-]*)?\\|(?:[a-z][\\w-]*)?|(?:dom|img|link|html):[^@]*(?:@[\\w-]+)?)$/\n\nexport interface MappingRule {\n /** Builder element, as the fact pack names it; a `:qualifier` narrows it (`core/template-part:header`). */\n match: string\n /** Extra conditions on the element. */\n when?: { attr?: Record<string, string | number | boolean>, class?: string }\n /** Kit component id. */\n component: string\n variant?: Record<string, string>\n /** Content the element lists rather than holds: one component per entry of the collection (`collection:posts`). */\n bind?: `collection:${string}`\n /** Consecutive sibling matches form one component (a row of Elementor icon boxes is one card grid). */\n group?: 'siblings'\n props?: Record<string, string>\n /** Array prop filled from repeated parts of the element (`each`) or the grouped siblings / bound entries. */\n into?: string\n /** Selector of the repeated parts inside the element, for `into`. */\n each?: string\n /** Props of one repeated part / sibling / entry, with sources relative to it. */\n item?: Record<string, string>\n}\n\nexport interface MappingTable {\n format: typeof MAPPING_FORMAT\n builder: KitBuilder\n /** Bumped on any rule change: plans record the version they applied. */\n version: string\n /** What an element no rule covers becomes when no decision is made: rich text. */\n fallback: 'prose'\n /**\n * Attribute values the builder leaves out of its data while they are at their default, per element:\n * Elementor saves no `video_type` for a YouTube video. `when.attr` and `attr:` read through them.\n */\n defaults?: Record<string, Record<string, string | number | boolean>>\n rules: MappingRule[]\n /**\n * Section rules: what a whole section of a page (an Elementor container, a Gutenberg group or columns,\n * a Divi section — facts `sections[]`) becomes, tried top to bottom before any element rule. The first\n * that matches wins; a section none matches falls through to the element `rules` inside it.\n */\n sections?: SectionRule[]\n}\n\n/**\n * A named group of a section's leaves: every leaf named `match` (and carrying `attr`) up to `max` of them.\n * `attr` values may list alternatives with `|` (`{ header_size: 'h1|h2' }`).\n */\nexport interface SectionSlot {\n match: string | string[]\n attr?: Record<string, string | number | boolean>\n /** At least this many leaves (default 0). */\n min?: number\n /** At most this many (default unbounded). */\n max?: number\n /** The leaf is a figure (`1,200`, `2018`, `$29`, `01`): its facts say so. `true` requires it, `false` rules it out. */\n numeric?: boolean\n /** The leaf is a step's ordinal (`1`, `01`, `Step 2` — at most two digits): its facts say so. A year or a count is a figure, not an ordinal. */\n ordinal?: boolean\n /** The image is logo-sized (small as displayed): its facts say so. Photos and banners are not. */\n small?: boolean\n /** The image is a portrait: a square-ish photo at least 200px across (a team member's face). Logos and banners are not. */\n portrait?: boolean\n /** The leaf's text is one short line — a name, a role, a label — not a description: its facts say so. */\n short?: boolean\n /**\n * `intro`: the slot holds the leaves above the repeated columns (a section's heading and lead line), not the\n * columns' own. Only with `when.repeat: 'columns'`; every other slot of such a rule reads inside a column.\n */\n scope?: 'intro'\n}\n\n/**\n * One section pattern. Values are the table's expressions, prefixed `@<slot> ` to read a slot's leaf\n * (`@title attr:title`); `a || b` takes the first non-empty (builder data first, rendered markup as fallback).\n * A slot of several leaves joins their text into a markdown prop; any other prop reads its first leaf.\n * A value without a slot reads the section's own root (a cover's background image, a container's setting).\n */\nexport interface SectionRule {\n /** Stable id (`hero.split`, `card-grid.icon-box`): plans record `<builder>:section:<id>`; JEW chooses among them. */\n id: string\n component: string\n variant?: Record<string, string>\n when?: {\n /** Where on the page: `first` is the page's first section, `last` its last. */\n position?: 'first' | 'last' | 'any'\n /** Column count(s) the section must have. */\n columns?: number | number[]\n /** Every leaf must fall into a slot (default true); false lets unclaimed leaves be. */\n only?: boolean\n /**\n * `columns`: each column must match the slots on its own (a row of team members, price plans); the leaves above\n * the row (`SectionMeta.intro`) go to the slots with `scope: 'intro'`.\n */\n repeat?: 'columns'\n /** The section's root element(s): `core/cover`, `core/media-text` (a loose run of blocks is `run`). */\n root?: string | string[]\n }\n /**\n * Named leaf groups. Each leaf, in document order, goes to the first declared slot that matches it and\n * still has room, so declare a specific slot (an `attr` filter) before a general one on the same element.\n */\n slots: Record<string, SectionSlot>\n props?: Record<string, string>\n /**\n * Array prop filled once per `each`: a slot's leaves (`@card`), the parts a selector finds inside them\n * (`@list figure.wp-block-image`, `@faq .elementor-accordion-item`: a gallery's images, an accordion's\n * items), or the columns (`column`, with `repeat: 'columns'`).\n */\n into?: string\n each?: string\n /** One array item: relative to the leaf (`each: '@slot'`) or to the column, where `@<slot>` is that column's slot. */\n item?: Record<string, string>\n}\n\n/** A section leaf as the fact pack gives it: a builder element with its `blocks:` path. */\nexport interface SectionLeaf extends MappingNode {\n path: string\n /** Facts' flag: the leaf's text is a figure (a number, an amount, a step number) — not the text itself. */\n numeric?: boolean\n /** Facts' flag: the leaf's text is an ordinal — one or two digits, nothing else. */\n ordinal?: boolean\n /** Facts' flag: the image is logo-sized as displayed. */\n small?: boolean\n /** Facts' flag: the image is a square-ish photo, at least 200px across. */\n portrait?: boolean\n /** Facts' flag: the leaf's text is one short line (at most 40 characters, no line break). */\n short?: boolean\n}\n\n/** Where a section sits (facts `index`/`count`) and what it is (`root`: facts `root`). */\nexport interface SectionMeta {\n index: number\n count: number\n root?: string | undefined\n /** The leaves above a row of columns, in the same wrapper (`Who we are` over its people): see `when.repeat`. */\n intro?: SectionLeaf[] | undefined\n}\n\n/** Leaf paths per slot; with `repeat: 'columns'`, also per column. */\nexport interface SectionMatch {\n slots: Record<string, string[]>\n columns?: Record<string, string[]>[]\n}\n\n/** The id a plan records for a section rule. */\nexport function sectionRuleId(builder: KitBuilder, rule: Pick<SectionRule, 'id'>): string {\n return `${builder}:section:${rule.id}`\n}\n\nconst SECTION_LAYOUT_LEAVES = new Set(['elementor/spacer', 'elementor/divider', 'core/spacer', 'core/separator', 'divi/et_pb_divider'])\n\nfunction slotFits(slot: SectionSlot, leaf: SectionLeaf, defaults?: MappingTable['defaults']): boolean {\n if (![slot.match].flat().includes(leaf.name)) return false\n // A flag the facts do not set reads as \"no\": a rule that asks for one never fires on facts that predate it.\n if (slot.numeric !== undefined && slot.numeric !== (leaf.numeric === true)) return false\n if (slot.ordinal !== undefined && slot.ordinal !== (leaf.ordinal === true)) return false\n if (slot.small !== undefined && slot.small !== (leaf.small === true)) return false\n if (slot.portrait !== undefined && slot.portrait !== (leaf.portrait === true)) return false\n if (slot.short !== undefined && slot.short !== (leaf.short === true)) return false\n const attrs = attrsOf(leaf, defaults)\n return Object.entries(slot.attr ?? {}).every(([key, want]) => String(want).split('|').includes(String(attrs[key])))\n}\n\n/**\n * Assign leaves to slots in document order, each to the first declared slot with room; null when the rule does not fit.\n * `scope` picks the slots in play: a repeated row's columns read the column slots, the leaves above it the intro slots;\n * any other section reads them all.\n */\nfunction claimLeaves(rule: SectionRule, leaves: SectionLeaf[], defaults?: MappingTable['defaults'], scope: 'all' | 'column' | 'intro' = 'all'): Record<string, string[]> | null {\n const slots = Object.entries(rule.slots).filter(([, def]) => scope === 'all' || (def.scope === 'intro') === (scope === 'intro'))\n const claimed: Record<string, string[]> = Object.fromEntries(slots.map(([name]) => [name, []]))\n for (const leaf of leaves) {\n if (SECTION_LAYOUT_LEAVES.has(leaf.name)) continue\n const slot = slots.find(([name, def]) => claimed[name]!.length < (def.max ?? Infinity) && slotFits(def, leaf, defaults))\n if (slot) claimed[slot[0]]!.push(leaf.path)\n else if (rule.when?.only !== false) return null\n }\n for (const [name, def] of slots) if (claimed[name]!.length < (def.min ?? 0)) return null\n return claimed\n}\n\n/**\n * Whether a section rule fits a section, and which leaves fill which slot. `columns` are the section's\n * columns left to right, each its leaves in document order (layout containers already opened).\n */\nexport function matchSection(rule: SectionRule, columns: SectionLeaf[][], meta: SectionMeta, defaults?: MappingTable['defaults']): SectionMatch | null {\n const when = rule.when ?? {}\n if (when.root !== undefined && !(meta.root !== undefined && [when.root].flat().includes(meta.root))) return null\n if (when.position === 'first' && meta.index !== 0) return null\n if (when.position === 'last' && meta.index !== meta.count - 1) return null\n if (when.columns !== undefined && ![when.columns].flat().includes(columns.length)) return null\n if (when.repeat === 'columns') {\n if (columns.length < 2) return null\n const intro = claimLeaves(rule, meta.intro ?? [], defaults, 'intro')\n if (!intro) return null\n const per = columns.map(leaves => claimLeaves(rule, leaves, defaults, 'column'))\n if (per.some(c => !c)) return null\n const slots: Record<string, string[]> = { ...intro }\n for (const [name, def] of Object.entries(rule.slots)) if (def.scope !== 'intro') slots[name] = per.flatMap(c => c![name]!)\n return { slots, columns: per as Record<string, string[]>[] }\n }\n // Without a repeat the leaves above the row are simply the section's first leaves, as before the facts split them off.\n const slots = claimLeaves(rule, [...(meta.intro ?? []), ...columns.flat()], defaults)\n return slots && { slots }\n}\n\n/** The first section rule of a table that fits, with its match. */\nexport function classifySection(table: MappingTable, columns: SectionLeaf[][], meta: SectionMeta): { rule: SectionRule, match: SectionMatch } | null {\n for (const rule of table.sections ?? []) {\n const match = matchSection(rule, columns, meta, table.defaults)\n if (match) return { rule, match }\n }\n return null\n}\n\n/** A section value: alternatives joined by ` || `, each an optional `@<slot> ` then a mapping value. Returns the slots it reads, or null when malformed. */\nexport function sectionValueSlots(value: string): string[] | null {\n const slots: string[] = []\n for (const alternative of value.split(' || ')) {\n const m = /^(?:@([a-z][\\w-]*) )?(.+)$/.exec(alternative)\n if (!m || !MAPPING_VALUE_PATTERN.test(m[2]!)) return null\n if (m[1]) slots.push(m[1])\n }\n return slots\n}\n\n/** Problems with a mapping table against the catalog, one line each; empty when it is sound. */\nexport function validateMapping(table: MappingTable, catalog: KitCatalog): string[] {\n const problems: string[] = []\n if (table.format !== MAPPING_FORMAT) problems.push(`format is not ${MAPPING_FORMAT}`)\n if (!KIT_BUILDERS.includes(table.builder)) problems.push(`unknown builder ${table.builder}`)\n if (!/^\\d+$/.test(table.version)) problems.push('version is not an integer string')\n for (const element of Object.keys(table.defaults ?? {})) if (!isBuilderElement(table.builder, element)) problems.push(`defaults ${element}: not a ${table.builder} element`)\n const components = new Map(catalog.components.map(c => [c.id, c]))\n const seen = new Set<string>()\n for (const rule of table.rules) {\n const at = `${table.builder} ${rule.match}${rule.when ? ' (when)' : ''}`\n const key = `${rule.match}|${JSON.stringify(rule.when ?? {})}`\n if (seen.has(key)) problems.push(`${at}: matched twice`)\n seen.add(key)\n if (!isBuilderElement(table.builder, rule.match)) problems.push(`${at}: not a ${table.builder} element`)\n const c = components.get(rule.component)\n if (!c) { problems.push(`${at}: component ${rule.component} is not in the catalog`); continue }\n for (const [axis, value] of Object.entries(rule.variant ?? {})) {\n // A variant is one option, or a `class:` choice between options read from the element.\n const choice = /^class:[\\w*-]+=([a-z][\\w-]*)?\\|([a-z][\\w-]*)?$/.exec(value)\n const options = choice ? [choice[1], choice[2]].filter((option): option is string => option !== undefined) : [value]\n if (!c.variants[axis]) problems.push(`${at}: ${c.id} has no variant axis ${axis}`)\n else for (const missing of options.filter(option => !c.variants[axis]!.options.includes(option))) problems.push(`${at}: ${c.id}.${axis} has no option ${missing}`)\n }\n for (const [prop, value] of Object.entries({ ...rule.props, ...(rule.into ? {} : rule.item) })) {\n if (!c.props[prop]) problems.push(`${at}: ${c.id} has no prop ${prop}`)\n if (!MAPPING_VALUE_PATTERN.test(value)) problems.push(`${at}: ${prop} = ${value} is not a mapping value`)\n }\n if (rule.into) {\n const target = c.props[rule.into]\n if (!target || target.type !== 'array') problems.push(`${at}: ${c.id}.${rule.into} is not an array prop`)\n const fields = target && typeof target.items === 'object' ? target.items.fields ?? {} : {}\n for (const [field, value] of Object.entries(rule.item ?? {})) {\n if (!fields[field]) problems.push(`${at}: ${c.id}.${rule.into}[] has no field ${field}`)\n if (!MAPPING_VALUE_PATTERN.test(value)) problems.push(`${at}: ${rule.into}[].${field} = ${value} is not a mapping value`)\n }\n if (!rule.each && !rule.group && !rule.bind) problems.push(`${at}: into ${rule.into} needs each, group or bind to say what repeats`)\n }\n if (rule.item && !rule.into && !rule.bind) problems.push(`${at}: item without into or bind`)\n const bound = new Set([...Object.keys(rule.props ?? {}), ...(rule.into ? [rule.into] : Object.keys(rule.item ?? {}))])\n for (const [prop, def] of Object.entries(c.props)) if (def.required && !bound.has(prop)) problems.push(`${at}: required prop ${c.id}.${prop} has no source`)\n }\n problems.push(...validateSections(table, components))\n return problems\n}\n\nfunction validateSections(table: MappingTable, components: Map<string, KitComponent>): string[] {\n const problems: string[] = []\n const seen = new Set<string>()\n for (const rule of table.sections ?? []) {\n const at = `${table.builder} section ${rule.id}`\n if (!/^[a-z][a-z0-9-]*(?:\\.[a-z0-9-]+)*$/.test(rule.id)) problems.push(`${at}: id is not dotted kebab-case`)\n if (seen.has(rule.id)) problems.push(`${at}: listed twice`)\n seen.add(rule.id)\n const c = components.get(rule.component)\n if (!c) { problems.push(`${at}: component ${rule.component} is not in the catalog`); continue }\n for (const [axis, value] of Object.entries(rule.variant ?? {})) {\n if (!c.variants[axis]) problems.push(`${at}: ${c.id} has no variant axis ${axis}`)\n else if (!c.variants[axis]!.options.includes(value)) problems.push(`${at}: ${c.id}.${axis} has no option ${value}`)\n }\n if (!Object.keys(rule.slots).length) problems.push(`${at}: no slots`)\n for (const [name, slot] of Object.entries(rule.slots)) {\n if (!/^[a-z][\\w-]*$/.test(name)) problems.push(`${at}: slot ${name} is not an identifier`)\n // A form or shortcode plugin (`classic/contact-form-7`) sits inside any builder's section, as the plan reads it from a shortcode.\n for (const element of [slot.match].flat()) if (!isBuilderElement(table.builder, element) && !isBuilderElement('classic', element)) problems.push(`${at}: slot ${name} matches ${element}, not a ${table.builder} or classic plugin element`)\n if (slot.min !== undefined && slot.max !== undefined && slot.min > slot.max) problems.push(`${at}: slot ${name} min > max`)\n }\n for (const root of [rule.when?.root ?? []].flat()) if (root !== 'run' && !isBuilderElement(table.builder, root)) problems.push(`${at}: root ${root} is not a ${table.builder} element or run`)\n if (rule.when?.repeat === 'columns' && rule.each !== 'column') problems.push(`${at}: repeat columns needs each: column`)\n if (rule.each === 'column' && rule.when?.repeat !== 'columns') problems.push(`${at}: each column needs when.repeat columns`)\n for (const [name, slot] of Object.entries(rule.slots)) if (slot.scope === 'intro' && rule.when?.repeat !== 'columns') problems.push(`${at}: slot ${name} is scope intro, which needs when.repeat columns`)\n const readsSlots = (value: string, where: string, type: string | undefined, slotted: boolean, item = false) => {\n const slots = sectionValueSlots(value)\n if (!slots) { problems.push(`${at}: ${where} = ${value} is not a section value`); return }\n if (!slotted && slots.length) problems.push(`${at}: ${where} reads a slot, but items of each ${rule.each} read their own leaf`)\n for (const slot of slots) {\n const def = rule.slots[slot]\n if (!def) problems.push(`${at}: ${where} reads undeclared slot ${slot}`)\n else if ((type === 'string' || type === 'text') && def.max !== 1) problems.push(`${at}: ${where} is ${type} but slot ${slot} may hold several leaves (max must be 1)`)\n else if (item && def.scope === 'intro') problems.push(`${at}: ${where} reads intro slot ${slot}, but the items of each column read their own column`)\n }\n }\n for (const [prop, value] of Object.entries(rule.props ?? {})) {\n const def = c.props[prop]\n if (!def) problems.push(`${at}: ${c.id} has no prop ${prop}`)\n readsSlots(value, prop, def?.type, true)\n }\n if (rule.into) {\n const target = c.props[rule.into]\n if (!target || target.type !== 'array') problems.push(`${at}: ${c.id}.${rule.into} is not an array prop`)\n if (!rule.each) problems.push(`${at}: into ${rule.into} needs each`)\n else if (rule.each !== 'column' && !rule.slots[/^@([a-z][\\w-]*)(?: \\S.*)?$/.exec(rule.each)?.[1] ?? '']) problems.push(`${at}: each ${rule.each} is not @<declared slot> [selector] or column`)\n const fields = target && typeof target.items === 'object' ? target.items.fields ?? {} : {}\n for (const [field, value] of Object.entries(rule.item ?? {})) {\n if (!fields[field]) problems.push(`${at}: ${c.id}.${rule.into}[] has no field ${field}`)\n readsSlots(value, `${rule.into}[].${field}`, fields[field]?.type, rule.each === 'column', true)\n }\n } else if (rule.item || rule.each) problems.push(`${at}: each/item without into`)\n const bound = new Set([...Object.keys(rule.props ?? {}), ...(rule.into ? [rule.into] : [])])\n for (const [prop, def] of Object.entries(c.props)) if (def.required && !bound.has(prop)) problems.push(`${at}: required prop ${c.id}.${prop} has no source`)\n }\n return problems\n}\n\n/** Catalog sources of a builder that no rule of its table matches, as an element rule or a section slot. */\nexport function unmappedSources(table: MappingTable, catalog: KitCatalog): string[] {\n const matched = new Set([...table.rules.map(r => r.match), ...(table.sections ?? []).flatMap(r => Object.values(r.slots).flatMap(s => [s.match].flat()))])\n const sources = catalog.components.flatMap((c: KitComponent) => c.sources[table.builder] ?? [])\n return [...new Set(sources)].filter(s => !matched.has(s)).toSorted()\n}\n\n/** Rules for an element, most specific (with `when` / qualifier) first. */\nexport function rulesFor(table: MappingTable, element: string): MappingRule[] {\n return table.rules\n .filter(r => r.match === element || r.match.split(':')[0] === element)\n .toSorted((a, b) => Number(!!b.when || b.match.includes(':')) - Number(!!a.when || a.match.includes(':')))\n}\n\n/** A builder element as the fact pack gives it (`FactBlock`): name, attributes, children. */\nexport interface MappingNode {\n name: string\n attrs?: Record<string, unknown>\n children?: MappingNode[]\n}\n\n/** An element's attributes with the builder's unsaved defaults filled in. */\nexport function attrsOf(node: MappingNode, defaults: MappingTable['defaults'] = {}): Record<string, unknown> {\n return { ...defaults[node.name], ...node.attrs }\n}\n\n/** Whether a rule's `match` (with an optional `:qualifier`) and `when.attr` fit an element. */\nexport function ruleMatches(rule: MappingRule, node: MappingNode, defaults?: MappingTable['defaults']): boolean {\n const [name, qualifier] = rule.match.split(':')\n if (name !== node.name) return false\n const attrs = attrsOf(node, defaults)\n // A qualifier names the element's role: a template part's area or slug, or a sub-part the fact pack marks.\n if (qualifier && ![attrs.area, attrs.slug, attrs.tagName, attrs.qualifier].includes(qualifier)) return false\n for (const [key, value] of Object.entries(rule.when?.attr ?? {})) if (attrs[key] !== value) return false\n if (rule.when?.class && !String(attrs.className ?? attrs.classes ?? '').split(/\\s+/).includes(rule.when.class)) return false\n return true\n}\n\n/**\n * Apply a table to a tree: the outermost matching element owns its whole subtree, and nothing inside\n * it is matched again — a header template part holding the site title and logo is one header, not three.\n */\nexport function claimMatches(nodes: MappingNode[], table: MappingTable, path = '0'): { path: string, node: MappingNode, rule: MappingRule }[] {\n return nodes.flatMap((node, i) => {\n const at = `${path}.${i}`\n const rule = rulesFor(table, node.name).find(r => ruleMatches(r, node, table.defaults))\n return rule ? [{ path: at, node, rule }] : claimMatches(node.children ?? [], table, at)\n })\n}\n","import { readFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { KitBuilder, KitCatalog } from './catalog.js'\nimport type { MappingTable } from './mapping.js'\n\nexport * from './catalog.js'\nexport * from './copy.js'\nexport * from './mapping.js'\n\nconst PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')\n\n/** The kit's `components/` directory — the source `copyComponents` copies from. */\nexport const KIT_COMPONENTS_DIR = join(PACKAGE_ROOT, 'components')\n\n/** A builder's mapping table (`mapping/<builder>.json`). */\nexport async function loadMapping(builder: KitBuilder): Promise<MappingTable> {\n return JSON.parse(await readFile(join(PACKAGE_ROOT, 'mapping', `${builder}.json`), 'utf8')) as MappingTable\n}\n\n/** The published catalog (`catalog.json`). */\nexport async function loadCatalog(): Promise<KitCatalog> {\n return JSON.parse(await readFile(join(PACKAGE_ROOT, 'catalog.json'), 'utf8')) as KitCatalog\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/catalog.ts","../src/copy.ts","../src/mapping.ts","../src/index.ts"],"sourcesContent":["// The kit catalog: what each component is, what it takes, how it varies and\n// which WordPress builder elements it stands in for. Machine-readable, so the\n// migration writer chooses components by querying it instead of reading\n// source, and the builder mapping tables (mapping/*.json) are checked against\n// it.\n//\n// Props are described with Contentrain's own `FieldDef`, so a model field and\n// the prop it feeds are compared type to type.\n\nimport type { FieldDef } from '@contentrain/types'\nimport { fieldDepth, sectionFieldName } from '@contentrain/types'\n\nexport const CATALOG_FORMAT = 'astro-kit-catalog@1'\n\n/** The WordPress builders whose elements a component is fed from. */\nexport type KitBuilder = 'gutenberg' | 'elementor' | 'divi' | 'classic'\nexport const KIT_BUILDERS: readonly KitBuilder[] = ['gutenberg', 'elementor', 'divi', 'classic']\n\n/** Element-name prefix per builder, as `facts.json` names elements (`core/cover`, `elementor/heading`, `divi/et_pb_blurb`). */\nexport const BUILDER_PREFIX: Record<KitBuilder, string> = {\n gutenberg: 'core/',\n elementor: 'elementor/',\n divi: 'divi/',\n classic: 'classic/',\n}\n\n/**\n * Whether an element name belongs to a builder. Gutenberg blocks are namespaced by their plugin\n * (`core/cover`, `yoast-seo/breadcrumbs`, `uagb/container`), so any `<ns>/<name>` that is not another\n * builder's is a Gutenberg block.\n */\nexport function isBuilderElement(builder: KitBuilder, name: string): boolean {\n if (builder !== 'gutenberg') return name.startsWith(BUILDER_PREFIX[builder])\n return /^[a-z0-9-]+\\/[a-z0-9-]+(:[\\w-]+)?$/.test(name) && !KIT_BUILDERS.some(b => b !== 'gutenberg' && name.startsWith(BUILDER_PREFIX[b]))\n}\n\nexport type KitCategory = 'chrome' | 'section' | 'content' | 'primitive'\n\n/** Client JavaScript a component ships: none, a few lines of dependency-free script (`vanilla`), Embla (carousel), Zag.js (accessible widget) or the Studio runtime. */\nexport type KitJs = 'none' | 'vanilla' | 'embla' | 'zag' | 'studio'\n\nexport type KitProp = FieldDef & {\n description: string\n /**\n * `false`: a setting the plan or writer passes (heading level, a label from `ui:`, an anchor id), not page\n * content. A section's page-singleton field holds only the other props, its content props.\n */\n content?: false\n}\n\n/**\n * A content prop's name: camelCase, a capital only to start a word (`imageAlt`, not `imageURL`), so it\n * turns into exactly one Contentrain field name.\n */\nexport const CONTENT_PROP_NAME = /^[a-z][a-z0-9]*(?:[A-Z][a-z0-9]+)*$/\n\n/**\n * The page-singleton field a content prop is stored in (DECISIONS §7b): the prop name in snake_case, as\n * Contentrain field names are (`ctaLabel` → `cta_label`). The view passes each prop from its field.\n */\nexport const contentFieldName: (prop: string) => string = sectionFieldName\n\n/** The props a section's page-singleton field holds: all but the settings. */\nexport function contentProps(component: KitComponentMeta): Record<string, KitProp> {\n return Object.fromEntries(Object.entries(component.props).filter(([, prop]) => prop.content !== false))\n}\n\n/** Fields under a field, by dotted path (`items[].imageAlt`). */\nfunction fieldsByPath(fields: Record<string, FieldDef>, prefix = ''): Array<[string, FieldDef]> {\n const out: Array<[string, FieldDef]> = []\n for (const [name, field] of Object.entries(fields)) {\n out.push([prefix + name, field], ...fieldsByPath(field.fields ?? {}, `${prefix}${name}.`))\n if (typeof field.items === 'object') out.push(...fieldsByPath(field.items.fields ?? {}, `${prefix}${name}[].`))\n }\n return out\n}\n\n/** A content prop path as its page-singleton field path: every name in it through `contentFieldName`. */\nexport function contentFieldPath(path: string): string {\n return path.replace(/[A-Za-z0-9]+/g, contentFieldName)\n}\n\nexport interface KitVariant {\n options: string[]\n default: string\n description: string\n}\n\n/** One component as written in `components/<id>/meta.json`. */\nexport interface KitComponentMeta {\n id: string\n name: string\n description: string\n category: KitCategory\n /** Kit components this one renders. */\n uses: string[]\n /** Files it imports from `components/_shared/`. */\n shared: string[]\n variants: Record<string, KitVariant>\n props: Record<string, KitProp>\n /** Named slots → what goes in them. `default` is the unnamed slot. */\n slots: Record<string, string>\n js: KitJs\n /** npm packages the site needs for this component, name → range. */\n dependencies: Record<string, string>\n /** Builder elements this component replaces, by builder — what the mapping tables map from. */\n sources: Partial<Record<KitBuilder, string[]>>\n /** How it meets its accessibility pattern. */\n a11y: string\n /**\n * Sections only: the section's shape in a few words and the signals that show it in a source page, a\n * closed-set label for the classifier and the migration architect (`image beside heading + text`).\n */\n pattern?: { summary: string, signals: string[] }\n /**\n * Sections only, written by the catalog build: each content prop path (`slides[].ctaLabel`) → the\n * page-singleton field path that holds it (`slides[].cta_label`).\n */\n contentFields?: Record<string, string>\n}\n\n/** A catalog entry: the meta plus the files that make the component. */\nexport interface KitComponent extends KitComponentMeta {\n /** Paths relative to `components/<id>/`. */\n files: string[]\n}\n\nexport interface KitCatalog {\n format: typeof CATALOG_FORMAT\n /** Design tokens (Tailwind @theme names) the components read; the site must define them. */\n tokens: string[]\n /** npm packages every kit component needs. */\n dependencies: Record<string, string>\n components: KitComponent[]\n}\n\n/** Problems with a catalog, one line each; empty when it is sound. */\nexport function validateCatalog(catalog: KitCatalog): string[] {\n const problems: string[] = []\n const ids = new Set(catalog.components.map(c => c.id))\n for (const c of catalog.components) {\n const at = `component ${c.id}`\n if (!/^[a-z][a-z0-9-]*$/.test(c.id)) problems.push(`${at}: id must be kebab-case`)\n if (!c.files.length) problems.push(`${at}: no files`)\n for (const used of c.uses) if (!ids.has(used)) problems.push(`${at}: uses unknown component \"${used}\"`)\n for (const [axis, variant] of Object.entries(c.variants)) {\n if (!variant.options.includes(variant.default)) problems.push(`${at}: variant ${axis} default \"${variant.default}\" is not an option`)\n }\n for (const [name, prop] of Object.entries(c.props)) {\n if (!prop.description) problems.push(`${at}: prop ${name} has no description`)\n if (prop.type === 'select' && !prop.options?.length) problems.push(`${at}: select prop ${name} has no options`)\n }\n if (c.category === 'section') {\n // A section is stored as one object field of its page singleton: each content prop needs one field\n // name there, and that field must stay within two object levels.\n const content = contentProps(c)\n const paths = fieldsByPath(content)\n for (const [path, field] of paths) {\n if (!CONTENT_PROP_NAME.test(path.split(/\\.|\\[\\]\\./).at(-1)!)) problems.push(`${at}: content prop ${path} must be camelCase with one capital per word (or content: false)`)\n // Studio shows both to editors, and the plan reads them as the field's label and help text.\n if (!field.label) problems.push(`${at}: content prop ${path} has no label`)\n if (!field.description) problems.push(`${at}: content prop ${path} has no description`)\n }\n if (c.contentFields) {\n const expected = Object.fromEntries(paths.map(([path]) => [path, contentFieldPath(path)]))\n if (JSON.stringify(c.contentFields) !== JSON.stringify(expected)) problems.push(`${at}: contentFields is stale — rebuild the catalog`)\n }\n for (const [name, prop] of Object.entries(content)) {\n if (1 + fieldDepth(prop) > 2) problems.push(`${at}: content prop ${name} nests objects deeper than a page singleton allows`)\n }\n }\n for (const [builder, names] of Object.entries(c.sources) as Array<[KitBuilder, string[]]>) {\n const prefix = BUILDER_PREFIX[builder]\n for (const name of names) if (!isBuilderElement(builder, name)) problems.push(`${at}: source \"${name}\" is not a ${builder} element (${prefix}…)`)\n }\n }\n const seen = new Set<string>()\n for (const c of catalog.components) {\n if (seen.has(c.id)) problems.push(`component ${c.id}: listed twice`)\n seen.add(c.id)\n }\n return problems\n}\n\n/** Components that replace a builder element, e.g. `core/cover` → hero. */\nexport function componentsForSource(catalog: KitCatalog, element: string): KitComponent[] {\n return catalog.components.filter(c => Object.values(c.sources).some(names => names?.includes(element)))\n}\n","// Copying components into a site — the shadcn model: the site owns the\n// source, there is no runtime dependency on this package. A component is\n// copied with every kit component it renders and every shared file it\n// imports, under `src/components/kit/`, keeping the relative layout its\n// imports expect.\n\nimport { copyFile, mkdir } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport type { KitCatalog } from './catalog.js'\n\n/** Where kit files land in a site, relative to the site root. */\nexport const KIT_DIR = 'src/components/kit'\n\nexport interface CopyPlan {\n /** Components copied, the requested ones and everything they render, sorted. */\n components: string[]\n /** Site-relative destination → path relative to the kit's `components/` directory. */\n files: Record<string, string>\n /** npm packages the site needs, name → range. */\n dependencies: Record<string, string>\n}\n\nexport function planCopy(catalog: KitCatalog, ids: readonly string[]): CopyPlan {\n const byId = new Map(catalog.components.map(c => [c.id, c]))\n const wanted = new Set<string>()\n const visit = (id: string) => {\n if (wanted.has(id)) return\n const component = byId.get(id)\n if (!component) throw new Error(`astro-kit has no component \"${id}\". Known: ${[...byId.keys()].join(', ')}.`)\n wanted.add(id)\n for (const used of component.uses) visit(used)\n }\n for (const id of ids) visit(id)\n\n const files: Record<string, string> = {}\n const dependencies: Record<string, string> = { ...catalog.dependencies }\n for (const id of [...wanted].toSorted()) {\n const component = byId.get(id)!\n for (const file of component.files) files[`${KIT_DIR}/${id}/${file}`] = `${id}/${file}`\n for (const file of component.shared) files[`${KIT_DIR}/_shared/${file}`] = `_shared/${file}`\n Object.assign(dependencies, component.dependencies)\n }\n return { components: [...wanted].toSorted(), files, dependencies }\n}\n\n/** Carry out a plan: `kitRoot` is the kit's `components/` directory, `siteRoot` the site. */\nexport async function copyComponents(plan: CopyPlan, kitRoot: string, siteRoot: string): Promise<void> {\n await Promise.all(Object.entries(plan.files).map(async ([dest, source]) => {\n const target = join(siteRoot, dest)\n await mkdir(dirname(target), { recursive: true })\n await copyFile(join(kitRoot, source), target)\n }))\n}\n","// Builder mapping tables: which kit component a WordPress builder element\n// becomes, and where each of its props comes from. One versioned table per\n// builder (`mapping/<builder>.json`), written once and applied\n// deterministically to every site — the migration model is asked only about\n// elements no rule covers (Migrate's `unmapped_element` decision).\n//\n// A prop's source is one expression from a small closed set. The first six are\n// the project plan's values (`@contentrain/types` `PlanValue`) and bind\n// content; the rest read the builder element itself or the page it is on:\n//\n// field:<name> · media:<field> · ref:<field>.<field> · href:self|<field> · term:<field> · ui:<key> · const:<value>\n// site:<field> a field of the `site` singleton (`site:title`, `site:logo`)\n// menu:primary|footer the menu the starter shows in that area\n// page:<value> what the route knows: `page:base`, `page:current`, `page:total`, `page:breadcrumb`\n// attr:<name> a builder attribute (block attribute, Elementor setting, Divi shortcode attribute)\n// dom:<selector> text of the first match inside the element (empty selector: the element)\n// dom:<selector>@<attr> an attribute of it (`dom:a@href`)\n// img:<selector> an image as the kit's `ImageInput` (src, alt, width, height)\n// img:<selector>@src|@alt one part of that image, for a list item's flat `image` + `imageAlt` fields\n// link:<selector> a link as `{ label, href }`\n// html:<selector> inner markup, kept as Markdown (rich text)\n// class:<cls>=<a>|<b> <a> when the element, or an ancestor inside the matched element, has class\n// <cls> (`*` matches any run of characters); <b> otherwise. An empty branch\n// reads nothing and the prop keeps its default. `class:is-style-outline=ghost|primary`\n// is WordPress's outline button as the kit's ghost action.\n//\n// Section rules (`sections`) classify a whole page section by the leaves it holds and fill the component's\n// props from them: the same expressions, read from a named slot's leaf (`@title attr:title`).\n\nimport type { KitBuilder, KitCatalog, KitComponent } from './catalog.js'\nimport { isBuilderElement, KIT_BUILDERS } from './catalog.js'\n\nexport const MAPPING_FORMAT = 'astro-kit-mapping@1'\n\nexport const MAPPING_VALUE_PATTERN = /^(?:field:[\\w-]+|media:[\\w-]+|ref:[\\w-]+\\.[\\w-]+|href:(?:self|[\\w-]+)|term:[\\w-]+|ui:[\\w.-]+|const:(?:true|false|-?\\d+(?:\\.\\d+)?|[a-z][a-z0-9_-]{0,31})|site:[\\w-]+|menu:(?:primary|footer)|page:(?:base|current|total|breadcrumb)|attr:[\\w.-]+|class:[\\w*-]+=(?:[a-z][\\w-]*)?\\|(?:[a-z][\\w-]*)?|(?:dom|img|link|html):[^@]*(?:@[\\w-]+)?)$/\n\nexport interface MappingRule {\n /** Builder element, as the fact pack names it; a `:qualifier` narrows it (`core/template-part:header`). */\n match: string\n /** Extra conditions on the element. */\n when?: { attr?: Record<string, string | number | boolean>, class?: string }\n /** Kit component id. */\n component: string\n variant?: Record<string, string>\n /** Content the element lists rather than holds: one component per entry of the collection (`collection:posts`). */\n bind?: `collection:${string}`\n /** Consecutive sibling matches form one component (a row of Elementor icon boxes is one card grid). */\n group?: 'siblings'\n props?: Record<string, string>\n /** Array prop filled from repeated parts of the element (`each`) or the grouped siblings / bound entries. */\n into?: string\n /** Selector of the repeated parts inside the element, for `into`. */\n each?: string\n /** Props of one repeated part / sibling / entry, with sources relative to it. */\n item?: Record<string, string>\n}\n\nexport interface MappingTable {\n format: typeof MAPPING_FORMAT\n builder: KitBuilder\n /** Bumped on any rule change: plans record the version they applied. */\n version: string\n /** What an element no rule covers becomes when no decision is made: rich text. */\n fallback: 'prose'\n /**\n * Attribute values the builder leaves out of its data while they are at their default, per element:\n * Elementor saves no `video_type` for a YouTube video. `when.attr` and `attr:` read through them.\n */\n defaults?: Record<string, Record<string, string | number | boolean>>\n rules: MappingRule[]\n /**\n * Section rules: what a whole section of a page (an Elementor container, a Gutenberg group or columns,\n * a Divi section — facts `sections[]`) becomes, tried top to bottom before any element rule. The first\n * that matches wins; a section none matches falls through to the element `rules` inside it.\n */\n sections?: SectionRule[]\n}\n\n/**\n * A named group of a section's leaves: every leaf named `match` (and carrying `attr`) up to `max` of them.\n * `attr` values may list alternatives with `|` (`{ header_size: 'h1|h2' }`).\n */\nexport interface SectionSlot {\n match: string | string[]\n attr?: Record<string, string | number | boolean>\n /** At least this many leaves (default 0). */\n min?: number\n /** At most this many (default unbounded). */\n max?: number\n /** The leaf is a figure (`1,200`, `2018`, `$29`, `01`): its facts say so. `true` requires it, `false` rules it out. */\n numeric?: boolean\n /** The leaf is a step's ordinal (`1`, `01`, `Step 2` — at most two digits): its facts say so. A year or a count is a figure, not an ordinal. */\n ordinal?: boolean\n /** The image is logo-sized (small as displayed): its facts say so. Photos and banners are not. */\n small?: boolean\n /** The image is a portrait: a square-ish photo at least 200px across (a team member's face). Logos and banners are not. */\n portrait?: boolean\n /** The leaf's text is one short line — a name, a role, a label — not a description: its facts say so. */\n short?: boolean\n /**\n * `intro`: the slot holds the leaves above the repeated columns (a section's heading and lead line), not the\n * columns' own. Only with `when.repeat: 'columns'`; every other slot of such a rule reads inside a column.\n */\n scope?: 'intro'\n}\n\n/**\n * One section pattern. Values are the table's expressions, prefixed `@<slot> ` to read a slot's leaf\n * (`@title attr:title`); `a || b` takes the first non-empty (builder data first, rendered markup as fallback).\n * A slot of several leaves joins their text into a markdown prop; any other prop reads its first leaf.\n * A value without a slot reads the section's own root (a cover's background image, a container's setting).\n */\nexport interface SectionRule {\n /** Stable id (`hero.split`, `card-grid.icon-box`): plans record `<builder>:section:<id>`; JEW chooses among them. */\n id: string\n component: string\n variant?: Record<string, string>\n when?: {\n /** Where on the page: `first` is the page's first section, `last` its last. */\n position?: 'first' | 'last' | 'any'\n /** Column count(s) the section must have. */\n columns?: number | number[]\n /** Every leaf must fall into a slot (default true); false lets unclaimed leaves be. */\n only?: boolean\n /**\n * `columns`: each column must match the slots on its own (a row of team members, price plans); the leaves above\n * the row (`SectionMeta.intro`) go to the slots with `scope: 'intro'`.\n */\n repeat?: 'columns'\n /** The section's root element(s): `core/cover`, `core/media-text` (a loose run of blocks is `run`). */\n root?: string | string[]\n /**\n * Two slots in the order the source writes them: where both hold a leaf (in each column, with `repeat`), the first\n * leaf of the one must come before the first of the other. `['figure', 'label']` is a figure written above its\n * label, so a rule can pick the variant the source's reading order needs (G-text) and leave the other order to the\n * next rule.\n */\n order?: [string, string]\n }\n /**\n * Named leaf groups. Each leaf, in document order, goes to the first declared slot that matches it and\n * still has room, so declare a specific slot (an `attr` filter) before a general one on the same element.\n */\n slots: Record<string, SectionSlot>\n props?: Record<string, string>\n /**\n * Array prop filled once per `each`: a slot's leaves (`@card`), the parts a selector finds inside them\n * (`@list figure.wp-block-image`, `@faq .elementor-accordion-item`: a gallery's images, an accordion's\n * items), or the columns (`column`, with `repeat: 'columns'`).\n */\n into?: string\n each?: string\n /** One array item: relative to the leaf (`each: '@slot'`) or to the column, where `@<slot>` is that column's slot. */\n item?: Record<string, string>\n}\n\n/** A section leaf as the fact pack gives it: a builder element with its `blocks:` path. */\nexport interface SectionLeaf extends MappingNode {\n path: string\n /** Facts' flag: the leaf's text is a figure (a number, an amount, a step number) — not the text itself. */\n numeric?: boolean\n /** Facts' flag: the leaf's text is an ordinal — one or two digits, nothing else. */\n ordinal?: boolean\n /** Facts' flag: the image is logo-sized as displayed. */\n small?: boolean\n /** Facts' flag: the image is a square-ish photo, at least 200px across. */\n portrait?: boolean\n /** Facts' flag: the leaf's text is one short line (at most 40 characters, no line break). */\n short?: boolean\n}\n\n/** Where a section sits (facts `index`/`count`) and what it is (`root`: facts `root`). */\nexport interface SectionMeta {\n index: number\n count: number\n root?: string | undefined\n /** The leaves above a row of columns, in the same wrapper (`Who we are` over its people): see `when.repeat`. */\n intro?: SectionLeaf[] | undefined\n}\n\n/** Leaf paths per slot; with `repeat: 'columns'`, also per column. */\nexport interface SectionMatch {\n slots: Record<string, string[]>\n columns?: Record<string, string[]>[]\n}\n\n/** The id a plan records for a section rule. */\nexport function sectionRuleId(builder: KitBuilder, rule: Pick<SectionRule, 'id'>): string {\n return `${builder}:section:${rule.id}`\n}\n\nconst SECTION_LAYOUT_LEAVES = new Set(['elementor/spacer', 'elementor/divider', 'core/spacer', 'core/separator', 'divi/et_pb_divider'])\n\nfunction slotFits(slot: SectionSlot, leaf: SectionLeaf, defaults?: MappingTable['defaults']): boolean {\n if (![slot.match].flat().includes(leaf.name)) return false\n // A flag the facts do not set reads as \"no\": a rule that asks for one never fires on facts that predate it.\n if (slot.numeric !== undefined && slot.numeric !== (leaf.numeric === true)) return false\n if (slot.ordinal !== undefined && slot.ordinal !== (leaf.ordinal === true)) return false\n if (slot.small !== undefined && slot.small !== (leaf.small === true)) return false\n if (slot.portrait !== undefined && slot.portrait !== (leaf.portrait === true)) return false\n if (slot.short !== undefined && slot.short !== (leaf.short === true)) return false\n const attrs = attrsOf(leaf, defaults)\n return Object.entries(slot.attr ?? {}).every(([key, want]) => String(want).split('|').includes(String(attrs[key])))\n}\n\n/**\n * Assign leaves to slots in document order, each to the first declared slot with room; null when the rule does not fit.\n * `scope` picks the slots in play: a repeated row's columns read the column slots, the leaves above it the intro slots;\n * any other section reads them all.\n */\nfunction claimLeaves(rule: SectionRule, leaves: SectionLeaf[], defaults?: MappingTable['defaults'], scope: 'all' | 'column' | 'intro' = 'all'): Record<string, string[]> | null {\n const slots = Object.entries(rule.slots).filter(([, def]) => scope === 'all' || (def.scope === 'intro') === (scope === 'intro'))\n const claimed: Record<string, string[]> = Object.fromEntries(slots.map(([name]) => [name, []]))\n for (const leaf of leaves) {\n if (SECTION_LAYOUT_LEAVES.has(leaf.name)) continue\n const slot = slots.find(([name, def]) => claimed[name]!.length < (def.max ?? Infinity) && slotFits(def, leaf, defaults))\n if (slot) claimed[slot[0]]!.push(leaf.path)\n else if (rule.when?.only !== false) return null\n }\n for (const [name, def] of slots) if (claimed[name]!.length < (def.min ?? 0)) return null\n const [first, then] = rule.when?.order ?? []\n const a = first ? claimed[first]?.[0] : undefined\n const b = then ? claimed[then]?.[0] : undefined\n if (a && b) {\n const at = (path: string) => leaves.findIndex(l => l.path === path)\n if (at(a) > at(b)) return null\n }\n return claimed\n}\n\n/**\n * Whether a section rule fits a section, and which leaves fill which slot. `columns` are the section's\n * columns left to right, each its leaves in document order (layout containers already opened).\n */\nexport function matchSection(rule: SectionRule, columns: SectionLeaf[][], meta: SectionMeta, defaults?: MappingTable['defaults']): SectionMatch | null {\n const when = rule.when ?? {}\n if (when.root !== undefined && !(meta.root !== undefined && [when.root].flat().includes(meta.root))) return null\n if (when.position === 'first' && meta.index !== 0) return null\n if (when.position === 'last' && meta.index !== meta.count - 1) return null\n if (when.columns !== undefined && ![when.columns].flat().includes(columns.length)) return null\n if (when.repeat === 'columns') {\n if (columns.length < 2) return null\n const intro = claimLeaves(rule, meta.intro ?? [], defaults, 'intro')\n if (!intro) return null\n const per = columns.map(leaves => claimLeaves(rule, leaves, defaults, 'column'))\n if (per.some(c => !c)) return null\n const slots: Record<string, string[]> = { ...intro }\n for (const [name, def] of Object.entries(rule.slots)) if (def.scope !== 'intro') slots[name] = per.flatMap(c => c![name]!)\n return { slots, columns: per as Record<string, string[]>[] }\n }\n // Without a repeat the leaves above the row are simply the section's first leaves, as before the facts split them off.\n const slots = claimLeaves(rule, [...(meta.intro ?? []), ...columns.flat()], defaults)\n return slots && { slots }\n}\n\n/** The first section rule of a table that fits, with its match. */\nexport function classifySection(table: MappingTable, columns: SectionLeaf[][], meta: SectionMeta): { rule: SectionRule, match: SectionMatch } | null {\n for (const rule of table.sections ?? []) {\n const match = matchSection(rule, columns, meta, table.defaults)\n if (match) return { rule, match }\n }\n return null\n}\n\n/** A section value: alternatives joined by ` || `, each an optional `@<slot> ` then a mapping value. Returns the slots it reads, or null when malformed. */\nexport function sectionValueSlots(value: string): string[] | null {\n const slots: string[] = []\n for (const alternative of value.split(' || ')) {\n const m = /^(?:@([a-z][\\w-]*) )?(.+)$/.exec(alternative)\n if (!m || !MAPPING_VALUE_PATTERN.test(m[2]!)) return null\n if (m[1]) slots.push(m[1])\n }\n return slots\n}\n\n/** Problems with a mapping table against the catalog, one line each; empty when it is sound. */\nexport function validateMapping(table: MappingTable, catalog: KitCatalog): string[] {\n const problems: string[] = []\n if (table.format !== MAPPING_FORMAT) problems.push(`format is not ${MAPPING_FORMAT}`)\n if (!KIT_BUILDERS.includes(table.builder)) problems.push(`unknown builder ${table.builder}`)\n if (!/^\\d+$/.test(table.version)) problems.push('version is not an integer string')\n for (const element of Object.keys(table.defaults ?? {})) if (!isBuilderElement(table.builder, element)) problems.push(`defaults ${element}: not a ${table.builder} element`)\n const components = new Map(catalog.components.map(c => [c.id, c]))\n const seen = new Set<string>()\n for (const rule of table.rules) {\n const at = `${table.builder} ${rule.match}${rule.when ? ' (when)' : ''}`\n const key = `${rule.match}|${JSON.stringify(rule.when ?? {})}`\n if (seen.has(key)) problems.push(`${at}: matched twice`)\n seen.add(key)\n if (!isBuilderElement(table.builder, rule.match)) problems.push(`${at}: not a ${table.builder} element`)\n const c = components.get(rule.component)\n if (!c) { problems.push(`${at}: component ${rule.component} is not in the catalog`); continue }\n for (const [axis, value] of Object.entries(rule.variant ?? {})) {\n // A variant is one option, or a `class:` choice between options read from the element.\n const choice = /^class:[\\w*-]+=([a-z][\\w-]*)?\\|([a-z][\\w-]*)?$/.exec(value)\n const options = choice ? [choice[1], choice[2]].filter((option): option is string => option !== undefined) : [value]\n if (!c.variants[axis]) problems.push(`${at}: ${c.id} has no variant axis ${axis}`)\n else for (const missing of options.filter(option => !c.variants[axis]!.options.includes(option))) problems.push(`${at}: ${c.id}.${axis} has no option ${missing}`)\n }\n for (const [prop, value] of Object.entries({ ...rule.props, ...(rule.into ? {} : rule.item) })) {\n if (!c.props[prop]) problems.push(`${at}: ${c.id} has no prop ${prop}`)\n if (!MAPPING_VALUE_PATTERN.test(value)) problems.push(`${at}: ${prop} = ${value} is not a mapping value`)\n }\n if (rule.into) {\n const target = c.props[rule.into]\n if (!target || target.type !== 'array') problems.push(`${at}: ${c.id}.${rule.into} is not an array prop`)\n const fields = target && typeof target.items === 'object' ? target.items.fields ?? {} : {}\n for (const [field, value] of Object.entries(rule.item ?? {})) {\n if (!fields[field]) problems.push(`${at}: ${c.id}.${rule.into}[] has no field ${field}`)\n if (!MAPPING_VALUE_PATTERN.test(value)) problems.push(`${at}: ${rule.into}[].${field} = ${value} is not a mapping value`)\n }\n if (!rule.each && !rule.group && !rule.bind) problems.push(`${at}: into ${rule.into} needs each, group or bind to say what repeats`)\n }\n if (rule.item && !rule.into && !rule.bind) problems.push(`${at}: item without into or bind`)\n const bound = new Set([...Object.keys(rule.props ?? {}), ...(rule.into ? [rule.into] : Object.keys(rule.item ?? {}))])\n for (const [prop, def] of Object.entries(c.props)) if (def.required && !bound.has(prop)) problems.push(`${at}: required prop ${c.id}.${prop} has no source`)\n }\n problems.push(...validateSections(table, components))\n return problems\n}\n\nfunction validateSections(table: MappingTable, components: Map<string, KitComponent>): string[] {\n const problems: string[] = []\n const seen = new Set<string>()\n for (const rule of table.sections ?? []) {\n const at = `${table.builder} section ${rule.id}`\n if (!/^[a-z][a-z0-9-]*(?:\\.[a-z0-9-]+)*$/.test(rule.id)) problems.push(`${at}: id is not dotted kebab-case`)\n if (seen.has(rule.id)) problems.push(`${at}: listed twice`)\n seen.add(rule.id)\n const c = components.get(rule.component)\n if (!c) { problems.push(`${at}: component ${rule.component} is not in the catalog`); continue }\n for (const [axis, value] of Object.entries(rule.variant ?? {})) {\n if (!c.variants[axis]) problems.push(`${at}: ${c.id} has no variant axis ${axis}`)\n else if (!c.variants[axis]!.options.includes(value)) problems.push(`${at}: ${c.id}.${axis} has no option ${value}`)\n }\n if (!Object.keys(rule.slots).length) problems.push(`${at}: no slots`)\n for (const [name, slot] of Object.entries(rule.slots)) {\n if (!/^[a-z][\\w-]*$/.test(name)) problems.push(`${at}: slot ${name} is not an identifier`)\n // A form or shortcode plugin (`classic/contact-form-7`) sits inside any builder's section, as the plan reads it from a shortcode.\n for (const element of [slot.match].flat()) if (!isBuilderElement(table.builder, element) && !isBuilderElement('classic', element)) problems.push(`${at}: slot ${name} matches ${element}, not a ${table.builder} or classic plugin element`)\n if (slot.min !== undefined && slot.max !== undefined && slot.min > slot.max) problems.push(`${at}: slot ${name} min > max`)\n }\n for (const root of [rule.when?.root ?? []].flat()) if (root !== 'run' && !isBuilderElement(table.builder, root)) problems.push(`${at}: root ${root} is not a ${table.builder} element or run`)\n if (rule.when?.repeat === 'columns' && rule.each !== 'column') problems.push(`${at}: repeat columns needs each: column`)\n if (rule.each === 'column' && rule.when?.repeat !== 'columns') problems.push(`${at}: each column needs when.repeat columns`)\n for (const [name, slot] of Object.entries(rule.slots)) if (slot.scope === 'intro' && rule.when?.repeat !== 'columns') problems.push(`${at}: slot ${name} is scope intro, which needs when.repeat columns`)\n for (const name of rule.when?.order ?? []) if (!rule.slots[name]) problems.push(`${at}: when.order names ${name}, which is not a slot`)\n if (rule.when?.order && (rule.slots[rule.when.order[0]]?.scope === 'intro') !== (rule.slots[rule.when.order[1]]?.scope === 'intro')) problems.push(`${at}: when.order compares an intro slot with a column slot`)\n const readsSlots = (value: string, where: string, type: string | undefined, slotted: boolean, item = false) => {\n const slots = sectionValueSlots(value)\n if (!slots) { problems.push(`${at}: ${where} = ${value} is not a section value`); return }\n if (!slotted && slots.length) problems.push(`${at}: ${where} reads a slot, but items of each ${rule.each} read their own leaf`)\n for (const slot of slots) {\n const def = rule.slots[slot]\n if (!def) problems.push(`${at}: ${where} reads undeclared slot ${slot}`)\n else if ((type === 'string' || type === 'text') && def.max !== 1) problems.push(`${at}: ${where} is ${type} but slot ${slot} may hold several leaves (max must be 1)`)\n else if (item && def.scope === 'intro') problems.push(`${at}: ${where} reads intro slot ${slot}, but the items of each column read their own column`)\n }\n }\n for (const [prop, value] of Object.entries(rule.props ?? {})) {\n const def = c.props[prop]\n if (!def) problems.push(`${at}: ${c.id} has no prop ${prop}`)\n readsSlots(value, prop, def?.type, true)\n }\n if (rule.into) {\n const target = c.props[rule.into]\n if (!target || target.type !== 'array') problems.push(`${at}: ${c.id}.${rule.into} is not an array prop`)\n if (!rule.each) problems.push(`${at}: into ${rule.into} needs each`)\n else if (rule.each !== 'column' && !rule.slots[/^@([a-z][\\w-]*)(?: \\S.*)?$/.exec(rule.each)?.[1] ?? '']) problems.push(`${at}: each ${rule.each} is not @<declared slot> [selector] or column`)\n const fields = target && typeof target.items === 'object' ? target.items.fields ?? {} : {}\n for (const [field, value] of Object.entries(rule.item ?? {})) {\n if (!fields[field]) problems.push(`${at}: ${c.id}.${rule.into}[] has no field ${field}`)\n readsSlots(value, `${rule.into}[].${field}`, fields[field]?.type, rule.each === 'column', true)\n }\n } else if (rule.item || rule.each) problems.push(`${at}: each/item without into`)\n const bound = new Set([...Object.keys(rule.props ?? {}), ...(rule.into ? [rule.into] : [])])\n for (const [prop, def] of Object.entries(c.props)) if (def.required && !bound.has(prop)) problems.push(`${at}: required prop ${c.id}.${prop} has no source`)\n }\n return problems\n}\n\n/** Catalog sources of a builder that no rule of its table matches, as an element rule or a section slot. */\nexport function unmappedSources(table: MappingTable, catalog: KitCatalog): string[] {\n const matched = new Set([...table.rules.map(r => r.match), ...(table.sections ?? []).flatMap(r => Object.values(r.slots).flatMap(s => [s.match].flat()))])\n const sources = catalog.components.flatMap((c: KitComponent) => c.sources[table.builder] ?? [])\n return [...new Set(sources)].filter(s => !matched.has(s)).toSorted()\n}\n\n/** Rules for an element, most specific (with `when` / qualifier) first. */\nexport function rulesFor(table: MappingTable, element: string): MappingRule[] {\n return table.rules\n .filter(r => r.match === element || r.match.split(':')[0] === element)\n .toSorted((a, b) => Number(!!b.when || b.match.includes(':')) - Number(!!a.when || a.match.includes(':')))\n}\n\n/** A builder element as the fact pack gives it (`FactBlock`): name, attributes, children. */\nexport interface MappingNode {\n name: string\n attrs?: Record<string, unknown>\n children?: MappingNode[]\n}\n\n/** An element's attributes with the builder's unsaved defaults filled in. */\nexport function attrsOf(node: MappingNode, defaults: MappingTable['defaults'] = {}): Record<string, unknown> {\n return { ...defaults[node.name], ...node.attrs }\n}\n\n/** Whether a rule's `match` (with an optional `:qualifier`) and `when.attr` fit an element. */\nexport function ruleMatches(rule: MappingRule, node: MappingNode, defaults?: MappingTable['defaults']): boolean {\n const [name, qualifier] = rule.match.split(':')\n if (name !== node.name) return false\n const attrs = attrsOf(node, defaults)\n // A qualifier names the element's role: a template part's area or slug, or a sub-part the fact pack marks.\n if (qualifier && ![attrs.area, attrs.slug, attrs.tagName, attrs.qualifier].includes(qualifier)) return false\n for (const [key, value] of Object.entries(rule.when?.attr ?? {})) if (attrs[key] !== value) return false\n if (rule.when?.class && !String(attrs.className ?? attrs.classes ?? '').split(/\\s+/).includes(rule.when.class)) return false\n return true\n}\n\n/**\n * Apply a table to a tree: the outermost matching element owns its whole subtree, and nothing inside\n * it is matched again — a header template part holding the site title and logo is one header, not three.\n */\nexport function claimMatches(nodes: MappingNode[], table: MappingTable, path = '0'): { path: string, node: MappingNode, rule: MappingRule }[] {\n return nodes.flatMap((node, i) => {\n const at = `${path}.${i}`\n const rule = rulesFor(table, node.name).find(r => ruleMatches(r, node, table.defaults))\n return rule ? [{ path: at, node, rule }] : claimMatches(node.children ?? [], table, at)\n })\n}\n","import { readFile } from 'node:fs/promises'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport type { KitBuilder, KitCatalog } from './catalog.js'\nimport type { MappingTable } from './mapping.js'\n\nexport * from './catalog.js'\nexport * from './copy.js'\nexport * from './mapping.js'\n\nconst PACKAGE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..')\n\n/** The kit's `components/` directory — the source `copyComponents` copies from. */\nexport const KIT_COMPONENTS_DIR = join(PACKAGE_ROOT, 'components')\n\n/** A builder's mapping table (`mapping/<builder>.json`). */\nexport async function loadMapping(builder: KitBuilder): Promise<MappingTable> {\n return JSON.parse(await readFile(join(PACKAGE_ROOT, 'mapping', `${builder}.json`), 'utf8')) as MappingTable\n}\n\n/** The published catalog (`catalog.json`). */\nexport async function loadCatalog(): Promise<KitCatalog> {\n return JSON.parse(await readFile(join(PACKAGE_ROOT, 'catalog.json'), 'utf8')) as KitCatalog\n}\n"],"mappings":"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"}
package/mapping/divi.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "format": "astro-kit-mapping@1",
3
3
  "builder": "divi",
4
- "version": "5",
4
+ "version": "6",
5
5
  "fallback": "prose",
6
6
  "rules": [
7
7
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "format": "astro-kit-mapping@1",
3
3
  "builder": "gutenberg",
4
- "version": "6",
4
+ "version": "7",
5
5
  "fallback": "prose",
6
6
  "rules": [
7
7
  {
@@ -218,7 +218,8 @@
218
218
  "component": "hero",
219
219
  "variant": {
220
220
  "layout": "cover",
221
- "align": "class:is-position-*-left=start|center"
221
+ "align": "class:is-position-*-left=start|center",
222
+ "width": "class:alignfull=full|content"
222
223
  },
223
224
  "props": {
224
225
  "heading": "dom:h1,h2",
@@ -677,6 +678,64 @@
677
678
  "root": [
678
679
  "core/columns",
679
680
  "core/group"
681
+ ],
682
+ "order": [
683
+ "figure",
684
+ "label"
685
+ ]
686
+ },
687
+ "slots": {
688
+ "heading": {
689
+ "match": "core/heading",
690
+ "max": 1,
691
+ "scope": "intro"
692
+ },
693
+ "lead": {
694
+ "match": "core/paragraph",
695
+ "max": 1,
696
+ "scope": "intro"
697
+ },
698
+ "figure": {
699
+ "match": "core/heading",
700
+ "min": 1,
701
+ "max": 1,
702
+ "numeric": true
703
+ },
704
+ "label": {
705
+ "match": "core/paragraph",
706
+ "max": 1
707
+ }
708
+ },
709
+ "props": {
710
+ "heading": "@heading dom:",
711
+ "intro": "@lead dom:"
712
+ },
713
+ "into": "items",
714
+ "each": "column",
715
+ "item": {
716
+ "value": "@figure dom:",
717
+ "label": "@label dom:"
718
+ }
719
+ },
720
+ {
721
+ "id": "stats.columns-label-first",
722
+ "component": "stats",
723
+ "when": {
724
+ "columns": [
725
+ 2,
726
+ 3,
727
+ 4,
728
+ 5,
729
+ 6
730
+ ],
731
+ "repeat": "columns",
732
+ "root": [
733
+ "core/columns",
734
+ "core/group"
735
+ ],
736
+ "order": [
737
+ "label",
738
+ "figure"
680
739
  ]
681
740
  },
682
741
  "slots": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentrain/astro-kit",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "license": "MIT",
5
5
  "description": "Copy-in Astro components for Contentrain sites (shadcn model): typed props, tailwind-variants, zero JavaScript by default — with a machine-readable catalog",
6
6
  "type": "module",