unframer 2.25.4 → 2.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/babel-jsx.d.ts +15 -0
- package/dist/babel-jsx.d.ts.map +1 -0
- package/dist/babel-jsx.js +223 -0
- package/dist/babel-jsx.js.map +1 -0
- package/dist/babel-plugin-imports.d.ts +0 -6
- package/dist/babel-plugin-imports.d.ts.map +1 -1
- package/dist/babel-plugin-imports.js +2 -135
- package/dist/babel-plugin-imports.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +31 -6
- package/dist/cli.js.map +1 -1
- package/dist/css.js +13 -13
- package/dist/esbuild.d.ts.map +1 -1
- package/dist/esbuild.js +82 -66
- package/dist/esbuild.js.map +1 -1
- package/dist/example-code.test.js +39 -39
- package/dist/example-code.test.js.map +1 -1
- package/dist/exporter.d.ts.map +1 -1
- package/dist/exporter.js +137 -87
- package/dist/exporter.js.map +1 -1
- package/dist/flat-cache-interceptor.d.ts +27 -0
- package/dist/flat-cache-interceptor.d.ts.map +1 -0
- package/dist/flat-cache-interceptor.js +99 -0
- package/dist/flat-cache-interceptor.js.map +1 -0
- package/dist/framer.d.ts.map +1 -1
- package/dist/framer.js +895 -741
- package/dist/framer.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +15 -3
- package/dist/react.js.map +1 -1
- package/dist/sentry.d.ts +1 -1
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js +2 -17
- package/dist/sentry.js.map +1 -1
- package/dist/undici-dispatcher.d.ts +2 -0
- package/dist/undici-dispatcher.d.ts.map +1 -0
- package/dist/undici-dispatcher.js +13 -0
- package/dist/undici-dispatcher.js.map +1 -0
- package/dist/utils.d.ts +3 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -10
- package/dist/utils.js.map +1 -1
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +143 -0
- package/dist/utils.test.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/babel-jsx.d.ts +15 -0
- package/esm/babel-jsx.d.ts.map +1 -0
- package/esm/babel-jsx.js +219 -0
- package/esm/babel-jsx.js.map +1 -0
- package/esm/babel-plugin-imports.d.ts +0 -6
- package/esm/babel-plugin-imports.d.ts.map +1 -1
- package/esm/babel-plugin-imports.js +2 -134
- package/esm/babel-plugin-imports.js.map +1 -1
- package/esm/cli.d.ts +1 -0
- package/esm/cli.d.ts.map +1 -1
- package/esm/cli.js +31 -6
- package/esm/cli.js.map +1 -1
- package/esm/css.js +13 -13
- package/esm/esbuild.d.ts.map +1 -1
- package/esm/esbuild.js +82 -66
- package/esm/esbuild.js.map +1 -1
- package/esm/example-code.test.js +40 -40
- package/esm/example-code.test.js.map +1 -1
- package/esm/exporter.d.ts.map +1 -1
- package/esm/exporter.js +100 -50
- package/esm/exporter.js.map +1 -1
- package/esm/flat-cache-interceptor.d.ts +27 -0
- package/esm/flat-cache-interceptor.d.ts.map +1 -0
- package/esm/flat-cache-interceptor.js +95 -0
- package/esm/flat-cache-interceptor.js.map +1 -0
- package/esm/framer.d.ts.map +1 -1
- package/esm/framer.js +871 -729
- package/esm/framer.js.map +1 -1
- package/esm/index.d.ts +1 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/react.d.ts.map +1 -1
- package/esm/react.js +15 -3
- package/esm/react.js.map +1 -1
- package/esm/sentry.d.ts +1 -1
- package/esm/sentry.d.ts.map +1 -1
- package/esm/sentry.js +2 -17
- package/esm/sentry.js.map +1 -1
- package/esm/undici-dispatcher.d.ts +2 -0
- package/esm/undici-dispatcher.d.ts.map +1 -0
- package/esm/undici-dispatcher.js +10 -0
- package/esm/undici-dispatcher.js.map +1 -0
- package/esm/utils.d.ts +3 -3
- package/esm/utils.d.ts.map +1 -1
- package/esm/utils.js +3 -9
- package/esm/utils.js.map +1 -1
- package/esm/utils.test.d.ts +2 -0
- package/esm/utils.test.d.ts.map +1 -0
- package/esm/utils.test.js +141 -0
- package/esm/utils.test.js.map +1 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +8 -10
- package/src/babel-jsx.ts +277 -0
- package/src/babel-plugin-imports.ts +6 -169
- package/src/cli.ts +45 -6
- package/src/css.ts +13 -13
- package/src/esbuild.ts +93 -74
- package/src/example-code.test.ts +40 -41
- package/src/exporter.ts +124 -54
- package/src/flat-cache-interceptor.ts +114 -0
- package/src/framer.js +921 -764
- package/src/index.ts +1 -1
- package/src/react.tsx +15 -1
- package/src/sentry.ts +3 -22
- package/src/undici-dispatcher.ts +13 -0
- package/src/utils.test.ts +148 -0
- package/src/utils.ts +4 -17
- package/src/version.ts +1 -1
package/src/css.ts
CHANGED
|
@@ -91,7 +91,7 @@ export function getFontsStyles(_fontsDefs: ComponentFontBundle[]) {
|
|
|
91
91
|
let str = ''
|
|
92
92
|
str += dedent`
|
|
93
93
|
@font-face {
|
|
94
|
-
font-family: '${x.family}';
|
|
94
|
+
font-family: '${x.family}';
|
|
95
95
|
src: url('${x.url}');\n
|
|
96
96
|
`
|
|
97
97
|
if (x.style) {
|
|
@@ -132,7 +132,7 @@ export const breakpointsStylesLegacy = (breakpointSizes?: BreakpointSizes) => {
|
|
|
132
132
|
@media (min-width: ${breakpointSizes.base}px) and (max-width: ${
|
|
133
133
|
breakpointSizes.sm - 1
|
|
134
134
|
}px) {
|
|
135
|
-
.unframer-hidden.unframer-base {
|
|
135
|
+
.unframer-hidden.unframer-base {
|
|
136
136
|
display: contents;
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -141,7 +141,7 @@ export const breakpointsStylesLegacy = (breakpointSizes?: BreakpointSizes) => {
|
|
|
141
141
|
@media (min-width: ${breakpointSizes.sm}px) and (max-width: ${
|
|
142
142
|
breakpointSizes.md - 1
|
|
143
143
|
}px) {
|
|
144
|
-
.unframer-hidden.unframer-sm {
|
|
144
|
+
.unframer-hidden.unframer-sm {
|
|
145
145
|
display: contents;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
@@ -150,7 +150,7 @@ export const breakpointsStylesLegacy = (breakpointSizes?: BreakpointSizes) => {
|
|
|
150
150
|
@media (min-width: ${breakpointSizes.md}px) and (max-width: ${
|
|
151
151
|
breakpointSizes.lg - 1
|
|
152
152
|
}px) {
|
|
153
|
-
.unframer-hidden.unframer-md {
|
|
153
|
+
.unframer-hidden.unframer-md {
|
|
154
154
|
display: contents;
|
|
155
155
|
}
|
|
156
156
|
}
|
|
@@ -159,7 +159,7 @@ export const breakpointsStylesLegacy = (breakpointSizes?: BreakpointSizes) => {
|
|
|
159
159
|
@media (min-width: ${breakpointSizes.lg}px) and (max-width: ${
|
|
160
160
|
breakpointSizes.xl - 1
|
|
161
161
|
}px) {
|
|
162
|
-
.unframer-hidden.unframer-lg {
|
|
162
|
+
.unframer-hidden.unframer-lg {
|
|
163
163
|
display: contents;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
@@ -168,14 +168,14 @@ export const breakpointsStylesLegacy = (breakpointSizes?: BreakpointSizes) => {
|
|
|
168
168
|
@media (min-width: ${breakpointSizes.xl}px) and (max-width: ${
|
|
169
169
|
breakpointSizes['2xl'] - 1
|
|
170
170
|
}px) {
|
|
171
|
-
.unframer-hidden.unframer-xl {
|
|
171
|
+
.unframer-hidden.unframer-xl {
|
|
172
172
|
display: contents;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/* 2 Extra Large */
|
|
177
177
|
@media (min-width: ${breakpointSizes['2xl']}px) {
|
|
178
|
-
.unframer-hidden.unframer-2xl {
|
|
178
|
+
.unframer-hidden.unframer-2xl {
|
|
179
179
|
display: contents;
|
|
180
180
|
}
|
|
181
181
|
}
|
|
@@ -190,42 +190,42 @@ export const breakpointsStyles = (breakpointSizes?: BreakpointSizes) => {
|
|
|
190
190
|
return /* css */ `
|
|
191
191
|
/* Base */
|
|
192
192
|
@media (min-width: ${breakpointSizes.base}px) and (max-width: ${breakpointSizes.sm - 1}px) {
|
|
193
|
-
.unframer:not(.unframer-base) {
|
|
193
|
+
.unframer:not(.unframer-base) {
|
|
194
194
|
display: none !important;
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/* Small */
|
|
199
199
|
@media (min-width: ${breakpointSizes.sm}px) and (max-width: ${breakpointSizes.md - 1}px) {
|
|
200
|
-
.unframer:not(.unframer-sm) {
|
|
200
|
+
.unframer:not(.unframer-sm) {
|
|
201
201
|
display: none !important;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
/* Medium */
|
|
206
206
|
@media (min-width: ${breakpointSizes.md}px) and (max-width: ${breakpointSizes.lg - 1}px) {
|
|
207
|
-
.unframer:not(.unframer-md) {
|
|
207
|
+
.unframer:not(.unframer-md) {
|
|
208
208
|
display: none !important;
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/* Large */
|
|
213
213
|
@media (min-width: ${breakpointSizes.lg}px) and (max-width: ${breakpointSizes.xl - 1}px) {
|
|
214
|
-
.unframer:not(.unframer-lg) {
|
|
214
|
+
.unframer:not(.unframer-lg) {
|
|
215
215
|
display: none !important;
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
/* Extra Large */
|
|
220
220
|
@media (min-width: ${breakpointSizes.xl}px) and (max-width: ${breakpointSizes['2xl'] - 1}px) {
|
|
221
|
-
.unframer:not(.unframer-xl) {
|
|
221
|
+
.unframer:not(.unframer-xl) {
|
|
222
222
|
display: none !important;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
/* 2 Extra Large */
|
|
227
227
|
@media (min-width: ${breakpointSizes['2xl']}px) {
|
|
228
|
-
.unframer:not(.unframer-2xl) {
|
|
228
|
+
.unframer:not(.unframer-2xl) {
|
|
229
229
|
display: none !important;
|
|
230
230
|
}
|
|
231
231
|
}
|
package/src/esbuild.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { fetch } from 'undici'
|
|
2
|
-
import {
|
|
2
|
+
import { RateLimit, Sema } from 'async-sema'
|
|
3
|
+
import { logger, spinner } from './utils'
|
|
3
4
|
|
|
4
5
|
import { Plugin, transform, type OnResolveArgs } from 'esbuild'
|
|
5
6
|
import { resolvePackage } from './exporter'
|
|
7
|
+
import { notifyError } from './sentry'
|
|
8
|
+
import { dispatcher } from './undici-dispatcher'
|
|
6
9
|
|
|
7
10
|
export const defaultExternalPackages = [
|
|
8
11
|
'react',
|
|
@@ -150,86 +153,94 @@ export function esbuildPluginBundleDependencies({
|
|
|
150
153
|
})
|
|
151
154
|
|
|
152
155
|
build.onLoad({ filter: /.*/, namespace }, async (args) => {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
const url = args.path
|
|
157
|
-
|
|
158
|
-
const resolved = await resolveRedirect({
|
|
159
|
-
url,
|
|
160
|
-
redirectCache,
|
|
161
|
-
signal,
|
|
162
|
-
})
|
|
163
|
-
if (codeCache.has(url)) {
|
|
164
|
-
const code = await codeCache.get(url)
|
|
165
|
-
return {
|
|
166
|
-
contents: code,
|
|
167
|
-
loader: 'js',
|
|
156
|
+
try {
|
|
157
|
+
if (signal?.aborted) {
|
|
158
|
+
throw new Error('aborted')
|
|
168
159
|
}
|
|
169
|
-
|
|
170
|
-
let loader = 'jsx' as any
|
|
171
|
-
const promise = Promise.resolve().then(async () => {
|
|
172
|
-
logger.log('fetching', url, 'because of', args.path)
|
|
173
|
-
spinner.update(`Fetching ${url.replace(/https?:\/\//, '')}`)
|
|
160
|
+
const url = args.path
|
|
174
161
|
|
|
175
|
-
const
|
|
162
|
+
const resolved = await resolveRedirect({
|
|
163
|
+
url,
|
|
164
|
+
redirectCache,
|
|
176
165
|
signal,
|
|
177
|
-
dispatcher,
|
|
178
166
|
})
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (
|
|
186
|
-
res.headers
|
|
187
|
-
.get('content-type')
|
|
188
|
-
?.startsWith('application/json')
|
|
189
|
-
) {
|
|
190
|
-
loader = 'json'
|
|
191
|
-
return await res.text()
|
|
167
|
+
if (codeCache.has(url)) {
|
|
168
|
+
const code = await codeCache.get(url)
|
|
169
|
+
return {
|
|
170
|
+
contents: code,
|
|
171
|
+
loader: 'js',
|
|
172
|
+
}
|
|
192
173
|
}
|
|
193
|
-
let
|
|
174
|
+
let loader = 'jsx' as any
|
|
175
|
+
const promise = Promise.resolve().then(async () => {
|
|
176
|
+
logger.log('fetching', url, 'because of', args.path)
|
|
177
|
+
spinner.update(
|
|
178
|
+
`Fetching ${url.replace(/https?:\/\//, '')}`,
|
|
179
|
+
)
|
|
194
180
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
181
|
+
const res = await fetchWithRetry(resolved, {
|
|
182
|
+
signal,
|
|
183
|
+
dispatcher,
|
|
184
|
+
})
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`Cannot fetch ${resolved}: ${res.status} ${res.statusText}`,
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
// console.log('type', res.headers.get('content-type'))
|
|
191
|
+
if (
|
|
192
|
+
res.headers
|
|
193
|
+
.get('content-type')
|
|
194
|
+
?.startsWith('application/json')
|
|
195
|
+
) {
|
|
196
|
+
loader = 'json'
|
|
197
|
+
return await res.text()
|
|
198
|
+
}
|
|
199
|
+
let text = await res.text()
|
|
204
200
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
201
|
+
// when it finds a line with /* webpackIgnore: true */
|
|
202
|
+
// it also adds /* @vite-ignore */
|
|
203
|
+
text = text.replace(
|
|
204
|
+
/(\/\* webpackIgnore: true \*\/)/g,
|
|
205
|
+
'$1 /* @vite-ignore */',
|
|
206
|
+
)
|
|
207
|
+
// if (!text.includes('import.meta.url')) {
|
|
208
|
+
// return text
|
|
209
|
+
// }
|
|
210
|
+
|
|
211
|
+
logger.log('transforming', url)
|
|
212
|
+
const transformed = await transform(text, {
|
|
213
|
+
define: {
|
|
214
|
+
'import.meta.url': JSON.stringify(resolved),
|
|
215
|
+
navigator: '__unframerNavigator',
|
|
216
|
+
},
|
|
217
|
+
// Fix lottie: https://github.com/airbnb/lottie-web/issues/3047
|
|
218
|
+
banner: `var __unframerNavigator = typeof window !== 'undefined' ? navigator : undefined;`,
|
|
219
|
+
minify: false,
|
|
220
|
+
format: 'esm',
|
|
221
|
+
jsx: 'transform',
|
|
222
|
+
logLevel: 'error',
|
|
223
|
+
loader,
|
|
224
|
+
platform: 'browser',
|
|
225
|
+
})
|
|
226
|
+
// console.log('transformed', resolved)
|
|
227
|
+
return transformed.code
|
|
219
228
|
})
|
|
220
|
-
// console.log('transformed', resolved)
|
|
221
|
-
return transformed.code
|
|
222
|
-
})
|
|
223
229
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
230
|
+
if (loader === 'jsx') {
|
|
231
|
+
codeCache.set(url, promise)
|
|
232
|
+
}
|
|
233
|
+
const code = await promise
|
|
228
234
|
|
|
229
|
-
|
|
230
|
-
|
|
235
|
+
return {
|
|
236
|
+
contents: code,
|
|
231
237
|
|
|
232
|
-
|
|
238
|
+
loader,
|
|
239
|
+
}
|
|
240
|
+
} catch (e) {
|
|
241
|
+
logger.error(e.message)
|
|
242
|
+
notifyError(e)
|
|
243
|
+
process.exit(1)
|
|
233
244
|
}
|
|
234
245
|
})
|
|
235
246
|
},
|
|
@@ -287,12 +298,20 @@ export async function recursiveResolveRedirect(
|
|
|
287
298
|
|
|
288
299
|
return url
|
|
289
300
|
}
|
|
301
|
+
let semaphore = new Sema(3)
|
|
302
|
+
let rateLimiter = RateLimit(20, { timeUnit: 1000 })
|
|
303
|
+
|
|
290
304
|
export const fetchWithRetry = retryTwice(
|
|
291
|
-
(url: string, options?: RequestInit) => {
|
|
305
|
+
async (url: string, options?: RequestInit) => {
|
|
306
|
+
await semaphore.acquire()
|
|
307
|
+
|
|
292
308
|
const timeout = setTimeout(() => {
|
|
293
|
-
logger.error('fetch taking more than
|
|
294
|
-
},
|
|
295
|
-
return fetch(url, options as any).finally(() =>
|
|
309
|
+
logger.error('fetch taking more than 10s', url)
|
|
310
|
+
}, 10000)
|
|
311
|
+
return await fetch(url, options as any).finally(() => {
|
|
312
|
+
clearTimeout(timeout)
|
|
313
|
+
semaphore.release()
|
|
314
|
+
})
|
|
296
315
|
},
|
|
297
316
|
) as typeof fetch
|
|
298
317
|
|
package/src/example-code.test.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { describe, expect, test } from 'vitest'
|
|
2
|
+
import { configFromFetch } from './cli.js'
|
|
2
3
|
import { createExampleComponentCode } from './exporter.js'
|
|
3
|
-
import { componentNameToPath } from './utils.js'
|
|
4
|
-
import { Config, configFromFetch } from './cli.js'
|
|
5
4
|
|
|
6
5
|
describe('createExampleComponentCode', () => {
|
|
7
6
|
test('should create example component code', async () => {
|
|
@@ -28,44 +27,44 @@ describe('createExampleComponentCode', () => {
|
|
|
28
27
|
import ArticlesCardFramerComponent from './src/articles-card'
|
|
29
28
|
|
|
30
29
|
export default function App() {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
30
|
+
return (
|
|
31
|
+
<div className='flex flex-col items-center gap-3 '>
|
|
32
|
+
<NavigationFramerComponent.Responsive
|
|
33
|
+
ctaVariant={"Primary"}
|
|
34
|
+
/>
|
|
35
|
+
<HeroFramerComponent.Responsive/>
|
|
36
|
+
<PricingBannerFramerComponent.Responsive/>
|
|
37
|
+
<FooterFramerComponent.Responsive
|
|
38
|
+
year={"2024"}
|
|
39
|
+
/>
|
|
40
|
+
<FeatureListFramerComponent.Responsive/>
|
|
41
|
+
<ServiceSliderFramerComponent.Responsive/>
|
|
42
|
+
<SectionTitleFramerComponent.Responsive
|
|
43
|
+
text={"We are pioneers in harnessing the power of Blockchain and Web3 technologies to drive innovation, security, and decentralization."}
|
|
44
|
+
title={"Smart Automation"}
|
|
45
|
+
tagline={"Systems and Building Web3"}
|
|
46
|
+
iconVisible={true}
|
|
47
|
+
textVisible={true}
|
|
48
|
+
/>
|
|
49
|
+
<ButtonFramerComponent.Responsive
|
|
50
|
+
link={"/news"}
|
|
51
|
+
buttonTitle={"Read all blog"}
|
|
52
|
+
iconVisibility={true}
|
|
53
|
+
/>
|
|
54
|
+
<BrandLogoFramerComponent.Responsive/>
|
|
55
|
+
<TestmonialItemFramerComponent.Responsive
|
|
56
|
+
name1={"Wade Warren"}
|
|
57
|
+
paragraph={"Security is non-negotiable in the decentralized world, and we take this aspect very seriously. Our solutions are built with a robust emphasis on security, utilizing advanced cryptographic"}
|
|
58
|
+
designation={"Flutter Developer"}
|
|
59
|
+
/>
|
|
60
|
+
<ArticlesCardFramerComponent.Responsive
|
|
61
|
+
date={"Mar 06, 2024 "}
|
|
62
|
+
link={"/news/:slug"}
|
|
63
|
+
title={"Discoveries from Our Thinkers"}
|
|
64
|
+
excerpt={"Experience seamless integration with decentralized applications (DApps)."}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
69
68
|
};"
|
|
70
69
|
`)
|
|
71
70
|
})
|