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/esm/exporter.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { build, context } from 'esbuild';
|
|
2
|
-
import packageJson from '../package.json';
|
|
3
2
|
import url from 'url';
|
|
4
3
|
import { Sema } from 'async-sema';
|
|
5
4
|
import { nodeModulesPolyfillPlugin } from 'esbuild-plugins-node-modules-polyfill';
|
|
@@ -10,12 +9,15 @@ import fs from 'fs';
|
|
|
10
9
|
import path from 'path';
|
|
11
10
|
import { dedent } from './utils.js';
|
|
12
11
|
import { babelPluginJsxTransform, removeJsxExpressionContainer, } from './babel-jsx.js';
|
|
12
|
+
import { babelPluginTypedoc } from './babel-typedoc.js';
|
|
13
13
|
import { propCamelCaseJustLikeFramer } from './compat.js';
|
|
14
14
|
import { breakpointsStyles, defaultBreakpointSizes, getFontsStyles, groupBy, logFontsUsage, } from './css.js';
|
|
15
15
|
import { defaultExternalPackages, esbuildPluginBundleDependencies, resolveRedirect, } from './esbuild';
|
|
16
16
|
import { ControlType, combinedCSSRules, } from './framer.js';
|
|
17
17
|
import { notifyError } from './sentry';
|
|
18
18
|
import { kebabCase, logger, spinner, stackblitzDemoExample, terminalMarkdown, } from './utils.js';
|
|
19
|
+
import { installPackagesBatch } from './package-manager.js';
|
|
20
|
+
import { version as currentUnframerVersion } from './version.js';
|
|
19
21
|
import { Biome, Distribution } from '@biomejs/js-api';
|
|
20
22
|
let biome;
|
|
21
23
|
export async function bundle({ config, cwd: out = '', watch = false, signal = undefined, }) {
|
|
@@ -26,6 +28,24 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
26
28
|
await fs.promises.mkdir(out, { recursive: true });
|
|
27
29
|
}
|
|
28
30
|
catch (e) { }
|
|
31
|
+
// Prefix for temporary .js files to avoid HMR issues
|
|
32
|
+
const tempJsPrefix = 'temp_';
|
|
33
|
+
// Helper function to handle file path transformations with temp prefix
|
|
34
|
+
function getFilePaths(filePath, outDir) {
|
|
35
|
+
const baseName = path.basename(filePath);
|
|
36
|
+
const dirName = path.dirname(filePath);
|
|
37
|
+
const tempFileName = tempJsPrefix + baseName;
|
|
38
|
+
const tempFilePath = path.join(dirName, tempFileName);
|
|
39
|
+
return {
|
|
40
|
+
originalPath: filePath,
|
|
41
|
+
tempJsPath: path.resolve(outDir, tempFilePath),
|
|
42
|
+
finalJsPath: path.resolve(outDir, filePath),
|
|
43
|
+
jsxPath: path.resolve(outDir, filePath.replace(/\.js$/, '.jsx')),
|
|
44
|
+
tempFilePath,
|
|
45
|
+
baseName,
|
|
46
|
+
dirName,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
29
49
|
spinner.start('exporting components...');
|
|
30
50
|
const otherRoutes = Object.fromEntries((config.framerWebPages || []).map((page) => [
|
|
31
51
|
page.webPageId,
|
|
@@ -42,7 +62,7 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
42
62
|
};
|
|
43
63
|
}
|
|
44
64
|
const fn = watch ? context : fakeContext;
|
|
45
|
-
|
|
65
|
+
const missingPackages = new Set();
|
|
46
66
|
const buildContext = await fn({
|
|
47
67
|
absWorkingDir: out,
|
|
48
68
|
entryPoints: Object.keys(components)
|
|
@@ -71,9 +91,12 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
71
91
|
signal,
|
|
72
92
|
externalPackages: config.externalPackages,
|
|
73
93
|
externalizeNpm: config.allExternal,
|
|
74
|
-
outDir:
|
|
94
|
+
outDir: out,
|
|
75
95
|
onMissingPackage: (e) => {
|
|
76
|
-
|
|
96
|
+
// No longer needed - packages are auto-installed
|
|
97
|
+
},
|
|
98
|
+
onCollectMissingPackage: (pkg) => {
|
|
99
|
+
missingPackages.add(pkg);
|
|
77
100
|
},
|
|
78
101
|
}),
|
|
79
102
|
nodeModulesPolyfillPlugin({}),
|
|
@@ -88,6 +111,13 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
88
111
|
});
|
|
89
112
|
build.onLoad({ filter: /.*/, namespace: 'virtual' }, async (args) => {
|
|
90
113
|
const name = args.path;
|
|
114
|
+
// Handle virtual routes module
|
|
115
|
+
if (name === '__routes') {
|
|
116
|
+
return {
|
|
117
|
+
contents: `export const routes = ${JSON.stringify(otherRoutes, null, 2)};`,
|
|
118
|
+
loader: 'js',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
91
121
|
const url = components[name];
|
|
92
122
|
const componentBreakpoints = config.componentBreakpoints?.filter((x) => x.componentName === name) || [];
|
|
93
123
|
const brk = breakpointSizes
|
|
@@ -109,6 +139,8 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
109
139
|
...brk.slice(1),
|
|
110
140
|
])
|
|
111
141
|
: {};
|
|
142
|
+
// Use virtual routes module
|
|
143
|
+
const routesImportPath = 'virtual:__routes';
|
|
112
144
|
return {
|
|
113
145
|
contents: /** js **/ `
|
|
114
146
|
'use client'
|
|
@@ -119,30 +151,31 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
119
151
|
signal,
|
|
120
152
|
})}'
|
|
121
153
|
import { WithFramerBreakpoints } from 'unframer'
|
|
154
|
+
import { routes } from '${routesImportPath}'
|
|
122
155
|
const locales = ${JSON.stringify(config.locales) || '[]'}
|
|
123
156
|
const defaultResponsiveVariants = ${JSON.stringify(responsiveVariants, null, 2)}
|
|
124
157
|
|
|
125
|
-
|
|
158
|
+
|
|
159
|
+
function ComponentWithRoot({ locale, ...rest }) {
|
|
126
160
|
return (
|
|
127
161
|
<ContextProviders
|
|
128
|
-
routes={
|
|
129
|
-
children={<
|
|
130
|
-
Component={Component}
|
|
131
|
-
variants={defaultResponsiveVariants}
|
|
132
|
-
{...rest}
|
|
133
|
-
/>}
|
|
162
|
+
routes={routes}
|
|
163
|
+
children={<Component {...rest} />}
|
|
134
164
|
framerSiteId={${JSON.stringify(config.fullFramerProjectId)}}
|
|
135
165
|
locale={locale}
|
|
136
166
|
locales={locales}
|
|
137
167
|
/>
|
|
138
168
|
)
|
|
139
169
|
}
|
|
140
|
-
|
|
141
|
-
export default function ComponentWithRoot({ locale, ...rest }) {
|
|
170
|
+
ComponentWithRoot.Responsive = ({ locale, ...rest }) => {
|
|
142
171
|
return (
|
|
143
172
|
<ContextProviders
|
|
144
|
-
routes={
|
|
145
|
-
children={<
|
|
173
|
+
routes={routes}
|
|
174
|
+
children={<WithFramerBreakpoints
|
|
175
|
+
Component={Component}
|
|
176
|
+
variants={defaultResponsiveVariants}
|
|
177
|
+
{...rest}
|
|
178
|
+
/>}
|
|
146
179
|
framerSiteId={${JSON.stringify(config.fullFramerProjectId)}}
|
|
147
180
|
locale={locale}
|
|
148
181
|
locales={locales}
|
|
@@ -150,6 +183,7 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
150
183
|
)
|
|
151
184
|
}
|
|
152
185
|
Object.assign(ComponentWithRoot, Component)
|
|
186
|
+
export default ComponentWithRoot
|
|
153
187
|
`,
|
|
154
188
|
loader: 'jsx',
|
|
155
189
|
};
|
|
@@ -162,10 +196,27 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
162
196
|
});
|
|
163
197
|
const doNotEditComment = `/* This file was generated by Unframer for Framer project ${config.projectId || ''} "${config.projectName}", do not edit manually */\n`;
|
|
164
198
|
async function rebuild() {
|
|
199
|
+
// Clear missing packages for each rebuild (important for watch mode)
|
|
200
|
+
missingPackages.clear();
|
|
201
|
+
try {
|
|
202
|
+
const installedVersion = await resolvePackageVersion({
|
|
203
|
+
cwd: out,
|
|
204
|
+
pkg: 'unframer',
|
|
205
|
+
});
|
|
206
|
+
if (isVersionGreater(installedVersion || '0.0.0', currentUnframerVersion || '0.0.0')) {
|
|
207
|
+
// Version mismatch, add with specific version
|
|
208
|
+
missingPackages.add(`unframer@${currentUnframerVersion}`);
|
|
209
|
+
spinner.info(`Different unframer version detected (${installedVersion}), will install unframer@${currentUnframerVersion}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
// Unframer not installed, add with specific version
|
|
214
|
+
missingPackages.add(`unframer@${currentUnframerVersion}`);
|
|
215
|
+
spinner.info(`Missing package detected: unframer@${currentUnframerVersion}`);
|
|
216
|
+
}
|
|
165
217
|
const prevFiles = await recursiveReaddir(out);
|
|
166
218
|
const buildResult = await buildContext.rebuild().catch((e) => {
|
|
167
219
|
if (e.message.includes('No matching export ')) {
|
|
168
|
-
foundError = true;
|
|
169
220
|
spinner.error(`esbuild failed to import from an external package, this usually means that the npm package version in Framer is older than the latest.`);
|
|
170
221
|
spinner.error(`Use --external to make all npm packagess external, then install the right version`);
|
|
171
222
|
process.exit(1);
|
|
@@ -173,105 +224,31 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
173
224
|
throw e;
|
|
174
225
|
});
|
|
175
226
|
spinner.update('Finished build');
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
!resultPathAbsJs.includes('/chunks/') &&
|
|
189
|
-
!resultPathAbsJs.includes('\\chunks\\')) {
|
|
190
|
-
try {
|
|
191
|
-
let res = transform(file.text || '', {
|
|
192
|
-
babelrc: false,
|
|
193
|
-
sourceType: 'module',
|
|
194
|
-
plugins: [
|
|
195
|
-
// babelPluginDeduplicateImports,
|
|
196
|
-
babelPluginJsxTransform,
|
|
197
|
-
removeJsxExpressionContainer,
|
|
198
|
-
],
|
|
199
|
-
// ast: true,
|
|
200
|
-
// code: false,
|
|
201
|
-
filename: 'x.jsx',
|
|
202
|
-
compact: false,
|
|
203
|
-
sourceMaps: false,
|
|
204
|
-
});
|
|
205
|
-
if (res?.code) {
|
|
206
|
-
if (!biome) {
|
|
207
|
-
biome = await Biome.create({
|
|
208
|
-
distribution: Distribution.NODE,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
let result = biome.formatContent(res.code, {
|
|
212
|
-
filePath: 'example.jsx',
|
|
213
|
-
});
|
|
214
|
-
didFormat = true;
|
|
215
|
-
formatted = result.content;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
catch (e) {
|
|
219
|
-
notifyError(e, 'babel transform and format');
|
|
220
|
-
}
|
|
227
|
+
// Install missing packages if any were collected
|
|
228
|
+
if (missingPackages.size > 0) {
|
|
229
|
+
const packagesToInstall = Array.from(missingPackages);
|
|
230
|
+
logger.log(`Installing missing packages: ${packagesToInstall.join(', ')}`);
|
|
231
|
+
const installResult = await installPackagesBatch({
|
|
232
|
+
packageNames: packagesToInstall,
|
|
233
|
+
cwd: out,
|
|
234
|
+
isDev: false,
|
|
235
|
+
});
|
|
236
|
+
if (!installResult.success) {
|
|
237
|
+
spinner.error(`Failed to install packages: ${installResult.error}`);
|
|
238
|
+
// Don't fail the build, just warn
|
|
221
239
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// formatted = dprint.format('file.jsx', file.text, {
|
|
227
|
-
// lineWidth: 140,
|
|
228
|
-
// quoteStyle: 'alwaysSingle',
|
|
229
|
-
// trailingCommas: 'always',
|
|
230
|
-
// semiColons: 'always',
|
|
231
|
-
// })
|
|
232
|
-
// }
|
|
233
|
-
// if (tooBig) {
|
|
234
|
-
// spinner.info(
|
|
235
|
-
// `skipping formatting ${path.relative(
|
|
236
|
-
// out,
|
|
237
|
-
// file.path,
|
|
238
|
-
// )}, too big`,
|
|
239
|
-
// )
|
|
240
|
-
// }
|
|
241
|
-
// if (framerWebPages?.length) {
|
|
242
|
-
// codeNew = replaceWebPageIds({
|
|
243
|
-
// code: codeNew,
|
|
244
|
-
// elements: framerWebPages,
|
|
245
|
-
// })
|
|
246
|
-
// }
|
|
247
|
-
// const lines = findRelativeLinks(codeNew)
|
|
248
|
-
// if (lines.length) {
|
|
249
|
-
// spinner.error(
|
|
250
|
-
// `found broken links for ${path.relative(out, file.path)}`,
|
|
251
|
-
// )
|
|
252
|
-
// lines.forEach((line) => {
|
|
253
|
-
// logger.log(`${path.resolve(out, file.path)}:${line + 1}`)
|
|
254
|
-
// })
|
|
255
|
-
// }
|
|
240
|
+
}
|
|
241
|
+
// First, write raw JS files for type extraction with temp prefix
|
|
242
|
+
for (let file of buildResult.outputFiles) {
|
|
243
|
+
const paths = getFilePaths(file.path, out);
|
|
256
244
|
const prefix = `// @ts-nocheck\n` + `/* eslint-disable */\n` + doNotEditComment;
|
|
257
|
-
const codeJsx = prefix + formatted;
|
|
258
245
|
const codeJs = prefix + file.text;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// }
|
|
262
|
-
logger.log(`writing`, path.relative(out, file.path));
|
|
263
|
-
await fs.promises.mkdir(path.dirname(resultPathAbsJsx), {
|
|
246
|
+
logger.log(`writing temp JS`, path.relative(out, paths.tempFilePath));
|
|
247
|
+
await fs.promises.mkdir(path.dirname(paths.tempJsPath), {
|
|
264
248
|
recursive: true,
|
|
265
249
|
});
|
|
266
|
-
|
|
267
|
-
await fs.promises.writeFile(resultPathAbsJs, codeJs, 'utf-8');
|
|
268
|
-
}
|
|
269
|
-
if (didFormat) {
|
|
270
|
-
await fs.promises.writeFile(resultPathAbsJsx, codeJsx, 'utf-8');
|
|
271
|
-
}
|
|
250
|
+
await fs.promises.writeFile(paths.tempJsPath, codeJs, 'utf-8');
|
|
272
251
|
}
|
|
273
|
-
spinner.stop();
|
|
274
|
-
await fs.promises.writeFile(path.resolve(out, '.cursorignore'), `**/*.js\nchunks\n`, 'utf-8');
|
|
275
252
|
if (!buildResult?.outputFiles) {
|
|
276
253
|
throw new Error('Failed to generate result');
|
|
277
254
|
}
|
|
@@ -287,7 +264,8 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
287
264
|
const name = path
|
|
288
265
|
.relative(out, file.path)
|
|
289
266
|
.replace(/\.jsx?$/, '');
|
|
290
|
-
const
|
|
267
|
+
const paths = getFilePaths(file.path, out);
|
|
268
|
+
const resultPathAbs = paths.tempJsPath;
|
|
291
269
|
if (!components[name]) {
|
|
292
270
|
return;
|
|
293
271
|
}
|
|
@@ -296,6 +274,7 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
296
274
|
return;
|
|
297
275
|
}
|
|
298
276
|
logger.log(`extracting types for ${name}`);
|
|
277
|
+
spinner.info(`Extracting types for component: ${name}`);
|
|
299
278
|
spinner.update(`Extracting types for ${name}`);
|
|
300
279
|
const { propertyControls, fonts } = await extractPropControlsUnsafe(resultPathAbs, name);
|
|
301
280
|
if (!propertyControls) {
|
|
@@ -305,17 +284,20 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
305
284
|
...x,
|
|
306
285
|
fileName: path.basename(file.path),
|
|
307
286
|
})));
|
|
308
|
-
const
|
|
287
|
+
const typedocComments = propControlsToTypedocComments({
|
|
309
288
|
controls: propertyControls,
|
|
310
289
|
fileName: name,
|
|
311
290
|
config,
|
|
312
291
|
});
|
|
292
|
+
logger.log(`Generated TypeDoc comments for ${name}: ${!!typedocComments.headerComment}`);
|
|
313
293
|
await fs.promises.mkdir(out, { recursive: true });
|
|
314
|
-
|
|
294
|
+
// .d.ts generation removed – types are now injected as typedoc
|
|
295
|
+
// comments directly inside the generated JSX file.
|
|
315
296
|
return {
|
|
316
297
|
propertyControls,
|
|
317
298
|
fonts,
|
|
318
299
|
name,
|
|
300
|
+
typedocComments,
|
|
319
301
|
};
|
|
320
302
|
}
|
|
321
303
|
finally {
|
|
@@ -329,7 +311,6 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
329
311
|
// Ignore error if file doesn't exist or can't be deleted
|
|
330
312
|
}
|
|
331
313
|
});
|
|
332
|
-
// spinner.stop()
|
|
333
314
|
const cssString = doNotEditComment +
|
|
334
315
|
'/* This css file has all the necessary styles to run all your components */\n' +
|
|
335
316
|
'\n' +
|
|
@@ -347,24 +328,27 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
347
328
|
.forEach((x) => logger.log(x));
|
|
348
329
|
const jsxFiles = buildResult.outputFiles
|
|
349
330
|
.filter((x) => x.path.endsWith('.js') &&
|
|
350
|
-
fs.existsSync(x.path
|
|
351
|
-
.map((x) => x.path
|
|
331
|
+
fs.existsSync(getFilePaths(x.path, out).jsxPath))
|
|
332
|
+
.map((x) => getFilePaths(x.path, out).jsxPath);
|
|
352
333
|
const outFiles = buildResult.outputFiles
|
|
353
|
-
.map((x) =>
|
|
334
|
+
.map((x) => {
|
|
335
|
+
const paths = getFilePaths(x.path, out);
|
|
336
|
+
if (x.path.endsWith('.js') && fs.existsSync(paths.jsxPath)) {
|
|
337
|
+
return null; // Will be handled by jsx files
|
|
338
|
+
}
|
|
339
|
+
return paths.finalJsPath;
|
|
340
|
+
})
|
|
341
|
+
.filter(Boolean)
|
|
354
342
|
.concat([
|
|
355
343
|
path.resolve(out, 'meta.json'),
|
|
356
344
|
path.resolve(out, 'tokens.css'),
|
|
357
345
|
path.resolve(out, '.cursorignore'),
|
|
358
346
|
path.resolve(out, 'styles.css'),
|
|
359
347
|
])
|
|
360
|
-
.concat(jsxFiles)
|
|
361
|
-
.concat(buildResult.outputFiles.map((x) => path.resolve(out, x.path.replace(/\.jsx?$/, '.d.ts'))));
|
|
348
|
+
.concat(jsxFiles);
|
|
362
349
|
const filesToDelete = prevFiles
|
|
363
350
|
.filter((x) => !outFiles.includes(x))
|
|
364
|
-
.
|
|
365
|
-
.map((x) => x.path)
|
|
366
|
-
.filter((js) => js.endsWith('.js') &&
|
|
367
|
-
jsxFiles.some((x) => x.startsWith(js))));
|
|
351
|
+
.filter((x) => !x.includes(tempJsPrefix)); // Don't delete temp files here, they're handled separately
|
|
368
352
|
for (let file of filesToDelete) {
|
|
369
353
|
logger.log('deleting', path.relative(out, file));
|
|
370
354
|
try {
|
|
@@ -381,12 +365,6 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
381
365
|
if (watch) {
|
|
382
366
|
logger.log('waiting for components or config changes');
|
|
383
367
|
}
|
|
384
|
-
if (!tokens?.length) {
|
|
385
|
-
const tokensCss = "/* 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" +
|
|
386
|
-
'/* Bug: https://www.framer.community/c/bugs/color-style-unlinks-when-copying-component-between-projects-resulting-in-potential-value-discrepancy */\n' +
|
|
387
|
-
getTokensCss({ out, result: buildResult });
|
|
388
|
-
await fs.promises.writeFile(path.resolve(out, 'tokens.css'), tokensCss, 'utf-8');
|
|
389
|
-
}
|
|
390
368
|
const res = {
|
|
391
369
|
components: Object.entries(components).map(([name, v]) => {
|
|
392
370
|
const propControls = propControlsData.find((x) => x?.name === name);
|
|
@@ -399,6 +377,131 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
399
377
|
};
|
|
400
378
|
}),
|
|
401
379
|
};
|
|
380
|
+
// Process and write JSX files with TypeDoc comments
|
|
381
|
+
spinner.update('Processing JSX files with TypeDoc comments');
|
|
382
|
+
for (let file of buildResult.outputFiles) {
|
|
383
|
+
const paths = getFilePaths(file.path, out);
|
|
384
|
+
const componentName = path
|
|
385
|
+
.relative(out, file.path)
|
|
386
|
+
.replace(/\.js$/, '');
|
|
387
|
+
const propData = propControlsData.find((p) => p?.name === componentName);
|
|
388
|
+
const typedocComments = propData?.typedocComments;
|
|
389
|
+
logger.log(`Processing component: ${componentName}`);
|
|
390
|
+
spinner.update(`Processing JSX for ${componentName}`);
|
|
391
|
+
if (!propData) {
|
|
392
|
+
logger.log(` No propData found for ${componentName}`);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
logger.log(` PropData found for ${componentName}, has propertyControls: ${!!propData.propertyControls}`);
|
|
396
|
+
if (!typedocComments) {
|
|
397
|
+
logger.log(` No typedocComments for ${componentName}`);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
logger.log(` TypeDoc comments available for ${componentName}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
const existing = await fs.promises
|
|
404
|
+
.readFile(paths.jsxPath, 'utf-8')
|
|
405
|
+
.catch(() => null);
|
|
406
|
+
const tooBigSize = 0.7 * 1024 * 1024;
|
|
407
|
+
let formatted = file.text;
|
|
408
|
+
let tooBig = file.text.length >= tooBigSize;
|
|
409
|
+
let didFormat = false;
|
|
410
|
+
if (config.jsx &&
|
|
411
|
+
!tooBig &&
|
|
412
|
+
!paths.tempJsPath.includes('/chunks/') &&
|
|
413
|
+
!paths.tempJsPath.includes('\\chunks\\')) {
|
|
414
|
+
try {
|
|
415
|
+
const plugins = [
|
|
416
|
+
// babelPluginDeduplicateImports,
|
|
417
|
+
babelPluginJsxTransform,
|
|
418
|
+
removeJsxExpressionContainer,
|
|
419
|
+
];
|
|
420
|
+
// Add TypeDoc plugin if we have comments for this component
|
|
421
|
+
if (typedocComments) {
|
|
422
|
+
logger.log(` Adding TypeDoc plugin for ${componentName}`);
|
|
423
|
+
plugins.push(babelPluginTypedoc(typedocComments));
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
logger.log(` No TypeDoc comments to add for ${componentName}`);
|
|
427
|
+
}
|
|
428
|
+
let res = transform(file.text || '', {
|
|
429
|
+
babelrc: false,
|
|
430
|
+
sourceType: 'module',
|
|
431
|
+
parserOpts: {
|
|
432
|
+
plugins: ['jsx'],
|
|
433
|
+
},
|
|
434
|
+
plugins,
|
|
435
|
+
// ast: true,
|
|
436
|
+
// code: false,
|
|
437
|
+
filename: 'x.jsx',
|
|
438
|
+
compact: false,
|
|
439
|
+
sourceMaps: false,
|
|
440
|
+
});
|
|
441
|
+
if (res?.code) {
|
|
442
|
+
if (!biome) {
|
|
443
|
+
biome = await Biome.create({
|
|
444
|
+
distribution: Distribution.NODE,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
let result = biome.formatContent(res.code, {
|
|
448
|
+
filePath: 'example.jsx',
|
|
449
|
+
});
|
|
450
|
+
didFormat = true;
|
|
451
|
+
formatted = result.content;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
catch (e) {
|
|
455
|
+
notifyError(e, 'babel transform and format');
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const prefix = `// @ts-nocheck\n` + `/* eslint-disable */\n` + doNotEditComment;
|
|
459
|
+
const codeJsx = prefix + formatted;
|
|
460
|
+
const codeJs = prefix + file.text;
|
|
461
|
+
logger.log(`writing`, path.relative(out, file.path));
|
|
462
|
+
await fs.promises.mkdir(path.dirname(paths.jsxPath), {
|
|
463
|
+
recursive: true,
|
|
464
|
+
});
|
|
465
|
+
// Always write the temp .js file for type extraction
|
|
466
|
+
await fs.promises.writeFile(paths.tempJsPath, codeJs, 'utf-8');
|
|
467
|
+
// Only write .jsx file if it's different from existing or if formatting was done
|
|
468
|
+
if (didFormat && codeJsx !== existing) {
|
|
469
|
+
await fs.promises.writeFile(paths.jsxPath, codeJsx, 'utf-8');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
spinner.stop();
|
|
473
|
+
// await fs.promises.writeFile(
|
|
474
|
+
// path.resolve(out, '.cursorignore'),
|
|
475
|
+
// `**/*.js\nchunks\n`,
|
|
476
|
+
// 'utf-8',
|
|
477
|
+
// )
|
|
478
|
+
// Clean up temp .js files and handle prefixes
|
|
479
|
+
for (let file of buildResult.outputFiles) {
|
|
480
|
+
if (file.path.endsWith('.js')) {
|
|
481
|
+
const paths = getFilePaths(file.path, out);
|
|
482
|
+
if (fs.existsSync(paths.jsxPath)) {
|
|
483
|
+
// Remove temp .js file if .jsx equivalent exists
|
|
484
|
+
logger.log('removing temp JS file with JSX equivalent:', path.relative(out, paths.tempJsPath));
|
|
485
|
+
try {
|
|
486
|
+
await fs.promises.rm(paths.tempJsPath);
|
|
487
|
+
await fs.promises.rm(paths.finalJsPath);
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
// Ignore error if file doesn't exist
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
// Rename temp .js file to final name if no .jsx equivalent
|
|
495
|
+
logger.log('renaming temp JS file to final name:', path.relative(out, paths.tempJsPath), '->', path.relative(out, paths.finalJsPath));
|
|
496
|
+
try {
|
|
497
|
+
await fs.promises.rename(paths.tempJsPath, paths.finalJsPath);
|
|
498
|
+
}
|
|
499
|
+
catch (error) {
|
|
500
|
+
// Ignore error if file doesn't exist
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
402
505
|
spinner.info(`Build completed`);
|
|
403
506
|
return res;
|
|
404
507
|
}
|
|
@@ -424,8 +527,11 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
424
527
|
const result = await rebuild();
|
|
425
528
|
console.log();
|
|
426
529
|
console.log();
|
|
427
|
-
const outDirForExample = path
|
|
428
|
-
|
|
530
|
+
const outDirForExample = path
|
|
531
|
+
.relative(process.cwd(), out)
|
|
532
|
+
.split(path.sep)
|
|
533
|
+
.join('/')
|
|
534
|
+
.replace(/^src\//, '') || 'framer'; // remove src so file works inside src
|
|
429
535
|
const { exampleCode } = await createExampleComponentCode({
|
|
430
536
|
outDir: out,
|
|
431
537
|
// buildResult: result,
|
|
@@ -438,38 +544,31 @@ export async function bundle({ config, cwd: out = '', watch = false, signal = un
|
|
|
438
544
|
});
|
|
439
545
|
await fs.promises.writeFile(stackblitzDemoExample, exampleCode);
|
|
440
546
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
# How to use the Framer components
|
|
547
|
+
console.log(terminalMarkdown(dedent `
|
|
548
|
+
# How to use the Framer components
|
|
444
549
|
|
|
445
|
-
|
|
550
|
+
Your components are exported to \`${outDirForExample}\` folder.
|
|
446
551
|
|
|
447
|
-
|
|
448
|
-
npm install unframer
|
|
449
|
-
\`\`\`
|
|
552
|
+
Each component has a \`.Responsive\` variant that allows you to specify different variants for different breakpoints.
|
|
450
553
|
|
|
451
|
-
|
|
554
|
+
You can use the components like this (try copy pasting the code below into your React app):
|
|
452
555
|
|
|
453
|
-
|
|
556
|
+
\`\`\`jsx
|
|
557
|
+
${exampleCode}
|
|
558
|
+
\`\`\`
|
|
454
559
|
|
|
455
|
-
|
|
456
|
-
${exampleCode}
|
|
457
|
-
\`\`\`
|
|
560
|
+
It's very important to import the \`styles.css\` file to include the necessary styles for the components.
|
|
458
561
|
|
|
459
|
-
|
|
562
|
+
To style components you can pass a \`style\` or \`className\` prop (but remember to use !important to increase the specificity).
|
|
460
563
|
|
|
461
|
-
|
|
564
|
+
Read more on GitHub: https://github.com/remorses/unframer
|
|
462
565
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
`));
|
|
466
|
-
}
|
|
467
|
-
await checkUnframerVersion({ cwd: out });
|
|
566
|
+
`));
|
|
468
567
|
console.log();
|
|
469
568
|
return { result, rebuild, buildContext };
|
|
470
569
|
}
|
|
471
570
|
const packageVersionCache = new Map();
|
|
472
|
-
export function
|
|
571
|
+
export function resolvePackageVersion({ cwd, pkg }) {
|
|
473
572
|
if (packageVersionCache.has(pkg)) {
|
|
474
573
|
return Promise.resolve(packageVersionCache.get(pkg));
|
|
475
574
|
}
|
|
@@ -484,7 +583,7 @@ export function resolvePackage({ cwd, pkg }) {
|
|
|
484
583
|
cwd,
|
|
485
584
|
}, (error, stdout, stderr) => {
|
|
486
585
|
if (error) {
|
|
487
|
-
|
|
586
|
+
// Package not installed - this is expected and handled by auto-install
|
|
488
587
|
reject(new Error(`${pkg} is not installed in your project`));
|
|
489
588
|
return;
|
|
490
589
|
}
|
|
@@ -494,17 +593,25 @@ export function resolvePackage({ cwd, pkg }) {
|
|
|
494
593
|
});
|
|
495
594
|
});
|
|
496
595
|
}
|
|
497
|
-
export
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
596
|
+
export function resolvePackage({ cwd, pkg }) {
|
|
597
|
+
return new Promise((resolve) => {
|
|
598
|
+
const code = `import('${pkg}/package.json', { with: { type: 'json' } }).then(()=>console.log('true')).catch(()=>import('${pkg}').then(()=>console.log('true')).catch(()=>console.log('false')));`;
|
|
599
|
+
const command = [
|
|
600
|
+
JSON.stringify(nodePath),
|
|
601
|
+
'-e',
|
|
602
|
+
JSON.stringify(code),
|
|
603
|
+
].join(' ');
|
|
604
|
+
exec(command, {
|
|
605
|
+
cwd,
|
|
606
|
+
}, (error, stdout) => {
|
|
607
|
+
if (error) {
|
|
608
|
+
resolve(false);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const exists = stdout.trim().split('\n').pop() === 'true';
|
|
612
|
+
resolve(exists);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
508
615
|
}
|
|
509
616
|
export function getDarkModeSelector(opts) {
|
|
510
617
|
const { darkModeType = 'class', content } = opts;
|
|
@@ -598,7 +705,7 @@ async function extractPropControlsSafe(text, name) {
|
|
|
598
705
|
logger.error(`Cannot get property controls for ${name}`, e.stack);
|
|
599
706
|
}
|
|
600
707
|
}
|
|
601
|
-
function getTokensCss({ out, result, }) {
|
|
708
|
+
async function getTokensCss({ out, result, }) {
|
|
602
709
|
const allTokens = [];
|
|
603
710
|
for (let file of result.outputFiles) {
|
|
604
711
|
const code = fs.readFileSync(path.resolve(out, file.path), 'utf-8');
|
|
@@ -728,7 +835,11 @@ function safeJsonParse(text) {
|
|
|
728
835
|
return null;
|
|
729
836
|
}
|
|
730
837
|
}
|
|
731
|
-
|
|
838
|
+
/**
|
|
839
|
+
* Generates TypeDoc comments that will be injected into JSX files
|
|
840
|
+
* instead of generating separate .d.ts files
|
|
841
|
+
*/
|
|
842
|
+
export function propControlsToTypedocComments({ config, fileName, controls, }) {
|
|
732
843
|
try {
|
|
733
844
|
const types = Object.entries(controls || {})
|
|
734
845
|
.map(([key, value]) => {
|
|
@@ -786,40 +897,76 @@ export function propControlsToType({ config, fileName, controls, }) {
|
|
|
786
897
|
if (!name) {
|
|
787
898
|
return '';
|
|
788
899
|
}
|
|
789
|
-
return `
|
|
900
|
+
return ` * ${name}?: ${typescriptType(value)} // ${value.title || name}`;
|
|
790
901
|
})
|
|
791
902
|
.filter(Boolean)
|
|
792
903
|
.join('\n');
|
|
793
904
|
const componentName = componentCamelCase(fileName);
|
|
794
|
-
const
|
|
795
|
-
'children?: React.ReactNode',
|
|
796
|
-
'locale?: Locale',
|
|
797
|
-
'style?: React.CSSProperties',
|
|
798
|
-
'className?: string',
|
|
799
|
-
'id?: string',
|
|
800
|
-
'
|
|
801
|
-
'
|
|
802
|
-
'
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
let
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
905
|
+
const defaultPropsJsDoc = [
|
|
906
|
+
' * children?: React.ReactNode',
|
|
907
|
+
' * locale?: Locale',
|
|
908
|
+
' * style?: React.CSSProperties',
|
|
909
|
+
' * className?: string',
|
|
910
|
+
' * id?: string',
|
|
911
|
+
' * ref?: any',
|
|
912
|
+
' * width?: any',
|
|
913
|
+
' * height?: any',
|
|
914
|
+
' * layoutId?: string',
|
|
915
|
+
].join('\n');
|
|
916
|
+
// Generate header comment with type definitions
|
|
917
|
+
let headerComment = '/**\n';
|
|
918
|
+
headerComment += ' * @typedef Locale\n';
|
|
919
|
+
// Generate union type from config.locales if available
|
|
920
|
+
const localeType = (() => {
|
|
921
|
+
if (config?.locales &&
|
|
922
|
+
Array.isArray(config.locales) &&
|
|
923
|
+
config.locales.length > 0) {
|
|
924
|
+
return config.locales
|
|
925
|
+
.map((locale) => `'${locale.slug}'`)
|
|
926
|
+
.join(' | ');
|
|
927
|
+
}
|
|
928
|
+
return 'string';
|
|
929
|
+
})();
|
|
930
|
+
headerComment += ` * ${localeType}\n`;
|
|
931
|
+
headerComment += ' */\n\n';
|
|
932
|
+
headerComment += '/**\n';
|
|
933
|
+
headerComment +=
|
|
934
|
+
' * @typedef {{\n';
|
|
935
|
+
headerComment += defaultPropsJsDoc;
|
|
936
|
+
if (types) {
|
|
937
|
+
headerComment += '\n' + types;
|
|
938
|
+
}
|
|
939
|
+
headerComment += `\n}} Props\n`;
|
|
940
|
+
headerComment += '\n */\n\n';
|
|
941
|
+
headerComment += '/**\n';
|
|
942
|
+
headerComment += ' * @type {import("unframer").UnframerBreakpoint}\n';
|
|
943
|
+
headerComment += ' * Represents a responsive breakpoint for unframer.\n';
|
|
944
|
+
headerComment += ' */\n\n';
|
|
945
|
+
headerComment += '/**\n';
|
|
946
|
+
headerComment += ' * @typedef VariantsMap\n';
|
|
947
|
+
headerComment +=
|
|
948
|
+
" * Partial record of UnframerBreakpoint to Props.variant, with a mandatory 'base' key.\n";
|
|
949
|
+
headerComment +=
|
|
950
|
+
" * { [key in UnframerBreakpoint]?: Props['variant'] } & { base: Props['variant'] }\n";
|
|
951
|
+
headerComment += ' */';
|
|
952
|
+
// Generate responsive comment
|
|
953
|
+
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 */`;
|
|
954
|
+
// Generate default export comment - use inline function type instead of referencing undefined type
|
|
955
|
+
const defaultExportComment = `/** @type {function(Props): any} */`;
|
|
956
|
+
return {
|
|
957
|
+
headerComment,
|
|
958
|
+
responsiveComment,
|
|
959
|
+
defaultExportComment,
|
|
960
|
+
};
|
|
819
961
|
}
|
|
820
962
|
catch (e) {
|
|
821
|
-
logger.error(
|
|
822
|
-
|
|
963
|
+
logger.error(e.message);
|
|
964
|
+
logger.error('cannot generate typedoc comments', e.stack);
|
|
965
|
+
return {
|
|
966
|
+
headerComment: '',
|
|
967
|
+
responsiveComment: '',
|
|
968
|
+
defaultExportComment: '',
|
|
969
|
+
};
|
|
823
970
|
}
|
|
824
971
|
}
|
|
825
972
|
export function parsePropertyControls(code) {
|
|
@@ -995,7 +1142,7 @@ async function recursiveReaddir(dir) {
|
|
|
995
1142
|
}));
|
|
996
1143
|
return files.flat();
|
|
997
1144
|
}
|
|
998
|
-
function indentWithTabs(str, tabs) {
|
|
1145
|
+
export function indentWithTabs(str, tabs) {
|
|
999
1146
|
if (!str)
|
|
1000
1147
|
return '';
|
|
1001
1148
|
return str
|
|
@@ -1004,8 +1151,10 @@ function indentWithTabs(str, tabs) {
|
|
|
1004
1151
|
.join('\n');
|
|
1005
1152
|
}
|
|
1006
1153
|
export async function createExampleComponentCode({ outDir, config, }) {
|
|
1007
|
-
const outDirForExample = path
|
|
1154
|
+
const outDirForExample = path
|
|
1008
1155
|
.relative(process.cwd(), outDir)
|
|
1156
|
+
.split(path.sep)
|
|
1157
|
+
.join('/')
|
|
1009
1158
|
.replace(/^src\//, ''); // remove src so file works inside src
|
|
1010
1159
|
const instances = config?.componentInstancesInIndexPage?.sort((a, b) => {
|
|
1011
1160
|
// Order first by nodeDepth (lower is better)
|
|
@@ -1051,7 +1200,42 @@ export async function createExampleComponentCode({ outDir, config, }) {
|
|
|
1051
1200
|
};
|
|
1052
1201
|
`;
|
|
1053
1202
|
return {
|
|
1203
|
+
outDirForExample,
|
|
1054
1204
|
exampleCode,
|
|
1055
1205
|
};
|
|
1056
1206
|
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Compares two semantic version strings.
|
|
1209
|
+
* Returns true if versionB is greater than versionA.
|
|
1210
|
+
* Handles x.y.z, x.y, x, and optional pre-release (-alpha, etc).
|
|
1211
|
+
*/
|
|
1212
|
+
export function isVersionGreater(versionA, versionB) {
|
|
1213
|
+
try {
|
|
1214
|
+
function parseVersion(version) {
|
|
1215
|
+
// Remove pre-release (e.g. -alpha.1)
|
|
1216
|
+
let [core] = version.trim().split('-');
|
|
1217
|
+
return core.split('.').map((x) => parseInt(x, 10));
|
|
1218
|
+
}
|
|
1219
|
+
const [a1 = 0, a2 = 0, a3 = 0] = parseVersion(versionA);
|
|
1220
|
+
const [b1 = 0, b2 = 0, b3 = 0] = parseVersion(versionB);
|
|
1221
|
+
if (b1 > a1)
|
|
1222
|
+
return true;
|
|
1223
|
+
if (b1 < a1)
|
|
1224
|
+
return false;
|
|
1225
|
+
if (b2 > a2)
|
|
1226
|
+
return true;
|
|
1227
|
+
if (b2 < a2)
|
|
1228
|
+
return false;
|
|
1229
|
+
if (b3 > a3)
|
|
1230
|
+
return true;
|
|
1231
|
+
if (b3 < a3)
|
|
1232
|
+
return false;
|
|
1233
|
+
// If all equal, not greater
|
|
1234
|
+
return false;
|
|
1235
|
+
}
|
|
1236
|
+
catch (error) {
|
|
1237
|
+
spinner.error(`Error comparing versions "${versionA}" and "${versionB}": ${error?.stack || error?.message || error}`);
|
|
1238
|
+
return true;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1057
1241
|
//# sourceMappingURL=exporter.js.map
|