@see-ms/converter 0.1.2 → 0.1.4

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/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/converter.ts
2
2
  import pc3 from "picocolors";
3
- import path9 from "path";
4
- import fs8 from "fs-extra";
3
+ import path11 from "path";
4
+ import fs9 from "fs-extra";
5
5
 
6
6
  // src/filesystem.ts
7
7
  import fs from "fs-extra";
@@ -472,13 +472,20 @@ import * as cheerio2 from "cheerio";
472
472
  import fs5 from "fs-extra";
473
473
  import path6 from "path";
474
474
  function cleanClassName(className) {
475
- return className.split(" ").filter((cls) => !cls.startsWith("c-") && !cls.startsWith("w-")).filter((cls) => cls.length > 0).map((cls) => cls.replace(/-/g, "_")).join(" ");
475
+ return className.split(" ").filter((cls) => !cls.startsWith("c-") && !cls.startsWith("w-")).filter((cls) => cls.length > 0).join(" ");
476
476
  }
477
477
  function getPrimaryClass(classAttr) {
478
478
  if (!classAttr) return null;
479
479
  const cleaned = cleanClassName(classAttr);
480
480
  const classes = cleaned.split(" ").filter((c) => c.length > 0);
481
- return classes[0] || null;
481
+ if (classes.length === 0) return null;
482
+ const original = classes[0];
483
+ return {
484
+ selector: original,
485
+ // Keep original with dashes for CSS selector
486
+ fieldName: original.replace(/-/g, "_")
487
+ // Normalize for field name
488
+ };
482
489
  }
483
490
  function getContextModifier(_$, $el) {
484
491
  let $current = $el.parent();
@@ -535,11 +542,11 @@ function detectEditableFields(templateHtml) {
535
542
  const potentialCollections = /* @__PURE__ */ new Map();
536
543
  $("[class]").each((_, el) => {
537
544
  const primaryClass = getPrimaryClass($(el).attr("class"));
538
- if (primaryClass && (primaryClass.includes("card") || primaryClass.includes("item") || primaryClass.includes("post") || primaryClass.includes("feature")) && !primaryClass.includes("image") && !primaryClass.includes("inner")) {
539
- if (!potentialCollections.has(primaryClass)) {
540
- potentialCollections.set(primaryClass, []);
545
+ if (primaryClass && (primaryClass.fieldName.includes("card") || primaryClass.fieldName.includes("item") || primaryClass.fieldName.includes("post") || primaryClass.fieldName.includes("feature")) && !primaryClass.fieldName.includes("image") && !primaryClass.fieldName.includes("inner")) {
546
+ if (!potentialCollections.has(primaryClass.fieldName)) {
547
+ potentialCollections.set(primaryClass.fieldName, []);
541
548
  }
542
- potentialCollections.get(primaryClass)?.push(el);
549
+ potentialCollections.get(primaryClass.fieldName)?.push(el);
543
550
  }
544
551
  });
545
552
  potentialCollections.forEach((elements, className) => {
@@ -553,40 +560,43 @@ function detectEditableFields(templateHtml) {
553
560
  collectionElements.add(child);
554
561
  });
555
562
  });
563
+ const collectionClassInfo = getPrimaryClass($(elements[0]).attr("class"));
564
+ const collectionSelector = collectionClassInfo ? `.${collectionClassInfo.selector}` : `.${className}`;
556
565
  $first.find("img").each((_, img) => {
557
566
  if (isInsideButton($, img)) return;
558
567
  const $img = $(img);
559
568
  const $parent = $img.parent();
560
- const parentClass = getPrimaryClass($parent.attr("class"));
561
- if (parentClass && parentClass.includes("image")) {
562
- collectionFields.image = `.${parentClass}`;
569
+ const parentClassInfo = getPrimaryClass($parent.attr("class"));
570
+ if (parentClassInfo && parentClassInfo.fieldName.includes("image")) {
571
+ collectionFields.image = `.${parentClassInfo.selector}`;
563
572
  return false;
564
573
  }
565
574
  });
566
575
  $first.find("div").each((_, el) => {
567
- const primaryClass = getPrimaryClass($(el).attr("class"));
568
- if (primaryClass && primaryClass.includes("tag") && !primaryClass.includes("container")) {
569
- collectionFields.tag = `.${primaryClass}`;
576
+ const classInfo = getPrimaryClass($(el).attr("class"));
577
+ if (classInfo && classInfo.fieldName.includes("tag") && !classInfo.fieldName.includes("container")) {
578
+ collectionFields.tag = `.${classInfo.selector}`;
570
579
  return false;
571
580
  }
572
581
  });
573
582
  $first.find("h1, h2, h3, h4, h5, h6").first().each((_, el) => {
574
- const primaryClass = getPrimaryClass($(el).attr("class"));
575
- if (primaryClass) {
576
- collectionFields.title = `.${primaryClass}`;
583
+ const classInfo = getPrimaryClass($(el).attr("class"));
584
+ if (classInfo) {
585
+ collectionFields.title = `.${classInfo.selector}`;
577
586
  }
578
587
  });
579
588
  $first.find("p").first().each((_, el) => {
580
- const primaryClass = getPrimaryClass($(el).attr("class"));
581
- if (primaryClass) {
582
- collectionFields.description = `.${primaryClass}`;
589
+ const classInfo = getPrimaryClass($(el).attr("class"));
590
+ if (classInfo) {
591
+ collectionFields.description = `.${classInfo.selector}`;
583
592
  }
584
593
  });
585
594
  $first.find("a, NuxtLink").not(".c_button, .c_icon_button").each((_, el) => {
586
595
  const $link = $(el);
587
596
  const linkText = $link.text().trim();
588
597
  if (linkText) {
589
- collectionFields.link = `.${getPrimaryClass($link.attr("class")) || "a"}`;
598
+ const classInfo = getPrimaryClass($link.attr("class"));
599
+ collectionFields.link = classInfo ? `.${classInfo.selector}` : "a";
590
600
  return false;
591
601
  }
592
602
  });
@@ -596,7 +606,7 @@ function detectEditableFields(templateHtml) {
596
606
  collectionName += "s";
597
607
  }
598
608
  detectedCollections[collectionName] = {
599
- selector: `.${className}`,
609
+ selector: collectionSelector,
600
610
  fields: collectionFields
601
611
  };
602
612
  }
@@ -607,25 +617,30 @@ function detectEditableFields(templateHtml) {
607
617
  if (collectionElements.has(el)) return;
608
618
  const $el = $(el);
609
619
  const text = $el.text().trim();
610
- const primaryClass = getPrimaryClass($el.attr("class"));
620
+ const classInfo = getPrimaryClass($el.attr("class"));
611
621
  if (text) {
612
622
  let fieldName;
613
- if (primaryClass && !primaryClass.startsWith("heading_")) {
614
- fieldName = primaryClass;
623
+ let selector;
624
+ if (classInfo && !classInfo.fieldName.startsWith("heading_")) {
625
+ fieldName = classInfo.fieldName;
626
+ selector = `.${classInfo.selector}`;
615
627
  } else {
616
628
  const $parent = $el.closest('[class*="header"], [class*="hero"], [class*="cta"]').first();
617
- const parentClass = getPrimaryClass($parent.attr("class"));
629
+ const parentClassInfo = getPrimaryClass($parent.attr("class"));
618
630
  const modifier = getContextModifier($, $el);
619
- if (parentClass) {
620
- fieldName = modifier ? `${modifier}_${parentClass}` : parentClass;
631
+ if (parentClassInfo) {
632
+ fieldName = modifier ? `${modifier}_${parentClassInfo.fieldName}` : parentClassInfo.fieldName;
633
+ selector = classInfo ? `.${classInfo.selector}` : `.${parentClassInfo.selector}`;
621
634
  } else if (modifier) {
622
635
  fieldName = `${modifier}_heading`;
636
+ selector = classInfo ? `.${classInfo.selector}` : el.tagName.toLowerCase();
623
637
  } else {
624
638
  fieldName = `heading_${index}`;
639
+ selector = classInfo ? `.${classInfo.selector}` : el.tagName.toLowerCase();
625
640
  }
626
641
  }
627
642
  detectedFields[fieldName] = {
628
- selector: primaryClass ? `.${primaryClass}` : el.tagName.toLowerCase(),
643
+ selector,
629
644
  type: "plain",
630
645
  editable: true
631
646
  };
@@ -635,11 +650,11 @@ function detectEditableFields(templateHtml) {
635
650
  if (collectionElements.has(el)) return;
636
651
  const $el = $(el);
637
652
  const text = $el.text().trim();
638
- const primaryClass = getPrimaryClass($el.attr("class"));
639
- if (text && text.length > 20 && primaryClass) {
653
+ const classInfo = getPrimaryClass($el.attr("class"));
654
+ if (text && text.length > 20 && classInfo) {
640
655
  const hasFormatting = $el.find("strong, em, b, i, a, NuxtLink").length > 0;
641
- detectedFields[primaryClass] = {
642
- selector: `.${primaryClass}`,
656
+ detectedFields[classInfo.fieldName] = {
657
+ selector: `.${classInfo.selector}`,
643
658
  type: hasFormatting ? "rich" : "plain",
644
659
  editable: true
645
660
  };
@@ -651,11 +666,11 @@ function detectEditableFields(templateHtml) {
651
666
  const $el = $(el);
652
667
  if (isDecorativeImage($, $el)) return;
653
668
  const $parent = $el.parent();
654
- const parentClass = getPrimaryClass($parent.attr("class"));
655
- if (parentClass) {
656
- const fieldName = parentClass.includes("image") ? parentClass : `${parentClass}_image`;
669
+ const parentClassInfo = getPrimaryClass($parent.attr("class"));
670
+ if (parentClassInfo) {
671
+ const fieldName = parentClassInfo.fieldName.includes("image") ? parentClassInfo.fieldName : `${parentClassInfo.fieldName}_image`;
657
672
  detectedFields[fieldName] = {
658
- selector: `.${parentClass}`,
673
+ selector: `.${parentClassInfo.selector}`,
659
674
  type: "image",
660
675
  editable: true
661
676
  };
@@ -669,8 +684,9 @@ function detectEditableFields(templateHtml) {
669
684
  }).first().text().trim();
670
685
  if (text && text.length > 2) {
671
686
  const $parent = $el.closest('[class*="cta"]').first();
672
- const parentClass = getPrimaryClass($parent.attr("class")) || "button";
673
- detectedFields[`${parentClass}_button_text`] = {
687
+ const parentClassInfo = getPrimaryClass($parent.attr("class"));
688
+ const fieldName = parentClassInfo ? `${parentClassInfo.fieldName}_button_text` : "button_text";
689
+ detectedFields[fieldName] = {
674
690
  selector: `.c_button`,
675
691
  type: "plain",
676
692
  editable: true
@@ -736,6 +752,18 @@ function mapFieldTypeToStrapi(fieldType) {
736
752
  };
737
753
  return typeMap[fieldType] || "string";
738
754
  }
755
+ function pluralize(word) {
756
+ if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) {
757
+ return word + "es";
758
+ }
759
+ if (word.endsWith("y") && word.length > 1) {
760
+ const secondLast = word[word.length - 2];
761
+ if (!"aeiou".includes(secondLast.toLowerCase())) {
762
+ return word.slice(0, -1) + "ies";
763
+ }
764
+ }
765
+ return word + "s";
766
+ }
739
767
  function pageToStrapiSchema(pageName, fields) {
740
768
  const attributes = {};
741
769
  for (const [fieldName, field] of Object.entries(fields)) {
@@ -748,12 +776,14 @@ function pageToStrapiSchema(pageName, fields) {
748
776
  }
749
777
  }
750
778
  const displayName = pageName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
779
+ const kebabCaseName = pageName;
780
+ const pluralName = pluralize(kebabCaseName);
751
781
  return {
752
782
  kind: "singleType",
753
- collectionName: pageName.replace(/-/g, "_"),
783
+ collectionName: kebabCaseName,
754
784
  info: {
755
- singularName: pageName.replace(/-/g, "_"),
756
- pluralName: pageName.replace(/-/g, "_"),
785
+ singularName: kebabCaseName,
786
+ pluralName,
757
787
  displayName
758
788
  },
759
789
  options: {
@@ -780,13 +810,14 @@ function collectionToStrapiSchema(collectionName, collection) {
780
810
  };
781
811
  }
782
812
  const displayName = collectionName.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
783
- const singularName = collectionName.endsWith("s") ? collectionName.slice(0, -1) : collectionName;
813
+ const kebabCaseName = collectionName.replace(/_/g, "-");
814
+ const singularName = kebabCaseName.endsWith("s") ? kebabCaseName.slice(0, -1) : kebabCaseName;
784
815
  return {
785
816
  kind: "collectionType",
786
- collectionName: collectionName.replace(/[-_]/g, "_"),
817
+ collectionName: kebabCaseName,
787
818
  info: {
788
- singularName: singularName.replace(/[-_]/g, "_"),
789
- pluralName: collectionName.replace(/[-_]/g, "_"),
819
+ singularName,
820
+ pluralName: kebabCaseName,
790
821
  displayName
791
822
  },
792
823
  options: {
@@ -898,6 +929,186 @@ const { data } = await $fetch('http://localhost:1337/api/portfolio-cards')
898
929
  await fs7.writeFile(readmePath, content, "utf-8");
899
930
  }
900
931
 
932
+ // src/content-extractor.ts
933
+ import * as cheerio3 from "cheerio";
934
+ import path9 from "path";
935
+ function extractContentFromHTML(html, _pageName, pageManifest) {
936
+ const $ = cheerio3.load(html);
937
+ const content = {
938
+ fields: {},
939
+ collections: {}
940
+ };
941
+ if (pageManifest.fields) {
942
+ for (const [fieldName, field] of Object.entries(pageManifest.fields)) {
943
+ const selector = field.selector;
944
+ const element = $(selector).first();
945
+ if (element.length > 0) {
946
+ if (field.type === "image") {
947
+ const src = element.attr("src") || element.find("img").attr("src") || "";
948
+ content.fields[fieldName] = src;
949
+ } else {
950
+ const text = element.text().trim();
951
+ content.fields[fieldName] = text;
952
+ }
953
+ }
954
+ }
955
+ }
956
+ if (pageManifest.collections) {
957
+ for (const [collectionName, collection] of Object.entries(pageManifest.collections)) {
958
+ const items = [];
959
+ const collectionElements = $(collection.selector);
960
+ collectionElements.each((_, elem) => {
961
+ const item = {};
962
+ const $elem = $(elem);
963
+ for (const [fieldName, fieldSelector] of Object.entries(collection.fields)) {
964
+ const fieldElement = $elem.find(fieldSelector).first();
965
+ if (fieldElement.length > 0) {
966
+ if (fieldName === "image" || fieldName.includes("image")) {
967
+ const src = fieldElement.attr("src") || fieldElement.find("img").attr("src") || "";
968
+ item[fieldName] = src;
969
+ } else if (fieldName === "link" || fieldName === "url") {
970
+ const href = fieldElement.attr("href") || "";
971
+ item[fieldName] = href;
972
+ } else {
973
+ const text = fieldElement.text().trim();
974
+ item[fieldName] = text;
975
+ }
976
+ }
977
+ }
978
+ if (Object.keys(item).length > 0) {
979
+ items.push(item);
980
+ }
981
+ });
982
+ if (items.length > 0) {
983
+ content.collections[collectionName] = items;
984
+ }
985
+ }
986
+ }
987
+ return content;
988
+ }
989
+ function extractAllContent(htmlFiles, manifest) {
990
+ const extractedContent = {
991
+ pages: {}
992
+ };
993
+ for (const [pageName, pageManifest] of Object.entries(manifest.pages)) {
994
+ const html = htmlFiles.get(pageName);
995
+ if (html) {
996
+ const content = extractContentFromHTML(html, pageName, pageManifest);
997
+ extractedContent.pages[pageName] = content;
998
+ }
999
+ }
1000
+ return extractedContent;
1001
+ }
1002
+ function normalizeImagePath(imageSrc) {
1003
+ if (!imageSrc) return "";
1004
+ if (imageSrc.startsWith("/")) return imageSrc;
1005
+ const filename = path9.basename(imageSrc);
1006
+ if (imageSrc.includes("images/")) {
1007
+ return `/images/${filename}`;
1008
+ }
1009
+ return `/${filename}`;
1010
+ }
1011
+ function formatForStrapi(extracted) {
1012
+ const seedData = {};
1013
+ for (const [pageName, content] of Object.entries(extracted.pages)) {
1014
+ if (Object.keys(content.fields).length > 0) {
1015
+ const formattedFields = {};
1016
+ for (const [fieldName, value] of Object.entries(content.fields)) {
1017
+ if (fieldName.includes("image") || fieldName.includes("bg")) {
1018
+ formattedFields[fieldName] = normalizeImagePath(value);
1019
+ } else {
1020
+ formattedFields[fieldName] = value;
1021
+ }
1022
+ }
1023
+ seedData[pageName] = formattedFields;
1024
+ }
1025
+ for (const [collectionName, items] of Object.entries(content.collections)) {
1026
+ const formattedItems = items.map((item) => {
1027
+ const formattedItem = {};
1028
+ for (const [fieldName, value] of Object.entries(item)) {
1029
+ if (fieldName === "image" || fieldName.includes("image")) {
1030
+ formattedItem[fieldName] = normalizeImagePath(value);
1031
+ } else {
1032
+ formattedItem[fieldName] = value;
1033
+ }
1034
+ }
1035
+ return formattedItem;
1036
+ });
1037
+ seedData[collectionName] = formattedItems;
1038
+ }
1039
+ }
1040
+ return seedData;
1041
+ }
1042
+
1043
+ // src/seed-writer.ts
1044
+ import fs8 from "fs-extra";
1045
+ import path10 from "path";
1046
+ async function writeSeedData(outputDir, seedData) {
1047
+ const seedDir = path10.join(outputDir, "cms-seed");
1048
+ await fs8.ensureDir(seedDir);
1049
+ const seedPath = path10.join(seedDir, "seed-data.json");
1050
+ await fs8.writeJson(seedPath, seedData, { spaces: 2 });
1051
+ }
1052
+ async function createSeedReadme(outputDir) {
1053
+ const readmePath = path10.join(outputDir, "cms-seed", "README.md");
1054
+ const content = `# CMS Seed Data
1055
+
1056
+ Auto-extracted content from your Webflow export, ready to seed into Strapi.
1057
+
1058
+ ## What's in this folder?
1059
+
1060
+ \`seed-data.json\` contains the actual content extracted from your HTML:
1061
+ - **Single types** - Page-specific content (homepage, about page, etc.)
1062
+ - **Collection types** - Repeating items (portfolio cards, team members, etc.)
1063
+
1064
+ ## Structure
1065
+
1066
+ \`\`\`json
1067
+ {
1068
+ "index": {
1069
+ "hero_heading_container": "Actual heading from HTML",
1070
+ "hero_bg_image": "/images/hero.jpg",
1071
+ ...
1072
+ },
1073
+ "portfolio_cards": [
1074
+ {
1075
+ "image": "/images/card1.jpg",
1076
+ "tag": "Technology",
1077
+ "description": "Card description"
1078
+ }
1079
+ ]
1080
+ }
1081
+ \`\`\`
1082
+
1083
+ ## How to Seed Strapi
1084
+
1085
+ ### Option 1: Manual Entry
1086
+ 1. Open Strapi admin panel
1087
+ 2. Go to Content Manager
1088
+ 3. Create entries using the data from \`seed-data.json\`
1089
+
1090
+ ### Option 2: Automated Seeding (Coming Soon)
1091
+ We'll provide a seeding script that:
1092
+ 1. Uploads images to Strapi media library
1093
+ 2. Creates content entries via Strapi API
1094
+ 3. Handles relationships between content types
1095
+
1096
+ ## Image Paths
1097
+
1098
+ Image paths in the seed data reference files in your Nuxt \`public/\` directory:
1099
+ - \`/images/hero.jpg\` \u2192 \`public/images/hero.jpg\`
1100
+
1101
+ When seeding Strapi, these images will be uploaded to Strapi's media library.
1102
+
1103
+ ## Next Steps
1104
+
1105
+ 1. Review the extracted data for accuracy
1106
+ 2. Set up your Strapi instance with the schemas from \`cms-schemas/\`
1107
+ 3. Use this seed data to populate your CMS
1108
+ `;
1109
+ await fs8.writeFile(readmePath, content, "utf-8");
1110
+ }
1111
+
901
1112
  // src/converter.ts
902
1113
  async function convertWebflowExport(options) {
903
1114
  const { inputDir, outputDir, boilerplate } = options;
@@ -906,7 +1117,7 @@ async function convertWebflowExport(options) {
906
1117
  console.log(pc3.dim(`Output: ${outputDir}`));
907
1118
  try {
908
1119
  await setupBoilerplate(boilerplate, outputDir);
909
- const inputExists = await fs8.pathExists(inputDir);
1120
+ const inputExists = await fs9.pathExists(inputDir);
910
1121
  if (!inputExists) {
911
1122
  throw new Error(`Input directory not found: ${inputDir}`);
912
1123
  }
@@ -922,10 +1133,17 @@ async function convertWebflowExport(options) {
922
1133
  console.log(pc3.blue("\n\u{1F50D} Finding HTML files..."));
923
1134
  const htmlFiles = await findHTMLFiles(inputDir);
924
1135
  console.log(pc3.green(` \u2713 Found ${htmlFiles.length} HTML files`));
1136
+ const htmlContentMap = /* @__PURE__ */ new Map();
1137
+ for (const htmlFile of htmlFiles) {
1138
+ const html = await readHTMLFile(inputDir, htmlFile);
1139
+ const pageName = htmlFile.replace(".html", "").replace(/\//g, "-");
1140
+ htmlContentMap.set(pageName, html);
1141
+ console.log(pc3.dim(` Stored: ${pageName} from ${htmlFile}`));
1142
+ }
925
1143
  console.log(pc3.blue("\n\u2699\uFE0F Converting HTML to Vue components..."));
926
1144
  let allEmbeddedStyles = "";
927
1145
  for (const htmlFile of htmlFiles) {
928
- const html = await readHTMLFile(inputDir, htmlFile);
1146
+ const html = htmlContentMap.get(htmlFile.replace(".html", "").replace(/\//g, "-"));
929
1147
  const parsed = parseHTML(html, htmlFile);
930
1148
  if (parsed.embeddedStyles) {
931
1149
  allEmbeddedStyles += `
@@ -941,7 +1159,7 @@ ${parsed.embeddedStyles}
941
1159
  }
942
1160
  await formatVueFiles(outputDir);
943
1161
  console.log(pc3.blue("\n\u{1F50D} Analyzing pages for CMS fields..."));
944
- const pagesDir = path9.join(outputDir, "pages");
1162
+ const pagesDir = path11.join(outputDir, "pages");
945
1163
  const manifest = await generateManifest(pagesDir);
946
1164
  await writeManifest(outputDir, manifest);
947
1165
  const totalFields = Object.values(manifest.pages).reduce(
@@ -955,12 +1173,26 @@ ${parsed.embeddedStyles}
955
1173
  console.log(pc3.green(` \u2713 Detected ${totalFields} fields across ${Object.keys(manifest.pages).length} pages`));
956
1174
  console.log(pc3.green(` \u2713 Detected ${totalCollections} collections`));
957
1175
  console.log(pc3.green(" \u2713 Generated cms-manifest.json"));
1176
+ console.log(pc3.blue("\n\u{1F4DD} Extracting content from HTML..."));
1177
+ console.log(pc3.dim(` HTML map has ${htmlContentMap.size} entries`));
1178
+ console.log(pc3.dim(` Manifest has ${Object.keys(manifest.pages).length} pages`));
1179
+ const extractedContent = extractAllContent(htmlContentMap, manifest);
1180
+ const seedData = formatForStrapi(extractedContent);
1181
+ await writeSeedData(outputDir, seedData);
1182
+ await createSeedReadme(outputDir);
1183
+ const pagesWithContent = Object.keys(seedData).filter((key) => {
1184
+ const data = seedData[key];
1185
+ if (Array.isArray(data)) return data.length > 0;
1186
+ return Object.keys(data).length > 0;
1187
+ }).length;
1188
+ console.log(pc3.green(` \u2713 Extracted content from ${pagesWithContent} pages`));
1189
+ console.log(pc3.green(` \u2713 Generated cms-seed/seed-data.json`));
958
1190
  console.log(pc3.blue("\n\u{1F4CB} Generating Strapi schemas..."));
959
1191
  const schemas = manifestToSchemas(manifest);
960
1192
  await writeAllSchemas(outputDir, schemas);
961
1193
  await createStrapiReadme(outputDir);
962
1194
  console.log(pc3.green(` \u2713 Generated ${Object.keys(schemas).length} Strapi content types`));
963
- console.log(pc3.dim(" View schemas in: strapi/src/api/"));
1195
+ console.log(pc3.dim(" View schemas in: cms-schemas/"));
964
1196
  if (allEmbeddedStyles.trim()) {
965
1197
  console.log(pc3.blue("\n\u2728 Writing embedded styles..."));
966
1198
  const dedupedStyles = deduplicateStyles(allEmbeddedStyles);
@@ -975,7 +1207,7 @@ ${parsed.embeddedStyles}
975
1207
  await updateNuxtConfig(outputDir, assets.css);
976
1208
  console.log(pc3.green(" \u2713 Config updated"));
977
1209
  } catch (error) {
978
- console.log(pc3.yellow(" \u26A0 Could not update nuxt.config.ts automatically"));
1210
+ console.log(pc3.yellow(" \u26A0 Could not update nuxt.config.ts automatically"));
979
1211
  console.log(pc3.dim(" Please add CSS files manually"));
980
1212
  }
981
1213
  console.log(pc3.blue("\n\u{1F3A8} Setting up editor overlay..."));
@@ -988,10 +1220,11 @@ ${parsed.embeddedStyles}
988
1220
  console.log(pc3.green("\n\u2705 Conversion completed successfully!"));
989
1221
  console.log(pc3.cyan("\n\u{1F4CB} Next steps:"));
990
1222
  console.log(pc3.dim(` 1. cd ${outputDir}`));
991
- console.log(pc3.dim(" 2. Review cms-manifest.json"));
992
- console.log(pc3.dim(" 3. Copy strapi/ schemas to your Strapi project"));
993
- console.log(pc3.dim(" 4. pnpm install && pnpm dev"));
994
- console.log(pc3.dim(" 5. Visit http://localhost:3000?preview=true to edit inline!"));
1223
+ console.log(pc3.dim(" 2. Review cms-manifest.json and cms-seed/seed-data.json"));
1224
+ console.log(pc3.dim(" 3. Set up Strapi and install schemas from cms-schemas/"));
1225
+ console.log(pc3.dim(" 4. Seed Strapi with data from cms-seed/"));
1226
+ console.log(pc3.dim(" 5. pnpm install && pnpm dev"));
1227
+ console.log(pc3.dim(" 6. Visit http://localhost:3000?preview=true to edit inline!"));
995
1228
  } catch (error) {
996
1229
  console.error(pc3.red("\n\u274C Conversion failed:"));
997
1230
  console.error(pc3.red(error instanceof Error ? error.message : String(error)));