meno-core 1.0.39 → 1.0.40
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/build-astro.ts +195 -68
- package/dist/bin/cli.js +1 -1
- package/dist/build-static.js +6 -6
- package/dist/chunks/{chunk-WK5XLASY.js → chunk-3NOZVNM4.js} +3 -3
- package/dist/chunks/{chunk-W6HDII4T.js → chunk-GKICS7CF.js} +27 -14
- package/dist/chunks/chunk-GKICS7CF.js.map +7 -0
- package/dist/chunks/{chunk-P3FX5HJM.js → chunk-LOJLO2EY.js} +1 -1
- package/dist/chunks/chunk-LOJLO2EY.js.map +7 -0
- package/dist/chunks/{chunk-HNAS6BSS.js → chunk-MOCRENNU.js} +55 -5
- package/dist/chunks/{chunk-HNAS6BSS.js.map → chunk-MOCRENNU.js.map} +3 -3
- package/dist/chunks/{chunk-NV25WXCA.js → chunk-OJ5SROQN.js} +5 -3
- package/dist/chunks/chunk-OJ5SROQN.js.map +7 -0
- package/dist/chunks/{chunk-AIXKUVNG.js → chunk-V4SVSX3X.js} +3 -3
- package/dist/chunks/{chunk-KULPBDC7.js → chunk-Z7SAOCDG.js} +5 -2
- package/dist/chunks/{chunk-KULPBDC7.js.map → chunk-Z7SAOCDG.js.map} +2 -2
- package/dist/chunks/{constants-5CRJRQNR.js → constants-L75FR445.js} +2 -2
- package/dist/entries/server-router.js +6 -6
- package/dist/lib/client/index.js +5 -5
- package/dist/lib/client/index.js.map +2 -2
- package/dist/lib/server/index.js +2007 -197
- package/dist/lib/server/index.js.map +4 -4
- package/dist/lib/shared/index.js +3 -3
- package/dist/lib/test-utils/index.js +1 -1
- package/lib/client/core/builders/embedBuilder.ts +2 -2
- package/lib/server/astro/cmsPageEmitter.ts +417 -0
- package/lib/server/astro/componentEmitter.ts +90 -5
- package/lib/server/astro/nodeToAstro.ts +830 -37
- package/lib/server/astro/pageEmitter.ts +39 -3
- package/lib/server/astro/tailwindMapper.ts +69 -8
- package/lib/server/astro/templateTransformer.ts +107 -0
- package/lib/server/index.ts +9 -0
- package/lib/server/routes/api/components.ts +62 -0
- package/lib/server/routes/api/core-routes.ts +8 -0
- package/lib/server/ssr/ssrRenderer.ts +30 -10
- package/lib/server/webflow/buildWebflow.ts +415 -0
- package/lib/server/webflow/index.ts +22 -0
- package/lib/server/webflow/nodeToWebflow.ts +423 -0
- package/lib/server/webflow/styleMapper.ts +241 -0
- package/lib/server/webflow/types.ts +196 -0
- package/lib/shared/constants.ts +2 -0
- package/lib/shared/types/components.ts +1 -0
- package/lib/shared/validation/schemas.ts +1 -0
- package/package.json +1 -1
- package/dist/chunks/chunk-NV25WXCA.js.map +0 -7
- package/dist/chunks/chunk-P3FX5HJM.js.map +0 -7
- package/dist/chunks/chunk-W6HDII4T.js.map +0 -7
- /package/dist/chunks/{chunk-WK5XLASY.js.map → chunk-3NOZVNM4.js.map} +0 -0
- /package/dist/chunks/{chunk-AIXKUVNG.js.map → chunk-V4SVSX3X.js.map} +0 -0
- /package/dist/chunks/{constants-5CRJRQNR.js.map → constants-L75FR445.js.map} +0 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webflow Export Build Orchestrator
|
|
3
|
+
* Loads the project, renders all pages, and converts to Webflow payload.
|
|
4
|
+
* Mirrors the pattern of build-astro.ts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readdirSync } from 'fs';
|
|
8
|
+
import { readFile } from 'fs/promises';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import {
|
|
11
|
+
loadJSONFile,
|
|
12
|
+
loadComponentDirectory,
|
|
13
|
+
mapPageNameToPath,
|
|
14
|
+
parseJSON,
|
|
15
|
+
loadI18nConfig,
|
|
16
|
+
loadBreakpointConfig,
|
|
17
|
+
loadResponsiveScalesConfig,
|
|
18
|
+
} from '../jsonLoader';
|
|
19
|
+
import { renderPageSSR } from '../ssr/ssrRenderer';
|
|
20
|
+
import { projectPaths } from '../projectContext';
|
|
21
|
+
import { loadProjectConfig } from '../../shared/fontLoader';
|
|
22
|
+
import { FileSystemCMSProvider } from '../providers/fileSystemCMSProvider';
|
|
23
|
+
import { CMSService } from '../services/cmsService';
|
|
24
|
+
import { isI18nValue, resolveI18nValue } from '../../shared/i18n';
|
|
25
|
+
import { configService } from '../services/configService';
|
|
26
|
+
import { colorService } from '../services/ColorService';
|
|
27
|
+
import { variableService } from '../services/VariableService';
|
|
28
|
+
import { migrateTemplatesDirectory } from '../migrateTemplates';
|
|
29
|
+
import type {
|
|
30
|
+
ComponentDefinition,
|
|
31
|
+
JSONPage,
|
|
32
|
+
CMSSchema,
|
|
33
|
+
CMSItem,
|
|
34
|
+
I18nConfig,
|
|
35
|
+
} from '../../shared/types';
|
|
36
|
+
import { isItemDraftForLocale } from '../../shared/types';
|
|
37
|
+
import type { SlugMap } from '../../shared/slugTranslator';
|
|
38
|
+
import type {
|
|
39
|
+
WebflowExportPayload,
|
|
40
|
+
WebflowPage,
|
|
41
|
+
WebflowStyleClass,
|
|
42
|
+
WebflowCMSCollection,
|
|
43
|
+
WebflowCMSField,
|
|
44
|
+
WebflowAssetRef,
|
|
45
|
+
} from './types';
|
|
46
|
+
import { mapCMSFieldType } from './types';
|
|
47
|
+
import { nodeToWebflow, type WebflowEmitContext } from './nodeToWebflow';
|
|
48
|
+
import { generateThemeColorVariablesCSS, generateVariablesCSS } from '../cssGenerator';
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Helpers
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
function scanJSONFiles(dir: string, prefix: string = ''): string[] {
|
|
55
|
+
const results: string[] = [];
|
|
56
|
+
if (!existsSync(dir)) return results;
|
|
57
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (entry.isFile() && entry.name.endsWith('.json')) {
|
|
60
|
+
results.push(prefix ? `${prefix}/${entry.name}` : entry.name);
|
|
61
|
+
} else if (entry.isDirectory()) {
|
|
62
|
+
results.push(...scanJSONFiles(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.name));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isCMSPage(pageData: JSONPage): boolean {
|
|
69
|
+
return pageData.meta?.source === 'cms' && !!pageData.meta?.cms;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function buildCMSItemPath(
|
|
73
|
+
urlPattern: string,
|
|
74
|
+
item: CMSItem,
|
|
75
|
+
slugField: string,
|
|
76
|
+
locale: string,
|
|
77
|
+
i18nConfig: I18nConfig
|
|
78
|
+
): string {
|
|
79
|
+
let slug = item[slugField] ?? item._slug ?? item._id;
|
|
80
|
+
if (isI18nValue(slug)) {
|
|
81
|
+
slug = resolveI18nValue(slug, locale, i18nConfig) as string;
|
|
82
|
+
}
|
|
83
|
+
return urlPattern.replace('{{slug}}', String(slug));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function scanAssets(projectRoot: string): WebflowAssetRef[] {
|
|
87
|
+
const assets: WebflowAssetRef[] = [];
|
|
88
|
+
const assetDirs: Array<{ dir: string; type: WebflowAssetRef['type'] }> = [
|
|
89
|
+
{ dir: 'images', type: 'image' },
|
|
90
|
+
{ dir: 'fonts', type: 'font' },
|
|
91
|
+
{ dir: 'videos', type: 'video' },
|
|
92
|
+
{ dir: 'assets', type: 'file' },
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const { dir, type } of assetDirs) {
|
|
96
|
+
const fullDir = join(projectRoot, dir);
|
|
97
|
+
if (!existsSync(fullDir)) continue;
|
|
98
|
+
const files = scanJSONFiles(fullDir).map(f => f.replace('.json', '')); // scanJSONFiles works for any extension
|
|
99
|
+
// Re-scan properly for all file types
|
|
100
|
+
const allFiles = scanAllFiles(fullDir);
|
|
101
|
+
for (const file of allFiles) {
|
|
102
|
+
assets.push({
|
|
103
|
+
localPath: `${dir}/${file}`,
|
|
104
|
+
type,
|
|
105
|
+
fileName: file.split('/').pop()!,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return assets;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function scanAllFiles(dir: string, prefix: string = ''): string[] {
|
|
114
|
+
const results: string[] = [];
|
|
115
|
+
if (!existsSync(dir)) return results;
|
|
116
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
119
|
+
if (entry.isFile()) {
|
|
120
|
+
results.push(relativePath);
|
|
121
|
+
} else if (entry.isDirectory()) {
|
|
122
|
+
results.push(...scanAllFiles(join(dir, entry.name), relativePath));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Extract CSS variables from theme and variables config
|
|
130
|
+
*/
|
|
131
|
+
function extractCSSVariables(
|
|
132
|
+
themeColorCSS: string,
|
|
133
|
+
variablesCSS: string
|
|
134
|
+
): Record<string, string> {
|
|
135
|
+
const vars: Record<string, string> = {};
|
|
136
|
+
const regex = /--([\w-]+)\s*:\s*([^;]+)/g;
|
|
137
|
+
|
|
138
|
+
for (const css of [themeColorCSS, variablesCSS]) {
|
|
139
|
+
let match;
|
|
140
|
+
while ((match = regex.exec(css)) !== null) {
|
|
141
|
+
vars[`--${match[1]}`] = match[2].trim();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return vars;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Main export
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
export async function buildWebflowPayload(
|
|
153
|
+
projectRoot?: string
|
|
154
|
+
): Promise<WebflowExportPayload> {
|
|
155
|
+
// 1. Setup: load project configuration
|
|
156
|
+
configService.reset();
|
|
157
|
+
|
|
158
|
+
const projectConfig = await loadProjectConfig();
|
|
159
|
+
const siteUrl = (projectConfig as { siteUrl?: string }).siteUrl?.replace(/\/$/, '') || '';
|
|
160
|
+
const i18nConfig = await loadI18nConfig();
|
|
161
|
+
|
|
162
|
+
await migrateTemplatesDirectory();
|
|
163
|
+
|
|
164
|
+
const { components } = await loadComponentDirectory(projectPaths.components());
|
|
165
|
+
const globalComponents: Record<string, ComponentDefinition> = {};
|
|
166
|
+
components.forEach((value, key) => { globalComponents[key] = value; });
|
|
167
|
+
|
|
168
|
+
const cmsProvider = new FileSystemCMSProvider(projectPaths.templates(), projectPaths.cms());
|
|
169
|
+
const cmsService = new CMSService(cmsProvider);
|
|
170
|
+
await cmsService.initialize();
|
|
171
|
+
|
|
172
|
+
const themeConfig = await colorService.loadThemeConfig();
|
|
173
|
+
const variablesConfig = await variableService.loadConfig();
|
|
174
|
+
const breakpoints = await loadBreakpointConfig();
|
|
175
|
+
const responsiveScales = await loadResponsiveScalesConfig();
|
|
176
|
+
|
|
177
|
+
await configService.load();
|
|
178
|
+
|
|
179
|
+
// 2. Scan pages
|
|
180
|
+
const pagesDir = projectPaths.pages();
|
|
181
|
+
if (!existsSync(pagesDir)) {
|
|
182
|
+
return emptyPayload();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const pageFiles = scanJSONFiles(pagesDir);
|
|
186
|
+
if (pageFiles.length === 0) {
|
|
187
|
+
return emptyPayload();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Collect slug mappings
|
|
191
|
+
const slugMappings: SlugMap[] = [];
|
|
192
|
+
for (const file of pageFiles) {
|
|
193
|
+
const pageName = file.replace('.json', '');
|
|
194
|
+
const basePath = mapPageNameToPath(pageName);
|
|
195
|
+
const pageContent = await loadJSONFile(join(pagesDir, file));
|
|
196
|
+
if (!pageContent) continue;
|
|
197
|
+
try {
|
|
198
|
+
const pageData = parseJSON<JSONPage>(pageContent);
|
|
199
|
+
if (pageData.meta?.slugs) {
|
|
200
|
+
const pageId = basePath === '/' ? 'index' : basePath.substring(1);
|
|
201
|
+
slugMappings.push({ pageId, slugs: pageData.meta.slugs });
|
|
202
|
+
}
|
|
203
|
+
} catch { /* ignore */ }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 3. Render and convert pages
|
|
207
|
+
const allPages: WebflowPage[] = [];
|
|
208
|
+
const allStyleClasses = new Map<string, WebflowStyleClass>();
|
|
209
|
+
|
|
210
|
+
// Regular pages
|
|
211
|
+
for (const file of pageFiles) {
|
|
212
|
+
const pageName = file.replace('.json', '');
|
|
213
|
+
const basePath = mapPageNameToPath(pageName);
|
|
214
|
+
const pageContent = await loadJSONFile(join(pagesDir, file));
|
|
215
|
+
if (!pageContent) continue;
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const pageData = parseJSON<JSONPage>(pageContent);
|
|
219
|
+
if (pageData.meta?.draft === true) continue;
|
|
220
|
+
|
|
221
|
+
const slugs = pageData.meta?.slugs;
|
|
222
|
+
|
|
223
|
+
for (const localeConfig of i18nConfig.locales) {
|
|
224
|
+
const locale = localeConfig.code;
|
|
225
|
+
const isDefault = locale === i18nConfig.defaultLocale;
|
|
226
|
+
|
|
227
|
+
let slug: string;
|
|
228
|
+
if (slugs && slugs[locale]) {
|
|
229
|
+
slug = slugs[locale];
|
|
230
|
+
} else if (basePath === '/') {
|
|
231
|
+
slug = '';
|
|
232
|
+
} else {
|
|
233
|
+
slug = basePath.substring(1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const urlPath = isDefault
|
|
237
|
+
? (slug === '' ? '/' : `/${slug}`)
|
|
238
|
+
: (slug === '' ? `/${locale}` : `/${locale}/${slug}`);
|
|
239
|
+
|
|
240
|
+
// Render via SSR to resolve all props
|
|
241
|
+
const result = await renderPageSSR(
|
|
242
|
+
pageData,
|
|
243
|
+
globalComponents,
|
|
244
|
+
urlPath,
|
|
245
|
+
siteUrl,
|
|
246
|
+
locale,
|
|
247
|
+
i18nConfig,
|
|
248
|
+
slugMappings,
|
|
249
|
+
undefined,
|
|
250
|
+
cmsService,
|
|
251
|
+
true
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
// Convert node tree to Webflow elements
|
|
255
|
+
const ctx: WebflowEmitContext = {
|
|
256
|
+
globalComponents,
|
|
257
|
+
elementPath: [0],
|
|
258
|
+
fileType: 'page',
|
|
259
|
+
fileName: pageName,
|
|
260
|
+
breakpoints,
|
|
261
|
+
styleClasses: allStyleClasses,
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const body = pageData.root || (pageData as any).node;
|
|
265
|
+
const elements = body ? nodeToWebflow(body, ctx) : [];
|
|
266
|
+
|
|
267
|
+
allPages.push({
|
|
268
|
+
title: result.title,
|
|
269
|
+
slug: slug || 'index',
|
|
270
|
+
metaDescription: typeof pageData.meta?.description === 'string' ? pageData.meta.description : undefined,
|
|
271
|
+
elements,
|
|
272
|
+
locale,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
} catch (error: any) {
|
|
276
|
+
console.error(`Error processing ${basePath}:`, error?.message);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// CMS template pages
|
|
281
|
+
const templatesDir = projectPaths.templates();
|
|
282
|
+
const cmsCollections: WebflowCMSCollection[] = [];
|
|
283
|
+
|
|
284
|
+
if (existsSync(templatesDir)) {
|
|
285
|
+
const templateFiles = readdirSync(templatesDir).filter(f => f.endsWith('.json'));
|
|
286
|
+
|
|
287
|
+
for (const file of templateFiles) {
|
|
288
|
+
const templateContent = await loadJSONFile(join(templatesDir, file));
|
|
289
|
+
if (!templateContent) continue;
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const pageData = parseJSON<JSONPage>(templateContent);
|
|
293
|
+
if (pageData.meta?.draft === true) continue;
|
|
294
|
+
if (!isCMSPage(pageData)) continue;
|
|
295
|
+
|
|
296
|
+
const cmsSchema = pageData.meta!.cms as CMSSchema;
|
|
297
|
+
const items = await cmsService.queryItems({ collection: cmsSchema.id });
|
|
298
|
+
|
|
299
|
+
// Build Webflow CMS collection
|
|
300
|
+
const fields: WebflowCMSField[] = [];
|
|
301
|
+
if (cmsSchema.fields) {
|
|
302
|
+
for (const [fieldName, fieldDef] of Object.entries(cmsSchema.fields)) {
|
|
303
|
+
fields.push({
|
|
304
|
+
name: fieldDef.label || fieldName,
|
|
305
|
+
slug: fieldName,
|
|
306
|
+
type: mapCMSFieldType(fieldDef.type),
|
|
307
|
+
required: fieldDef.required,
|
|
308
|
+
options: fieldDef.options,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Resolve i18n values in items for default locale
|
|
314
|
+
const resolvedItems: Record<string, unknown>[] = [];
|
|
315
|
+
for (const item of items) {
|
|
316
|
+
const resolved: Record<string, unknown> = {};
|
|
317
|
+
for (const [key, value] of Object.entries(item)) {
|
|
318
|
+
if (isI18nValue(value)) {
|
|
319
|
+
resolved[key] = resolveI18nValue(value, i18nConfig.defaultLocale, i18nConfig);
|
|
320
|
+
} else {
|
|
321
|
+
resolved[key] = value;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
resolvedItems.push(resolved);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
cmsCollections.push({
|
|
328
|
+
name: cmsSchema.id,
|
|
329
|
+
slug: cmsSchema.id,
|
|
330
|
+
urlPattern: cmsSchema.urlPattern,
|
|
331
|
+
fields,
|
|
332
|
+
items: resolvedItems,
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Render CMS item pages
|
|
336
|
+
for (const item of items) {
|
|
337
|
+
for (const localeConfig of i18nConfig.locales) {
|
|
338
|
+
const locale = localeConfig.code;
|
|
339
|
+
if (isItemDraftForLocale(item, locale)) continue;
|
|
340
|
+
|
|
341
|
+
const itemPath = buildCMSItemPath(cmsSchema.urlPattern, item, cmsSchema.slugField, locale, i18nConfig);
|
|
342
|
+
const itemWithUrl: CMSItem = { ...item, _url: itemPath };
|
|
343
|
+
|
|
344
|
+
const result = await renderPageSSR(
|
|
345
|
+
pageData,
|
|
346
|
+
globalComponents,
|
|
347
|
+
itemPath,
|
|
348
|
+
siteUrl,
|
|
349
|
+
locale,
|
|
350
|
+
i18nConfig,
|
|
351
|
+
slugMappings,
|
|
352
|
+
{ cms: itemWithUrl },
|
|
353
|
+
cmsService,
|
|
354
|
+
true
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
const ctx: WebflowEmitContext = {
|
|
358
|
+
globalComponents,
|
|
359
|
+
elementPath: [0],
|
|
360
|
+
fileType: 'page',
|
|
361
|
+
fileName: file.replace('.json', ''),
|
|
362
|
+
breakpoints,
|
|
363
|
+
styleClasses: allStyleClasses,
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const body = pageData.root || (pageData as any).node;
|
|
367
|
+
// Pass CMS item data as props so {{cms.field}} templates resolve
|
|
368
|
+
const cmsProps = { cms: itemWithUrl };
|
|
369
|
+
const elements = body ? nodeToWebflow(body, ctx, cmsProps) : [];
|
|
370
|
+
|
|
371
|
+
const slug = itemPath.startsWith('/') ? itemPath.substring(1) : itemPath;
|
|
372
|
+
allPages.push({
|
|
373
|
+
title: result.title,
|
|
374
|
+
slug,
|
|
375
|
+
elements,
|
|
376
|
+
locale,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} catch (error: any) {
|
|
381
|
+
console.error(`Error processing template ${file}:`, error?.message);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 4. Collect CSS variables
|
|
387
|
+
const themeColorCSS = generateThemeColorVariablesCSS(themeConfig);
|
|
388
|
+
const variablesCSS = generateVariablesCSS(variablesConfig, breakpoints, responsiveScales);
|
|
389
|
+
const cssVariables = extractCSSVariables(themeColorCSS, variablesCSS);
|
|
390
|
+
|
|
391
|
+
// 5. Scan assets
|
|
392
|
+
const assets = scanAssets(projectPaths.project);
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
version: 1,
|
|
396
|
+
exportedAt: new Date().toISOString(),
|
|
397
|
+
pages: allPages,
|
|
398
|
+
styles: Array.from(allStyleClasses.values()),
|
|
399
|
+
cms: cmsCollections,
|
|
400
|
+
assets,
|
|
401
|
+
cssVariables: Object.keys(cssVariables).length > 0 ? cssVariables : undefined,
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function emptyPayload(): WebflowExportPayload {
|
|
406
|
+
return {
|
|
407
|
+
version: 1,
|
|
408
|
+
exportedAt: new Date().toISOString(),
|
|
409
|
+
pages: [],
|
|
410
|
+
styles: [],
|
|
411
|
+
cms: [],
|
|
412
|
+
assets: [],
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webflow Export Module
|
|
3
|
+
* Re-exports the main build function and types.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { buildWebflowPayload } from './buildWebflow';
|
|
7
|
+
export { nodeToWebflow } from './nodeToWebflow';
|
|
8
|
+
export { mapStylesToWebflow } from './styleMapper';
|
|
9
|
+
export type {
|
|
10
|
+
WebflowExportPayload,
|
|
11
|
+
WebflowPage,
|
|
12
|
+
WebflowElement,
|
|
13
|
+
WebflowStyleClass,
|
|
14
|
+
WebflowCMSCollection,
|
|
15
|
+
WebflowCMSField,
|
|
16
|
+
WebflowAssetRef,
|
|
17
|
+
WebflowBreakpoint,
|
|
18
|
+
WebflowPseudoState,
|
|
19
|
+
WebflowFieldType,
|
|
20
|
+
CSSProperties,
|
|
21
|
+
} from './types';
|
|
22
|
+
export { mapCMSFieldType } from './types';
|