@getvitops/astro 0.1.0 → 0.2.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.
@@ -0,0 +1,178 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ description?: string;
5
+ image?: string | string[];
6
+ author?: {
7
+ name: string;
8
+ url?: string;
9
+ };
10
+ datePublished?: string;
11
+ prepTime?: string;
12
+ cookTime?: string;
13
+ totalTime?: string;
14
+ recipeYield?: string | number;
15
+ recipeCategory?: string;
16
+ recipeCuisine?: string;
17
+ keywords?: string[];
18
+ nutrition?: {
19
+ calories?: string;
20
+ fatContent?: string;
21
+ saturatedFatContent?: string;
22
+ cholesterolContent?: string;
23
+ sodiumContent?: string;
24
+ carbohydrateContent?: string;
25
+ fiberContent?: string;
26
+ sugarContent?: string;
27
+ proteinContent?: string;
28
+ servingSize?: string;
29
+ };
30
+ recipeIngredient: string[];
31
+ recipeInstructions: (string | { name?: string; text: string; image?: string; url?: string })[];
32
+ video?: {
33
+ name: string;
34
+ description: string;
35
+ thumbnailUrl: string | string[];
36
+ contentUrl?: string;
37
+ embedUrl?: string;
38
+ uploadDate: string;
39
+ duration?: string;
40
+ expires?: string;
41
+ };
42
+ aggregateRating?: {
43
+ ratingValue: number;
44
+ ratingCount?: number;
45
+ reviewCount?: number;
46
+ bestRating?: number;
47
+ worstRating?: number;
48
+ };
49
+ review?: {
50
+ author: string;
51
+ datePublished: string;
52
+ reviewBody?: string;
53
+ reviewRating?: {
54
+ ratingValue: number;
55
+ bestRating?: number;
56
+ worstRating?: number;
57
+ };
58
+ }[];
59
+ suitableForDiet?: ('DiabeticDiet' | 'GlutenFreeDiet' | 'HalalDiet' | 'HinduDiet' | 'KosherDiet' | 'LowCalorieDiet' | 'LowFatDiet' | 'LowLactoseDiet' | 'LowSaltDiet' | 'VeganDiet' | 'VegetarianDiet')[];
60
+ }
61
+
62
+ const {
63
+ name,
64
+ description,
65
+ image,
66
+ author,
67
+ datePublished,
68
+ prepTime,
69
+ cookTime,
70
+ totalTime,
71
+ recipeYield,
72
+ recipeCategory,
73
+ recipeCuisine,
74
+ keywords,
75
+ nutrition,
76
+ recipeIngredient,
77
+ recipeInstructions,
78
+ video,
79
+ aggregateRating,
80
+ review,
81
+ suitableForDiet,
82
+ } = Astro.props;
83
+
84
+ const jsonLd = {
85
+ '@context': 'https://schema.org',
86
+ '@type': 'Recipe',
87
+ name,
88
+ ...(description && { description }),
89
+ ...(image && { image: Array.isArray(image) ? image : [image] }),
90
+ ...(author && {
91
+ author: {
92
+ '@type': 'Person',
93
+ name: author.name,
94
+ ...(author.url && { url: author.url }),
95
+ },
96
+ }),
97
+ ...(datePublished && { datePublished }),
98
+ ...(prepTime && { prepTime }),
99
+ ...(cookTime && { cookTime }),
100
+ ...(totalTime && { totalTime }),
101
+ ...(recipeYield && { recipeYield: String(recipeYield) }),
102
+ ...(recipeCategory && { recipeCategory }),
103
+ ...(recipeCuisine && { recipeCuisine }),
104
+ ...(keywords && { keywords: keywords.join(', ') }),
105
+ ...(nutrition && {
106
+ nutrition: {
107
+ '@type': 'NutritionInformation',
108
+ ...(nutrition.calories && { calories: nutrition.calories }),
109
+ ...(nutrition.fatContent && { fatContent: nutrition.fatContent }),
110
+ ...(nutrition.saturatedFatContent && { saturatedFatContent: nutrition.saturatedFatContent }),
111
+ ...(nutrition.cholesterolContent && { cholesterolContent: nutrition.cholesterolContent }),
112
+ ...(nutrition.sodiumContent && { sodiumContent: nutrition.sodiumContent }),
113
+ ...(nutrition.carbohydrateContent && { carbohydrateContent: nutrition.carbohydrateContent }),
114
+ ...(nutrition.fiberContent && { fiberContent: nutrition.fiberContent }),
115
+ ...(nutrition.sugarContent && { sugarContent: nutrition.sugarContent }),
116
+ ...(nutrition.proteinContent && { proteinContent: nutrition.proteinContent }),
117
+ ...(nutrition.servingSize && { servingSize: nutrition.servingSize }),
118
+ },
119
+ }),
120
+ recipeIngredient,
121
+ recipeInstructions: recipeInstructions.map((step, index) =>
122
+ typeof step === 'string'
123
+ ? { '@type': 'HowToStep', text: step }
124
+ : {
125
+ '@type': 'HowToStep',
126
+ ...(step.name && { name: step.name }),
127
+ text: step.text,
128
+ ...(step.image && { image: step.image }),
129
+ ...(step.url && { url: step.url }),
130
+ }
131
+ ),
132
+ ...(video && {
133
+ video: {
134
+ '@type': 'VideoObject',
135
+ name: video.name,
136
+ description: video.description,
137
+ thumbnailUrl: video.thumbnailUrl,
138
+ ...(video.contentUrl && { contentUrl: video.contentUrl }),
139
+ ...(video.embedUrl && { embedUrl: video.embedUrl }),
140
+ uploadDate: video.uploadDate,
141
+ ...(video.duration && { duration: video.duration }),
142
+ ...(video.expires && { expires: video.expires }),
143
+ },
144
+ }),
145
+ ...(aggregateRating && {
146
+ aggregateRating: {
147
+ '@type': 'AggregateRating',
148
+ ratingValue: aggregateRating.ratingValue,
149
+ ...(aggregateRating.ratingCount && { ratingCount: aggregateRating.ratingCount }),
150
+ ...(aggregateRating.reviewCount && { reviewCount: aggregateRating.reviewCount }),
151
+ bestRating: aggregateRating.bestRating ?? 5,
152
+ worstRating: aggregateRating.worstRating ?? 1,
153
+ },
154
+ }),
155
+ ...(review && {
156
+ review: review.map(r => ({
157
+ '@type': 'Review',
158
+ author: { '@type': 'Person', name: r.author },
159
+ datePublished: r.datePublished,
160
+ ...(r.reviewBody && { reviewBody: r.reviewBody }),
161
+ ...(r.reviewRating && {
162
+ reviewRating: {
163
+ '@type': 'Rating',
164
+ ratingValue: r.reviewRating.ratingValue,
165
+ bestRating: r.reviewRating.bestRating ?? 5,
166
+ worstRating: r.reviewRating.worstRating ?? 1,
167
+ },
168
+ }),
169
+ })),
170
+ }),
171
+ ...(suitableForDiet && {
172
+ suitableForDiet: suitableForDiet.map(diet => `https://schema.org/${diet}`),
173
+ }),
174
+ };
175
+ ---
176
+
177
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
178
+ <slot />
@@ -0,0 +1,138 @@
1
+ ---
2
+ export interface Props {
3
+ itemReviewed: {
4
+ type: 'Product' | 'LocalBusiness' | 'Organization' | 'Book' | 'Movie' | 'Restaurant' | 'Event' | 'CreativeWork' | 'SoftwareApplication' | 'MusicRecording' | 'Recipe';
5
+ name: string;
6
+ image?: string;
7
+ url?: string;
8
+ sameAs?: string;
9
+ author?: string;
10
+ isbn?: string;
11
+ director?: string;
12
+ datePublished?: string;
13
+ address?: {
14
+ streetAddress?: string;
15
+ addressLocality?: string;
16
+ addressRegion?: string;
17
+ postalCode?: string;
18
+ addressCountry?: string;
19
+ };
20
+ priceRange?: string;
21
+ telephone?: string;
22
+ };
23
+ reviewRating: {
24
+ ratingValue: number;
25
+ bestRating?: number;
26
+ worstRating?: number;
27
+ };
28
+ author: {
29
+ name: string;
30
+ type?: 'Person' | 'Organization';
31
+ };
32
+ datePublished?: string;
33
+ reviewBody?: string;
34
+ publisher?: {
35
+ name: string;
36
+ type?: 'Person' | 'Organization';
37
+ };
38
+ positiveNotes?: string | string[];
39
+ negativeNotes?: string | string[];
40
+ }
41
+
42
+ const {
43
+ itemReviewed,
44
+ reviewRating,
45
+ author,
46
+ datePublished,
47
+ reviewBody,
48
+ publisher,
49
+ positiveNotes,
50
+ negativeNotes,
51
+ } = Astro.props;
52
+
53
+ const buildItemReviewed = () => {
54
+ const base = {
55
+ '@type': itemReviewed.type,
56
+ name: itemReviewed.name,
57
+ ...(itemReviewed.image && { image: itemReviewed.image }),
58
+ ...(itemReviewed.url && { url: itemReviewed.url }),
59
+ ...(itemReviewed.sameAs && { sameAs: itemReviewed.sameAs }),
60
+ };
61
+
62
+ switch (itemReviewed.type) {
63
+ case 'Book':
64
+ return {
65
+ ...base,
66
+ ...(itemReviewed.author && { author: { '@type': 'Person', name: itemReviewed.author } }),
67
+ ...(itemReviewed.isbn && { isbn: itemReviewed.isbn }),
68
+ };
69
+ case 'Movie':
70
+ return {
71
+ ...base,
72
+ ...(itemReviewed.director && { director: { '@type': 'Person', name: itemReviewed.director } }),
73
+ ...(itemReviewed.datePublished && { datePublished: itemReviewed.datePublished }),
74
+ };
75
+ case 'LocalBusiness':
76
+ case 'Restaurant':
77
+ return {
78
+ ...base,
79
+ ...(itemReviewed.address && {
80
+ address: {
81
+ '@type': 'PostalAddress',
82
+ ...(itemReviewed.address.streetAddress && { streetAddress: itemReviewed.address.streetAddress }),
83
+ ...(itemReviewed.address.addressLocality && { addressLocality: itemReviewed.address.addressLocality }),
84
+ ...(itemReviewed.address.addressRegion && { addressRegion: itemReviewed.address.addressRegion }),
85
+ ...(itemReviewed.address.postalCode && { postalCode: itemReviewed.address.postalCode }),
86
+ ...(itemReviewed.address.addressCountry && { addressCountry: itemReviewed.address.addressCountry }),
87
+ },
88
+ }),
89
+ ...(itemReviewed.priceRange && { priceRange: itemReviewed.priceRange }),
90
+ ...(itemReviewed.telephone && { telephone: itemReviewed.telephone }),
91
+ };
92
+ default:
93
+ return base;
94
+ }
95
+ };
96
+
97
+ const formatNotes = (notes: string | string[] | undefined, type: 'Positive' | 'Negative') => {
98
+ if (!notes) return undefined;
99
+ const noteArray = Array.isArray(notes) ? notes : [notes];
100
+ return {
101
+ '@type': 'ItemList',
102
+ itemListElement: noteArray.map((note, index) => ({
103
+ '@type': 'ListItem',
104
+ position: index + 1,
105
+ name: note,
106
+ })),
107
+ };
108
+ };
109
+
110
+ const jsonLd = {
111
+ '@context': 'https://schema.org',
112
+ '@type': 'Review',
113
+ itemReviewed: buildItemReviewed(),
114
+ reviewRating: {
115
+ '@type': 'Rating',
116
+ ratingValue: reviewRating.ratingValue,
117
+ bestRating: reviewRating.bestRating ?? 5,
118
+ worstRating: reviewRating.worstRating ?? 1,
119
+ },
120
+ author: {
121
+ '@type': author.type || 'Person',
122
+ name: author.name,
123
+ },
124
+ ...(datePublished && { datePublished }),
125
+ ...(reviewBody && { reviewBody }),
126
+ ...(publisher && {
127
+ publisher: {
128
+ '@type': publisher.type || 'Organization',
129
+ name: publisher.name,
130
+ },
131
+ }),
132
+ ...(positiveNotes && { positiveNotes: formatNotes(positiveNotes, 'Positive') }),
133
+ ...(negativeNotes && { negativeNotes: formatNotes(negativeNotes, 'Negative') }),
134
+ };
135
+ ---
136
+
137
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
138
+ <slot />
@@ -0,0 +1,172 @@
1
+ ---
2
+ export interface Props {
3
+ name: string;
4
+ description?: string;
5
+ applicationCategory?: 'GameApplication' | 'SocialNetworkingApplication' | 'TravelApplication' | 'ShoppingApplication' | 'SportsApplication' | 'LifestyleApplication' | 'BusinessApplication' | 'DesignApplication' | 'DeveloperApplication' | 'DriverApplication' | 'EducationalApplication' | 'HealthApplication' | 'FinanceApplication' | 'SecurityApplication' | 'BrowserApplication' | 'CommunicationApplication' | 'DesktopEnhancementApplication' | 'EntertainmentApplication' | 'MultimediaApplication' | 'HomeApplication' | 'UtilitiesApplication' | 'ReferenceApplication';
6
+ operatingSystem?: string;
7
+ url?: string;
8
+ image?: string;
9
+ screenshot?: string | string[];
10
+ offers?: {
11
+ price: number | string;
12
+ priceCurrency: string;
13
+ availability?: 'InStock' | 'OutOfStock' | 'PreOrder';
14
+ };
15
+ aggregateRating?: {
16
+ ratingValue: number;
17
+ ratingCount?: number;
18
+ reviewCount?: number;
19
+ bestRating?: number;
20
+ worstRating?: number;
21
+ };
22
+ review?: {
23
+ author: string;
24
+ datePublished: string;
25
+ reviewBody?: string;
26
+ reviewRating?: {
27
+ ratingValue: number;
28
+ bestRating?: number;
29
+ worstRating?: number;
30
+ };
31
+ }[];
32
+ softwareVersion?: string;
33
+ releaseNotes?: string;
34
+ datePublished?: string;
35
+ downloadUrl?: string;
36
+ installUrl?: string;
37
+ featureList?: string | string[];
38
+ requirements?: string;
39
+ memoryRequirements?: string;
40
+ storageRequirements?: string;
41
+ permissions?: string;
42
+ author?: {
43
+ name: string;
44
+ type?: 'Person' | 'Organization';
45
+ url?: string;
46
+ };
47
+ publisher?: {
48
+ name: string;
49
+ type?: 'Person' | 'Organization';
50
+ url?: string;
51
+ };
52
+ inLanguage?: string | string[];
53
+ isAccessibleForFree?: boolean;
54
+ countriesSupported?: string[];
55
+ supportingData?: {
56
+ name: string;
57
+ url: string;
58
+ };
59
+ }
60
+
61
+ const {
62
+ name,
63
+ description,
64
+ applicationCategory,
65
+ operatingSystem,
66
+ url,
67
+ image,
68
+ screenshot,
69
+ offers,
70
+ aggregateRating,
71
+ review,
72
+ softwareVersion,
73
+ releaseNotes,
74
+ datePublished,
75
+ downloadUrl,
76
+ installUrl,
77
+ featureList,
78
+ requirements,
79
+ memoryRequirements,
80
+ storageRequirements,
81
+ permissions,
82
+ author,
83
+ publisher,
84
+ inLanguage,
85
+ isAccessibleForFree,
86
+ countriesSupported,
87
+ supportingData,
88
+ } = Astro.props;
89
+
90
+ const jsonLd = {
91
+ '@context': 'https://schema.org',
92
+ '@type': 'SoftwareApplication',
93
+ name,
94
+ ...(description && { description }),
95
+ ...(applicationCategory && { applicationCategory }),
96
+ ...(operatingSystem && { operatingSystem }),
97
+ ...(url && { url }),
98
+ ...(image && { image }),
99
+ ...(screenshot && { screenshot: Array.isArray(screenshot) ? screenshot : [screenshot] }),
100
+ ...(offers && {
101
+ offers: {
102
+ '@type': 'Offer',
103
+ price: offers.price,
104
+ priceCurrency: offers.priceCurrency,
105
+ ...(offers.availability && { availability: `https://schema.org/${offers.availability}` }),
106
+ },
107
+ }),
108
+ ...(aggregateRating && {
109
+ aggregateRating: {
110
+ '@type': 'AggregateRating',
111
+ ratingValue: aggregateRating.ratingValue,
112
+ ...(aggregateRating.ratingCount && { ratingCount: aggregateRating.ratingCount }),
113
+ ...(aggregateRating.reviewCount && { reviewCount: aggregateRating.reviewCount }),
114
+ bestRating: aggregateRating.bestRating ?? 5,
115
+ worstRating: aggregateRating.worstRating ?? 1,
116
+ },
117
+ }),
118
+ ...(review && {
119
+ review: review.map(r => ({
120
+ '@type': 'Review',
121
+ author: { '@type': 'Person', name: r.author },
122
+ datePublished: r.datePublished,
123
+ ...(r.reviewBody && { reviewBody: r.reviewBody }),
124
+ ...(r.reviewRating && {
125
+ reviewRating: {
126
+ '@type': 'Rating',
127
+ ratingValue: r.reviewRating.ratingValue,
128
+ bestRating: r.reviewRating.bestRating ?? 5,
129
+ worstRating: r.reviewRating.worstRating ?? 1,
130
+ },
131
+ }),
132
+ })),
133
+ }),
134
+ ...(softwareVersion && { softwareVersion }),
135
+ ...(releaseNotes && { releaseNotes }),
136
+ ...(datePublished && { datePublished }),
137
+ ...(downloadUrl && { downloadUrl }),
138
+ ...(installUrl && { installUrl }),
139
+ ...(featureList && { featureList: Array.isArray(featureList) ? featureList.join(', ') : featureList }),
140
+ ...(requirements && { requirements }),
141
+ ...(memoryRequirements && { memoryRequirements }),
142
+ ...(storageRequirements && { storageRequirements }),
143
+ ...(permissions && { permissions }),
144
+ ...(author && {
145
+ author: {
146
+ '@type': author.type || 'Person',
147
+ name: author.name,
148
+ ...(author.url && { url: author.url }),
149
+ },
150
+ }),
151
+ ...(publisher && {
152
+ publisher: {
153
+ '@type': publisher.type || 'Organization',
154
+ name: publisher.name,
155
+ ...(publisher.url && { url: publisher.url }),
156
+ },
157
+ }),
158
+ ...(inLanguage && { inLanguage }),
159
+ ...(isAccessibleForFree !== undefined && { isAccessibleForFree }),
160
+ ...(countriesSupported && { countriesSupported }),
161
+ ...(supportingData && {
162
+ supportingData: {
163
+ '@type': 'DataFeed',
164
+ name: supportingData.name,
165
+ url: supportingData.url,
166
+ },
167
+ }),
168
+ };
169
+ ---
170
+
171
+ <script is:inline type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
172
+ <slot />
File without changes