unframer 2.6.6 → 2.7.1

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/src/exporter.ts CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  logFontsUsage,
28
28
  } from './css.js'
29
29
  import dedent from 'dedent'
30
- import { logger } from './utils.js'
30
+ import { logger, spinner, terminalMarkdown } from './utils.js'
31
31
  import {
32
32
  esbuildPluginBundleDependencies,
33
33
  resolveRedirect,
@@ -42,10 +42,18 @@ function validateUrl(url: string) {
42
42
  }
43
43
  }
44
44
 
45
+ export type StyleToken = {
46
+ id: string
47
+ name?: string
48
+ lightColor: string
49
+ darkColor: string
50
+ }
51
+
45
52
  export async function bundle({
46
53
  cwd: out = '',
47
54
  watch = false,
48
55
  components = {} as Record<string, string>,
56
+ tokens = [] as StyleToken[],
49
57
  breakpoints = {} as BreakpointSizes,
50
58
  signal = undefined as AbortSignal | undefined,
51
59
  }) {
@@ -55,6 +63,7 @@ export async function bundle({
55
63
  fs.mkdirSync(out, { recursive: true })
56
64
  } catch (e) {}
57
65
 
66
+ spinner.start()
58
67
  const buildContext = await context({
59
68
  // entryPoints: {
60
69
  // index: url,
@@ -88,7 +97,7 @@ export async function bundle({
88
97
  esbuildPluginBundleDependencies({
89
98
  signal,
90
99
  }),
91
- polyfillNode({}),
100
+ polyfillNode({}) as any,
92
101
  {
93
102
  name: 'virtual loader',
94
103
  setup(build) {
@@ -143,6 +152,7 @@ export async function bundle({
143
152
  let codeNew =
144
153
  `// @ts-nocheck\n` +
145
154
  `/* eslint-disable */\n` +
155
+ '/* This file was generated by Unframer, do not edit manually */\n' +
146
156
  dprint.format(resultPathAbs, file.text, {
147
157
  lineWidth: 140,
148
158
  quoteStyle: 'alwaysSingle',
@@ -151,14 +161,14 @@ export async function bundle({
151
161
  })
152
162
  const lines = findRelativeLinks(codeNew)
153
163
  if (lines.length) {
154
- logger.error(
164
+ spinner.error(
155
165
  `found broken links for ${path.relative(
156
166
  out,
157
167
  file.path,
158
168
  )}, don't use relative links in Framer components`,
159
169
  )
160
170
  lines.forEach((line) => {
161
- logger.error(`${path.resolve(out, file.path)}:${line + 1}`)
171
+ logger.log(`${path.resolve(out, file.path)}:${line + 1}`)
162
172
  })
163
173
  }
164
174
 
@@ -168,6 +178,8 @@ export async function bundle({
168
178
  logger.log(`writing`, path.relative(out, file.path))
169
179
  fs.writeFileSync(resultPathAbs, codeNew, 'utf-8')
170
180
  }
181
+ spinner.stop()
182
+
171
183
  let allFonts = [] as ComponentFontBundle[]
172
184
  const sema = new Sema(10)
173
185
  const packageJson = path.resolve(out, 'package.json')
@@ -176,9 +188,13 @@ export async function bundle({
176
188
  JSON.stringify({ type: 'module' }),
177
189
  'utf-8',
178
190
  )
191
+ if (!result?.outputFiles) {
192
+ throw new Error('Failed to generate result')
193
+ }
179
194
 
195
+ spinner.start('Extracting types')
180
196
  const propControlsData = await Promise.all(
181
- result.outputFiles.map(async (file) => {
197
+ result?.outputFiles.map(async (file) => {
182
198
  try {
183
199
  await sema.acquire()
184
200
  const name = path.basename(file.path).replace(/\.js$/, '')
@@ -187,6 +203,7 @@ export async function bundle({
187
203
  return
188
204
  }
189
205
  logger.log(`extracting types for ${name}`)
206
+ spinner.update(`Extracting types for ${name}`)
190
207
  const { propertyControls, fonts } =
191
208
  await extractPropControlsUnsafe(resultPathAbs, name)
192
209
  if (!propertyControls) {
@@ -215,16 +232,19 @@ export async function bundle({
215
232
  }
216
233
  }),
217
234
  ).finally(() => fs.rmSync(packageJson))
235
+ spinner.stop()
218
236
 
219
237
  const cssString =
238
+ '/* This file was generated by Unframer, do not edit manually */\n' +
220
239
  '/* This css file has all the necessary styles to run all your components */\n' +
240
+ '\n' +
241
+ getStyleTokensCss(tokens) +
221
242
  breakpointsStyles(breakpoints) +
222
243
  '\n\n' +
223
244
  combinedCSSRules
224
245
  .map((x) => (x?.startsWith(' ') ? dedent(x) : x))
225
246
  .join('\n') +
226
247
  getFontsStyles(allFonts)
227
-
228
248
  fs.writeFileSync(path.resolve(out, 'styles.css'), cssString, 'utf-8')
229
249
 
230
250
  logFontsUsage(allFonts)
@@ -263,12 +283,17 @@ export async function bundle({
263
283
  if (watch) {
264
284
  logger.log('waiting for components or config changes')
265
285
  }
266
-
267
- const tokensCss =
268
- "/* This css file contains your color variables, sometimes these get desynced when updated in Framer so it's good that you copy and paste this snippet into your app css */\n" +
269
- '/* Bug: https://www.framer.community/c/bugs/color-style-unlinks-when-copying-component-between-projects-resulting-in-potential-value-discrepancy */\n' +
270
- getTokensCss({ out, result })
271
- fs.writeFileSync(path.resolve(out, 'tokens.css'), tokensCss, 'utf-8')
286
+ if (!tokens?.length) {
287
+ const tokensCss =
288
+ "/* This css file contains your color variables, sometimes these get desynced when updated in Framer so it's good that you copy and paste this snippet into your app css */\n" +
289
+ '/* Bug: https://www.framer.community/c/bugs/color-style-unlinks-when-copying-component-between-projects-resulting-in-potential-value-discrepancy */\n' +
290
+ getTokensCss({ out, result })
291
+ fs.writeFileSync(
292
+ path.resolve(out, 'tokens.css'),
293
+ tokensCss,
294
+ 'utf-8',
295
+ )
296
+ }
272
297
  const res = {
273
298
  components: Object.entries(components).map(([name, v]) => {
274
299
  const propControls = propControlsData.find(
@@ -284,60 +309,204 @@ export async function bundle({
284
309
  }
285
310
  }),
286
311
  }
312
+
287
313
  return res
288
314
  }
289
315
 
290
316
  if (!watch) {
291
317
  const result = await rebuild()
292
318
  await buildContext.dispose()
319
+ console.log()
320
+ console.log()
321
+
322
+ let exampleComponent = result?.components?.find((x) => {
323
+ if (!x.propertyControls) return false
324
+ const variants = getVariantsFromPropControls(x.propertyControls)
325
+ return variants?.breakpoints.length >= 2
326
+ })
327
+ if (!exampleComponent) {
328
+ logger.log(
329
+ `No example component found with breakpoints, using random example`,
330
+ )
331
+ // Create an example component if none found with breakpoints
332
+ exampleComponent = {
333
+ path: 'hero',
334
+ componentName: 'HeroFramerComponent',
335
+ propertyControls: {
336
+ variant: {
337
+ type: ControlType.Enum,
338
+ options: ['Desktop', 'Tablet', 'Mobile'],
339
+ optionTitles: ['Desktop', 'Tablet', 'Mobile'],
340
+ },
341
+ } as any,
342
+ name: 'Hero',
343
+ url: '',
344
+ }
345
+ if (!exampleComponent) {
346
+ return
347
+ }
348
+ }
349
+ const variants = getVariantsFromPropControls(
350
+ exampleComponent?.propertyControls,
351
+ )
352
+ const breakpoints = variants?.breakpoints
353
+ if (!breakpoints) {
354
+ return
355
+ }
356
+ logger.log(
357
+ 'exampleComponent?.propertyControls',
358
+ exampleComponent?.propertyControls,
359
+ )
360
+ const variantsExample = {
361
+ lg: breakpoints[1],
362
+ base: breakpoints[0],
363
+ }
364
+ let prop =
365
+ findExampleProperty(exampleComponent?.propertyControls) ||
366
+ 'exampleFramerVariable'
367
+ const outDir = path.posix.relative(process.cwd(), out)
368
+ console.log(
369
+ terminalMarkdown(dedent`
370
+ # How to use the Framer components
371
+
372
+ Your components are exported to \`${outDir}\` folder. Now please install the \`unframer\` runtime dependency:
373
+
374
+ \`\`\`sh
375
+ npm install unframer
376
+ \`\`\`
377
+
378
+ Each component has a \`.Responsive\` variant that allows you to specify different variants for different breakpoints.
379
+
380
+ You can use the components like this (try copy pasting the code below into your React app):
381
+
382
+ \`\`\`jsx
383
+ import './${outDir}/styles.css'
384
+ import ${exampleComponent?.componentName} from './${outDir}/${
385
+ exampleComponent?.path
386
+ }'
387
+
388
+ export default function App() {
389
+ return (
390
+ <div>
391
+ <${exampleComponent?.componentName}
392
+ ${prop}='example'
393
+ style={{ width: '100%' }}
394
+ />
395
+ <${exampleComponent?.componentName}.Responsive
396
+ ${prop}='example'
397
+ variants={${JSON.stringify(variantsExample || {})}}
398
+ />
399
+ </div>
400
+ );
401
+ };
402
+ \`\`\`
403
+
404
+ It's very important to import the \`styles.css\` file to include the necessary styles for the components.
405
+
406
+ To style components you can pass a \`style\` or \`className\` prop (but remember to use !important to increase the specificity).
407
+
408
+ Read more on GitHub: https://github.com/remorses/unframer
409
+ `),
410
+ )
293
411
  return result
294
412
  }
295
413
 
296
- // when user press ctrl+c dispose
297
- process.on('SIGINT', async () => {
298
- await buildContext.cancel()
299
- buildContext.dispose()
300
- })
301
- process.on('SIGABRT', async () => {
302
- await buildContext.cancel()
303
- buildContext.dispose()
304
- })
305
- signal?.addEventListener('abort', async () => {
306
- await buildContext.cancel()
307
- buildContext.dispose()
308
- })
414
+ // // when user press ctrl+c dispose
415
+ // process.on('SIGINT', async () => {
416
+ // await buildContext.cancel()
417
+ // buildContext.dispose()
418
+ // })
419
+ // process.on('SIGABRT', async () => {
420
+ // await buildContext.cancel()
421
+ // buildContext.dispose()
422
+ // })
423
+ // signal?.addEventListener('abort', async () => {
424
+ // await buildContext.cancel()
425
+ // buildContext.dispose()
426
+ // })
427
+
428
+ // const res = await rebuild()
429
+
430
+ // /**
431
+ // * Get resolved URLs for all components and also wait for 1 second if it took less time than that
432
+ // */
433
+ // const getResolvedUrls = () =>
434
+ // Promise.all([
435
+ // ...Object.values(components).map((u) => {
436
+ // const url = new URL(u)
437
+ // url.searchParams.set('ts', Date.now().toString())
438
+ // return resolveRedirect({ url: url.toString(), signal })
439
+ // }),
440
+ // new Promise((res) => setTimeout(res, 5000)),
441
+ // ])
442
+ // let prevUrls = await getResolvedUrls()
443
+ // while (!signal?.aborted) {
444
+ // const urls = await getResolvedUrls()
445
+ // const changed = urls
446
+ // .map((x, i) => (x !== prevUrls[i] ? i : null))
447
+ // .filter(Boolean)
448
+ // if (!changed?.length) {
449
+ // continue
450
+ // }
451
+ // const changedNames = Object.keys(components).filter((_, i) =>
452
+ // changed.includes(i),
453
+ // )
454
+ // logger.log(`found new component URLs for ${changedNames.join(', ')}`)
455
+ // prevUrls = urls
456
+ // await rebuild()
457
+ // }
458
+ // return res
459
+ }
309
460
 
310
- const res = await rebuild()
311
-
312
- /**
313
- * Get resolved URLs for all components and also wait for 1 second if it took less time than that
314
- */
315
- const getResolvedUrls = () =>
316
- Promise.all([
317
- ...Object.values(components).map((u) => {
318
- const url = new URL(u)
319
- url.searchParams.set('ts', Date.now().toString())
320
- return resolveRedirect({ url: url.toString(), signal })
321
- }),
322
- new Promise((res) => setTimeout(res, 5000)),
323
- ])
324
- let prevUrls = await getResolvedUrls()
325
- while (!signal?.aborted) {
326
- const urls = await getResolvedUrls()
327
- const changed = urls
328
- .map((x, i) => (x !== prevUrls[i] ? i : null))
329
- .filter(Boolean)
330
- if (!changed?.length) {
331
- continue
332
- }
333
- const changedNames = Object.keys(components).filter((_, i) =>
334
- changed.includes(i),
461
+ export function getDarkModeSelector(opts: {
462
+ darkModeType?: 'class' | 'media'
463
+ content: string
464
+ }) {
465
+ const { darkModeType = 'class', content } = opts
466
+ if (darkModeType === 'media') {
467
+ return (
468
+ '@media (prefers-color-scheme: dark) {\n' +
469
+ ' :root {\n' +
470
+ content +
471
+ '\n' +
472
+ ' }\n' +
473
+ '}'
335
474
  )
336
- logger.log(`found new component URLs for ${changedNames.join(', ')}`)
337
- prevUrls = urls
338
- await rebuild()
339
475
  }
340
- return res
476
+ return '.dark:root {\n' + content + '\n' + '}'
477
+ }
478
+
479
+ export function getStyleTokensCss(
480
+ tokens: StyleToken[],
481
+ darkModeType: 'class' | 'media' = 'class',
482
+ ) {
483
+ if (!tokens?.length) {
484
+ return ''
485
+ }
486
+
487
+ const lightTokens = tokens
488
+ .map(
489
+ (token) =>
490
+ ' --token-' + token.id + ': ' + token.lightColor + ';',
491
+ )
492
+ .join('\n')
493
+
494
+ const darkTokens = tokens
495
+ .map(
496
+ (token) => ' --token-' + token.id + ': ' + token.darkColor + ';',
497
+ )
498
+ .join('\n')
499
+
500
+ return (
501
+ ':root {\n' +
502
+ lightTokens +
503
+ '\n' +
504
+ '}\n\n' +
505
+ getDarkModeSelector({
506
+ darkModeType,
507
+ content: darkTokens,
508
+ })
509
+ )
341
510
  }
342
511
 
343
512
  function decapitalize(str: string) {
@@ -615,6 +784,7 @@ export function propControlsToType(controls: PropertyControls, fileName) {
615
784
  .map((line) => ` ${line}`)
616
785
  .join('\n') + '\n'
617
786
  let t = ''
787
+ t += '/* This file was generated by Unframer, do not edit manually */\n'
618
788
  t += 'import * as React from "react"\n\n'
619
789
  t += 'import { UnframerBreakpoint } from "unframer"\n\n'
620
790
  t += `export interface Props {\n${defaultPropsTypes}${types}\n}\n\n`
@@ -669,17 +839,17 @@ export function parsePropertyControls(code: string) {
669
839
  return propControls.slice(realStart + 1, -1)
670
840
  }
671
841
 
672
- type TokenInfo = {
842
+ type ExtractedTokenInfo = {
673
843
  tokenName: string
674
844
  metadata?: Record<string, any>
675
845
 
676
846
  defaultValue: string
677
847
  }
678
848
 
679
- export function extractTokenInfo(code: string): TokenInfo[] {
849
+ export function extractTokenInfo(code: string): ExtractedTokenInfo[] {
680
850
  const lines = code.split('\n')
681
851
  const tokenLines = lines.filter((line) => line.includes('var(--token'))
682
- const tokens: TokenInfo[] = []
852
+ const tokens: ExtractedTokenInfo[] = []
683
853
 
684
854
  for (const line of tokenLines) {
685
855
  let startIndex = 0
@@ -755,3 +925,42 @@ export function componentCamelCase(str: string) {
755
925
  str = str + 'FramerComponent'
756
926
  return str
757
927
  }
928
+
929
+ const breakpointVariants = ['mobile', 'tablet', 'desktop']
930
+
931
+ function getVariantsFromPropControls(propControls?: PropertyControls) {
932
+ if (!propControls?.variant) {
933
+ return null
934
+ }
935
+
936
+ let variants =
937
+ propControls.variant?.['optionTitles'] ||
938
+ propControls.variant?.['options'] ||
939
+ []
940
+ // Sort breakpoint-related variants first
941
+ return {
942
+ variants: variants,
943
+ breakpoints: variants.filter((v) =>
944
+ breakpointVariants.some((device) =>
945
+ v.toLowerCase().includes(device),
946
+ ),
947
+ ),
948
+ }
949
+ }
950
+
951
+ function findExampleProperty(propertyControls?: PropertyControls) {
952
+ if (!propertyControls) {
953
+ return null
954
+ }
955
+
956
+ const stringProp = Object.entries(propertyControls).find(([_, control]) => {
957
+ // console.log('control', _, control)
958
+ return control?.type === ControlType.String
959
+ })
960
+
961
+ if (!stringProp) {
962
+ return null
963
+ }
964
+
965
+ return stringProp[0]
966
+ }
package/src/framer.js CHANGED
@@ -15349,7 +15349,7 @@ function steps(numSteps, direction = 'end',) {
15349
15349
  };
15350
15350
  }
15351
15351
 
15352
- // https :https://app.framerstatic.com/framer.PLXHDWID.mjs
15352
+ // https :https://app.framerstatic.com/framer.7SGKTLCE.mjs
15353
15353
  init_chunk_QLPHEVXG();
15354
15354
  import React4 from 'react';
15355
15355
  import { startTransition as startTransition2, } from 'react';
@@ -18651,6 +18651,7 @@ function Router({
18651
18651
  locales = EMPTY_ARRAY,
18652
18652
  preserveQueryParams = false,
18653
18653
  enableAsyncURLUpdates = false,
18654
+ LayoutTemplate,
18654
18655
  },) {
18655
18656
  useMarkRouterEffects();
18656
18657
  useReplaceInitialState({
@@ -18879,14 +18880,27 @@ function Router({
18879
18880
  notFoundPage,
18880
18881
  defaultPageStyle,
18881
18882
  forceUpdateKey: dep,
18882
- children: jsxs(Fragment, {
18883
- children: [
18884
- jsx(MarkSuspenseEffects.Start, {},),
18885
- pageExistsInCurrentLocale
18886
- ? renderPage(current.page, defaultPageStyle,)
18887
- : notFoundPage && renderPage(notFoundPage, defaultPageStyle,),
18888
- ],
18889
- }, remountKey,),
18883
+ children: jsx(WithLayoutTemplate, {
18884
+ LayoutTemplate,
18885
+ routeId: currentRouteId,
18886
+ children: jsxs(Fragment, {
18887
+ children: [
18888
+ jsx(MarkSuspenseEffects.Start, {},),
18889
+ pageExistsInCurrentLocale
18890
+ ? renderPage(
18891
+ current.page,
18892
+ LayoutTemplate
18893
+ ? {
18894
+ ...defaultPageStyle,
18895
+ display: 'content',
18896
+ }
18897
+ : defaultPageStyle,
18898
+ )
18899
+ : // LAYOUT_TEMPLATE @TODO: display: content for not found page?
18900
+ notFoundPage && renderPage(notFoundPage, defaultPageStyle,),
18901
+ ],
18902
+ }, remountKey,),
18903
+ },),
18890
18904
  },),
18891
18905
  jsx(TurnOnReactEventHandling, {},),
18892
18906
  jsx(MarkSuspenseEffects.End, {},),
@@ -18895,6 +18909,17 @@ function Router({
18895
18909
  },),
18896
18910
  },);
18897
18911
  }
18912
+ function WithLayoutTemplate({
18913
+ LayoutTemplate,
18914
+ routeId,
18915
+ children,
18916
+ },) {
18917
+ if (!LayoutTemplate) return children;
18918
+ return jsx(LayoutTemplate, {
18919
+ routeId,
18920
+ children,
18921
+ },);
18922
+ }
18898
18923
  function scrollElementIntoView(element, smoothScroll,) {
18899
18924
  const scrollIntoViewOptions = smoothScroll
18900
18925
  ? {
@@ -36218,9 +36243,7 @@ function createHook(forwardedRef,) {
36218
36243
  let preventNextCall = false;
36219
36244
  function cloneChildrenWithPropsAndRef(children, props,) {
36220
36245
  if (preventNextCall) {
36221
- throw new ReferenceError(
36222
- 'useCloneChildrenWithPropsAndRef: You should not call cloneChildrenWithPropsAndRef more than once during the render cycle.',
36223
- );
36246
+ ;
36224
36247
  }
36225
36248
  preventNextCall = true;
36226
36249
  if (React2.Children.count(children,) > 1 && forwardedRef) {
@@ -42137,6 +42160,18 @@ var Ordering = class {
42137
42160
  if (this.length !== other.length) return false;
42138
42161
  return this.getHash() === other.getHash();
42139
42162
  }
42163
+ providedByFields(fields,) {
42164
+ for (
42165
+ const {
42166
+ field,
42167
+ } of this.fields
42168
+ ) {
42169
+ if (fields.has(field,)) continue;
42170
+ if (field.name === VIRTUAL_INDEX_FIELD) continue;
42171
+ return false;
42172
+ }
42173
+ return true;
42174
+ }
42140
42175
  };
42141
42176
  var Scope = class {
42142
42177
  constructor(parent,) {
@@ -44740,13 +44775,21 @@ var Normalizer = class {
44740
44775
  return this.newRelationalLeftJoin(right, left, constraint,);
44741
44776
  }
44742
44777
  newRelationalFilter(input, predicate,) {
44743
- if (input instanceof RelationalLeftJoin && predicate.referencedFields.subsetOf(input.leftGroup.relational.outputFields,)) {
44744
- const left = this.newRelationalFilter(input.left, predicate,);
44745
- return this.newRelationalLeftJoin(left, input.right, input.constraint,);
44778
+ if (
44779
+ input instanceof RelationalLeftJoin &&
44780
+ // Check that the predicate doesn't depend on any joined field.
44781
+ predicate.referencedFields.subsetOf(input.leftGroup.relational.outputFields,)
44782
+ ) {
44783
+ const pushedFilter = this.newRelationalFilter(input.left, predicate,);
44784
+ return this.newRelationalLeftJoin(pushedFilter, input.right, input.constraint,);
44746
44785
  }
44747
- if (input instanceof RelationalRightJoin && predicate.referencedFields.subsetOf(input.rightGroup.relational.outputFields,)) {
44748
- const right = this.newRelationalFilter(input.right, predicate,);
44749
- return this.newRelationalLeftJoin(input.left, right, input.constraint,);
44786
+ if (
44787
+ input instanceof RelationalRightJoin &&
44788
+ // Check that the predicate doesn't depend on any joined field.
44789
+ predicate.referencedFields.subsetOf(input.rightGroup.relational.outputFields,)
44790
+ ) {
44791
+ const pushedFilter = this.newRelationalFilter(input.right, predicate,);
44792
+ return this.newRelationalLeftJoin(input.left, pushedFilter, input.constraint,);
44750
44793
  }
44751
44794
  const node = new RelationalFilter(input, predicate,);
44752
44795
  return this.finishRelational(node,);
@@ -44756,6 +44799,16 @@ var Normalizer = class {
44756
44799
  return this.finishRelational(node,);
44757
44800
  }
44758
44801
  newRelationalLimit(input, limit, ordering,) {
44802
+ if (
44803
+ input instanceof RelationalProject &&
44804
+ // Check that the limit doesn't depend on any projected field.
44805
+ limit.referencedFields.subsetOf(input.inputGroup.relational.outputFields,) &&
44806
+ // Check that the ordering doesn't depend on any projected field.
44807
+ ordering.providedByFields(input.inputGroup.relational.outputFields,)
44808
+ ) {
44809
+ const pushedLimit = this.newRelationalLimit(input.input, limit, ordering,);
44810
+ return this.newRelationalProject(pushedLimit, input.projections, input.passthrough,);
44811
+ }
44759
44812
  const node = new RelationalLimit(input, limit, ordering,);
44760
44813
  return this.finishRelational(node,);
44761
44814
  }
@@ -53073,7 +53126,7 @@ var package_default = {
53073
53126
  react: '^18.2.0',
53074
53127
  'react-dom': '^18.2.0',
53075
53128
  semver: '^7.5.2',
53076
- typescript: '^5.6.2',
53129
+ typescript: '^5.7.2',
53077
53130
  yargs: '^17.6.2',
53078
53131
  },
53079
53132
  peerDependencies: {
package/src/utils.ts CHANGED
@@ -1,8 +1,25 @@
1
1
  import pico from 'picocolors'
2
2
 
3
+ import { marked } from 'marked'
4
+ import { markedTerminal } from 'marked-terminal'
5
+ import { createSpinner } from 'nanospinner'
6
+
7
+ export const spinner = createSpinner('Downloading Framer Components') as any
8
+
9
+ marked.use(markedTerminal())
10
+
11
+ export function terminalMarkdown(markdown: string) {
12
+ return marked(markdown)
13
+ }
14
+
15
+ const shouldDebugUnframer = !!process.env.DEBUG_UNFRAMER
16
+
3
17
  const prefix = '[unframer]'
4
18
  export const logger = {
5
19
  log(...args) {
20
+ if (!shouldDebugUnframer) {
21
+ return
22
+ }
6
23
  console.log(prefix, ...args)
7
24
  },
8
25
  green(...args) {