unframer 2.25.2 → 2.25.4
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/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +79 -48
- package/dist/cli.js.map +1 -1
- package/dist/example-code.test.d.ts +2 -0
- package/dist/example-code.test.d.ts.map +1 -0
- package/dist/example-code.test.js +72 -0
- package/dist/example-code.test.js.map +1 -0
- package/dist/exporter.d.ts +17 -55
- package/dist/exporter.d.ts.map +1 -1
- package/dist/exporter.js +73 -69
- package/dist/exporter.js.map +1 -1
- package/dist/framer.d.ts.map +1 -1
- package/dist/framer.js +99 -53
- package/dist/framer.js.map +1 -1
- package/dist/generated/api-client.js +0 -1
- package/dist/generated/api-client.js.map +1 -1
- package/dist/generated/api-client.test.js +0 -69
- package/dist/generated/api-client.test.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/cli.d.ts +21 -0
- package/esm/cli.d.ts.map +1 -1
- package/esm/cli.js +78 -48
- package/esm/cli.js.map +1 -1
- package/esm/example-code.test.d.ts +2 -0
- package/esm/example-code.test.d.ts.map +1 -0
- package/esm/example-code.test.js +70 -0
- package/esm/example-code.test.js.map +1 -0
- package/esm/exporter.d.ts +17 -55
- package/esm/exporter.d.ts.map +1 -1
- package/esm/exporter.js +72 -69
- package/esm/exporter.js.map +1 -1
- package/esm/framer.d.ts.map +1 -1
- package/esm/framer.js +99 -53
- package/esm/framer.js.map +1 -1
- package/esm/generated/api-client.js +0 -1
- package/esm/generated/api-client.js.map +1 -1
- package/esm/generated/api-client.test.d.ts +0 -1
- package/esm/generated/api-client.test.js +1 -69
- package/esm/generated/api-client.test.js.map +1 -1
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +4 -4
- package/src/{cli.tsx → cli.ts} +114 -64
- package/src/example-code.test.ts +72 -0
- package/src/exporter.ts +99 -81
- package/src/framer.js +128 -79
- package/src/generated/api-client.d.ts +1048 -682
- package/src/generated/api-client.d.ts.map +1 -1
- package/src/generated/api-client.js +5 -6
- package/src/generated/api-client.js.map +1 -1
- package/src/generated/api-client.test.d.ts +1 -1
- package/src/generated/api-client.test.js +1 -69
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/generated/api-client.js"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE;IACtC,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/generated/api-client.js"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAE,GAAG,EAAE;IACtC,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IAClE,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,EAAE;QACtC,oCAAoC;QACpC,kDAAkD;QAClD,qBAAqB;QACrB,4CAA4C;QAC5C,QAAQ;QACR,iBAAiB;QACjB,KAAK;QACL,+BAA+B;QAC/B,0BAA0B;QAC1B,iDAAiD;QACjD,QAAQ;QACR,sBAAsB;QACtB,KAAK;QACL,OAAO;YACH,OAAO;YACH,yFAAyF;aAC5F,CAAA;QACL,CAAC;KACJ,CAAC,CAAA;IACF,OAAO,MAAM,CAAA;AACjB,CAAC;AACD,iFAAiF"}
|
|
@@ -1,70 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { createClient } from './client';
|
|
3
|
-
import { env, supabaseRef } from './env';
|
|
4
|
-
import { createSupabaseAdmin, createSupabaseAnon } from './supabase.server';
|
|
5
|
-
import { db } from 'db/kysely';
|
|
6
|
-
test('hono client', async () => {
|
|
7
|
-
const admin = createSupabaseAdmin();
|
|
8
|
-
const email = 'test@example.com';
|
|
9
|
-
const password = 'xxpasswordxx99776';
|
|
10
|
-
const { error: signupError } = await admin.auth.admin.createUser({
|
|
11
|
-
email,
|
|
12
|
-
password,
|
|
13
|
-
email_confirm: true,
|
|
14
|
-
});
|
|
15
|
-
// expect(signupError).toBe(null)
|
|
16
|
-
const supabase = createSupabaseAnon();
|
|
17
|
-
const { data, error } = await supabase.auth.signInWithPassword({
|
|
18
|
-
email,
|
|
19
|
-
password,
|
|
20
|
-
});
|
|
21
|
-
expect(error).toBe(null);
|
|
22
|
-
const session = data.session;
|
|
23
|
-
const client = createClient({
|
|
24
|
-
url: `http://localhost:${env.PORT}`,
|
|
25
|
-
session,
|
|
26
|
-
supabaseRef,
|
|
27
|
-
});
|
|
28
|
-
const res = await client.api.v1.health.$get({});
|
|
29
|
-
expect(res.status).toBe(200);
|
|
30
|
-
const json = await res.json();
|
|
31
|
-
expect(json).toMatchInlineSnapshot(`
|
|
32
|
-
{
|
|
33
|
-
"ok": true,
|
|
34
|
-
}
|
|
35
|
-
`);
|
|
36
|
-
});
|
|
37
|
-
test('get conversations with my account', async () => {
|
|
38
|
-
const admin = createSupabaseAdmin();
|
|
39
|
-
const email = 'beats.by.morse@gmail.com';
|
|
40
|
-
const password = env.CRISP_WEBHOOKS_SECRET;
|
|
41
|
-
const user = await db
|
|
42
|
-
.selectFrom('auth.users')
|
|
43
|
-
.selectAll()
|
|
44
|
-
.where('email', '=', email)
|
|
45
|
-
.executeTakeFirst();
|
|
46
|
-
// uncomment this to set the password the first time
|
|
47
|
-
// const { error: signupError } = await admin.auth.admin.updateUserById(
|
|
48
|
-
// user!.id,
|
|
49
|
-
// { password },
|
|
50
|
-
// )
|
|
51
|
-
// expect(signupError).toBe(null)
|
|
52
|
-
const supabase = createSupabaseAnon();
|
|
53
|
-
const { data, error } = await supabase.auth.signInWithPassword({
|
|
54
|
-
email,
|
|
55
|
-
password,
|
|
56
|
-
});
|
|
57
|
-
expect(error).toBe(null);
|
|
58
|
-
const session = data.session;
|
|
59
|
-
const client = createClient({
|
|
60
|
-
url: `http://localhost:${env.PORT}`,
|
|
61
|
-
session,
|
|
62
|
-
supabaseRef,
|
|
63
|
-
});
|
|
64
|
-
const res = await client.api.v1.conversations.$get({});
|
|
65
|
-
expect(res.status).toBe(200);
|
|
66
|
-
const json = await res.json();
|
|
67
|
-
expect(json.conversations.length).toBeGreaterThan(0);
|
|
68
|
-
console.log(json.conversations);
|
|
69
|
-
});
|
|
1
|
+
"use strict";
|
|
70
2
|
//# sourceMappingURL=api-client.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../../src/generated/api-client.test.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../../src/generated/api-client.test.js"],"names":[],"mappings":""}
|
package/esm/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "2.25.
|
|
1
|
+
export declare const version = "2.25.4";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/esm/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '2.25.
|
|
1
|
+
export const version = '2.25.4';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unframer",
|
|
3
|
-
"version": "2.25.
|
|
3
|
+
"version": "2.25.4",
|
|
4
4
|
"description": "Import Framer components directly in your React app, type safe and customizable",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"repository": "https://github.com/remorses/unframer",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"node": ">=18.0.0"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@sentry/node": "^9.
|
|
48
|
+
"@sentry/node": "^9.22.0",
|
|
49
49
|
"async-sema": "^3.1.1",
|
|
50
50
|
"cac": "^6.7.14",
|
|
51
|
-
"esbuild": "^0.25.
|
|
51
|
+
"esbuild": "^0.25.4",
|
|
52
52
|
"esbuild-plugins-node-modules-polyfill": "^1.6.8",
|
|
53
53
|
"find-up": "^7.0.0",
|
|
54
54
|
"json5": "^2.2.3",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@types/babel__core": "^7.20.5",
|
|
73
73
|
"@types/babel__traverse": "^7.20.6",
|
|
74
74
|
"@types/bun": "^1.1.6",
|
|
75
|
-
"@types/node": "^22.
|
|
75
|
+
"@types/node": "^22.15.21",
|
|
76
76
|
"@types/react": "^19.1.3",
|
|
77
77
|
"@xmorse/deployment-utils": "^0.2.19",
|
|
78
78
|
"concurrently": "^9.1.2",
|
package/src/{cli.tsx → cli.ts}
RENAMED
|
@@ -9,7 +9,7 @@ import { cac } from 'cac'
|
|
|
9
9
|
|
|
10
10
|
import fs from 'fs'
|
|
11
11
|
import path, { basename } from 'path'
|
|
12
|
-
import { BreakpointSizes } from './css.js'
|
|
12
|
+
import { BreakpointSizes, defaultBreakpointSizes } from './css.js'
|
|
13
13
|
import {
|
|
14
14
|
componentNameToPath,
|
|
15
15
|
dispatcher,
|
|
@@ -53,72 +53,15 @@ cli.command('[projectId]', 'Run unframer with optional project ID')
|
|
|
53
53
|
const signal = controller.signal
|
|
54
54
|
const watch = options.watch
|
|
55
55
|
if (projectId) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
'https://unframer.co',
|
|
62
|
-
|
|
56
|
+
const { config, cwd, websiteUrl } = await configFromFetch({
|
|
57
|
+
allExternal,
|
|
58
|
+
externalPackages,
|
|
59
|
+
outDir,
|
|
60
|
+
projectId,
|
|
63
61
|
})
|
|
64
|
-
|
|
65
|
-
spinner.start(`Fetching config for project ${projectId}`)
|
|
66
|
-
const { data, error } =
|
|
67
|
-
await client.api.plugins.reactExportPlugin
|
|
68
|
-
.project({ projectId })
|
|
69
|
-
.get()
|
|
70
|
-
if (error) {
|
|
71
|
-
spinner.error('Error fetching project data:')
|
|
72
|
-
console.error(error)
|
|
73
|
-
throw error
|
|
74
|
-
}
|
|
75
|
-
spinner.info(`Got Framer project data`)
|
|
76
|
-
const websiteUrl = data?.project?.websiteUrl
|
|
77
|
-
|
|
78
|
-
const projectName = data?.project?.projectName || ''
|
|
79
|
-
if (projectName) {
|
|
80
|
-
spinner.info(`Using project: ${projectName}`)
|
|
81
|
-
}
|
|
82
|
-
let cwd = path.resolve(process.cwd(), outDir || 'framer')
|
|
83
|
-
logger.log('bundling', cwd)
|
|
84
62
|
const { rebuild, buildContext } = await bundle({
|
|
85
|
-
config
|
|
86
|
-
outDir,
|
|
87
|
-
externalPackages,
|
|
88
|
-
allExternal,
|
|
89
|
-
projectId: data?.project?.projectId,
|
|
90
|
-
projectName,
|
|
91
|
-
fullFramerProjectId:
|
|
92
|
-
data?.project?.fullFramerProjectId!,
|
|
93
|
-
locales: data?.locales,
|
|
94
|
-
components: Object.fromEntries(
|
|
95
|
-
data.components.map((c) => [
|
|
96
|
-
componentNameToPath(c.name),
|
|
97
|
-
c.url,
|
|
98
|
-
]),
|
|
99
|
-
),
|
|
100
|
-
componentBreakpoints:
|
|
101
|
-
data.breakpoints
|
|
102
|
-
?.map((b) => {
|
|
103
|
-
const c = data.components.find(
|
|
104
|
-
(c) => c.id === b.componentId,
|
|
105
|
-
)
|
|
106
|
-
if (!c) {
|
|
107
|
-
return
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
...b,
|
|
111
|
-
componentName: componentNameToPath(
|
|
112
|
-
c.name,
|
|
113
|
-
),
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
.filter(isTruthy) || [],
|
|
117
|
-
tokens: data.colorStyles,
|
|
118
|
-
framerWebPages: data.framerWebPages || [],
|
|
119
|
-
},
|
|
63
|
+
config,
|
|
120
64
|
watch,
|
|
121
|
-
|
|
122
65
|
cwd,
|
|
123
66
|
signal,
|
|
124
67
|
})
|
|
@@ -264,4 +207,111 @@ export type Config = {
|
|
|
264
207
|
breakpoints?: BreakpointSizes
|
|
265
208
|
tokens?: StyleToken[]
|
|
266
209
|
outDir?: string
|
|
210
|
+
componentInstancesInIndexPage: ComponentInstanceInPage[]
|
|
211
|
+
pageBackgroundColor?: string
|
|
212
|
+
// [key: string]: any
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type ComponentInstanceInPage = {
|
|
216
|
+
pageOrdering: number
|
|
217
|
+
componentId: string
|
|
218
|
+
componentPathSlug: string
|
|
219
|
+
controls: Record<string, any>
|
|
220
|
+
nodeDepth: number
|
|
221
|
+
// pagePath: string
|
|
222
|
+
webPageId: string
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export async function configFromFetch({
|
|
226
|
+
projectId,
|
|
227
|
+
externalPackages = [] as string[],
|
|
228
|
+
allExternal = false,
|
|
229
|
+
outDir = undefined as undefined | string,
|
|
230
|
+
}) {
|
|
231
|
+
logger.log(`Fetching config for project ${projectId}`)
|
|
232
|
+
|
|
233
|
+
const url = process.env.UNFRAMER_SERVER_URL
|
|
234
|
+
if (url) {
|
|
235
|
+
console.log(`using server url ${url}`)
|
|
236
|
+
}
|
|
237
|
+
const client = await createClient({
|
|
238
|
+
url: url || 'https://unframer.co',
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
spinner.start(`Fetching config for project ${projectId}`)
|
|
242
|
+
const { data, error } = await client.api.plugins.reactExportPlugin
|
|
243
|
+
.project({ projectId })
|
|
244
|
+
.get()
|
|
245
|
+
if (error) {
|
|
246
|
+
spinner.error('Error fetching project data:')
|
|
247
|
+
console.error(error)
|
|
248
|
+
throw error
|
|
249
|
+
}
|
|
250
|
+
spinner.info(`Got Framer project data`)
|
|
251
|
+
const websiteUrl = data?.project?.websiteUrl
|
|
252
|
+
|
|
253
|
+
const projectName = data?.project?.projectName || ''
|
|
254
|
+
if (projectName) {
|
|
255
|
+
spinner.info(`Using project: ${projectName}`)
|
|
256
|
+
}
|
|
257
|
+
let cwd = path.resolve(process.cwd(), outDir || 'framer')
|
|
258
|
+
|
|
259
|
+
const indexPage = data?.framerWebPages?.find((x) => x.path === '/')
|
|
260
|
+
const componentInstancesInIndexPage =
|
|
261
|
+
data.componentInstances
|
|
262
|
+
?.filter((x) => x.webPageId === indexPage?.webPageId)
|
|
263
|
+
.map((x) => {
|
|
264
|
+
const component = data.components.find((c) => {
|
|
265
|
+
return x.componentId === c.id
|
|
266
|
+
})
|
|
267
|
+
if (!component) {
|
|
268
|
+
console.error(
|
|
269
|
+
new Error(
|
|
270
|
+
`cannot find component for instance ${x.componentId}`,
|
|
271
|
+
),
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
const componentPathSlug = componentNameToPath(
|
|
275
|
+
component?.name || '',
|
|
276
|
+
)
|
|
277
|
+
const res: ComponentInstanceInPage = { ...x, componentPathSlug }
|
|
278
|
+
return res
|
|
279
|
+
})
|
|
280
|
+
.sort((a, b) => {
|
|
281
|
+
return a.pageOrdering - b.pageOrdering
|
|
282
|
+
}) || []
|
|
283
|
+
const config: Config = {
|
|
284
|
+
...data,
|
|
285
|
+
breakpoints: defaultBreakpointSizes,
|
|
286
|
+
outDir,
|
|
287
|
+
externalPackages,
|
|
288
|
+
allExternal,
|
|
289
|
+
projectId: data?.project?.projectId,
|
|
290
|
+
projectName,
|
|
291
|
+
fullFramerProjectId: data?.project?.fullFramerProjectId!,
|
|
292
|
+
locales: data?.locales,
|
|
293
|
+
|
|
294
|
+
components: Object.fromEntries(
|
|
295
|
+
data.components.map((c) => [componentNameToPath(c.name), c.url]),
|
|
296
|
+
),
|
|
297
|
+
componentBreakpoints:
|
|
298
|
+
data.breakpoints
|
|
299
|
+
?.map((b) => {
|
|
300
|
+
const c = data.components.find(
|
|
301
|
+
(c) => c.id === b.componentId,
|
|
302
|
+
)
|
|
303
|
+
if (!c) {
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
...b,
|
|
308
|
+
componentName: componentNameToPath(c.name),
|
|
309
|
+
}
|
|
310
|
+
})
|
|
311
|
+
.filter(isTruthy) || [],
|
|
312
|
+
tokens: data.colorStyles,
|
|
313
|
+
componentInstancesInIndexPage,
|
|
314
|
+
framerWebPages: data.framerWebPages || [],
|
|
315
|
+
}
|
|
316
|
+
return { websiteUrl, cwd, config }
|
|
267
317
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { expect, describe, test } from 'vitest'
|
|
2
|
+
import { createExampleComponentCode } from './exporter.js'
|
|
3
|
+
import { componentNameToPath } from './utils.js'
|
|
4
|
+
import { Config, configFromFetch } from './cli.js'
|
|
5
|
+
|
|
6
|
+
describe('createExampleComponentCode', () => {
|
|
7
|
+
test('should create example component code', async () => {
|
|
8
|
+
const projectId = 'cf755ed7d59e0319'
|
|
9
|
+
|
|
10
|
+
const { config } = await configFromFetch({ projectId })
|
|
11
|
+
const { exampleCode } = await createExampleComponentCode({
|
|
12
|
+
config,
|
|
13
|
+
outDir: 'src',
|
|
14
|
+
})
|
|
15
|
+
expect(exampleCode).toMatchInlineSnapshot(`
|
|
16
|
+
"import './src/styles.css'
|
|
17
|
+
|
|
18
|
+
import NavigationFramerComponent from './src/navigation'
|
|
19
|
+
import HeroFramerComponent from './src/hero'
|
|
20
|
+
import PricingBannerFramerComponent from './src/pricing-banner'
|
|
21
|
+
import FooterFramerComponent from './src/footer'
|
|
22
|
+
import FeatureListFramerComponent from './src/feature-list'
|
|
23
|
+
import ServiceSliderFramerComponent from './src/service-slider'
|
|
24
|
+
import SectionTitleFramerComponent from './src/section-title'
|
|
25
|
+
import ButtonFramerComponent from './src/button'
|
|
26
|
+
import BrandLogoFramerComponent from './src/brand-logo'
|
|
27
|
+
import TestmonialItemFramerComponent from './src/testmonial-item'
|
|
28
|
+
import ArticlesCardFramerComponent from './src/articles-card'
|
|
29
|
+
|
|
30
|
+
export default function App() {
|
|
31
|
+
return (
|
|
32
|
+
<div className='flex flex-col'>
|
|
33
|
+
<NavigationFramerComponent.Responsive
|
|
34
|
+
ctaVariant={"ia7uVki50"}
|
|
35
|
+
/>
|
|
36
|
+
<HeroFramerComponent.Responsive/>
|
|
37
|
+
<PricingBannerFramerComponent.Responsive/>
|
|
38
|
+
<FooterFramerComponent.Responsive
|
|
39
|
+
year={"2024"}
|
|
40
|
+
/>
|
|
41
|
+
<FeatureListFramerComponent.Responsive/>
|
|
42
|
+
<ServiceSliderFramerComponent.Responsive/>
|
|
43
|
+
<SectionTitleFramerComponent.Responsive
|
|
44
|
+
text={"We are pioneers in harnessing the power of Blockchain and Web3 technologies to drive innovation, security, and decentralization."}
|
|
45
|
+
title={"Smart Automation"}
|
|
46
|
+
tagline={"Systems and Building Web3"}
|
|
47
|
+
iconVisible={true}
|
|
48
|
+
textVisible={true}
|
|
49
|
+
/>
|
|
50
|
+
<ButtonFramerComponent.Responsive
|
|
51
|
+
link={"/news"}
|
|
52
|
+
buttonTitle={"Read all blog"}
|
|
53
|
+
iconVisibility={true}
|
|
54
|
+
/>
|
|
55
|
+
<BrandLogoFramerComponent.Responsive/>
|
|
56
|
+
<TestmonialItemFramerComponent.Responsive
|
|
57
|
+
name1={"Wade Warren"}
|
|
58
|
+
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"}
|
|
59
|
+
designation={"Flutter Developer"}
|
|
60
|
+
/>
|
|
61
|
+
<ArticlesCardFramerComponent.Responsive
|
|
62
|
+
date={"Mar 06, 2024 "}
|
|
63
|
+
link={"/news/:slug"}
|
|
64
|
+
title={"Discoveries from Our Thinkers"}
|
|
65
|
+
excerpt={"Experience seamless integration with decentralized applications (DApps)."}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};"
|
|
70
|
+
`)
|
|
71
|
+
})
|
|
72
|
+
}, 1000 * 10)
|
package/src/exporter.ts
CHANGED
|
@@ -480,14 +480,14 @@ export async function bundle({
|
|
|
480
480
|
'utf-8',
|
|
481
481
|
)
|
|
482
482
|
}
|
|
483
|
-
const res = {
|
|
483
|
+
const res: BundleResult = {
|
|
484
484
|
components: Object.entries(components).map(([name, v]) => {
|
|
485
485
|
const propControls = propControlsData.find(
|
|
486
486
|
(x) => x?.name === name,
|
|
487
487
|
)
|
|
488
488
|
|
|
489
489
|
return {
|
|
490
|
-
|
|
490
|
+
componentPathSlug: name,
|
|
491
491
|
url: v,
|
|
492
492
|
name,
|
|
493
493
|
componentName: componentCamelCase(name),
|
|
@@ -523,83 +523,14 @@ export async function bundle({
|
|
|
523
523
|
const result = await rebuild()
|
|
524
524
|
console.log()
|
|
525
525
|
console.log()
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (aHasBreakpoints && !bHasBreakpoints) return -1
|
|
535
|
-
if (!aHasBreakpoints && bHasBreakpoints) return 1
|
|
536
|
-
|
|
537
|
-
// Within each group, prefer components with example properties
|
|
538
|
-
const aProp = findExampleProperty(a.propertyControls)
|
|
539
|
-
const bProp = findExampleProperty(b.propertyControls)
|
|
540
|
-
return (bProp ? 1 : 0) - (aProp ? 1 : 0)
|
|
541
|
-
})?.[0]
|
|
542
|
-
if (!exampleComponent) {
|
|
543
|
-
logger.log(
|
|
544
|
-
`No example component found with breakpoints, using random example`,
|
|
545
|
-
)
|
|
546
|
-
// Create an example component if none found with breakpoints
|
|
547
|
-
exampleComponent = {
|
|
548
|
-
path: 'hero',
|
|
549
|
-
componentName: 'HeroFramerComponent',
|
|
550
|
-
propertyControls: {
|
|
551
|
-
variant: {
|
|
552
|
-
type: ControlType.Enum,
|
|
553
|
-
options: ['Desktop', 'Tablet', 'Mobile'],
|
|
554
|
-
optionTitles: ['Desktop', 'Tablet', 'Mobile'],
|
|
555
|
-
},
|
|
556
|
-
} as any,
|
|
557
|
-
name: 'Hero',
|
|
558
|
-
url: '',
|
|
559
|
-
}
|
|
560
|
-
if (!exampleComponent) {
|
|
561
|
-
return { rebuild, buildContext }
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
const outDirForExample = path.posix
|
|
566
|
-
.relative(process.cwd(), out)
|
|
567
|
-
.replace(/^src\//, '') // remove src so file works inside src
|
|
568
|
-
logger.log(
|
|
569
|
-
'exampleComponent?.propertyControls',
|
|
570
|
-
exampleComponent?.propertyControls,
|
|
571
|
-
)
|
|
572
|
-
const prop = findExampleProperty(exampleComponent?.propertyControls)
|
|
573
|
-
const propStr = prop ? ` ${prop}='example'` : ''
|
|
574
|
-
const responsiveComponent = dedent`
|
|
575
|
-
{/* use .Responsive for components with breakpoints */}
|
|
576
|
-
<${exampleComponent?.componentName}.Responsive${propStr} />
|
|
577
|
-
`
|
|
578
|
-
const nonResponsiveComponent = dedent`
|
|
579
|
-
<${exampleComponent?.componentName}
|
|
580
|
-
${prop}='example'
|
|
581
|
-
style={{ width: '100%' }}
|
|
582
|
-
/>
|
|
583
|
-
`
|
|
584
|
-
const exampleCode = dedent`
|
|
585
|
-
import './${outDirForExample}/styles.css'
|
|
586
|
-
// this file imported below is generated when you run \`npm run framer\`
|
|
587
|
-
import ${exampleComponent?.componentName} from './${outDirForExample}/${
|
|
588
|
-
exampleComponent?.path
|
|
589
|
-
}'
|
|
590
|
-
|
|
591
|
-
export default function App() {
|
|
592
|
-
return (
|
|
593
|
-
<div className='flex flex-col'>
|
|
594
|
-
${responsiveComponent
|
|
595
|
-
.split('\n')
|
|
596
|
-
.map((line, i) => (!i ? line : ' ' + line))
|
|
597
|
-
.join('\n')}
|
|
598
|
-
</div>
|
|
599
|
-
);
|
|
600
|
-
};
|
|
601
|
-
`
|
|
602
|
-
|
|
526
|
+
const outDirForExample =
|
|
527
|
+
path.posix.relative(process.cwd(), out).replace(/^src\//, '') ||
|
|
528
|
+
'framer' // remove src so file works inside src
|
|
529
|
+
const { exampleCode } = await createExampleComponentCode({
|
|
530
|
+
outDir: out,
|
|
531
|
+
// buildResult: result,
|
|
532
|
+
config,
|
|
533
|
+
})
|
|
603
534
|
if (stackblitzDemoExample) {
|
|
604
535
|
logger.log(`Inside Stackblitz demo, writing App.tsx`)
|
|
605
536
|
await fs.promises.mkdir(path.dirname(stackblitzDemoExample), {
|
|
@@ -1283,7 +1214,6 @@ function findExampleProperty(propertyControls?: PropertyControls) {
|
|
|
1283
1214
|
return propCamelCaseJustLikeFramer(stringProp[1]?.title || '')
|
|
1284
1215
|
}
|
|
1285
1216
|
|
|
1286
|
-
|
|
1287
1217
|
// these styles are global styles injected by Framer in the generated websites, without them things like icons can look weird
|
|
1288
1218
|
const resetCssStyles = `
|
|
1289
1219
|
|
|
@@ -1301,7 +1231,11 @@ const resetCssStyles = `
|
|
|
1301
1231
|
margin: 0;
|
|
1302
1232
|
}
|
|
1303
1233
|
|
|
1304
|
-
.unframer
|
|
1234
|
+
.unframer {
|
|
1235
|
+
line-height: normal;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
.unframer, .unframer input, .unframer textarea, .unframer select, .unframer button {
|
|
1305
1239
|
font-size: 12px;
|
|
1306
1240
|
font-family: sans-serif;
|
|
1307
1241
|
}
|
|
@@ -1317,3 +1251,87 @@ async function recursiveReaddir(dir: string): Promise<string[]> {
|
|
|
1317
1251
|
)
|
|
1318
1252
|
return files.flat()
|
|
1319
1253
|
}
|
|
1254
|
+
|
|
1255
|
+
function indentWithTabs(str: string, tabs: string) {
|
|
1256
|
+
return str
|
|
1257
|
+
.split('\n')
|
|
1258
|
+
.map((line, i) => (!i ? line : tabs + line))
|
|
1259
|
+
.join('\n')
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
export async function createExampleComponentCode({
|
|
1263
|
+
outDir,
|
|
1264
|
+
config,
|
|
1265
|
+
}: {
|
|
1266
|
+
outDir: string
|
|
1267
|
+
config: Config
|
|
1268
|
+
}) {
|
|
1269
|
+
const outDirForExample = path.posix
|
|
1270
|
+
.relative(process.cwd(), outDir)
|
|
1271
|
+
.replace(/^src\//, '') // remove src so file works inside src
|
|
1272
|
+
const instances = config?.componentInstancesInIndexPage.sort((a, b) => {
|
|
1273
|
+
// Order first by nodeDepth (lower is better)
|
|
1274
|
+
return a.nodeDepth - b.nodeDepth || a.pageOrdering - b.pageOrdering
|
|
1275
|
+
})
|
|
1276
|
+
|
|
1277
|
+
const imports = instances.map((exampleComponent) => {
|
|
1278
|
+
return `import ${componentCamelCase(exampleComponent?.componentPathSlug)} from './${outDirForExample}/${
|
|
1279
|
+
exampleComponent?.componentPathSlug
|
|
1280
|
+
}'`
|
|
1281
|
+
})
|
|
1282
|
+
|
|
1283
|
+
const jsx = instances.map((exampleComponent) => {
|
|
1284
|
+
let propStr = ''
|
|
1285
|
+
for (let [key, value] of Object.entries(
|
|
1286
|
+
exampleComponent.controls || {},
|
|
1287
|
+
)) {
|
|
1288
|
+
if (key === 'variant') {
|
|
1289
|
+
continue
|
|
1290
|
+
}
|
|
1291
|
+
if (typeof value === 'object') {
|
|
1292
|
+
continue
|
|
1293
|
+
}
|
|
1294
|
+
// TODO get property controls to render enums much better? maybe do this in plugin instead
|
|
1295
|
+
propStr += '\n'
|
|
1296
|
+
propStr += ` ${key}={${JSON.stringify(value)}}`
|
|
1297
|
+
}
|
|
1298
|
+
if (propStr) propStr += '\n'
|
|
1299
|
+
const responsiveComponent = dedent`
|
|
1300
|
+
<${componentCamelCase(exampleComponent?.componentPathSlug)}.Responsive${propStr}/>
|
|
1301
|
+
`
|
|
1302
|
+
return responsiveComponent
|
|
1303
|
+
})
|
|
1304
|
+
|
|
1305
|
+
let containerClasses = ''
|
|
1306
|
+
if (config.pageBackgroundColor) {
|
|
1307
|
+
let bg = config.pageBackgroundColor?.replace(' ', '_')
|
|
1308
|
+
containerClasses += `bg-[${bg}]`
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
const exampleCode = dedent`
|
|
1312
|
+
import './${outDirForExample}/styles.css'
|
|
1313
|
+
|
|
1314
|
+
${indentWithTabs(imports.join('\n'), '')}
|
|
1315
|
+
|
|
1316
|
+
export default function App() {
|
|
1317
|
+
return (
|
|
1318
|
+
<div className='flex flex-col items-center gap-3 ${containerClasses}'>
|
|
1319
|
+
${indentWithTabs(jsx.join('\n'), ' ')}
|
|
1320
|
+
</div>
|
|
1321
|
+
);
|
|
1322
|
+
};
|
|
1323
|
+
`
|
|
1324
|
+
return {
|
|
1325
|
+
exampleCode,
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
type BundleResult = {
|
|
1330
|
+
components: Array<{
|
|
1331
|
+
componentPathSlug: string
|
|
1332
|
+
name: string
|
|
1333
|
+
url: string
|
|
1334
|
+
componentName: string
|
|
1335
|
+
propertyControls?: PropertyControls
|
|
1336
|
+
}>
|
|
1337
|
+
}
|