unframer 2.27.2 → 3.0.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.
- package/dist/babel-jsx.js +2 -2
- package/dist/babel-jsx.js.map +1 -1
- package/dist/babel-typedoc.d.ts +39 -0
- package/dist/babel-typedoc.d.ts.map +1 -0
- package/dist/babel-typedoc.js +77 -0
- package/dist/babel-typedoc.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +7 -2
- package/dist/cli.js.map +1 -1
- package/dist/esbuild.d.ts +2 -1
- package/dist/esbuild.d.ts.map +1 -1
- package/dist/esbuild.js +16 -9
- package/dist/esbuild.js.map +1 -1
- package/dist/exporter.d.ts +25 -8
- package/dist/exporter.d.ts.map +1 -1
- package/dist/exporter.js +381 -195
- package/dist/exporter.js.map +1 -1
- package/dist/exporter.test.js +0 -4
- package/dist/exporter.test.js.map +1 -1
- package/dist/framer.js +229 -102
- package/dist/generated/api-client.d.ts +3 -3
- package/dist/generated/api-client.d.ts.map +1 -1
- package/dist/package-manager.d.ts +10 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +145 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/react.d.ts +32 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +1 -3
- package/dist/react.js.map +1 -1
- package/dist/undici-dispatcher.js +1 -2
- package/dist/undici-dispatcher.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/esm/babel-jsx.js +2 -2
- package/esm/babel-jsx.js.map +1 -1
- package/esm/babel-typedoc.d.ts +39 -0
- package/esm/babel-typedoc.d.ts.map +1 -0
- package/esm/babel-typedoc.js +74 -0
- package/esm/babel-typedoc.js.map +1 -0
- package/esm/cli.d.ts.map +1 -1
- package/esm/cli.js +7 -2
- package/esm/cli.js.map +1 -1
- package/esm/esbuild.d.ts +2 -1
- package/esm/esbuild.d.ts.map +1 -1
- package/esm/esbuild.js +16 -9
- package/esm/esbuild.js.map +1 -1
- package/esm/exporter.d.ts +25 -8
- package/esm/exporter.d.ts.map +1 -1
- package/esm/exporter.js +378 -194
- package/esm/exporter.js.map +1 -1
- package/esm/exporter.test.js +0 -4
- package/esm/exporter.test.js.map +1 -1
- package/esm/framer.js +229 -102
- package/esm/package-manager.d.ts +10 -0
- package/esm/package-manager.d.ts.map +1 -0
- package/esm/package-manager.js +141 -0
- package/esm/package-manager.js.map +1 -0
- package/esm/react.d.ts +32 -0
- package/esm/react.d.ts.map +1 -1
- package/esm/react.js +1 -3
- package/esm/react.js.map +1 -1
- package/esm/undici-dispatcher.js +1 -2
- package/esm/undici-dispatcher.js.map +1 -1
- package/esm/version.d.ts +1 -1
- package/esm/version.d.ts.map +1 -1
- package/esm/version.js +1 -1
- package/esm/version.js.map +1 -1
- package/package.json +5 -4
- package/src/babel-jsx.ts +2 -2
- package/src/babel-typedoc.ts +132 -0
- package/src/cli.ts +7 -2
- package/src/esbuild.ts +17 -12
- package/src/exporter.test.ts +0 -5
- package/src/exporter.ts +448 -237
- package/src/framer.js +237 -103
- package/src/package-manager.ts +164 -0
- package/src/react.tsx +33 -0
- package/src/undici-dispatcher.ts +1 -1
- package/src/version.ts +1 -1
- package/dist/framer.d.ts.map +0 -1
- package/dist/framer.js.map +0 -1
- package/esm/framer-chunks/chunk-22NYTOTD.d.ts +0 -14
- package/esm/framer-chunks/chunk-22NYTOTD.d.ts.map +0 -1
- package/esm/framer-chunks/chunk-22NYTOTD.js +0 -99
- package/esm/framer-chunks/chunk-22NYTOTD.js.map +0 -1
- package/esm/framer-chunks/fontshare-GSJIWLGZ-7BHTUG6K.d.ts +0 -115
- package/esm/framer-chunks/fontshare-GSJIWLGZ-7BHTUG6K.d.ts.map +0 -1
- package/esm/framer-chunks/fontshare-GSJIWLGZ-7BHTUG6K.js +0 -5
- package/esm/framer-chunks/fontshare-GSJIWLGZ-7BHTUG6K.js.map +0 -1
- package/esm/framer-chunks/fontshare-SSHBFVID-ZX5Y6FJ4.d.ts +0 -781
- package/esm/framer-chunks/fontshare-SSHBFVID-ZX5Y6FJ4.d.ts.map +0 -1
- package/esm/framer-chunks/fontshare-SSHBFVID-ZX5Y6FJ4.js +0 -5
- package/esm/framer-chunks/fontshare-SSHBFVID-ZX5Y6FJ4.js.map +0 -1
- package/esm/framer-chunks/fontshare-X6MCIXW5-FUMOBUA2.d.ts +0 -634
- package/esm/framer-chunks/fontshare-X6MCIXW5-FUMOBUA2.d.ts.map +0 -1
- package/esm/framer-chunks/fontshare-X6MCIXW5-FUMOBUA2.js +0 -5
- package/esm/framer-chunks/fontshare-X6MCIXW5-FUMOBUA2.js.map +0 -1
- package/esm/framer-chunks/framer-font-TNC5DMGA-XVG7BST3.d.ts +0 -18
- package/esm/framer-chunks/framer-font-TNC5DMGA-XVG7BST3.d.ts.map +0 -1
- package/esm/framer-chunks/framer-font-TNC5DMGA-XVG7BST3.js +0 -5
- package/esm/framer-chunks/framer-font-TNC5DMGA-XVG7BST3.js.map +0 -1
- package/esm/framer-chunks/google-3GQMHAEU-KEOTHDV6.d.ts +0 -9827
- package/esm/framer-chunks/google-3GQMHAEU-KEOTHDV6.d.ts.map +0 -1
- package/esm/framer-chunks/google-3GQMHAEU-KEOTHDV6.js +0 -5
- package/esm/framer-chunks/google-3GQMHAEU-KEOTHDV6.js.map +0 -1
- package/esm/framer-chunks/google-42BCYVR5-PDCHFNPY.d.ts +0 -3231
- package/esm/framer-chunks/google-42BCYVR5-PDCHFNPY.d.ts.map +0 -1
- package/esm/framer-chunks/google-42BCYVR5-PDCHFNPY.js +0 -5
- package/esm/framer-chunks/google-42BCYVR5-PDCHFNPY.js.map +0 -1
- package/esm/framer-chunks/google-LHIHIYDX-FZZ6UXE7.d.ts +0 -1499
- package/esm/framer-chunks/google-LHIHIYDX-FZZ6UXE7.d.ts.map +0 -1
- package/esm/framer-chunks/google-LHIHIYDX-FZZ6UXE7.js +0 -5
- package/esm/framer-chunks/google-LHIHIYDX-FZZ6UXE7.js.map +0 -1
- package/esm/framer.d.ts.map +0 -1
- package/esm/framer.js.map +0 -1
package/src/exporter.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
babelPluginJsxTransform,
|
|
20
20
|
removeJsxExpressionContainer,
|
|
21
21
|
} from './babel-jsx.js'
|
|
22
|
+
import { babelPluginTypedoc } from './babel-typedoc.js'
|
|
22
23
|
import { propCamelCaseJustLikeFramer } from './compat.js'
|
|
23
24
|
import {
|
|
24
25
|
ComponentFontBundle,
|
|
@@ -48,6 +49,8 @@ import {
|
|
|
48
49
|
stackblitzDemoExample,
|
|
49
50
|
terminalMarkdown,
|
|
50
51
|
} from './utils.js'
|
|
52
|
+
import { installPackagesBatch } from './package-manager.js'
|
|
53
|
+
import { version as currentUnframerVersion } from './version.js'
|
|
51
54
|
|
|
52
55
|
import { Biome, Distribution } from '@biomejs/js-api'
|
|
53
56
|
|
|
@@ -78,6 +81,27 @@ export async function bundle({
|
|
|
78
81
|
await fs.promises.mkdir(out, { recursive: true })
|
|
79
82
|
} catch (e) {}
|
|
80
83
|
|
|
84
|
+
// Prefix for temporary .js files to avoid HMR issues
|
|
85
|
+
const tempJsPrefix = 'temp_'
|
|
86
|
+
|
|
87
|
+
// Helper function to handle file path transformations with temp prefix
|
|
88
|
+
function getFilePaths(filePath: string, outDir: string) {
|
|
89
|
+
const baseName = path.basename(filePath)
|
|
90
|
+
const dirName = path.dirname(filePath)
|
|
91
|
+
const tempFileName = tempJsPrefix + baseName
|
|
92
|
+
const tempFilePath = path.join(dirName, tempFileName)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
originalPath: filePath,
|
|
96
|
+
tempJsPath: path.resolve(outDir, tempFilePath),
|
|
97
|
+
finalJsPath: path.resolve(outDir, filePath),
|
|
98
|
+
jsxPath: path.resolve(outDir, filePath.replace(/\.js$/, '.jsx')),
|
|
99
|
+
tempFilePath,
|
|
100
|
+
baseName,
|
|
101
|
+
dirName,
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
spinner.start('exporting components...')
|
|
82
106
|
|
|
83
107
|
const otherRoutes = Object.fromEntries(
|
|
@@ -100,7 +124,7 @@ export async function bundle({
|
|
|
100
124
|
}
|
|
101
125
|
}
|
|
102
126
|
const fn = watch ? context : fakeContext
|
|
103
|
-
|
|
127
|
+
const missingPackages = new Set<string>()
|
|
104
128
|
|
|
105
129
|
const buildContext = await fn({
|
|
106
130
|
absWorkingDir: out,
|
|
@@ -130,9 +154,12 @@ export async function bundle({
|
|
|
130
154
|
signal,
|
|
131
155
|
externalPackages: config.externalPackages,
|
|
132
156
|
externalizeNpm: config.allExternal,
|
|
133
|
-
outDir:
|
|
157
|
+
outDir: out,
|
|
134
158
|
onMissingPackage: (e) => {
|
|
135
|
-
|
|
159
|
+
// No longer needed - packages are auto-installed
|
|
160
|
+
},
|
|
161
|
+
onCollectMissingPackage: (pkg) => {
|
|
162
|
+
missingPackages.add(pkg)
|
|
136
163
|
},
|
|
137
164
|
}),
|
|
138
165
|
nodeModulesPolyfillPlugin({}),
|
|
@@ -150,6 +177,15 @@ export async function bundle({
|
|
|
150
177
|
{ filter: /.*/, namespace: 'virtual' },
|
|
151
178
|
async (args) => {
|
|
152
179
|
const name = args.path
|
|
180
|
+
|
|
181
|
+
// Handle virtual routes module
|
|
182
|
+
if (name === '__routes') {
|
|
183
|
+
return {
|
|
184
|
+
contents: `export const routes = ${JSON.stringify(otherRoutes, null, 2)};`,
|
|
185
|
+
loader: 'js',
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
153
189
|
const url = components[name]
|
|
154
190
|
const componentBreakpoints =
|
|
155
191
|
config.componentBreakpoints?.filter(
|
|
@@ -184,6 +220,9 @@ export async function bundle({
|
|
|
184
220
|
])
|
|
185
221
|
: {}
|
|
186
222
|
|
|
223
|
+
// Use virtual routes module
|
|
224
|
+
const routesImportPath = 'virtual:__routes'
|
|
225
|
+
|
|
187
226
|
return {
|
|
188
227
|
contents: /** js **/ `
|
|
189
228
|
'use client'
|
|
@@ -194,6 +233,7 @@ export async function bundle({
|
|
|
194
233
|
signal,
|
|
195
234
|
})}'
|
|
196
235
|
import { WithFramerBreakpoints } from 'unframer'
|
|
236
|
+
import { routes } from '${routesImportPath}'
|
|
197
237
|
const locales = ${
|
|
198
238
|
JSON.stringify(config.locales) || '[]'
|
|
199
239
|
}
|
|
@@ -203,17 +243,12 @@ export async function bundle({
|
|
|
203
243
|
2,
|
|
204
244
|
)}
|
|
205
245
|
|
|
206
|
-
|
|
246
|
+
|
|
247
|
+
function ComponentWithRoot({ locale, ...rest }) {
|
|
207
248
|
return (
|
|
208
249
|
<ContextProviders
|
|
209
|
-
routes={
|
|
210
|
-
|
|
211
|
-
)}}
|
|
212
|
-
children={<WithFramerBreakpoints
|
|
213
|
-
Component={Component}
|
|
214
|
-
variants={defaultResponsiveVariants}
|
|
215
|
-
{...rest}
|
|
216
|
-
/>}
|
|
250
|
+
routes={routes}
|
|
251
|
+
children={<Component {...rest} />}
|
|
217
252
|
framerSiteId={${JSON.stringify(
|
|
218
253
|
config.fullFramerProjectId,
|
|
219
254
|
)}}
|
|
@@ -222,16 +257,15 @@ export async function bundle({
|
|
|
222
257
|
/>
|
|
223
258
|
)
|
|
224
259
|
}
|
|
225
|
-
|
|
226
|
-
export default function ComponentWithRoot({ locale, ...rest }) {
|
|
260
|
+
ComponentWithRoot.Responsive = ({ locale, ...rest }) => {
|
|
227
261
|
return (
|
|
228
262
|
<ContextProviders
|
|
229
|
-
routes={
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
263
|
+
routes={routes}
|
|
264
|
+
children={<WithFramerBreakpoints
|
|
265
|
+
Component={Component}
|
|
266
|
+
variants={defaultResponsiveVariants}
|
|
267
|
+
{...rest}
|
|
268
|
+
/>}
|
|
235
269
|
framerSiteId={${JSON.stringify(
|
|
236
270
|
config.fullFramerProjectId,
|
|
237
271
|
)}}
|
|
@@ -241,6 +275,7 @@ export async function bundle({
|
|
|
241
275
|
)
|
|
242
276
|
}
|
|
243
277
|
Object.assign(ComponentWithRoot, Component)
|
|
278
|
+
export default ComponentWithRoot
|
|
244
279
|
`,
|
|
245
280
|
loader: 'jsx',
|
|
246
281
|
}
|
|
@@ -258,10 +293,35 @@ export async function bundle({
|
|
|
258
293
|
} "${config.projectName}", do not edit manually */\n`
|
|
259
294
|
|
|
260
295
|
async function rebuild() {
|
|
296
|
+
// Clear missing packages for each rebuild (important for watch mode)
|
|
297
|
+
missingPackages.clear()
|
|
298
|
+
try {
|
|
299
|
+
const installedVersion = await resolvePackageVersion({
|
|
300
|
+
cwd: out,
|
|
301
|
+
pkg: 'unframer',
|
|
302
|
+
})
|
|
303
|
+
if (
|
|
304
|
+
isVersionGreater(
|
|
305
|
+
installedVersion || '0.0.0',
|
|
306
|
+
currentUnframerVersion || '0.0.0',
|
|
307
|
+
)
|
|
308
|
+
) {
|
|
309
|
+
// Version mismatch, add with specific version
|
|
310
|
+
missingPackages.add(`unframer@${currentUnframerVersion}`)
|
|
311
|
+
spinner.info(
|
|
312
|
+
`Different unframer version detected (${installedVersion}), will install unframer@${currentUnframerVersion}`,
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
} catch (e) {
|
|
316
|
+
// Unframer not installed, add with specific version
|
|
317
|
+
missingPackages.add(`unframer@${currentUnframerVersion}`)
|
|
318
|
+
spinner.info(
|
|
319
|
+
`Missing package detected: unframer@${currentUnframerVersion}`,
|
|
320
|
+
)
|
|
321
|
+
}
|
|
261
322
|
const prevFiles = await recursiveReaddir(out)
|
|
262
323
|
const buildResult = await buildContext.rebuild().catch((e) => {
|
|
263
324
|
if (e.message.includes('No matching export ')) {
|
|
264
|
-
foundError = true
|
|
265
325
|
spinner.error(
|
|
266
326
|
`esbuild failed to import from an external package, this usually means that the npm package version in Framer is older than the latest.`,
|
|
267
327
|
)
|
|
@@ -275,117 +335,43 @@ export async function bundle({
|
|
|
275
335
|
|
|
276
336
|
spinner.update('Finished build')
|
|
277
337
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
.catch(() => null)
|
|
285
|
-
const tooBigSize = 0.7 * 1024 * 1024
|
|
338
|
+
// Install missing packages if any were collected
|
|
339
|
+
if (missingPackages.size > 0) {
|
|
340
|
+
const packagesToInstall = Array.from(missingPackages)
|
|
341
|
+
logger.log(
|
|
342
|
+
`Installing missing packages: ${packagesToInstall.join(', ')}`,
|
|
343
|
+
)
|
|
286
344
|
|
|
287
|
-
|
|
345
|
+
const installResult = await installPackagesBatch({
|
|
346
|
+
packageNames: packagesToInstall,
|
|
347
|
+
cwd: out,
|
|
348
|
+
isDev: false,
|
|
349
|
+
})
|
|
288
350
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
}
|
|
351
|
+
if (!installResult.success) {
|
|
352
|
+
spinner.error(
|
|
353
|
+
`Failed to install packages: ${installResult.error}`,
|
|
354
|
+
)
|
|
355
|
+
// Don't fail the build, just warn
|
|
327
356
|
}
|
|
357
|
+
}
|
|
328
358
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
// spinner.update(`Formatting ${path.relative(out, file.path)}`)
|
|
333
|
-
// formatted = dprint.format('file.jsx', file.text, {
|
|
334
|
-
// lineWidth: 140,
|
|
335
|
-
// quoteStyle: 'alwaysSingle',
|
|
336
|
-
// trailingCommas: 'always',
|
|
337
|
-
// semiColons: 'always',
|
|
338
|
-
// })
|
|
339
|
-
// }
|
|
340
|
-
// if (tooBig) {
|
|
341
|
-
// spinner.info(
|
|
342
|
-
// `skipping formatting ${path.relative(
|
|
343
|
-
// out,
|
|
344
|
-
// file.path,
|
|
345
|
-
// )}, too big`,
|
|
346
|
-
// )
|
|
347
|
-
// }
|
|
348
|
-
|
|
349
|
-
// if (framerWebPages?.length) {
|
|
350
|
-
// codeNew = replaceWebPageIds({
|
|
351
|
-
// code: codeNew,
|
|
352
|
-
// elements: framerWebPages,
|
|
353
|
-
// })
|
|
354
|
-
// }
|
|
355
|
-
// const lines = findRelativeLinks(codeNew)
|
|
356
|
-
// if (lines.length) {
|
|
357
|
-
// spinner.error(
|
|
358
|
-
// `found broken links for ${path.relative(out, file.path)}`,
|
|
359
|
-
// )
|
|
360
|
-
// lines.forEach((line) => {
|
|
361
|
-
// logger.log(`${path.resolve(out, file.path)}:${line + 1}`)
|
|
362
|
-
// })
|
|
363
|
-
// }
|
|
364
|
-
|
|
359
|
+
// First, write raw JS files for type extraction with temp prefix
|
|
360
|
+
for (let file of buildResult.outputFiles!) {
|
|
361
|
+
const paths = getFilePaths(file.path, out)
|
|
365
362
|
const prefix =
|
|
366
363
|
`// @ts-nocheck\n` + `/* eslint-disable */\n` + doNotEditComment
|
|
367
|
-
const codeJsx = prefix + formatted
|
|
368
364
|
const codeJs = prefix + file.text
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
365
|
+
|
|
366
|
+
logger.log(
|
|
367
|
+
`writing temp JS`,
|
|
368
|
+
path.relative(out, paths.tempFilePath),
|
|
369
|
+
)
|
|
370
|
+
await fs.promises.mkdir(path.dirname(paths.tempJsPath), {
|
|
374
371
|
recursive: true,
|
|
375
372
|
})
|
|
376
|
-
|
|
377
|
-
await fs.promises.writeFile(resultPathAbsJs, codeJs, 'utf-8')
|
|
378
|
-
}
|
|
379
|
-
if (didFormat) {
|
|
380
|
-
await fs.promises.writeFile(resultPathAbsJsx, codeJsx, 'utf-8')
|
|
381
|
-
}
|
|
373
|
+
await fs.promises.writeFile(paths.tempJsPath, codeJs, 'utf-8')
|
|
382
374
|
}
|
|
383
|
-
spinner.stop()
|
|
384
|
-
await fs.promises.writeFile(
|
|
385
|
-
path.resolve(out, '.cursorignore'),
|
|
386
|
-
`**/*.js\nchunks\n`,
|
|
387
|
-
'utf-8',
|
|
388
|
-
)
|
|
389
375
|
|
|
390
376
|
if (!buildResult?.outputFiles) {
|
|
391
377
|
throw new Error('Failed to generate result')
|
|
@@ -408,7 +394,8 @@ export async function bundle({
|
|
|
408
394
|
const name = path
|
|
409
395
|
.relative(out, file.path)
|
|
410
396
|
.replace(/\.jsx?$/, '')
|
|
411
|
-
const
|
|
397
|
+
const paths = getFilePaths(file.path, out)
|
|
398
|
+
const resultPathAbs = paths.tempJsPath
|
|
412
399
|
if (!components[name]) {
|
|
413
400
|
return
|
|
414
401
|
}
|
|
@@ -419,6 +406,7 @@ export async function bundle({
|
|
|
419
406
|
return
|
|
420
407
|
}
|
|
421
408
|
logger.log(`extracting types for ${name}`)
|
|
409
|
+
spinner.info(`Extracting types for component: ${name}`)
|
|
422
410
|
spinner.update(`Extracting types for ${name}`)
|
|
423
411
|
const { propertyControls, fonts } =
|
|
424
412
|
await extractPropControlsUnsafe(resultPathAbs, name)
|
|
@@ -432,21 +420,23 @@ export async function bundle({
|
|
|
432
420
|
fileName: path.basename(file.path),
|
|
433
421
|
})),
|
|
434
422
|
)
|
|
435
|
-
const
|
|
423
|
+
const typedocComments = propControlsToTypedocComments({
|
|
436
424
|
controls: propertyControls!,
|
|
437
425
|
fileName: name,
|
|
438
426
|
config,
|
|
439
427
|
})
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
path.resolve(out, `${name}.d.ts`),
|
|
443
|
-
types,
|
|
428
|
+
logger.log(
|
|
429
|
+
`Generated TypeDoc comments for ${name}: ${!!typedocComments.headerComment}`,
|
|
444
430
|
)
|
|
431
|
+
await fs.promises.mkdir(out, { recursive: true })
|
|
432
|
+
// .d.ts generation removed – types are now injected as typedoc
|
|
433
|
+
// comments directly inside the generated JSX file.
|
|
445
434
|
|
|
446
435
|
return {
|
|
447
436
|
propertyControls,
|
|
448
437
|
fonts,
|
|
449
438
|
name,
|
|
439
|
+
typedocComments,
|
|
450
440
|
}
|
|
451
441
|
} finally {
|
|
452
442
|
sema.release()
|
|
@@ -459,7 +449,6 @@ export async function bundle({
|
|
|
459
449
|
// Ignore error if file doesn't exist or can't be deleted
|
|
460
450
|
}
|
|
461
451
|
})
|
|
462
|
-
// spinner.stop()
|
|
463
452
|
|
|
464
453
|
const cssString =
|
|
465
454
|
doNotEditComment +
|
|
@@ -487,11 +476,18 @@ export async function bundle({
|
|
|
487
476
|
.filter(
|
|
488
477
|
(x) =>
|
|
489
478
|
x.path.endsWith('.js') &&
|
|
490
|
-
fs.existsSync(x.path
|
|
479
|
+
fs.existsSync(getFilePaths(x.path, out).jsxPath),
|
|
491
480
|
)
|
|
492
|
-
.map((x) => x.path
|
|
481
|
+
.map((x) => getFilePaths(x.path, out).jsxPath)
|
|
493
482
|
const outFiles = buildResult.outputFiles
|
|
494
|
-
.map((x) =>
|
|
483
|
+
.map((x) => {
|
|
484
|
+
const paths = getFilePaths(x.path, out)
|
|
485
|
+
if (x.path.endsWith('.js') && fs.existsSync(paths.jsxPath)) {
|
|
486
|
+
return null // Will be handled by jsx files
|
|
487
|
+
}
|
|
488
|
+
return paths.finalJsPath
|
|
489
|
+
})
|
|
490
|
+
.filter(Boolean)
|
|
495
491
|
.concat([
|
|
496
492
|
path.resolve(out, 'meta.json'),
|
|
497
493
|
path.resolve(out, 'tokens.css'),
|
|
@@ -499,23 +495,10 @@ export async function bundle({
|
|
|
499
495
|
path.resolve(out, 'styles.css'),
|
|
500
496
|
])
|
|
501
497
|
.concat(jsxFiles)
|
|
502
|
-
.concat(
|
|
503
|
-
buildResult.outputFiles.map((x) =>
|
|
504
|
-
path.resolve(out, x.path.replace(/\.jsx?$/, '.d.ts')),
|
|
505
|
-
),
|
|
506
|
-
)
|
|
507
498
|
|
|
508
499
|
const filesToDelete = prevFiles
|
|
509
500
|
.filter((x) => !outFiles.includes(x))
|
|
510
|
-
.
|
|
511
|
-
buildResult.outputFiles
|
|
512
|
-
.map((x) => x.path)
|
|
513
|
-
.filter(
|
|
514
|
-
(js) =>
|
|
515
|
-
js.endsWith('.js') &&
|
|
516
|
-
jsxFiles.some((x) => x.startsWith(js)),
|
|
517
|
-
),
|
|
518
|
-
)
|
|
501
|
+
.filter((x) => !x.includes(tempJsPrefix)) // Don't delete temp files here, they're handled separately
|
|
519
502
|
|
|
520
503
|
for (let file of filesToDelete) {
|
|
521
504
|
logger.log('deleting', path.relative(out, file))
|
|
@@ -539,17 +522,7 @@ export async function bundle({
|
|
|
539
522
|
if (watch) {
|
|
540
523
|
logger.log('waiting for components or config changes')
|
|
541
524
|
}
|
|
542
|
-
|
|
543
|
-
const tokensCss =
|
|
544
|
-
"/* 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" +
|
|
545
|
-
'/* Bug: https://www.framer.community/c/bugs/color-style-unlinks-when-copying-component-between-projects-resulting-in-potential-value-discrepancy */\n' +
|
|
546
|
-
getTokensCss({ out, result: buildResult })
|
|
547
|
-
await fs.promises.writeFile(
|
|
548
|
-
path.resolve(out, 'tokens.css'),
|
|
549
|
-
tokensCss,
|
|
550
|
-
'utf-8',
|
|
551
|
-
)
|
|
552
|
-
}
|
|
525
|
+
|
|
553
526
|
const res: BundleResult = {
|
|
554
527
|
components: Object.entries(components).map(([name, v]) => {
|
|
555
528
|
const propControls = propControlsData.find(
|
|
@@ -566,6 +539,160 @@ export async function bundle({
|
|
|
566
539
|
}),
|
|
567
540
|
}
|
|
568
541
|
|
|
542
|
+
// Process and write JSX files with TypeDoc comments
|
|
543
|
+
spinner.update('Processing JSX files with TypeDoc comments')
|
|
544
|
+
for (let file of buildResult.outputFiles!) {
|
|
545
|
+
const paths = getFilePaths(file.path, out)
|
|
546
|
+
|
|
547
|
+
const componentName = path
|
|
548
|
+
.relative(out, file.path)
|
|
549
|
+
.replace(/\.js$/, '')
|
|
550
|
+
const propData = propControlsData.find(
|
|
551
|
+
(p) => p?.name === componentName,
|
|
552
|
+
)
|
|
553
|
+
const typedocComments = propData?.typedocComments
|
|
554
|
+
|
|
555
|
+
logger.log(`Processing component: ${componentName}`)
|
|
556
|
+
spinner.update(`Processing JSX for ${componentName}`)
|
|
557
|
+
if (!propData) {
|
|
558
|
+
logger.log(` No propData found for ${componentName}`)
|
|
559
|
+
} else {
|
|
560
|
+
logger.log(
|
|
561
|
+
` PropData found for ${componentName}, has propertyControls: ${!!propData.propertyControls}`,
|
|
562
|
+
)
|
|
563
|
+
if (!typedocComments) {
|
|
564
|
+
logger.log(` No typedocComments for ${componentName}`)
|
|
565
|
+
} else {
|
|
566
|
+
logger.log(
|
|
567
|
+
` TypeDoc comments available for ${componentName}`,
|
|
568
|
+
)
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const existing = await fs.promises
|
|
573
|
+
.readFile(paths.jsxPath, 'utf-8')
|
|
574
|
+
.catch(() => null)
|
|
575
|
+
const tooBigSize = 0.7 * 1024 * 1024
|
|
576
|
+
|
|
577
|
+
let formatted = file.text
|
|
578
|
+
|
|
579
|
+
let tooBig = file.text.length >= tooBigSize
|
|
580
|
+
let didFormat = false
|
|
581
|
+
if (
|
|
582
|
+
config.jsx &&
|
|
583
|
+
!tooBig &&
|
|
584
|
+
!paths.tempJsPath.includes('/chunks/') &&
|
|
585
|
+
!paths.tempJsPath.includes('\\chunks\\')
|
|
586
|
+
) {
|
|
587
|
+
try {
|
|
588
|
+
const plugins = [
|
|
589
|
+
// babelPluginDeduplicateImports,
|
|
590
|
+
babelPluginJsxTransform,
|
|
591
|
+
removeJsxExpressionContainer,
|
|
592
|
+
]
|
|
593
|
+
|
|
594
|
+
// Add TypeDoc plugin if we have comments for this component
|
|
595
|
+
if (typedocComments) {
|
|
596
|
+
logger.log(
|
|
597
|
+
` Adding TypeDoc plugin for ${componentName}`,
|
|
598
|
+
)
|
|
599
|
+
plugins.push(babelPluginTypedoc(typedocComments))
|
|
600
|
+
} else {
|
|
601
|
+
logger.log(
|
|
602
|
+
` No TypeDoc comments to add for ${componentName}`,
|
|
603
|
+
)
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
let res = transform(file.text || '', {
|
|
607
|
+
babelrc: false,
|
|
608
|
+
sourceType: 'module',
|
|
609
|
+
parserOpts: {
|
|
610
|
+
plugins: ['jsx'],
|
|
611
|
+
},
|
|
612
|
+
plugins,
|
|
613
|
+
// ast: true,
|
|
614
|
+
// code: false,
|
|
615
|
+
filename: 'x.jsx',
|
|
616
|
+
compact: false,
|
|
617
|
+
sourceMaps: false,
|
|
618
|
+
})
|
|
619
|
+
if (res?.code) {
|
|
620
|
+
if (!biome) {
|
|
621
|
+
biome = await Biome.create({
|
|
622
|
+
distribution: Distribution.NODE,
|
|
623
|
+
})
|
|
624
|
+
}
|
|
625
|
+
let result = biome.formatContent(res.code, {
|
|
626
|
+
filePath: 'example.jsx',
|
|
627
|
+
})
|
|
628
|
+
didFormat = true
|
|
629
|
+
formatted = result.content
|
|
630
|
+
}
|
|
631
|
+
} catch (e) {
|
|
632
|
+
notifyError(e, 'babel transform and format')
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const prefix =
|
|
637
|
+
`// @ts-nocheck\n` + `/* eslint-disable */\n` + doNotEditComment
|
|
638
|
+
const codeJsx = prefix + formatted
|
|
639
|
+
const codeJs = prefix + file.text
|
|
640
|
+
logger.log(`writing`, path.relative(out, file.path))
|
|
641
|
+
await fs.promises.mkdir(path.dirname(paths.jsxPath), {
|
|
642
|
+
recursive: true,
|
|
643
|
+
})
|
|
644
|
+
// Always write the temp .js file for type extraction
|
|
645
|
+
await fs.promises.writeFile(paths.tempJsPath, codeJs, 'utf-8')
|
|
646
|
+
|
|
647
|
+
// Only write .jsx file if it's different from existing or if formatting was done
|
|
648
|
+
if (didFormat && codeJsx !== existing) {
|
|
649
|
+
await fs.promises.writeFile(paths.jsxPath, codeJsx, 'utf-8')
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
spinner.stop()
|
|
653
|
+
// await fs.promises.writeFile(
|
|
654
|
+
// path.resolve(out, '.cursorignore'),
|
|
655
|
+
// `**/*.js\nchunks\n`,
|
|
656
|
+
// 'utf-8',
|
|
657
|
+
// )
|
|
658
|
+
|
|
659
|
+
// Clean up temp .js files and handle prefixes
|
|
660
|
+
for (let file of buildResult.outputFiles!) {
|
|
661
|
+
if (file.path.endsWith('.js')) {
|
|
662
|
+
const paths = getFilePaths(file.path, out)
|
|
663
|
+
|
|
664
|
+
if (fs.existsSync(paths.jsxPath)) {
|
|
665
|
+
// Remove temp .js file if .jsx equivalent exists
|
|
666
|
+
logger.log(
|
|
667
|
+
'removing temp JS file with JSX equivalent:',
|
|
668
|
+
path.relative(out, paths.tempJsPath),
|
|
669
|
+
)
|
|
670
|
+
try {
|
|
671
|
+
await fs.promises.rm(paths.tempJsPath)
|
|
672
|
+
await fs.promises.rm(paths.finalJsPath)
|
|
673
|
+
} catch (error) {
|
|
674
|
+
// Ignore error if file doesn't exist
|
|
675
|
+
}
|
|
676
|
+
} else {
|
|
677
|
+
// Rename temp .js file to final name if no .jsx equivalent
|
|
678
|
+
logger.log(
|
|
679
|
+
'renaming temp JS file to final name:',
|
|
680
|
+
path.relative(out, paths.tempJsPath),
|
|
681
|
+
'->',
|
|
682
|
+
path.relative(out, paths.finalJsPath),
|
|
683
|
+
)
|
|
684
|
+
try {
|
|
685
|
+
await fs.promises.rename(
|
|
686
|
+
paths.tempJsPath,
|
|
687
|
+
paths.finalJsPath,
|
|
688
|
+
)
|
|
689
|
+
} catch (error) {
|
|
690
|
+
// Ignore error if file doesn't exist
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
569
696
|
spinner.info(`Build completed`)
|
|
570
697
|
return res
|
|
571
698
|
}
|
|
@@ -594,8 +721,11 @@ export async function bundle({
|
|
|
594
721
|
console.log()
|
|
595
722
|
console.log()
|
|
596
723
|
const outDirForExample =
|
|
597
|
-
path
|
|
598
|
-
|
|
724
|
+
path
|
|
725
|
+
.relative(process.cwd(), out)
|
|
726
|
+
.split(path.sep)
|
|
727
|
+
.join('/')
|
|
728
|
+
.replace(/^src\//, '') || 'framer' // remove src so file works inside src
|
|
599
729
|
const { exampleCode } = await createExampleComponentCode({
|
|
600
730
|
outDir: out,
|
|
601
731
|
// buildResult: result,
|
|
@@ -608,42 +738,35 @@ export async function bundle({
|
|
|
608
738
|
})
|
|
609
739
|
await fs.promises.writeFile(stackblitzDemoExample, exampleCode)
|
|
610
740
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
# How to use the Framer components
|
|
615
|
-
|
|
616
|
-
Your components are exported to \`${outDirForExample}\` folder. Now please install the \`unframer\` runtime dependency:
|
|
741
|
+
console.log(
|
|
742
|
+
terminalMarkdown(dedent`
|
|
743
|
+
# How to use the Framer components
|
|
617
744
|
|
|
618
|
-
|
|
619
|
-
npm install unframer
|
|
620
|
-
\`\`\`
|
|
745
|
+
Your components are exported to \`${outDirForExample}\` folder.
|
|
621
746
|
|
|
622
|
-
|
|
747
|
+
Each component has a \`.Responsive\` variant that allows you to specify different variants for different breakpoints.
|
|
623
748
|
|
|
624
|
-
|
|
749
|
+
You can use the components like this (try copy pasting the code below into your React app):
|
|
625
750
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
751
|
+
\`\`\`jsx
|
|
752
|
+
${exampleCode}
|
|
753
|
+
\`\`\`
|
|
629
754
|
|
|
630
|
-
|
|
755
|
+
It's very important to import the \`styles.css\` file to include the necessary styles for the components.
|
|
631
756
|
|
|
632
|
-
|
|
757
|
+
To style components you can pass a \`style\` or \`className\` prop (but remember to use !important to increase the specificity).
|
|
633
758
|
|
|
634
|
-
|
|
759
|
+
Read more on GitHub: https://github.com/remorses/unframer
|
|
635
760
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
}
|
|
639
|
-
await checkUnframerVersion({ cwd: out })
|
|
761
|
+
`),
|
|
762
|
+
)
|
|
640
763
|
console.log()
|
|
641
764
|
return { result, rebuild, buildContext }
|
|
642
765
|
}
|
|
643
766
|
|
|
644
767
|
const packageVersionCache = new Map<string, string>()
|
|
645
768
|
|
|
646
|
-
export function
|
|
769
|
+
export function resolvePackageVersion({ cwd, pkg }) {
|
|
647
770
|
if (packageVersionCache.has(pkg)) {
|
|
648
771
|
return Promise.resolve(packageVersionCache.get(pkg))
|
|
649
772
|
}
|
|
@@ -664,10 +787,8 @@ export function resolvePackage({ cwd, pkg }) {
|
|
|
664
787
|
},
|
|
665
788
|
(error, stdout, stderr) => {
|
|
666
789
|
if (error) {
|
|
667
|
-
|
|
668
|
-
reject(
|
|
669
|
-
new Error(`${pkg} is not installed in your project`),
|
|
670
|
-
)
|
|
790
|
+
// Package not installed - this is expected and handled by auto-install
|
|
791
|
+
reject(new Error(`${pkg} is not installed in your project`))
|
|
671
792
|
return
|
|
672
793
|
}
|
|
673
794
|
const version = stdout.trim()
|
|
@@ -678,20 +799,31 @@ export function resolvePackage({ cwd, pkg }) {
|
|
|
678
799
|
})
|
|
679
800
|
}
|
|
680
801
|
|
|
681
|
-
export
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
)
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
802
|
+
export function resolvePackage({ cwd, pkg }) {
|
|
803
|
+
return new Promise<boolean>((resolve) => {
|
|
804
|
+
const code = `import('${pkg}/package.json', { with: { type: 'json' } }).then(()=>console.log('true')).catch(()=>import('${pkg}').then(()=>console.log('true')).catch(()=>console.log('false')));`
|
|
805
|
+
|
|
806
|
+
const command = [
|
|
807
|
+
JSON.stringify(nodePath),
|
|
808
|
+
'-e',
|
|
809
|
+
JSON.stringify(code),
|
|
810
|
+
].join(' ')
|
|
811
|
+
|
|
812
|
+
exec(
|
|
813
|
+
command,
|
|
814
|
+
{
|
|
815
|
+
cwd,
|
|
816
|
+
},
|
|
817
|
+
(error, stdout) => {
|
|
818
|
+
if (error) {
|
|
819
|
+
resolve(false)
|
|
820
|
+
return
|
|
821
|
+
}
|
|
822
|
+
const exists = stdout.trim().split('\n').pop() === 'true'
|
|
823
|
+
resolve(exists)
|
|
824
|
+
},
|
|
693
825
|
)
|
|
694
|
-
}
|
|
826
|
+
})
|
|
695
827
|
}
|
|
696
828
|
|
|
697
829
|
export function getDarkModeSelector(opts: {
|
|
@@ -820,7 +952,7 @@ async function extractPropControlsSafe(text, name) {
|
|
|
820
952
|
}
|
|
821
953
|
}
|
|
822
954
|
|
|
823
|
-
function getTokensCss({
|
|
955
|
+
async function getTokensCss({
|
|
824
956
|
out,
|
|
825
957
|
result,
|
|
826
958
|
}: {
|
|
@@ -1007,14 +1139,18 @@ function safeJsonParse(text) {
|
|
|
1007
1139
|
}
|
|
1008
1140
|
}
|
|
1009
1141
|
|
|
1010
|
-
|
|
1142
|
+
/**
|
|
1143
|
+
* Generates TypeDoc comments that will be injected into JSX files
|
|
1144
|
+
* instead of generating separate .d.ts files
|
|
1145
|
+
*/
|
|
1146
|
+
export function propControlsToTypedocComments({
|
|
1011
1147
|
config,
|
|
1012
1148
|
fileName,
|
|
1013
1149
|
controls,
|
|
1014
1150
|
}: {
|
|
1015
1151
|
controls: PropertyControls
|
|
1016
|
-
fileName
|
|
1017
|
-
config
|
|
1152
|
+
fileName: string
|
|
1153
|
+
config: Config
|
|
1018
1154
|
}) {
|
|
1019
1155
|
try {
|
|
1020
1156
|
const types = Object.entries(controls || ({} as PropertyControls))
|
|
@@ -1074,46 +1210,86 @@ export function propControlsToType({
|
|
|
1074
1210
|
if (!name) {
|
|
1075
1211
|
return ''
|
|
1076
1212
|
}
|
|
1077
|
-
return `
|
|
1213
|
+
return ` * ${name}?: ${typescriptType(value)} // ${value.title || name}`
|
|
1078
1214
|
})
|
|
1079
1215
|
.filter(Boolean)
|
|
1080
1216
|
.join('\n')
|
|
1081
1217
|
|
|
1082
1218
|
const componentName = componentCamelCase(fileName)
|
|
1083
1219
|
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
let
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1220
|
+
const defaultPropsJsDoc = [
|
|
1221
|
+
' * children?: React.ReactNode',
|
|
1222
|
+
' * locale?: Locale',
|
|
1223
|
+
' * style?: React.CSSProperties',
|
|
1224
|
+
' * className?: string',
|
|
1225
|
+
' * id?: string',
|
|
1226
|
+
' * ref?: any',
|
|
1227
|
+
' * width?: any',
|
|
1228
|
+
' * height?: any',
|
|
1229
|
+
' * layoutId?: string',
|
|
1230
|
+
].join('\n')
|
|
1231
|
+
|
|
1232
|
+
// Generate header comment with type definitions
|
|
1233
|
+
let headerComment = '/**\n'
|
|
1234
|
+
headerComment += ' * @typedef Locale\n'
|
|
1235
|
+
|
|
1236
|
+
// Generate union type from config.locales if available
|
|
1237
|
+
const localeType = (() => {
|
|
1238
|
+
if (
|
|
1239
|
+
config?.locales &&
|
|
1240
|
+
Array.isArray(config.locales) &&
|
|
1241
|
+
config.locales.length > 0
|
|
1242
|
+
) {
|
|
1243
|
+
return config.locales
|
|
1244
|
+
.map((locale) => `'${locale.slug}'`)
|
|
1245
|
+
.join(' | ')
|
|
1246
|
+
}
|
|
1247
|
+
return 'string'
|
|
1248
|
+
})()
|
|
1249
|
+
|
|
1250
|
+
headerComment += ` * ${localeType}\n`
|
|
1251
|
+
headerComment += ' */\n\n'
|
|
1252
|
+
headerComment += '/**\n'
|
|
1253
|
+
headerComment +=
|
|
1254
|
+
' * @typedef {{\n'
|
|
1255
|
+
headerComment += defaultPropsJsDoc
|
|
1256
|
+
|
|
1257
|
+
if (types) {
|
|
1258
|
+
headerComment += '\n' + types
|
|
1259
|
+
}
|
|
1260
|
+
headerComment += `\n}} Props\n`
|
|
1261
|
+
headerComment += '\n */\n\n'
|
|
1262
|
+
headerComment += '/**\n'
|
|
1263
|
+
headerComment += ' * @type {import("unframer").UnframerBreakpoint}\n'
|
|
1264
|
+
headerComment += ' * Represents a responsive breakpoint for unframer.\n'
|
|
1265
|
+
headerComment += ' */\n\n'
|
|
1266
|
+
headerComment += '/**\n'
|
|
1267
|
+
headerComment += ' * @typedef VariantsMap\n'
|
|
1268
|
+
headerComment +=
|
|
1269
|
+
" * Partial record of UnframerBreakpoint to Props.variant, with a mandatory 'base' key.\n"
|
|
1270
|
+
headerComment +=
|
|
1271
|
+
" * { [key in UnframerBreakpoint]?: Props['variant'] } & { base: Props['variant'] }\n"
|
|
1272
|
+
headerComment += ' */'
|
|
1273
|
+
|
|
1274
|
+
// Generate responsive comment
|
|
1275
|
+
const responsiveComment = `/**\n * Renders ${componentName} for all breakpoints with a variants map. Variant prop is inferred per breakpoint.\n * @function\n * @param {Omit<Props, 'variant'> & {variants?: VariantsMap}} props\n * @returns {any}\n */`
|
|
1276
|
+
|
|
1277
|
+
// Generate default export comment - use inline function type instead of referencing undefined type
|
|
1278
|
+
const defaultExportComment = `/** @type {function(Props): any} */`
|
|
1279
|
+
|
|
1280
|
+
return {
|
|
1281
|
+
headerComment,
|
|
1282
|
+
responsiveComment,
|
|
1283
|
+
defaultExportComment,
|
|
1284
|
+
}
|
|
1114
1285
|
} catch (e: any) {
|
|
1115
|
-
logger.error(
|
|
1116
|
-
|
|
1286
|
+
logger.error(e.message)
|
|
1287
|
+
logger.error('cannot generate typedoc comments', e.stack)
|
|
1288
|
+
return {
|
|
1289
|
+
headerComment: '',
|
|
1290
|
+
responsiveComment: '',
|
|
1291
|
+
defaultExportComment: '',
|
|
1292
|
+
}
|
|
1117
1293
|
}
|
|
1118
1294
|
}
|
|
1119
1295
|
|
|
@@ -1322,7 +1498,7 @@ async function recursiveReaddir(dir: string): Promise<string[]> {
|
|
|
1322
1498
|
return files.flat()
|
|
1323
1499
|
}
|
|
1324
1500
|
|
|
1325
|
-
function indentWithTabs(str: string, tabs: string) {
|
|
1501
|
+
export function indentWithTabs(str: string, tabs: string) {
|
|
1326
1502
|
if (!str) return ''
|
|
1327
1503
|
return str
|
|
1328
1504
|
.split('\n')
|
|
@@ -1337,8 +1513,10 @@ export async function createExampleComponentCode({
|
|
|
1337
1513
|
outDir: string
|
|
1338
1514
|
config: Config
|
|
1339
1515
|
}) {
|
|
1340
|
-
const outDirForExample = path
|
|
1516
|
+
const outDirForExample = path
|
|
1341
1517
|
.relative(process.cwd(), outDir)
|
|
1518
|
+
.split(path.sep)
|
|
1519
|
+
.join('/')
|
|
1342
1520
|
.replace(/^src\//, '') // remove src so file works inside src
|
|
1343
1521
|
const instances = config?.componentInstancesInIndexPage?.sort((a, b) => {
|
|
1344
1522
|
// Order first by nodeDepth (lower is better)
|
|
@@ -1391,6 +1569,7 @@ export async function createExampleComponentCode({
|
|
|
1391
1569
|
};
|
|
1392
1570
|
`
|
|
1393
1571
|
return {
|
|
1572
|
+
outDirForExample,
|
|
1394
1573
|
exampleCode,
|
|
1395
1574
|
}
|
|
1396
1575
|
}
|
|
@@ -1404,3 +1583,35 @@ type BundleResult = {
|
|
|
1404
1583
|
propertyControls?: PropertyControls
|
|
1405
1584
|
}>
|
|
1406
1585
|
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Compares two semantic version strings.
|
|
1589
|
+
* Returns true if versionB is greater than versionA.
|
|
1590
|
+
* Handles x.y.z, x.y, x, and optional pre-release (-alpha, etc).
|
|
1591
|
+
*/
|
|
1592
|
+
export function isVersionGreater(versionA: string, versionB: string): boolean {
|
|
1593
|
+
try {
|
|
1594
|
+
function parseVersion(version: string) {
|
|
1595
|
+
// Remove pre-release (e.g. -alpha.1)
|
|
1596
|
+
let [core] = version.trim().split('-')
|
|
1597
|
+
return core.split('.').map((x) => parseInt(x, 10))
|
|
1598
|
+
}
|
|
1599
|
+
const [a1 = 0, a2 = 0, a3 = 0] = parseVersion(versionA)
|
|
1600
|
+
const [b1 = 0, b2 = 0, b3 = 0] = parseVersion(versionB)
|
|
1601
|
+
|
|
1602
|
+
if (b1 > a1) return true
|
|
1603
|
+
if (b1 < a1) return false
|
|
1604
|
+
if (b2 > a2) return true
|
|
1605
|
+
if (b2 < a2) return false
|
|
1606
|
+
if (b3 > a3) return true
|
|
1607
|
+
if (b3 < a3) return false
|
|
1608
|
+
|
|
1609
|
+
// If all equal, not greater
|
|
1610
|
+
return false
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
spinner.error(
|
|
1613
|
+
`Error comparing versions "${versionA}" and "${versionB}": ${error?.stack || error?.message || error}`,
|
|
1614
|
+
)
|
|
1615
|
+
return true
|
|
1616
|
+
}
|
|
1617
|
+
}
|