unframer 2.25.5 → 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.
Files changed (117) hide show
  1. package/dist/babel-jsx.d.ts +15 -0
  2. package/dist/babel-jsx.d.ts.map +1 -0
  3. package/dist/babel-jsx.js +223 -0
  4. package/dist/babel-jsx.js.map +1 -0
  5. package/dist/babel-plugin-imports.d.ts +0 -6
  6. package/dist/babel-plugin-imports.d.ts.map +1 -1
  7. package/dist/babel-plugin-imports.js +2 -135
  8. package/dist/babel-plugin-imports.js.map +1 -1
  9. package/dist/cli.d.ts +1 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +31 -6
  12. package/dist/cli.js.map +1 -1
  13. package/dist/css.js +13 -13
  14. package/dist/esbuild.d.ts.map +1 -1
  15. package/dist/esbuild.js +5 -5
  16. package/dist/esbuild.js.map +1 -1
  17. package/dist/example-code.test.js +39 -39
  18. package/dist/example-code.test.js.map +1 -1
  19. package/dist/exporter.d.ts.map +1 -1
  20. package/dist/exporter.js +137 -87
  21. package/dist/exporter.js.map +1 -1
  22. package/dist/flat-cache-interceptor.d.ts +27 -0
  23. package/dist/flat-cache-interceptor.d.ts.map +1 -0
  24. package/dist/flat-cache-interceptor.js +99 -0
  25. package/dist/flat-cache-interceptor.js.map +1 -0
  26. package/dist/framer.d.ts.map +1 -1
  27. package/dist/framer.js +837 -684
  28. package/dist/framer.js.map +1 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/react.d.ts.map +1 -1
  32. package/dist/react.js +15 -3
  33. package/dist/react.js.map +1 -1
  34. package/dist/sentry.d.ts.map +1 -1
  35. package/dist/sentry.js +1 -16
  36. package/dist/sentry.js.map +1 -1
  37. package/dist/undici-dispatcher.d.ts +2 -0
  38. package/dist/undici-dispatcher.d.ts.map +1 -0
  39. package/dist/undici-dispatcher.js +13 -0
  40. package/dist/undici-dispatcher.js.map +1 -0
  41. package/dist/utils.d.ts +3 -3
  42. package/dist/utils.d.ts.map +1 -1
  43. package/dist/utils.js +4 -10
  44. package/dist/utils.js.map +1 -1
  45. package/dist/utils.test.d.ts +2 -0
  46. package/dist/utils.test.d.ts.map +1 -0
  47. package/dist/utils.test.js +143 -0
  48. package/dist/utils.test.js.map +1 -0
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.js +1 -1
  51. package/esm/babel-jsx.d.ts +15 -0
  52. package/esm/babel-jsx.d.ts.map +1 -0
  53. package/esm/babel-jsx.js +219 -0
  54. package/esm/babel-jsx.js.map +1 -0
  55. package/esm/babel-plugin-imports.d.ts +0 -6
  56. package/esm/babel-plugin-imports.d.ts.map +1 -1
  57. package/esm/babel-plugin-imports.js +2 -134
  58. package/esm/babel-plugin-imports.js.map +1 -1
  59. package/esm/cli.d.ts +1 -0
  60. package/esm/cli.d.ts.map +1 -1
  61. package/esm/cli.js +31 -6
  62. package/esm/cli.js.map +1 -1
  63. package/esm/css.js +13 -13
  64. package/esm/esbuild.d.ts.map +1 -1
  65. package/esm/esbuild.js +4 -4
  66. package/esm/esbuild.js.map +1 -1
  67. package/esm/example-code.test.js +40 -40
  68. package/esm/example-code.test.js.map +1 -1
  69. package/esm/exporter.d.ts.map +1 -1
  70. package/esm/exporter.js +100 -50
  71. package/esm/exporter.js.map +1 -1
  72. package/esm/flat-cache-interceptor.d.ts +27 -0
  73. package/esm/flat-cache-interceptor.d.ts.map +1 -0
  74. package/esm/flat-cache-interceptor.js +95 -0
  75. package/esm/flat-cache-interceptor.js.map +1 -0
  76. package/esm/framer.d.ts.map +1 -1
  77. package/esm/framer.js +813 -672
  78. package/esm/framer.js.map +1 -1
  79. package/esm/index.d.ts +1 -1
  80. package/esm/index.d.ts.map +1 -1
  81. package/esm/react.d.ts.map +1 -1
  82. package/esm/react.js +15 -3
  83. package/esm/react.js.map +1 -1
  84. package/esm/sentry.d.ts.map +1 -1
  85. package/esm/sentry.js +1 -16
  86. package/esm/sentry.js.map +1 -1
  87. package/esm/undici-dispatcher.d.ts +2 -0
  88. package/esm/undici-dispatcher.d.ts.map +1 -0
  89. package/esm/undici-dispatcher.js +10 -0
  90. package/esm/undici-dispatcher.js.map +1 -0
  91. package/esm/utils.d.ts +3 -3
  92. package/esm/utils.d.ts.map +1 -1
  93. package/esm/utils.js +3 -9
  94. package/esm/utils.js.map +1 -1
  95. package/esm/utils.test.d.ts +2 -0
  96. package/esm/utils.test.d.ts.map +1 -0
  97. package/esm/utils.test.js +141 -0
  98. package/esm/utils.test.js.map +1 -0
  99. package/esm/version.d.ts +1 -1
  100. package/esm/version.js +1 -1
  101. package/package.json +8 -10
  102. package/src/babel-jsx.ts +277 -0
  103. package/src/babel-plugin-imports.ts +6 -169
  104. package/src/cli.ts +45 -6
  105. package/src/css.ts +13 -13
  106. package/src/esbuild.ts +5 -4
  107. package/src/example-code.test.ts +40 -41
  108. package/src/exporter.ts +124 -54
  109. package/src/flat-cache-interceptor.ts +114 -0
  110. package/src/framer.js +902 -739
  111. package/src/index.ts +1 -1
  112. package/src/react.tsx +15 -1
  113. package/src/sentry.ts +1 -20
  114. package/src/undici-dispatcher.ts +13 -0
  115. package/src/utils.test.ts +148 -0
  116. package/src/utils.ts +4 -17
  117. 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,10 +1,11 @@
1
1
  import { fetch } from 'undici'
2
2
  import { RateLimit, Sema } from 'async-sema'
3
- import { dispatcher, logger, spinner } from './utils'
3
+ import { logger, spinner } from './utils'
4
4
 
5
5
  import { Plugin, transform, type OnResolveArgs } from 'esbuild'
6
6
  import { resolvePackage } from './exporter'
7
7
  import { notifyError } from './sentry'
8
+ import { dispatcher } from './undici-dispatcher'
8
9
 
9
10
  export const defaultExternalPackages = [
10
11
  'react',
@@ -297,13 +298,13 @@ export async function recursiveResolveRedirect(
297
298
 
298
299
  return url
299
300
  }
300
- let semaphore = new Sema(4)
301
- let rateLimiter = RateLimit(12, { timeUnit: 1000 })
301
+ let semaphore = new Sema(3)
302
+ let rateLimiter = RateLimit(20, { timeUnit: 1000 })
302
303
 
303
304
  export const fetchWithRetry = retryTwice(
304
305
  async (url: string, options?: RequestInit) => {
305
306
  await semaphore.acquire()
306
- await rateLimiter()
307
+
307
308
  const timeout = setTimeout(() => {
308
309
  logger.error('fetch taking more than 10s', url)
309
310
  }, 10000)
@@ -1,7 +1,6 @@
1
- import { expect, describe, test } from 'vitest'
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
- return (
32
- <div className='flex flex-col items-center gap-3 '>
33
- <NavigationFramerComponent.Responsive
34
- ctaVariant={"Primary"}
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
- );
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
  })
package/src/exporter.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BuildResult, build, context, type BuildOptions } from 'esbuild'
2
+
2
3
  import packageJson from '../package.json'
3
4
 
4
5
  import url from 'url'
@@ -8,10 +9,17 @@ import { Sema } from 'async-sema'
8
9
 
9
10
  import { nodeModulesPolyfillPlugin } from 'esbuild-plugins-node-modules-polyfill'
10
11
 
12
+ import { transform } from '@babel/core'
11
13
  import { exec } from 'child_process'
14
+ import { error } from 'console'
12
15
  import fs from 'fs'
13
16
  import path from 'path'
14
- import dedent from 'string-dedent'
17
+ import { dedent } from './utils.js'
18
+ import {
19
+ babelPluginJsxTransform,
20
+ removeJsxExpressionContainer,
21
+ } from './babel-jsx.js'
22
+ import { propCamelCaseJustLikeFramer } from './compat.js'
15
23
  import {
16
24
  ComponentFontBundle,
17
25
  breakpointsStyles,
@@ -32,16 +40,18 @@ import {
32
40
  PropertyControls,
33
41
  combinedCSSRules,
34
42
  } from './framer.js'
43
+ import { notifyError } from './sentry'
35
44
  import {
36
- stackblitzDemoExample,
37
45
  kebabCase,
38
46
  logger,
39
47
  spinner,
48
+ stackblitzDemoExample,
40
49
  terminalMarkdown,
41
50
  } from './utils.js'
42
- import { error } from 'console'
43
- import { notifyError } from './sentry'
44
- import { propCamelCaseJustLikeFramer } from './compat'
51
+
52
+ import { Biome, Distribution } from '@biomejs/js-api'
53
+
54
+ let biome: Biome
45
55
 
46
56
  export type StyleToken = {
47
57
  id: string
@@ -68,7 +78,7 @@ export async function bundle({
68
78
  await fs.promises.mkdir(out, { recursive: true })
69
79
  } catch (e) {}
70
80
 
71
- spinner.start()
81
+ spinner.start('exporting components...')
72
82
 
73
83
  const otherRoutes = Object.fromEntries(
74
84
  (config.framerWebPages || []).map((page) => [
@@ -103,6 +113,7 @@ export async function bundle({
103
113
  }
104
114
  }),
105
115
  jsx: 'automatic',
116
+ // jsxFactory: '_jsx',
106
117
  bundle: true,
107
118
  platform: 'browser',
108
119
  metafile: true,
@@ -265,28 +276,57 @@ export async function bundle({
265
276
  spinner.update('Finished build')
266
277
 
267
278
  for (let file of buildResult.outputFiles!) {
268
- const resultPathAbs = path.resolve(out, file.path)
279
+ const resultPathAbsJs = path.resolve(out, file.path)
280
+ const resultPathAbsJsx = resultPathAbsJs.replace(/\.js$/, '.jsx')
281
+
269
282
  const existing = await fs.promises
270
- .readFile(file.path, 'utf-8')
283
+ .readFile(resultPathAbsJsx, 'utf-8')
271
284
  .catch(() => null)
272
- // let res = transform(file.text || '', {
273
- // babelrc: false,
274
- // sourceType: 'module',
275
- // plugins: [
276
- // babelPluginDeduplicateImports,
277
-
278
- // babelPluginJsxTransform(),
279
- // ],
280
- // filename: 'x.js',
281
- // compact: true,
282
- // sourceMaps: false,
283
- // })
284
- // let inputCode = res!.code!
285
-
286
- const tooBigSize = 1 * 1024 * 1024
285
+ const tooBigSize = 0.7 * 1024 * 1024
287
286
 
288
287
  let formatted = file.text
289
- // let tooBig = file.text.length >= tooBigSize
288
+
289
+ let tooBig = file.text.length >= tooBigSize
290
+ let didFormat = false
291
+ if (
292
+ config.jsx &&
293
+ !tooBig &&
294
+ !resultPathAbsJs.includes('/chunks/') &&
295
+ !resultPathAbsJs.includes('\\chunks\\')
296
+ ) {
297
+ try {
298
+ let res = transform(file.text || '', {
299
+ babelrc: false,
300
+ sourceType: 'module',
301
+ plugins: [
302
+ // babelPluginDeduplicateImports,
303
+ babelPluginJsxTransform,
304
+ removeJsxExpressionContainer,
305
+ ],
306
+ // ast: true,
307
+ // code: false,
308
+ filename: 'x.jsx',
309
+ compact: false,
310
+ sourceMaps: false,
311
+ })
312
+ if (res?.code) {
313
+ if (!biome) {
314
+ biome = await Biome.create({
315
+ distribution: Distribution.NODE,
316
+ })
317
+ }
318
+ let result = biome.formatContent(res.code, {
319
+ filePath: 'example.jsx',
320
+ })
321
+ didFormat = true
322
+ formatted = result.content
323
+ }
324
+ } catch (e) {
325
+ notifyError(e, 'babel transform and format')
326
+ }
327
+ }
328
+
329
+ // let inputCode = res!.code!
290
330
  // let shouldFormat = !tooBig && !file.path.includes('chunks')
291
331
  // if (shouldFormat) {
292
332
  // spinner.update(`Formatting ${path.relative(out, file.path)}`)
@@ -306,11 +346,6 @@ export async function bundle({
306
346
  // )
307
347
  // }
308
348
 
309
- let codeNew =
310
- `// @ts-nocheck\n` +
311
- `/* eslint-disable */\n` +
312
- doNotEditComment +
313
- formatted
314
349
  // if (framerWebPages?.length) {
315
350
  // codeNew = replaceWebPageIds({
316
351
  // code: codeNew,
@@ -327,14 +362,23 @@ export async function bundle({
327
362
  // })
328
363
  // }
329
364
 
330
- if (existing === codeNew) {
331
- continue
332
- }
365
+ const prefix =
366
+ `// @ts-nocheck\n` + `/* eslint-disable */\n` + doNotEditComment
367
+ const codeJsx = prefix + formatted
368
+ const codeJs = prefix + file.text
369
+ // if (existing === codeJsx) {
370
+ // continue
371
+ // }
333
372
  logger.log(`writing`, path.relative(out, file.path))
334
- await fs.promises.mkdir(path.dirname(resultPathAbs), {
373
+ await fs.promises.mkdir(path.dirname(resultPathAbsJsx), {
335
374
  recursive: true,
336
375
  })
337
- await fs.promises.writeFile(resultPathAbs, codeNew, 'utf-8')
376
+ if (codeJs !== codeJsx || !didFormat) {
377
+ await fs.promises.writeFile(resultPathAbsJs, codeJs, 'utf-8')
378
+ }
379
+ if (didFormat) {
380
+ await fs.promises.writeFile(resultPathAbsJsx, codeJsx, 'utf-8')
381
+ }
338
382
  }
339
383
  spinner.stop()
340
384
  await fs.promises.writeFile(
@@ -353,7 +397,7 @@ export async function bundle({
353
397
  'utf-8',
354
398
  )
355
399
 
356
- const sema = new Sema(stackblitzDemoExample ? 5 : 10)
400
+ const sema = new Sema(stackblitzDemoExample ? 5 : 6)
357
401
  spinner.update('Extracting types')
358
402
  logger.log(`using node path`, nodePath)
359
403
  let allFonts = [] as ComponentFontBundle[]
@@ -363,11 +407,17 @@ export async function bundle({
363
407
  await sema.acquire()
364
408
  const name = path
365
409
  .relative(out, file.path)
366
- .replace(/\.js$/, '')
410
+ .replace(/\.jsx?$/, '')
367
411
  const resultPathAbs = path.resolve(out, file.path)
368
412
  if (!components[name]) {
369
413
  return
370
414
  }
415
+ if (!fs.existsSync(resultPathAbs)) {
416
+ spinner.error(
417
+ `cannot extract types for ${name}, missing output file`,
418
+ )
419
+ return
420
+ }
371
421
  logger.log(`extracting types for ${name}`)
372
422
  spinner.update(`Extracting types for ${name}`)
373
423
  const { propertyControls, fonts } =
@@ -392,6 +442,7 @@ export async function bundle({
392
442
  path.resolve(out, `${name}.d.ts`),
393
443
  types,
394
444
  )
445
+
395
446
  return {
396
447
  propertyControls,
397
448
  fonts,
@@ -430,9 +481,16 @@ export async function bundle({
430
481
  )
431
482
 
432
483
  logFontsUsage(allFonts)
433
- .split('\n')
484
+ ?.split('\n')
434
485
  .forEach((x) => logger.log(x))
435
486
 
487
+ const jsxFiles = buildResult.outputFiles
488
+ .filter(
489
+ (x) =>
490
+ x.path.endsWith('.js') &&
491
+ fs.existsSync(x.path.replace(/\.js$/, '.jsx')),
492
+ )
493
+ .map((x) => x.path.replace(/\.js$/, '.jsx'))
436
494
  const outFiles = buildResult.outputFiles
437
495
  .map((x) => path.resolve(out, x.path))
438
496
  .concat([
@@ -441,12 +499,25 @@ export async function bundle({
441
499
  path.resolve(out, '.cursorignore'),
442
500
  path.resolve(out, 'styles.css'),
443
501
  ])
502
+ .concat(jsxFiles)
444
503
  .concat(
445
504
  buildResult.outputFiles.map((x) =>
446
- path.resolve(out, x.path.replace('.js', '.d.ts')),
505
+ path.resolve(out, x.path.replace(/\.jsx?$/, '.d.ts')),
447
506
  ),
448
507
  )
449
- const filesToDelete = prevFiles.filter((x) => !outFiles.includes(x))
508
+
509
+ const filesToDelete = prevFiles
510
+ .filter((x) => !outFiles.includes(x))
511
+ .concat(
512
+ buildResult.outputFiles
513
+ .map((x) => x.path)
514
+ .filter(
515
+ (js) =>
516
+ js.endsWith('.js') &&
517
+ jsxFiles.some((x) => x.startsWith(js)),
518
+ ),
519
+ )
520
+
450
521
  for (let file of filesToDelete) {
451
522
  logger.log('deleting', path.relative(out, file))
452
523
  try {
@@ -596,7 +667,7 @@ export function resolvePackage({ cwd, pkg }) {
596
667
  if (error) {
597
668
  logger.log(stderr)
598
669
  reject(
599
- new Error('Unframer is not installed in your project'),
670
+ new Error(`${pkg} is not installed in your project`),
600
671
  )
601
672
  return
602
673
  }
@@ -1162,7 +1233,7 @@ function splitOnce(str: string, separator: string) {
1162
1233
  }
1163
1234
 
1164
1235
  export function componentCamelCase(str: string) {
1165
- str = str?.replace(/\.js$/, '')
1236
+ str = str?.replace(/\.jsx?$/, '')
1166
1237
  if (!str) {
1167
1238
  return 'FramerComponent'
1168
1239
  }
@@ -1253,6 +1324,7 @@ async function recursiveReaddir(dir: string): Promise<string[]> {
1253
1324
  }
1254
1325
 
1255
1326
  function indentWithTabs(str: string, tabs: string) {
1327
+ if (!str) return ''
1256
1328
  return str
1257
1329
  .split('\n')
1258
1330
  .map((line, i) => (!i ? line : tabs + line))
@@ -1269,18 +1341,18 @@ export async function createExampleComponentCode({
1269
1341
  const outDirForExample = path.posix
1270
1342
  .relative(process.cwd(), outDir)
1271
1343
  .replace(/^src\//, '') // remove src so file works inside src
1272
- const instances = config?.componentInstancesInIndexPage.sort((a, b) => {
1344
+ const instances = config?.componentInstancesInIndexPage?.sort((a, b) => {
1273
1345
  // Order first by nodeDepth (lower is better)
1274
1346
  return a.nodeDepth - b.nodeDepth || a.pageOrdering - b.pageOrdering
1275
1347
  })
1276
1348
 
1277
- const imports = instances.map((exampleComponent) => {
1349
+ const imports = instances?.map((exampleComponent) => {
1278
1350
  return `import ${componentCamelCase(exampleComponent?.componentPathSlug)} from './${outDirForExample}/${
1279
1351
  exampleComponent?.componentPathSlug
1280
1352
  }'`
1281
1353
  })
1282
1354
 
1283
- const jsx = instances.map((exampleComponent) => {
1355
+ const jsx = instances?.map((exampleComponent) => {
1284
1356
  let propStr = ''
1285
1357
  for (let [key, value] of Object.entries(
1286
1358
  exampleComponent.controls || {},
@@ -1293,12 +1365,10 @@ export async function createExampleComponentCode({
1293
1365
  }
1294
1366
  // TODO get property controls to render enums much better? maybe do this in plugin instead
1295
1367
  propStr += '\n'
1296
- propStr += ` ${key}={${JSON.stringify(value)}}`
1368
+ propStr += ` ${key}={${JSON.stringify(value)}}`
1297
1369
  }
1298
1370
  if (propStr) propStr += '\n'
1299
- const responsiveComponent = dedent`
1300
- <${componentCamelCase(exampleComponent?.componentPathSlug)}.Responsive${propStr}/>
1301
- `
1371
+ const responsiveComponent = `<${componentCamelCase(exampleComponent?.componentPathSlug)}.Responsive${propStr}/>`
1302
1372
  return responsiveComponent
1303
1373
  })
1304
1374
 
@@ -1311,14 +1381,14 @@ export async function createExampleComponentCode({
1311
1381
  const exampleCode = dedent`
1312
1382
  import './${outDirForExample}/styles.css'
1313
1383
 
1314
- ${indentWithTabs(imports.join('\n'), '')}
1384
+ ${indentWithTabs(imports?.join('\n'), '')}
1315
1385
 
1316
1386
  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
- );
1387
+ return (
1388
+ <div className='flex flex-col items-center gap-3 ${containerClasses}'>
1389
+ ${indentWithTabs(jsx?.join('\n'), ' ')}
1390
+ </div>
1391
+ );
1322
1392
  };
1323
1393
  `
1324
1394
  return {