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.
Files changed (55) hide show
  1. package/dist/cli.d.ts +21 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +79 -48
  4. package/dist/cli.js.map +1 -1
  5. package/dist/example-code.test.d.ts +2 -0
  6. package/dist/example-code.test.d.ts.map +1 -0
  7. package/dist/example-code.test.js +72 -0
  8. package/dist/example-code.test.js.map +1 -0
  9. package/dist/exporter.d.ts +17 -55
  10. package/dist/exporter.d.ts.map +1 -1
  11. package/dist/exporter.js +73 -69
  12. package/dist/exporter.js.map +1 -1
  13. package/dist/framer.d.ts.map +1 -1
  14. package/dist/framer.js +99 -53
  15. package/dist/framer.js.map +1 -1
  16. package/dist/generated/api-client.js +0 -1
  17. package/dist/generated/api-client.js.map +1 -1
  18. package/dist/generated/api-client.test.js +0 -69
  19. package/dist/generated/api-client.test.js.map +1 -1
  20. package/dist/version.d.ts +1 -1
  21. package/dist/version.js +1 -1
  22. package/esm/cli.d.ts +21 -0
  23. package/esm/cli.d.ts.map +1 -1
  24. package/esm/cli.js +78 -48
  25. package/esm/cli.js.map +1 -1
  26. package/esm/example-code.test.d.ts +2 -0
  27. package/esm/example-code.test.d.ts.map +1 -0
  28. package/esm/example-code.test.js +70 -0
  29. package/esm/example-code.test.js.map +1 -0
  30. package/esm/exporter.d.ts +17 -55
  31. package/esm/exporter.d.ts.map +1 -1
  32. package/esm/exporter.js +72 -69
  33. package/esm/exporter.js.map +1 -1
  34. package/esm/framer.d.ts.map +1 -1
  35. package/esm/framer.js +99 -53
  36. package/esm/framer.js.map +1 -1
  37. package/esm/generated/api-client.js +0 -1
  38. package/esm/generated/api-client.js.map +1 -1
  39. package/esm/generated/api-client.test.d.ts +0 -1
  40. package/esm/generated/api-client.test.js +1 -69
  41. package/esm/generated/api-client.test.js.map +1 -1
  42. package/esm/version.d.ts +1 -1
  43. package/esm/version.js +1 -1
  44. package/package.json +4 -4
  45. package/src/{cli.tsx → cli.ts} +114 -64
  46. package/src/example-code.test.ts +72 -0
  47. package/src/exporter.ts +99 -81
  48. package/src/framer.js +128 -79
  49. package/src/generated/api-client.d.ts +1048 -682
  50. package/src/generated/api-client.d.ts.map +1 -1
  51. package/src/generated/api-client.js +5 -6
  52. package/src/generated/api-client.js.map +1 -1
  53. package/src/generated/api-client.test.d.ts +1 -1
  54. package/src/generated/api-client.test.js +1 -69
  55. package/src/version.ts +1 -1
@@ -23,5 +23,4 @@ export async function createClient({ url }) {
23
23
  return client;
24
24
  }
25
25
  // export const websiteApiClient = createClient({ url: process.env.PUBLIC_URL! })
26
- //# sourceMappingURL=api-client.js.map
27
26
  //# sourceMappingURL=api-client.js.map
@@ -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,CAAC;IACnE,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;YACP,yFAAyF;aACxF,CAAC;QACN,CAAC;KACJ,CAAC,CAAC;IACH,OAAO,MAAM,CAAC;AAClB,CAAC;AACD,iFAAiF;AACjF,sCAAsC"}
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,2 +1 @@
1
- export {};
2
1
  //# sourceMappingURL=api-client.test.d.ts.map
@@ -1,70 +1,2 @@
1
- import { test, expect } from 'vitest';
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":"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"}
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.2";
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.2';
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.2",
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.13.0",
48
+ "@sentry/node": "^9.22.0",
49
49
  "async-sema": "^3.1.1",
50
50
  "cac": "^6.7.14",
51
- "esbuild": "^0.25.3",
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.13.4",
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",
@@ -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
- logger.log(`Fetching config for project ${projectId}`)
57
-
58
- const client = await createClient({
59
- url:
60
- process.env.UNFRAMER_SERVER_URL ||
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
- path: name,
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
- let exampleComponent = result?.components?.sort((a, b) => {
528
- const aVariants = getVariantsFromPropControls(a.propertyControls)
529
- const bVariants = getVariantsFromPropControls(b.propertyControls)
530
- const aHasBreakpoints = (aVariants?.breakpoints?.length || 0) >= 2
531
- const bHasBreakpoints = (bVariants?.breakpoints?.length || 0) >= 2
532
-
533
- // Sort components with breakpoints first
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 body, .unframer input, .unframer textarea, .unframer select, .unframer button {
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
+ }