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/esm/utils.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export declare const spinner: any;
2
+ export declare function terminalMarkdown(markdown: string): string | Promise<string>;
1
3
  export declare const logger: {
2
4
  log(...args: any[]): void;
3
5
  green(...args: any[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,MAAM;;;;CAUlB,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,OAAO,EAAqD,GAAG,CAAA;AAI5E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,4BAEhD;AAKD,eAAO,MAAM,MAAM;;;;CAalB,CAAA"}
package/esm/utils.js CHANGED
@@ -1,7 +1,19 @@
1
1
  import pico from 'picocolors';
2
+ import { marked } from 'marked';
3
+ import { markedTerminal } from 'marked-terminal';
4
+ import { createSpinner } from 'nanospinner';
5
+ export const spinner = createSpinner('Downloading Framer Components');
6
+ marked.use(markedTerminal());
7
+ export function terminalMarkdown(markdown) {
8
+ return marked(markdown);
9
+ }
10
+ const shouldDebugUnframer = !!process.env.DEBUG_UNFRAMER;
2
11
  const prefix = '[unframer]';
3
12
  export const logger = {
4
13
  log(...args) {
14
+ if (!shouldDebugUnframer) {
15
+ return;
16
+ }
5
17
  console.log(prefix, ...args);
6
18
  },
7
19
  green(...args) {
package/esm/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,YAAY,CAAA;AAE7B,MAAM,MAAM,GAAG,YAAY,CAAA;AAC3B,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,GAAG,CAAC,GAAG,IAAI;QACP,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC;IACD,KAAK,CAAC,GAAG,IAAI;QACT,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACtE,CAAC;IACD,KAAK,CAAC,GAAG,IAAI;QACT,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACtE,CAAC;CACJ,CAAA"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,YAAY,CAAA;AAE7B,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,CAAC,MAAM,OAAO,GAAG,aAAa,CAAC,+BAA+B,CAAQ,CAAA;AAE5E,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,CAAA;AAE5B,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC7C,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,mBAAmB,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAA;AAExD,MAAM,MAAM,GAAG,YAAY,CAAA;AAC3B,MAAM,CAAC,MAAM,MAAM,GAAG;IAClB,GAAG,CAAC,GAAG,IAAI;QACP,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACvB,OAAM;QACV,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAA;IAChC,CAAC;IACD,KAAK,CAAC,GAAG,IAAI;QACT,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACtE,CAAC;IACD,KAAK,CAAC,GAAG,IAAI;QACT,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IACtE,CAAC;CACJ,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unframer",
3
- "version": "2.6.6",
3
+ "version": "2.7.1",
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",
@@ -44,12 +44,15 @@
44
44
  "chokidar": "^3.6.0",
45
45
  "dedent": "^1.5.3",
46
46
  "dprint-node": "^1.0.8",
47
- "esbuild": "^0.21.5",
47
+ "esbuild": "^0.24.0",
48
48
  "esbuild-plugin-polyfill-node": "^0.3.0",
49
49
  "eventemitter3": "^3.1.2",
50
50
  "find-up": "^5.0.0",
51
51
  "fs-extra": "^11.2.0",
52
52
  "json5": "^2.2.3",
53
+ "marked": "^15.0.3",
54
+ "marked-terminal": "^7.2.1",
55
+ "nanospinner": "^1.2.0",
53
56
  "native-fetch": "^4.0.2",
54
57
  "picocolors": "^1.0.1",
55
58
  "real-framer-motion": "npm:framer-motion@11.2.10",
@@ -69,12 +72,12 @@
69
72
  "@types/fs-extra": "^11.0.4",
70
73
  "@types/node": "^22.7.4",
71
74
  "@types/react": "^18.3.12",
72
- "@xmorse/deployment-utils": "^0.2.17",
75
+ "@xmorse/deployment-utils": "^0.2.18",
73
76
  "concurrently": "^8.2.2",
74
77
  "openai": "^4.52.7",
75
78
  "posthtml": "^0.16.6",
76
- "react": "19.0.0-rc-e4953922-20240919",
77
- "react-dom": "19.0.0-rc-e4953922-20240919",
79
+ "react": "19.0.0",
80
+ "react-dom": "19.0.0",
78
81
  "tiktoken": "^1.0.15",
79
82
  "typescript": "^5.6.2"
80
83
  },
package/src/cli.tsx CHANGED
@@ -1,85 +1,90 @@
1
- import { bundle } from './exporter.js'
1
+ import { setMaxListeners } from 'events'
2
2
  import JSON from 'json5'
3
- import events, { EventEmitter, setMaxListeners } from 'events'
3
+ import { bundle, StyleToken } from './exporter.js'
4
4
 
5
- import chokidar from 'chokidar'
6
- import fs from 'fs-extra'
5
+ import { cac } from 'cac'
7
6
  import findUp from 'find-up'
8
- import tmp from 'tmp'
7
+ import fs from 'fs-extra'
9
8
  import path, { basename } from 'path'
10
- const configNames = ['unframer.config.json', 'unframer.json']
11
- import { cac } from 'cac'
12
- import { logger } from './utils.js'
13
9
  import { BreakpointSizes } from './css.js'
10
+ import { logger } from './utils.js'
11
+ const configNames = ['unframer.config.json', 'unframer.json']
14
12
 
15
13
  export const cli = cac('unframer')
16
14
 
17
- cli.command('', 'Run unframer')
18
- .option('--watch', 'Watch for Framer and unframer.config.json changes')
19
- .action(async function main(options) {
15
+ let defaultOutDir = 'framer'
16
+
17
+ function nameToFolder(name: string) {
18
+ return name
19
+ .replace(/[^a-zA-Z0-9]/g, '-') // Replace non-alphanumeric with dash
20
+ .replace(/-+/g, '-') // Replace multiple dashes with single dash
21
+ .replace(/^-|-$/g, '') // Remove leading/trailing dashes
22
+ .toLowerCase()
23
+ }
24
+
25
+ cli.command('[projectId]', 'Run unframer with optional project ID')
26
+ .option('--outDir <dir>', 'Output directory', { default: defaultOutDir })
27
+ .action(async function main(projectId, options) {
28
+ const outDir = options.outDir
29
+ if (projectId) {
30
+ logger.log(`Fetching config for project ${projectId}`)
31
+ const response = await fetch(
32
+ new URL(
33
+ `/api/plugins/reactExportPlugin/project/${projectId}`,
34
+ process.env.UNFRAMER_SERVER_URL || 'https://unframer.co',
35
+ ).toString(),
36
+ )
37
+ if (!response.ok) {
38
+ console.error(`Failed to fetch Framer config`)
39
+ logger.error('Response: ' + (await response.text()))
40
+ return
41
+ }
42
+ const data = await response.json()
43
+ return processConfig({
44
+ config: {
45
+ outDir,
46
+ components: Object.fromEntries(
47
+ data.components.map((c) => [
48
+ nameToFolder(c.name),
49
+ c.url,
50
+ ]),
51
+ ),
52
+ tokens: data.colorStyles,
53
+ },
54
+ watch: false,
55
+
56
+ configBasename: 'remote config',
57
+ signal: new AbortController().signal,
58
+ })
59
+ }
60
+
20
61
  fixOldUnframerPath()
21
62
  const cwd = process.cwd()
22
- const watch = process.argv.includes('--watch')
23
63
  logger.log(`Looking for ${configNames.join(', ')} in ${cwd}`)
24
64
  const configPath = await findUp(configNames, { cwd })
25
65
  if (!configPath) {
26
66
  logger.log(`No ${configNames.join(', ')} found`)
27
67
  return
28
68
  }
29
- let configBasename = basename(configPath!)
69
+ const configBasename = basename(configPath!)
30
70
  const configContent = fs.readFileSync(configPath, 'utf8')
31
71
  if (!configContent) {
32
72
  logger.log(`No ${configBasename} contents found`)
33
73
  return
34
74
  }
35
- let config = JSON.parse(configContent)
75
+ const config = JSON.parse(configContent)
76
+ if (outDir !== defaultOutDir) {
77
+ config.outDir = outDir
78
+ }
36
79
 
37
80
  let controller = new AbortController()
38
81
  setMaxListeners(0, controller.signal)
39
82
  processConfig({
40
83
  config,
41
- watch,
84
+ watch: false,
42
85
  signal: controller.signal,
43
86
  configBasename,
44
87
  })
45
- if (!watch) {
46
- return
47
- }
48
-
49
- const watcher = chokidar.watch(configPath!, {
50
- persistent: true,
51
- })
52
-
53
- watcher.on('change', async (path) => {
54
- logger.log(`${configBasename} changed`)
55
- console.log()
56
- controller.abort()
57
-
58
- controller = new AbortController()
59
- setMaxListeners(0, controller.signal)
60
-
61
- const newConfig = safeJsonParse(
62
- fs.readFileSync(configPath!, 'utf8'),
63
- )
64
- if (!newConfig) {
65
- logger.log(`Invalid ${configBasename} file`)
66
- return
67
- }
68
- const newNames = getNewNames(config, newConfig)
69
- if (newNames.length) {
70
- logger.log(`New components found: ${newNames.join(', ')}`)
71
- await processConfig({
72
- config: {
73
- ...newConfig,
74
- components: pluck(newConfig.components, newNames),
75
- },
76
- watch,
77
- configBasename,
78
- // signal: controller.signal,
79
- })
80
- }
81
- config = newConfig
82
- })
83
88
  })
84
89
 
85
90
  const defaultConfig = `{
@@ -105,6 +110,9 @@ function fixOldUnframerPath() {
105
110
  }
106
111
  return false
107
112
  }
113
+ const version = require('../package.json').version
114
+
115
+ cli.version(version).help()
108
116
 
109
117
  cli.command('init', 'Init the unframer.config.json config').action(
110
118
  async (options) => {
@@ -118,10 +126,6 @@ cli.command('init', 'Init the unframer.config.json config').action(
118
126
  },
119
127
  )
120
128
 
121
- const version = require('../package.json').version
122
-
123
- cli.version(version).help()
124
-
125
129
  function safeJsonParse(json: string) {
126
130
  try {
127
131
  return JSON.parse(json)
@@ -155,6 +159,7 @@ type Config = {
155
159
  [name: string]: string
156
160
  }
157
161
  breakpoints?: BreakpointSizes
162
+ tokens?: StyleToken[]
158
163
  outDir?: string
159
164
  }
160
165
  async function processConfig({
@@ -181,6 +186,7 @@ async function processConfig({
181
186
  breakpoints,
182
187
  cwd: installDir,
183
188
  watch,
189
+ tokens: config.tokens,
184
190
  signal,
185
191
  })
186
192
  } catch (e: any) {
package/src/css.ts CHANGED
@@ -92,7 +92,7 @@ export function getFontsStyles(_fontsDefs: ComponentFontBundle[]) {
92
92
  str += dedent`
93
93
  @font-face {
94
94
  font-family: '${x.family}';
95
- src: url(${x.url});\n`
95
+ src: url('${x.url}');\n`
96
96
  if (x.style) {
97
97
  str += ` font-style: ${x.style};\n`
98
98
  }
package/src/esbuild.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { logger } from './utils'
2
+ import { createSpinner } from 'nanospinner'
3
+
2
4
  import { Plugin, transform } from 'esbuild'
3
5
 
4
6
  export const externalPackages = [
@@ -15,7 +17,8 @@ export function esbuildPluginBundleDependencies({
15
17
  externalizeNpm = false,
16
18
  }) {
17
19
  const codeCache = new Map()
18
-
20
+ const spinner = createSpinner('Fetching Framer Components Modules')
21
+ spinner.start()
19
22
  const plugin: Plugin = {
20
23
  name: 'esbuild-plugin',
21
24
  setup(build) {
@@ -90,6 +93,10 @@ export function esbuildPluginBundleDependencies({
90
93
  }
91
94
  // build.onResolve({ filter: /^\w/ }, resolveDep)
92
95
  build.onResolve({ filter: /.*/, namespace }, resolveDep)
96
+ build.onEnd(() => {
97
+ spinner.stop()
98
+ })
99
+
93
100
  build.onLoad({ filter: /.*/, namespace }, async (args) => {
94
101
  if (signal?.aborted) {
95
102
  throw new Error('aborted')
@@ -111,6 +118,8 @@ export function esbuildPluginBundleDependencies({
111
118
  let loader = 'jsx' as any
112
119
  const promise = Promise.resolve().then(async () => {
113
120
  logger.log('fetching', url.replace(/https?:\/\//, ''))
121
+ spinner.update(`Fetching ${url.replace(/https?:\/\//, '')}`)
122
+
114
123
  const res = await fetchWithRetry(resolved, { signal })
115
124
  if (!res.ok) {
116
125
  throw new Error(