@uniweb/semantic-parser 1.1.4 → 1.1.6

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.
@@ -1,416 +0,0 @@
1
- /**
2
- * Pre-built extractors for common component patterns
3
- *
4
- * All extractors work with the flat content structure:
5
- * - Root level: title, pretitle, subtitle, paragraphs, links, imgs, items, etc.
6
- * - Items array: each item has flat structure (title, paragraphs, etc.)
7
- */
8
-
9
- import { first, joinParagraphs } from "./helpers.js";
10
-
11
- /**
12
- * Extract hero component data
13
- * Common pattern: Large header with title, subtitle, image, and CTA
14
- *
15
- * @param {Object} parsed - Parsed content from parseContent()
16
- * @returns {Object} Hero component data
17
- */
18
- function hero(parsed) {
19
- const links = parsed?.links || [];
20
- const buttonLink = links.find(l => l.role?.startsWith('button'));
21
- const plainLink = links.find(l => !l.role?.startsWith('button'));
22
-
23
- return {
24
- title: parsed?.title || null,
25
- subtitle: parsed?.subtitle || null,
26
- kicker: parsed?.pretitle || null,
27
- description: parsed?.paragraphs || [],
28
- image: first(parsed?.imgs)?.url || null,
29
- imageAlt: first(parsed?.imgs)?.alt || null,
30
- banner: null, // Banner detection would need to be added separately
31
- cta: buttonLink || plainLink || null,
32
- };
33
- }
34
-
35
- /**
36
- * Extract card component data
37
- * Common pattern: Title, description, image, and link
38
- *
39
- * @param {Object} parsed - Parsed content from parseContent()
40
- * @param {Object} options - Extraction options
41
- * @param {boolean} options.useItems - Extract from items instead of main
42
- * @param {number} options.itemIndex - Specific item index to extract from
43
- * @returns {Object|Array} Card data or array of cards if useItems=true
44
- */
45
- function card(parsed, options = {}) {
46
- const { useItems = false, itemIndex } = options;
47
-
48
- const extractCard = (content) => {
49
- if (!content) return null;
50
-
51
- const links = content.links || [];
52
- const buttonLink = links.find(l => l.role?.startsWith('button'));
53
- const plainLink = links.find(l => !l.role?.startsWith('button'));
54
-
55
- return {
56
- title: content.title || null,
57
- subtitle: content.subtitle || null,
58
- description: content.paragraphs || [],
59
- image: first(content.imgs)?.url || null,
60
- imageAlt: first(content.imgs)?.alt || null,
61
- icon: first(content.icons) || null,
62
- link: plainLink || null,
63
- cta: buttonLink || plainLink || null,
64
- };
65
- };
66
-
67
- if (useItems) {
68
- const items = parsed?.items || [];
69
- if (itemIndex !== undefined) {
70
- return extractCard(items[itemIndex]);
71
- }
72
- return items.map(extractCard).filter(Boolean);
73
- }
74
-
75
- return extractCard(parsed);
76
- }
77
-
78
- /**
79
- * Extract article/blog content
80
- * Common pattern: Title, author info, content blocks, images
81
- *
82
- * @param {Object} parsed - Parsed content from parseContent()
83
- * @returns {Object} Article data
84
- */
85
- function article(parsed) {
86
- return {
87
- title: parsed?.title || null,
88
- subtitle: parsed?.subtitle || null,
89
- kicker: parsed?.pretitle || null,
90
- author: null, // Would need metadata support
91
- date: null, // Would need metadata support
92
- banner: null, // Banner detection would need to be added separately
93
- content: parsed?.paragraphs || [],
94
- images: parsed?.imgs || [],
95
- videos: parsed?.videos || [],
96
- links: parsed?.links || [],
97
- };
98
- }
99
-
100
- /**
101
- * Extract statistics/metrics data
102
- * Common pattern: Numeric value with label
103
- *
104
- * @param {Object} parsed - Parsed content from parseContent()
105
- * @returns {Array} Array of stat objects
106
- */
107
- function stats(parsed) {
108
- const items = parsed?.items || [];
109
-
110
- return items
111
- .map((item) => ({
112
- value: item.title || null,
113
- label: item.subtitle || first(item.paragraphs) || null,
114
- description: item.paragraphs || [],
115
- }))
116
- .filter((stat) => stat.value);
117
- }
118
-
119
- /**
120
- * Extract navigation menu structure
121
- * Common pattern: Hierarchical menu with labels, links, and optional children
122
- *
123
- * @param {Object} parsed - Parsed content from parseContent()
124
- * @returns {Array} Navigation items
125
- */
126
- function navigation(parsed) {
127
- const items = parsed?.items || [];
128
-
129
- return items
130
- .map((item) => {
131
- const navItem = {
132
- label: item.title || null,
133
- href: first(item.links)?.href || null,
134
- };
135
-
136
- // Extract children from nested lists
137
- const firstList = first(item.lists);
138
- if (firstList && firstList.length > 0) {
139
- navItem.children = firstList
140
- .map((listItem) => ({
141
- label: joinParagraphs(listItem.paragraphs) || null,
142
- href: first(listItem.links)?.href || null,
143
- icon: first(listItem.icons) || null,
144
- }))
145
- .filter((child) => child.label);
146
- }
147
-
148
- return navItem;
149
- })
150
- .filter((item) => item.label);
151
- }
152
-
153
- /**
154
- * Extract feature list
155
- * Common pattern: Icon/image, title, description
156
- *
157
- * @param {Object} parsed - Parsed content from parseContent()
158
- * @returns {Array} Feature items
159
- */
160
- function features(parsed) {
161
- const items = parsed?.items || [];
162
-
163
- return items
164
- .map((item) => ({
165
- title: item.title || null,
166
- subtitle: item.subtitle || null,
167
- description: item.paragraphs || [],
168
- icon: first(item.icons) || null,
169
- image: first(item.imgs)?.url || null,
170
- link: first(item.links) || null,
171
- }))
172
- .filter((feature) => feature.title);
173
- }
174
-
175
- /**
176
- * Extract testimonial data
177
- * Common pattern: Quote, author name, role, image
178
- *
179
- * @param {Object} parsed - Parsed content from parseContent()
180
- * @param {Object} options - Extraction options
181
- * @param {boolean} options.useItems - Extract from items instead of main
182
- * @returns {Object|Array} Testimonial data
183
- */
184
- function testimonial(parsed, options = {}) {
185
- const { useItems = false } = options;
186
-
187
- const extractTestimonial = (content) => {
188
- if (!content) return null;
189
-
190
- return {
191
- quote: content.paragraphs || [],
192
- author: content.title || null,
193
- role: content.subtitle || null,
194
- company: content.pretitle || null,
195
- image: first(content.imgs)?.url || null,
196
- imageAlt: first(content.imgs)?.alt || null,
197
- };
198
- };
199
-
200
- if (useItems) {
201
- const items = parsed?.items || [];
202
- return items.map(extractTestimonial).filter(Boolean);
203
- }
204
-
205
- return extractTestimonial(parsed);
206
- }
207
-
208
- /**
209
- * Extract FAQ (question and answer pairs)
210
- * Common pattern: Question as title, answer as content
211
- *
212
- * @param {Object} parsed - Parsed content from parseContent()
213
- * @returns {Array} FAQ items
214
- */
215
- function faq(parsed) {
216
- const items = parsed?.items || [];
217
-
218
- return items
219
- .map((item) => ({
220
- question: item.title || null,
221
- answer: item.paragraphs || [],
222
- links: item.links || [],
223
- }))
224
- .filter((item) => item.question);
225
- }
226
-
227
- /**
228
- * Extract pricing tier data
229
- * Common pattern: Plan name, price, features list, CTA
230
- *
231
- * @param {Object} parsed - Parsed content from parseContent()
232
- * @returns {Array} Pricing tiers
233
- */
234
- function pricing(parsed) {
235
- const items = parsed?.items || [];
236
-
237
- return items
238
- .map((item) => {
239
- const firstList = first(item.lists);
240
- const links = item.links || [];
241
- const buttonLink = links.find(l => l.role?.startsWith('button'));
242
-
243
- return {
244
- name: item.title || null,
245
- price: item.subtitle || null,
246
- description: first(item.paragraphs) || null,
247
- features: firstList
248
- ? firstList
249
- .map((listItem) =>
250
- joinParagraphs(listItem.paragraphs)
251
- )
252
- .filter(Boolean)
253
- : [],
254
- cta: buttonLink || first(links) || null,
255
- highlighted:
256
- item.pretitle?.toLowerCase().includes("popular") || false,
257
- };
258
- })
259
- .filter((tier) => tier.name);
260
- }
261
-
262
- /**
263
- * Extract team member data
264
- * Common pattern: Name, role, bio, image, social links
265
- *
266
- * @param {Object} parsed - Parsed content from parseContent()
267
- * @returns {Array} Team members
268
- */
269
- function team(parsed) {
270
- const items = parsed?.items || [];
271
-
272
- return items
273
- .map((item) => ({
274
- name: item.title || null,
275
- role: item.subtitle || null,
276
- department: item.pretitle || null,
277
- bio: item.paragraphs || [],
278
- image: first(item.imgs)?.url || null,
279
- imageAlt: first(item.imgs)?.alt || null,
280
- links: item.links || [],
281
- }))
282
- .filter((member) => member.name);
283
- }
284
-
285
- /**
286
- * Extract gallery images
287
- * Common pattern: Collection of images with captions
288
- *
289
- * @param {Object} parsed - Parsed content from parseContent()
290
- * @param {Object} options - Extraction options
291
- * @param {string} options.source - Source to extract from: 'main', 'items', 'all'
292
- * @returns {Array} Gallery images
293
- */
294
- function gallery(parsed, options = {}) {
295
- const { source = "all" } = options;
296
- const images = [];
297
-
298
- if (source === "main" || source === "all") {
299
- const mainImages = parsed?.imgs || [];
300
- images.push(...mainImages);
301
- }
302
-
303
- if (source === "items" || source === "all") {
304
- const items = parsed?.items || [];
305
- items.forEach((item) => {
306
- const itemImages = item.imgs || [];
307
- images.push(...itemImages);
308
- });
309
- }
310
-
311
- return images.map((img) => ({
312
- url: img.url,
313
- alt: img.alt || null,
314
- caption: img.caption || null,
315
- }));
316
- }
317
-
318
- /**
319
- * Extract content in legacy Article class format
320
- * Used for backward compatibility with existing components
321
- *
322
- * This extractor transforms the new flat parser output into the nested format
323
- * used by the legacy Article class, enabling drop-in replacement without
324
- * breaking existing components.
325
- *
326
- * NOTE: Reconstructs deprecated fields (buttons, cards, documents, forms, alignment)
327
- * from the new consolidated structure for backwards compatibility.
328
- *
329
- * @param {Object} parsed - Parsed content from parseContent() (flat structure)
330
- * @returns {Object} Legacy format { main, items } with nested header/body structure
331
- *
332
- * @example
333
- * const { parseContent, mappers } = require('@uniweb/semantic-parser');
334
- * const parsed = parseContent(doc);
335
- * const legacy = mappers.extractors.legacy(parsed);
336
- * // Returns: { main: { header: {...}, body: {...} }, items: [...] }
337
- */
338
- function legacy(parsed) {
339
- const transformToNested = (content) => {
340
- if (!content) return null;
341
-
342
- let imgs = content.imgs || [];
343
- let banner = imgs.filter((item) => {
344
- return (item.role = "banner");
345
- })?.[0];
346
-
347
- if (!banner) banner = imgs[0];
348
-
349
- // Reconstruct deprecated fields from new structure
350
- const links = content.links || [];
351
- const buttons = links
352
- .filter(l => l.role?.startsWith('button'))
353
- .map(l => ({ attrs: l, content: l.label }));
354
- const documents = links
355
- .filter(l => l.role === 'document')
356
- .map(l => ({ title: l.label, href: l.href, coverImg: l.preview }));
357
- const plainLinks = links.filter(l => !l.role?.startsWith('button') && l.role !== 'document');
358
-
359
- const cards = content.data?.cards || [];
360
- const form = content.data?.form || null;
361
- const forms = form ? [form] : [];
362
-
363
- return {
364
- header: {
365
- title: content.title || "",
366
- subtitle: content.subtitle || "",
367
- subtitle2: content.subtitle2 || "",
368
- pretitle: content.pretitle || "",
369
- // Auto-fill description (legacy behavior)
370
- description:
371
- content.subtitle2 ||
372
- first(content.paragraphs) ||
373
- "",
374
- alignment: "", // Deprecated: always empty
375
- },
376
- banner,
377
- body: {
378
- paragraphs: content.paragraphs || [],
379
- headings: content.headings || [],
380
- imgs,
381
- videos: content.videos || [],
382
- lists: content.lists || [],
383
- links: plainLinks,
384
- icons: content.icons || [],
385
- buttons,
386
- cards,
387
- documents,
388
- forms,
389
- form,
390
- quotes: content.quotes || [],
391
- properties: content.data || {},
392
- propertyBlocks: [],
393
- },
394
- };
395
- };
396
-
397
- return {
398
- main: transformToNested(parsed),
399
- items: (parsed?.items || []).map(transformToNested),
400
- };
401
- }
402
-
403
- export {
404
- hero,
405
- card,
406
- article,
407
- stats,
408
- navigation,
409
- features,
410
- testimonial,
411
- faq,
412
- pricing,
413
- team,
414
- gallery,
415
- legacy,
416
- };
@@ -1,234 +0,0 @@
1
- /**
2
- * Helper utilities for content transformation
3
- */
4
-
5
- import { createExcerpt, stripMarkup } from './types.js';
6
-
7
- /**
8
- * Get the first item from an array or return default value
9
- * @param {Array} arr - Array to get first item from
10
- * @param {*} defaultValue - Value to return if array is empty or undefined
11
- * @returns {*} First item or default value
12
- */
13
- function first(arr, defaultValue = null) {
14
- return arr?.[0] ?? defaultValue;
15
- }
16
-
17
- /**
18
- * Get the last item from an array or return default value
19
- * @param {Array} arr - Array to get last item from
20
- * @param {*} defaultValue - Value to return if array is empty or undefined
21
- * @returns {*} Last item or default value
22
- */
23
- function last(arr, defaultValue = null) {
24
- if (!arr || arr.length === 0) return defaultValue;
25
- return arr[arr.length - 1];
26
- }
27
-
28
- /**
29
- * Transform an array using a mapping function
30
- * @param {Array} arr - Array to transform
31
- * @param {Function} transform - Transformation function
32
- * @returns {Array} Transformed array
33
- */
34
- function transformArray(arr, transform) {
35
- if (!Array.isArray(arr)) return [];
36
- return arr.map(transform);
37
- }
38
-
39
- /**
40
- * Filter array and optionally transform
41
- * @param {Array} arr - Array to filter
42
- * @param {Function} predicate - Filter function
43
- * @param {Function} transform - Optional transformation function
44
- * @returns {Array} Filtered (and transformed) array
45
- */
46
- function filterArray(arr, predicate, transform = null) {
47
- if (!Array.isArray(arr)) return [];
48
- const filtered = arr.filter(predicate);
49
- return transform ? filtered.map(transform) : filtered;
50
- }
51
-
52
- /**
53
- * Join array of strings with separator
54
- * @param {Array} arr - Array to join
55
- * @param {string} separator - Separator string
56
- * @returns {string} Joined string
57
- */
58
- function joinText(arr, separator = ' ') {
59
- if (!Array.isArray(arr)) return '';
60
- return arr.filter(Boolean).join(separator);
61
- }
62
-
63
- /**
64
- * Flatten nested array structure
65
- * @param {Array} arr - Array to flatten
66
- * @param {number} depth - Depth to flatten (default: 1)
67
- * @returns {Array} Flattened array
68
- */
69
- function flatten(arr, depth = 1) {
70
- if (!Array.isArray(arr)) return [];
71
- return arr.flat(depth);
72
- }
73
-
74
- /**
75
- * Check if value exists (not null, undefined, or empty string)
76
- * @param {*} value - Value to check
77
- * @returns {boolean} True if value exists
78
- */
79
- function exists(value) {
80
- return value !== null && value !== undefined && value !== '';
81
- }
82
-
83
- /**
84
- * Get value with fallback
85
- * @param {*} value - Primary value
86
- * @param {*} fallback - Fallback value
87
- * @returns {*} Value or fallback
88
- */
89
- function withDefault(value, fallback) {
90
- return exists(value) ? value : fallback;
91
- }
92
-
93
- /**
94
- * Validate that required fields are present
95
- * @param {Object} data - Data object to validate
96
- * @param {Array<string>} required - Array of required field names
97
- * @returns {Object} Validation result { valid: boolean, missing: Array<string> }
98
- */
99
- function validateRequired(data, required) {
100
- const missing = required.filter(field => !exists(data[field]));
101
- return {
102
- valid: missing.length === 0,
103
- missing
104
- };
105
- }
106
-
107
- /**
108
- * Pick specific properties from object
109
- * @param {Object} obj - Source object
110
- * @param {Array<string>} keys - Keys to pick
111
- * @returns {Object} Object with only picked keys
112
- */
113
- function pick(obj, keys) {
114
- if (!obj) return {};
115
- return keys.reduce((result, key) => {
116
- if (key in obj) {
117
- result[key] = obj[key];
118
- }
119
- return result;
120
- }, {});
121
- }
122
-
123
- /**
124
- * Omit specific properties from object
125
- * @param {Object} obj - Source object
126
- * @param {Array<string>} keys - Keys to omit
127
- * @returns {Object} Object without omitted keys
128
- */
129
- function omit(obj, keys) {
130
- if (!obj) return {};
131
- const result = { ...obj };
132
- keys.forEach(key => delete result[key]);
133
- return result;
134
- }
135
-
136
- /**
137
- * Safely access nested property
138
- * @param {Object} obj - Object to access
139
- * @param {string} path - Dot-notation path (e.g., 'a.b.c')
140
- * @param {*} defaultValue - Default value if path doesn't exist
141
- * @returns {*} Value at path or default
142
- */
143
- function get(obj, path, defaultValue = undefined) {
144
- if (!obj || !path) return defaultValue;
145
-
146
- const keys = path.split('.');
147
- let result = obj;
148
-
149
- for (const key of keys) {
150
- if (result === null || result === undefined) {
151
- return defaultValue;
152
- }
153
- result = result[key];
154
- }
155
-
156
- return result !== undefined ? result : defaultValue;
157
- }
158
-
159
- /**
160
- * Compact an array (remove null, undefined, empty string)
161
- * @param {Array} arr - Array to compact
162
- * @returns {Array} Compacted array
163
- */
164
- function compact(arr) {
165
- if (!Array.isArray(arr)) return [];
166
- return arr.filter(exists);
167
- }
168
-
169
- /**
170
- * Create a safe extractor that won't throw on missing data
171
- * @param {Function} extractFn - Extraction function that might throw
172
- * @param {*} defaultValue - Default value if extraction fails
173
- * @returns {Function} Safe extraction function
174
- */
175
- function safe(extractFn, defaultValue = null) {
176
- return (...args) => {
177
- try {
178
- return extractFn(...args) ?? defaultValue;
179
- } catch (error) {
180
- return defaultValue;
181
- }
182
- };
183
- }
184
-
185
- /**
186
- * Join paragraphs into a single string
187
- * @param {Array|string} paragraphs - Array of paragraphs or single paragraph
188
- * @param {string} separator - Separator to use between paragraphs
189
- * @returns {string} Joined text
190
- */
191
- function joinParagraphs(paragraphs, separator = ' ') {
192
- if (!Array.isArray(paragraphs)) return paragraphs || '';
193
- return paragraphs.filter(Boolean).join(separator);
194
- }
195
-
196
- /**
197
- * Create an excerpt from paragraphs
198
- * @param {Array|string} paragraphs - Array of paragraphs or single paragraph
199
- * @param {Object} options - Excerpt options (maxLength, boundary, ellipsis)
200
- * @returns {string} Generated excerpt
201
- */
202
- function excerptFromParagraphs(paragraphs, options = {}) {
203
- return createExcerpt(paragraphs, options);
204
- }
205
-
206
- /**
207
- * Count words in text or paragraphs
208
- * @param {Array|string} text - Text or array of paragraphs
209
- * @returns {number} Word count
210
- */
211
- function countWords(text) {
212
- const plain = Array.isArray(text) ? text.join(' ') : text;
213
- return stripMarkup(plain).split(/\s+/).filter(Boolean).length;
214
- }
215
-
216
- export {
217
- first,
218
- last,
219
- transformArray,
220
- filterArray,
221
- joinText,
222
- flatten,
223
- exists,
224
- withDefault,
225
- validateRequired,
226
- pick,
227
- omit,
228
- get,
229
- compact,
230
- safe,
231
- joinParagraphs,
232
- excerptFromParagraphs,
233
- countWords
234
- };
@@ -1,28 +0,0 @@
1
- /**
2
- * Content mapping utilities for transforming parsed content
3
- * into component-specific formats
4
- */
5
-
6
- import * as helpers from './helpers.js';
7
- import * as accessor from './accessor.js';
8
- import * as extractors from './extractors.js';
9
- import * as types from './types.js';
10
-
11
- export {
12
- // Helper utilities
13
- helpers,
14
-
15
- // Path-based accessor
16
- accessor,
17
-
18
- // Common extractors
19
- extractors,
20
-
21
- // Type system
22
- types
23
- };
24
-
25
- // Re-export all functions for direct access
26
- export * from './helpers.js';
27
- export * from './accessor.js';
28
- export * from './extractors.js';