create-dev-to 1.6.1 → 1.6.2

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.
Files changed (2) hide show
  1. package/dist/index.js +468 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,10 +14,10 @@ import { InstallLogger } from "./installLogger.js";
14
14
  import { displayInstallSummary } from "./visualComponents.js";
15
15
  const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"];
16
16
  const __BUILD_INFO__ = {
17
- commit: "b15ce4b",
17
+ commit: "98fa359",
18
18
  branch: "main",
19
- buildTime: "2026-01-16 06:56",
20
- version: "1.6.1"
19
+ buildTime: "2026-01-16 17:32",
20
+ version: "1.6.2"
21
21
  };
22
22
  const FRAMEWORKS = [
23
23
  {
@@ -558,13 +558,226 @@ function editFile(file, callback) {
558
558
  const content = fs.readFileSync(file, "utf-8");
559
559
  fs.writeFileSync(file, callback(content), "utf-8");
560
560
  }
561
+ const DEVTO_LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
562
+ <rect width="128" height="128" rx="24" fill="#0d1117"/>
563
+ <text x="64" y="75" text-anchor="middle" dominant-baseline="middle" fill="#3fb950" font-family="system-ui, -apple-system, sans-serif" font-weight="700" font-size="125">\u0189</text>
564
+ </svg>
565
+ `;
566
+ function ensureDevtoLogo(root) {
567
+ const assetsDir = path.join(root, "src", "assets");
568
+ if (!fs.existsSync(assetsDir)) {
569
+ fs.mkdirSync(assetsDir, { recursive: true });
570
+ }
571
+ const logoPath = path.join(assetsDir, "devto.svg");
572
+ fs.writeFileSync(logoPath, DEVTO_LOGO_SVG, "utf-8");
573
+ }
574
+ function updateAppTemplate(root, componentName, isTs) {
575
+ const appPath = path.join(root, "src", `App.${isTs ? "tsx" : "jsx"}`);
576
+ if (!fs.existsSync(appPath)) {
577
+ return;
578
+ }
579
+ const appContent = `import './App.css'
580
+ import ${componentName} from './${componentName}/index'
581
+
582
+ export default function App() {
583
+ return (
584
+ <div className="app">
585
+ <header className="app-header">
586
+ <span className="eyebrow">dev-to template</span>
587
+ <h1>Component preview</h1>
588
+ <p className="subtitle">
589
+ Vite dev server with <code>@dev-to/react-plugin</code>
590
+ </p>
591
+ </header>
592
+
593
+ <section className="preview">
594
+ <div className="preview-inner">
595
+ <${componentName} />
596
+ </div>
597
+ </section>
598
+
599
+ </div>
600
+ )
601
+ }
602
+ `;
603
+ fs.writeFileSync(appPath, appContent, "utf-8");
604
+ const appCssPath = path.join(root, "src", "App.css");
605
+ const appCssContent = `#root {
606
+ max-width: 1100px;
607
+ margin: 0 auto;
608
+ padding: 32px 20px 40px;
609
+ }
610
+
611
+ .app {
612
+ display: flex;
613
+ flex-direction: column;
614
+ align-items: center;
615
+ gap: 32px;
616
+ text-align: center;
617
+ }
618
+
619
+ .app-header {
620
+ max-width: 720px;
621
+ }
622
+
623
+ .app-header h1 {
624
+ margin: 4px 0 6px;
625
+ font-size: 34px;
626
+ line-height: 1.15;
627
+ text-wrap: balance;
628
+ }
629
+
630
+ .eyebrow {
631
+ display: inline-block;
632
+ font-size: 10px;
633
+ letter-spacing: 1.8px;
634
+ text-transform: uppercase;
635
+ color: rgba(255, 255, 255, 0.6);
636
+ }
637
+
638
+ .subtitle {
639
+ margin: 0;
640
+ font-size: 13px;
641
+ line-height: 1.5;
642
+ text-wrap: balance;
643
+ color: rgba(255, 255, 255, 0.72);
644
+ }
645
+
646
+ .preview {
647
+ width: min(960px, 100%);
648
+ padding: 1px;
649
+ border-radius: 26px;
650
+ background: linear-gradient(130deg, #22d3ee, #38bdf8 35%, #34d399 70%, #fbbf24);
651
+ }
652
+
653
+ .preview-inner {
654
+ border-radius: 25px;
655
+ padding: 28px 20px;
656
+ background: rgba(15, 23, 42, 0.85);
657
+ box-shadow: 0 20px 60px rgba(15, 23, 42, 0.35);
658
+ }
659
+
660
+ .app-footer {
661
+ margin: 0;
662
+ font-size: 12px;
663
+ text-wrap: balance;
664
+ color: rgba(255, 255, 255, 0.6);
665
+ }
666
+
667
+ code {
668
+ background: rgba(255, 255, 255, 0.12);
669
+ padding: 2px 6px;
670
+ border-radius: 6px;
671
+ font-size: 11px;
672
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
673
+ monospace;
674
+ }
675
+
676
+ @media (prefers-color-scheme: dark) {
677
+ .app-header h1 {
678
+ color: #f8fafc;
679
+ }
680
+
681
+ .preview-inner {
682
+ background: #0b1220;
683
+ border: 1px solid rgba(148, 163, 184, 0.18);
684
+ box-shadow: 0 18px 50px rgba(2, 6, 23, 0.65);
685
+ }
686
+
687
+ .subtitle {
688
+ color: rgba(226, 232, 240, 0.78);
689
+ }
690
+
691
+ code {
692
+ background: rgba(148, 163, 184, 0.16);
693
+ }
694
+ }
695
+
696
+ @media (prefers-color-scheme: light) {
697
+ .eyebrow {
698
+ color: #6b7280;
699
+ }
700
+
701
+ .subtitle {
702
+ color: #4b5563;
703
+ }
704
+
705
+ .preview-inner {
706
+ background: #ffffff;
707
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
708
+ }
709
+
710
+ .app-footer {
711
+ color: #6b7280;
712
+ }
713
+
714
+ code {
715
+ background: #e2e8f0;
716
+ }
717
+ }
718
+
719
+ @media (max-width: 640px) {
720
+ #root {
721
+ padding: 28px 16px 36px;
722
+ }
723
+
724
+ .preview-inner {
725
+ padding: 24px 16px;
726
+ }
727
+
728
+ .app-header h1 {
729
+ font-size: 30px;
730
+ }
731
+ }
732
+
733
+ @media (max-width: 360px) {
734
+ #root {
735
+ padding: 24px 12px 32px;
736
+ }
737
+
738
+ .app {
739
+ gap: 24px;
740
+ }
741
+
742
+ .app-header h1 {
743
+ font-size: 26px;
744
+ }
745
+
746
+ .eyebrow {
747
+ font-size: 9px;
748
+ letter-spacing: 1.4px;
749
+ }
750
+
751
+ .subtitle {
752
+ font-size: 12px;
753
+ }
754
+
755
+ .preview-inner {
756
+ padding: 20px 12px;
757
+ }
758
+
759
+ .app-footer {
760
+ font-size: 11px;
761
+ }
762
+
763
+ code {
764
+ font-size: 10px;
765
+ }
766
+ }
767
+ `;
768
+ fs.writeFileSync(appCssPath, appCssContent, "utf-8");
769
+ }
561
770
  function createComponentFile(root, componentName, isTs) {
562
771
  const componentDir = path.join(root, "src", componentName);
563
772
  const componentFile = path.join(componentDir, `index.${isTs ? "tsx" : "jsx"}`);
564
773
  if (!fs.existsSync(componentDir)) {
565
774
  fs.mkdirSync(componentDir, { recursive: true });
566
775
  }
776
+ const componentFileName = `index.${isTs ? "tsx" : "jsx"}`;
567
777
  const componentContent = isTs ? `import { useState } from 'react'
778
+ import devtoLogo from '../assets/devto.svg'
779
+ import reactLogo from '../assets/react.svg'
780
+ import viteLogo from '/vite.svg'
568
781
  import './index.css'
569
782
 
570
783
  export interface ${componentName}Props {
@@ -573,110 +786,308 @@ export interface ${componentName}Props {
573
786
 
574
787
  export default function ${componentName}(props: ${componentName}Props) {
575
788
  const [count, setCount] = useState(0)
789
+ const title = props.title || '\u0189evTo + Vite + React'
576
790
 
577
791
  return (
578
- <div className="${componentName.toLowerCase()}-container">
579
- <h1>{props.title || 'Hello from ${componentName}'}</h1>
580
- <div className="card">
792
+ <div className="${componentName.toLowerCase()}-component">
793
+ <div className="logo-row">
794
+ <a href="https://github.com/YangYongAn/dev-to" target="_blank" rel="noreferrer">
795
+ <img src={devtoLogo} className="logo devto" alt="dev-to logo" />
796
+ </a>
797
+ <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
798
+ <img src={viteLogo} className="logo" alt="Vite logo" />
799
+ </a>
800
+ <a href="https://react.dev" target="_blank" rel="noreferrer">
801
+ <img src={reactLogo} className="logo react" alt="React logo" />
802
+ </a>
803
+ </div>
804
+ <h1>{title}</h1>
805
+ <p className="subtitle">
806
+ <span>Remote component served by</span>
807
+ <code>@dev-to/react-plugin</code>
808
+ </p>
809
+ <div className="counter-card">
581
810
  <button onClick={() => setCount(count => count + 1)}>
582
811
  count is {count}
583
812
  </button>
584
813
  <p>
585
- Edit <code>src/${componentName}/index.tsx</code> to test HMR
814
+ Edit <code>src/${componentName}/${componentFileName}</code> to test HMR
586
815
  </p>
587
816
  </div>
588
- <p className="info">
589
- This component is loaded remotely via <code>@dev-to/react-loader</code>
817
+ <p className="read-the-docs">
818
+ Click on the \u0189evTo, Vite, and React logos to learn more
590
819
  </p>
591
820
  </div>
592
821
  )
593
822
  }
594
823
  ` : `import { useState } from 'react'
824
+ import devtoLogo from '../assets/devto.svg'
825
+ import reactLogo from '../assets/react.svg'
826
+ import viteLogo from '/vite.svg'
595
827
  import './index.css'
596
828
 
597
829
  export default function ${componentName}(props) {
598
830
  const [count, setCount] = useState(0)
831
+ const title = props.title || '\u0189evTo + Vite + React'
599
832
 
600
833
  return (
601
- <div className="${componentName.toLowerCase()}-container">
602
- <h1>{props.title || 'Hello from ${componentName}'}</h1>
603
- <div className="card">
834
+ <div className="${componentName.toLowerCase()}-component">
835
+ <div className="logo-row">
836
+ <a href="https://github.com/YangYongAn/dev-to" target="_blank" rel="noreferrer">
837
+ <img src={devtoLogo} className="logo devto" alt="dev-to logo" />
838
+ </a>
839
+ <a href="https://vitejs.dev" target="_blank" rel="noreferrer">
840
+ <img src={viteLogo} className="logo" alt="Vite logo" />
841
+ </a>
842
+ <a href="https://react.dev" target="_blank" rel="noreferrer">
843
+ <img src={reactLogo} className="logo react" alt="React logo" />
844
+ </a>
845
+ </div>
846
+ <h1>{title}</h1>
847
+ <p className="subtitle">
848
+ <span>Remote component served by</span>
849
+ <code>@dev-to/react-plugin</code>
850
+ </p>
851
+ <div className="counter-card">
604
852
  <button onClick={() => setCount(count => count + 1)}>
605
853
  count is {count}
606
854
  </button>
607
855
  <p>
608
- Edit <code>src/${componentName}/index.jsx</code> to test HMR
856
+ Edit <code>src/${componentName}/${componentFileName}</code> to test HMR
609
857
  </p>
610
858
  </div>
611
- <p className="info">
612
- This component is loaded remotely via <code>@dev-to/react-loader</code>
859
+ <p className="read-the-docs">
860
+ Click on the \u0189evTo, Vite, and React logos to learn more
613
861
  </p>
614
862
  </div>
615
863
  )
616
864
  }
617
865
  `;
618
- const styleContent = `.${componentName.toLowerCase()}-container {
619
- max-width: 800px;
866
+ const rootClass = `${componentName.toLowerCase()}-component`;
867
+ const styleContent = `.${rootClass} {
868
+ max-width: 720px;
620
869
  margin: 0 auto;
621
- padding: 2rem;
870
+ padding: 20px 12px 28px;
622
871
  text-align: center;
623
872
  }
624
873
 
625
- .${componentName.toLowerCase()}-container h1 {
626
- font-size: 3.2em;
874
+ .${rootClass} .logo-row {
875
+ display: flex;
876
+ flex-wrap: wrap;
877
+ align-items: center;
878
+ justify-content: center;
879
+ gap: 10px;
880
+ margin-bottom: 18px;
881
+ }
882
+
883
+ .${rootClass} .logo {
884
+ height: 48px;
885
+ padding: 0;
886
+ will-change: filter, transform;
887
+ transition: transform 0.25s ease, filter 0.25s ease;
888
+ }
889
+
890
+ .${rootClass} .logo.devto {
891
+ border-radius: 14px;
892
+ background: rgba(15, 23, 42, 0.9);
893
+ box-shadow: 0 10px 30px rgba(15, 23, 42, 0.35);
894
+ }
895
+
896
+ .${rootClass} .logo:hover {
897
+ filter: drop-shadow(0 0 18px rgba(56, 189, 248, 0.6));
898
+ }
899
+
900
+ .${rootClass} .logo.react:hover {
901
+ filter: drop-shadow(0 0 18px rgba(97, 218, 251, 0.65));
902
+ }
903
+
904
+ .${rootClass} h1 {
905
+ margin: 12px 0 6px;
906
+ font-size: 28px;
627
907
  line-height: 1.1;
628
- margin-bottom: 2rem;
908
+ white-space: nowrap;
909
+ }
910
+
911
+ .${rootClass} .subtitle {
912
+ margin: 0 auto 18px;
913
+ max-width: 500px;
914
+ font-size: 13px;
915
+ line-height: 1.5;
916
+ text-wrap: balance;
917
+ color: rgba(255, 255, 255, 0.72);
918
+ }
919
+
920
+ .${rootClass} .subtitle span {
921
+ display: block;
629
922
  }
630
923
 
631
- .card {
632
- padding: 2em;
633
- background-color: #f9f9f9;
634
- border-radius: 8px;
635
- margin: 2rem 0;
924
+ .${rootClass} .subtitle code {
925
+ display: inline-block;
926
+ margin-top: 4px;
636
927
  }
637
928
 
638
- button {
639
- border-radius: 8px;
640
- border: 1px solid transparent;
641
- padding: 0.6em 1.2em;
642
- font-size: 1em;
643
- font-weight: 500;
644
- font-family: inherit;
645
- background-color: #1a1a1a;
646
- color: white;
929
+ .${rootClass} .counter-card {
930
+ margin: 18px auto;
931
+ padding: 20px 18px;
932
+ border-radius: 16px;
933
+ background: rgba(255, 255, 255, 0.06);
934
+ border: 1px solid rgba(255, 255, 255, 0.12);
935
+ backdrop-filter: blur(6px);
936
+ }
937
+
938
+ .${rootClass} .counter-card p {
939
+ margin: 18px 0;
940
+ font-size: 10px;
941
+ white-space: nowrap;
942
+ }
943
+
944
+ .${rootClass} .counter-card button {
945
+ border: 1px solid rgba(63, 185, 80, 0.45);
946
+ border-radius: 999px;
947
+ padding: 9px 24px;
948
+ font-size: 14px;
949
+ font-weight: 600;
950
+ color: #ffffff;
951
+ background: linear-gradient(135deg, #3fb950, #22c55e 55%, #16a34a);
952
+ box-shadow: 0 10px 20px rgba(22, 163, 74, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.25);
647
953
  cursor: pointer;
648
- transition: border-color 0.25s;
954
+ transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
649
955
  }
650
956
 
651
- button:hover {
652
- border-color: #646cff;
957
+ .${rootClass} .counter-card button:hover {
958
+ transform: translateY(-1px);
959
+ box-shadow: 0 14px 28px rgba(22, 163, 74, 0.32), inset 0 1px 0 rgba(255, 255, 255, 0.35);
960
+ filter: brightness(1.03);
653
961
  }
654
962
 
655
- button:focus,
656
- button:focus-visible {
657
- outline: 4px auto -webkit-focus-ring-color;
963
+ .${rootClass} .counter-card button:active {
964
+ transform: translateY(0);
658
965
  }
659
966
 
660
- .info {
661
- color: #888;
662
- font-size: 0.9em;
663
- margin-top: 2rem;
967
+ .${rootClass} .counter-card button:focus-visible {
968
+ outline: 3px solid rgba(63, 185, 80, 0.35);
969
+ outline-offset: 3px;
664
970
  }
665
971
 
666
- code {
667
- background-color: #f0f0f0;
668
- padding: 0.2em 0.4em;
669
- border-radius: 3px;
670
- font-family: 'Courier New', monospace;
972
+ .${rootClass} .read-the-docs {
973
+ color: rgba(255, 255, 255, 0.58);
974
+ font-size: 12px;
975
+ text-wrap: balance;
976
+ }
977
+
978
+ .${rootClass} code {
979
+ background: rgba(255, 255, 255, 0.12);
980
+ padding: 2px 6px;
981
+ border-radius: 6px;
982
+ font-size: 11px;
983
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New',
984
+ monospace;
985
+ }
986
+
987
+ @keyframes logo-spin {
988
+ from {
989
+ transform: rotate(0deg);
990
+ }
991
+ to {
992
+ transform: rotate(360deg);
993
+ }
994
+ }
995
+
996
+ @media (prefers-reduced-motion: no-preference) {
997
+ .${rootClass} .logo.react {
998
+ animation: logo-spin infinite 20s linear;
999
+ }
671
1000
  }
672
1001
 
673
1002
  @media (prefers-color-scheme: dark) {
674
- .card {
675
- background-color: #2a2a2a;
1003
+ .${rootClass} h1 {
1004
+ color: #f8fafc;
676
1005
  }
677
1006
 
678
- code {
679
- background-color: #3a3a3a;
1007
+ .${rootClass} .logo.devto {
1008
+ border: 1px solid rgba(63, 185, 80, 0.35);
1009
+ box-shadow: 0 10px 26px rgba(2, 6, 23, 0.55);
1010
+ }
1011
+
1012
+ .${rootClass} .subtitle {
1013
+ color: rgba(226, 232, 240, 0.8);
1014
+ }
1015
+
1016
+ .${rootClass} .counter-card {
1017
+ background: rgba(255, 255, 255, 0.08);
1018
+ border-color: rgba(148, 163, 184, 0.25);
1019
+ }
1020
+
1021
+ .${rootClass} .counter-card p {
1022
+ color: rgba(226, 232, 240, 0.82);
1023
+ }
1024
+
1025
+ .${rootClass} .read-the-docs {
1026
+ color: rgba(148, 163, 184, 0.75);
1027
+ }
1028
+
1029
+ .${rootClass} code {
1030
+ background: rgba(148, 163, 184, 0.18);
1031
+ }
1032
+ }
1033
+
1034
+ @media (prefers-color-scheme: light) {
1035
+ .${rootClass} .subtitle {
1036
+ color: #4b5563;
1037
+ }
1038
+
1039
+ .${rootClass} .counter-card {
1040
+ background: #f8fafc;
1041
+ border-color: #e2e8f0;
1042
+ }
1043
+
1044
+ .${rootClass} .counter-card button {
1045
+ color: #ffffff;
1046
+ box-shadow: 0 10px 18px rgba(15, 23, 42, 0.18), inset 0 1px 0 rgba(255, 255, 255, 0.3);
1047
+ }
1048
+
1049
+ .${rootClass} .read-the-docs {
1050
+ color: #6b7280;
1051
+ }
1052
+
1053
+ .${rootClass} code {
1054
+ background: #e2e8f0;
1055
+ }
1056
+ }
1057
+
1058
+ @media (max-width: 360px) {
1059
+ .${rootClass} {
1060
+ padding: 18px 10px 24px;
1061
+ }
1062
+
1063
+ .${rootClass} .logo-row {
1064
+ gap: 8px;
1065
+ margin-bottom: 14px;
1066
+ }
1067
+
1068
+ .${rootClass} .logo {
1069
+ height: 40px;
1070
+ }
1071
+
1072
+ .${rootClass} h1 {
1073
+ font-size: 28px;
1074
+ }
1075
+
1076
+ .${rootClass} .subtitle {
1077
+ font-size: 12px;
1078
+ }
1079
+
1080
+ .${rootClass} .counter-card {
1081
+ padding: 18px 16px;
1082
+ }
1083
+
1084
+ .${rootClass} .counter-card button {
1085
+ padding: 8px 20px;
1086
+ font-size: 13px;
1087
+ }
1088
+
1089
+ .${rootClass} .read-the-docs {
1090
+ font-size: 11px;
680
1091
  }
681
1092
  }
682
1093
  `;
@@ -891,6 +1302,9 @@ For now, please use React or stay tuned for updates!`,
891
1302
  patched = updatePluginComponentName(patched, pluginName, componentName, projectName);
892
1303
  fs.writeFileSync(viteConfigPath, patched);
893
1304
  }
1305
+ spinner.message("Updating template files...");
1306
+ ensureDevtoLogo(root);
1307
+ updateAppTemplate(root, componentName, isTs);
894
1308
  spinner.message(`Creating component ${componentName}...`);
895
1309
  createComponentFile(root, componentName, isTs);
896
1310
  let completionMessage = "Project created";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dev-to",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {