@woodsportal/hubspot-kit 1.0.27 → 1.0.30
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/CHANGELOG.md +19 -0
- package/README.md +6 -6
- package/dist/{chunk-U4KFGTEW.js → chunk-S2YJDWZU.js} +5 -5
- package/dist/chunk-S2YJDWZU.js.map +1 -0
- package/dist/cli.js +486 -316
- package/dist/cli.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/vite/index.js +1 -1
- package/examples/greenfield/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +4 -4
- package/examples/greenfield/module-monolith/src/features/starter/content/woodscli-guide-content.ts +4 -4
- package/examples/smoke/run-matrix.mjs +1 -1
- package/package.json +1 -1
- package/scripts/detect-layout.mjs +71 -4
- package/scripts/run-hubspot-module-postbuild.mjs +26 -3
- package/templates/module-hybrid-cdn/src/features/starter/content/woodscli-guide-content.ts +4 -4
- package/templates/module-monolith/src/features/starter/content/woodscli-guide-content.ts +4 -4
- package/templates/shared/.env.prod.example +1 -1
- package/dist/chunk-U4KFGTEW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "./chunk-GTTIJVAR.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/program.ts
|
|
12
|
-
import { readFileSync as
|
|
13
|
-
import { resolve as
|
|
12
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
13
|
+
import { resolve as resolve23 } from "path";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
|
|
16
16
|
// src/lib/config.ts
|
|
@@ -185,14 +185,14 @@ function getKitRoot() {
|
|
|
185
185
|
return kitRoot;
|
|
186
186
|
}
|
|
187
187
|
function findProjectRoot2(from = process.cwd()) {
|
|
188
|
-
const
|
|
189
|
-
if (!
|
|
188
|
+
const resolved = findProjectRoot(from);
|
|
189
|
+
if (!resolved) {
|
|
190
190
|
throw new WpHsError("Could not find woodscli.json in this directory or any parent.", {
|
|
191
191
|
exitCode: ExitCode.USER_ERROR,
|
|
192
|
-
remediation: "Run wp init,
|
|
192
|
+
remediation: "Run wp init, or pass --project <path>. Legacy config: wp migrate --yes."
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
|
-
return
|
|
195
|
+
return resolved;
|
|
196
196
|
}
|
|
197
197
|
function kitPath(...segments) {
|
|
198
198
|
return resolve2(kitRoot, ...segments);
|
|
@@ -205,7 +205,7 @@ function loadConfig(projectRoot) {
|
|
|
205
205
|
if (!resolved) {
|
|
206
206
|
throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {
|
|
207
207
|
exitCode: ExitCode.USER_ERROR,
|
|
208
|
-
remediation: "Run wp init
|
|
208
|
+
remediation: "Run wp init. Legacy woodsportal.hs.json \u2192 run wp migrate --yes."
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
let raw;
|
|
@@ -293,8 +293,8 @@ function defaultBaselineDir(projectRoot, config) {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
// src/cli/commands/doctor.ts
|
|
296
|
-
import { existsSync as
|
|
297
|
-
import { join, resolve as
|
|
296
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
297
|
+
import { join, resolve as resolve6 } from "path";
|
|
298
298
|
|
|
299
299
|
// src/lib/cli-spinner.ts
|
|
300
300
|
import * as p from "@clack/prompts";
|
|
@@ -520,7 +520,7 @@ var VITE_DEV_PEER_DEPS = {
|
|
|
520
520
|
tailwindcss: "^4.0.6"
|
|
521
521
|
};
|
|
522
522
|
var KIT_DEV_DEP = "@woodsportal/hubspot-kit";
|
|
523
|
-
var KIT_DEV_DEP_VERSION = "^1.0.
|
|
523
|
+
var KIT_DEV_DEP_VERSION = "^1.0.30";
|
|
524
524
|
var MODULE_CONFIG_UI_DEPS = [
|
|
525
525
|
"@dnd-kit/core",
|
|
526
526
|
"@dnd-kit/sortable",
|
|
@@ -533,6 +533,16 @@ function templateDevDependencies() {
|
|
|
533
533
|
};
|
|
534
534
|
}
|
|
535
535
|
|
|
536
|
+
// src/lib/upload-only-module.ts
|
|
537
|
+
import { existsSync as existsSync4 } from "fs";
|
|
538
|
+
import { resolve as resolve5 } from "path";
|
|
539
|
+
function isUploadOnlyPreexistingProject(projectRoot, moduleArtifactDirs) {
|
|
540
|
+
if (moduleArtifactDirs.length === 0) return false;
|
|
541
|
+
if (existsSync4(resolve5(projectRoot, "package.json"))) return false;
|
|
542
|
+
if (existsSync4(resolve5(projectRoot, "src"))) return false;
|
|
543
|
+
return moduleArtifactDirs.some((dir) => existsSync4(resolve5(dir, "fields.json")));
|
|
544
|
+
}
|
|
545
|
+
|
|
536
546
|
// src/cli/commands/doctor.ts
|
|
537
547
|
function checkNode() {
|
|
538
548
|
const major = Number(process.versions.node.split(".")[0]);
|
|
@@ -598,8 +608,8 @@ function checkConfig(ctx) {
|
|
|
598
608
|
function checkEnvFiles(ctx) {
|
|
599
609
|
return ctx.config.env.tiers.map((tier) => {
|
|
600
610
|
const file = tier === "dev" ? ".env.dev" : tier === "stg" ? ".env.stg" : ".env.prod";
|
|
601
|
-
const path =
|
|
602
|
-
const ok =
|
|
611
|
+
const path = resolve6(ctx.projectRoot, file);
|
|
612
|
+
const ok = existsSync5(path);
|
|
603
613
|
return {
|
|
604
614
|
name: `env-${tier}`,
|
|
605
615
|
ok,
|
|
@@ -611,8 +621,8 @@ function checkEnvFiles(ctx) {
|
|
|
611
621
|
}
|
|
612
622
|
function checkBuildId(ctx) {
|
|
613
623
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
614
|
-
const file =
|
|
615
|
-
const ok =
|
|
624
|
+
const file = resolve6(ctx.projectRoot, ctx.config.cdn.buildIdFile);
|
|
625
|
+
const ok = existsSync5(file);
|
|
616
626
|
return {
|
|
617
627
|
name: "build-id",
|
|
618
628
|
ok,
|
|
@@ -621,9 +631,23 @@ function checkBuildId(ctx) {
|
|
|
621
631
|
severity: "warn"
|
|
622
632
|
};
|
|
623
633
|
}
|
|
634
|
+
function isUploadOnlyProject(ctx) {
|
|
635
|
+
const moduleDirs = [resolve6(ctx.projectRoot, ctx.config.module.uploadPath)];
|
|
636
|
+
return isUploadOnlyPreexistingProject(ctx.projectRoot, moduleDirs);
|
|
637
|
+
}
|
|
638
|
+
function checkUploadOnlyNote(ctx) {
|
|
639
|
+
if (!isUploadOnlyProject(ctx)) return null;
|
|
640
|
+
return {
|
|
641
|
+
name: "upload-only-module",
|
|
642
|
+
ok: true,
|
|
643
|
+
message: "Upload-only module (no local dev app)",
|
|
644
|
+
remediation: "Add package.json + src/ via wp init in a new folder, or continue upload-only with wp upload",
|
|
645
|
+
severity: "warn"
|
|
646
|
+
};
|
|
647
|
+
}
|
|
624
648
|
function checkDevViteConfig(ctx) {
|
|
625
|
-
const path =
|
|
626
|
-
const ok =
|
|
649
|
+
const path = resolve6(ctx.projectRoot, "vite.config.dev.mjs");
|
|
650
|
+
const ok = existsSync5(path);
|
|
627
651
|
return {
|
|
628
652
|
name: "vite-dev-config",
|
|
629
653
|
ok,
|
|
@@ -634,8 +658,8 @@ function checkDevViteConfig(ctx) {
|
|
|
634
658
|
}
|
|
635
659
|
function checkDevEntry(ctx) {
|
|
636
660
|
const checks = [];
|
|
637
|
-
const indexHtml =
|
|
638
|
-
const hasIndex =
|
|
661
|
+
const indexHtml = resolve6(ctx.projectRoot, "index.html");
|
|
662
|
+
const hasIndex = existsSync5(indexHtml);
|
|
639
663
|
checks.push({
|
|
640
664
|
name: "index-html",
|
|
641
665
|
ok: hasIndex,
|
|
@@ -643,8 +667,8 @@ function checkDevEntry(ctx) {
|
|
|
643
667
|
remediation: hasIndex ? void 0 : "Re-run wp init or copy index.html from kit templates",
|
|
644
668
|
severity: "error"
|
|
645
669
|
});
|
|
646
|
-
const localDev =
|
|
647
|
-
const hasEntry =
|
|
670
|
+
const localDev = resolve6(ctx.projectRoot, ctx.config.entries.localDev);
|
|
671
|
+
const hasEntry = existsSync5(localDev);
|
|
648
672
|
checks.push({
|
|
649
673
|
name: "local-dev-entry",
|
|
650
674
|
ok: hasEntry,
|
|
@@ -671,7 +695,7 @@ function checkDevEntry(ctx) {
|
|
|
671
695
|
}
|
|
672
696
|
function checkKitScripts() {
|
|
673
697
|
const script = kitPath("scripts", "build-cdn.mjs");
|
|
674
|
-
const ok =
|
|
698
|
+
const ok = existsSync5(script);
|
|
675
699
|
return {
|
|
676
700
|
name: "kit-scripts",
|
|
677
701
|
ok,
|
|
@@ -684,8 +708,8 @@ function checkWphsWorkspace(ctx) {
|
|
|
684
708
|
const workspace = ctx.config.dev.moduleConfigDir;
|
|
685
709
|
const checks = [];
|
|
686
710
|
for (const legacy of [".dev", ".woodsportal"]) {
|
|
687
|
-
const legacyPath =
|
|
688
|
-
if (
|
|
711
|
+
const legacyPath = resolve6(ctx.projectRoot, legacy);
|
|
712
|
+
if (existsSync5(legacyPath)) {
|
|
689
713
|
checks.push({
|
|
690
714
|
name: `legacy-${legacy}`,
|
|
691
715
|
ok: false,
|
|
@@ -695,7 +719,7 @@ function checkWphsWorkspace(ctx) {
|
|
|
695
719
|
});
|
|
696
720
|
}
|
|
697
721
|
}
|
|
698
|
-
if (
|
|
722
|
+
if (existsSync5(resolve6(ctx.projectRoot, ".wp-hs-baseline"))) {
|
|
699
723
|
checks.push({
|
|
700
724
|
name: "legacy-baseline",
|
|
701
725
|
ok: false,
|
|
@@ -714,10 +738,10 @@ function checkWphsWorkspace(ctx) {
|
|
|
714
738
|
});
|
|
715
739
|
return checks;
|
|
716
740
|
}
|
|
717
|
-
const wphsRoot =
|
|
741
|
+
const wphsRoot = resolve6(ctx.projectRoot, workspace);
|
|
718
742
|
const required = ["config", "assets/branding", "fixtures", "cache", "audit/baseline", "state"];
|
|
719
743
|
for (const sub of required) {
|
|
720
|
-
const ok =
|
|
744
|
+
const ok = existsSync5(join(wphsRoot, sub));
|
|
721
745
|
checks.push({
|
|
722
746
|
name: `wphs-${sub.replace(/\//g, "-")}`,
|
|
723
747
|
ok,
|
|
@@ -730,20 +754,20 @@ function checkWphsWorkspace(ctx) {
|
|
|
730
754
|
}
|
|
731
755
|
function checkAdapter(ctx) {
|
|
732
756
|
if (isThemeProject(ctx.config)) return null;
|
|
733
|
-
const adapterPath =
|
|
734
|
-
const ok =
|
|
757
|
+
const adapterPath = resolve6(ctx.projectRoot, ctx.config.dev.adapter);
|
|
758
|
+
const ok = existsSync5(adapterPath);
|
|
735
759
|
return {
|
|
736
760
|
name: "wphs-adapter",
|
|
737
761
|
ok,
|
|
738
762
|
message: ok ? `${ctx.config.dev.adapter} present` : `${ctx.config.dev.adapter} missing`,
|
|
739
|
-
remediation: ok ? void 0 : "
|
|
763
|
+
remediation: ok ? void 0 : "Create wphs.adapter.ts (see kit templates)",
|
|
740
764
|
severity: "warn"
|
|
741
765
|
};
|
|
742
766
|
}
|
|
743
767
|
function checkModuleConfigUiDeps(ctx) {
|
|
744
768
|
if (isThemeProject(ctx.config)) return [];
|
|
745
|
-
const pkgPath =
|
|
746
|
-
if (!
|
|
769
|
+
const pkgPath = resolve6(ctx.projectRoot, "package.json");
|
|
770
|
+
if (!existsSync5(pkgPath)) return [];
|
|
747
771
|
let deps = {};
|
|
748
772
|
try {
|
|
749
773
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
|
|
@@ -763,11 +787,11 @@ function checkModuleConfigUiDeps(ctx) {
|
|
|
763
787
|
});
|
|
764
788
|
}
|
|
765
789
|
function checkViteInstalled(ctx) {
|
|
766
|
-
if (isThemeProject(ctx.config) && !
|
|
790
|
+
if (isThemeProject(ctx.config) && !existsSync5(resolve6(ctx.projectRoot, "vite.config.dev.mjs"))) {
|
|
767
791
|
return null;
|
|
768
792
|
}
|
|
769
|
-
const localBin =
|
|
770
|
-
const ok =
|
|
793
|
+
const localBin = resolve6(ctx.projectRoot, "node_modules", ".bin", "vite");
|
|
794
|
+
const ok = existsSync5(localBin);
|
|
771
795
|
return {
|
|
772
796
|
name: "vite",
|
|
773
797
|
ok,
|
|
@@ -796,10 +820,16 @@ async function gatherDoctorChecks(ctx) {
|
|
|
796
820
|
{
|
|
797
821
|
title: "Checking Node, Vite, and kit scripts",
|
|
798
822
|
task: async () => {
|
|
799
|
-
checks.push(checkNode(), checkKitScripts()
|
|
800
|
-
const
|
|
801
|
-
if (
|
|
802
|
-
|
|
823
|
+
checks.push(checkNode(), checkKitScripts());
|
|
824
|
+
const uploadOnlyNote = checkUploadOnlyNote(ctx);
|
|
825
|
+
if (uploadOnlyNote) {
|
|
826
|
+
checks.push(uploadOnlyNote);
|
|
827
|
+
} else {
|
|
828
|
+
checks.push(checkDevViteConfig(ctx), ...checkDevEntry(ctx));
|
|
829
|
+
const viteCheck = checkViteInstalled(ctx);
|
|
830
|
+
if (viteCheck) checks.push(viteCheck);
|
|
831
|
+
checks.push(...checkModuleConfigUiDeps(ctx));
|
|
832
|
+
}
|
|
803
833
|
}
|
|
804
834
|
},
|
|
805
835
|
{
|
|
@@ -900,7 +930,7 @@ async function runConfigValidate(ctx) {
|
|
|
900
930
|
}
|
|
901
931
|
|
|
902
932
|
// src/cli/commands/build.ts
|
|
903
|
-
import { resolve as
|
|
933
|
+
import { resolve as resolve7 } from "path";
|
|
904
934
|
|
|
905
935
|
// src/lib/build-tier.ts
|
|
906
936
|
var DEFAULT_BUILD_TIER = "dev";
|
|
@@ -926,7 +956,7 @@ function buildTasks(ctx, options, run) {
|
|
|
926
956
|
title: `Building monolith module (${tier})`,
|
|
927
957
|
task: async () => {
|
|
928
958
|
run("run-vite-build.mjs", [
|
|
929
|
-
|
|
959
|
+
resolve7(ctx.projectRoot, "vite.config.monolith.mjs"),
|
|
930
960
|
...tierToStgFlag(tier)
|
|
931
961
|
]);
|
|
932
962
|
run("run-hubspot-module-postbuild.mjs");
|
|
@@ -940,7 +970,7 @@ function buildTasks(ctx, options, run) {
|
|
|
940
970
|
task: async () => {
|
|
941
971
|
if (options.cdnEsm) {
|
|
942
972
|
run("run-vite-build.mjs", [
|
|
943
|
-
|
|
973
|
+
resolve7(ctx.projectRoot, "vite.config.cdn-esm.mjs"),
|
|
944
974
|
...tierToStgFlag(tier)
|
|
945
975
|
]);
|
|
946
976
|
} else {
|
|
@@ -955,7 +985,7 @@ function buildTasks(ctx, options, run) {
|
|
|
955
985
|
title: `Building HubSpot module (${tier})`,
|
|
956
986
|
task: async () => {
|
|
957
987
|
run("run-vite-build.mjs", [
|
|
958
|
-
|
|
988
|
+
resolve7(ctx.projectRoot, "vite.config.hubspot.mjs"),
|
|
959
989
|
...tierToStgFlag(tier)
|
|
960
990
|
]);
|
|
961
991
|
run("run-hubspot-module-postbuild.mjs");
|
|
@@ -1011,7 +1041,7 @@ async function runBuild(ctx, options) {
|
|
|
1011
1041
|
command: "build",
|
|
1012
1042
|
tier,
|
|
1013
1043
|
ok: true,
|
|
1014
|
-
outDir:
|
|
1044
|
+
outDir: resolve7(ctx.projectRoot, ctx.config.module.outDir)
|
|
1015
1045
|
});
|
|
1016
1046
|
} else {
|
|
1017
1047
|
printCommandDone(ctx, "Build complete");
|
|
@@ -1025,8 +1055,8 @@ async function runBuild(ctx, options) {
|
|
|
1025
1055
|
}
|
|
1026
1056
|
|
|
1027
1057
|
// src/cli/commands/dev.ts
|
|
1028
|
-
import { existsSync as
|
|
1029
|
-
import { resolve as
|
|
1058
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1059
|
+
import { resolve as resolve10 } from "path";
|
|
1030
1060
|
import { spawn } from "child_process";
|
|
1031
1061
|
|
|
1032
1062
|
// src/lib/cli-banner.ts
|
|
@@ -1034,14 +1064,14 @@ import pc3 from "picocolors";
|
|
|
1034
1064
|
|
|
1035
1065
|
// src/lib/kit-version.ts
|
|
1036
1066
|
import { readFileSync as readFileSync5 } from "fs";
|
|
1037
|
-
import { resolve as
|
|
1067
|
+
import { resolve as resolve8 } from "path";
|
|
1038
1068
|
var cachedVersion = null;
|
|
1039
1069
|
function readKitVersion() {
|
|
1040
1070
|
if (cachedVersion) {
|
|
1041
1071
|
return cachedVersion;
|
|
1042
1072
|
}
|
|
1043
1073
|
try {
|
|
1044
|
-
const pkg = JSON.parse(readFileSync5(
|
|
1074
|
+
const pkg = JSON.parse(readFileSync5(resolve8(getKitRoot(), "package.json"), "utf8"));
|
|
1045
1075
|
cachedVersion = pkg.version ?? "0.0.0";
|
|
1046
1076
|
} catch {
|
|
1047
1077
|
cachedVersion = "0.0.0";
|
|
@@ -1079,7 +1109,7 @@ function formatProjectLabel(config) {
|
|
|
1079
1109
|
return `${config.module.name} \xB7 ${config.projectType}${routerNote}`;
|
|
1080
1110
|
}
|
|
1081
1111
|
function section(color, title, body) {
|
|
1082
|
-
const bold = (
|
|
1112
|
+
const bold = (text3) => color ? pc3.bold(text3) : text3;
|
|
1083
1113
|
return ` ${bold(title)}
|
|
1084
1114
|
${body}`;
|
|
1085
1115
|
}
|
|
@@ -1088,10 +1118,10 @@ function printWoodsCliDevBanner(options) {
|
|
|
1088
1118
|
return;
|
|
1089
1119
|
}
|
|
1090
1120
|
const color = useCliColor();
|
|
1091
|
-
const cyan = (
|
|
1092
|
-
const dim = (
|
|
1093
|
-
const bold = (
|
|
1094
|
-
const yellow = (
|
|
1121
|
+
const cyan = (text3) => color ? pc3.cyan(text3) : text3;
|
|
1122
|
+
const dim = (text3) => color ? pc3.dim(text3) : text3;
|
|
1123
|
+
const bold = (text3) => color ? pc3.bold(text3) : text3;
|
|
1124
|
+
const yellow = (text3) => color ? pc3.yellow(text3) : text3;
|
|
1095
1125
|
const version = options.version ?? readKitVersion();
|
|
1096
1126
|
const project = formatProjectLabel(options.config);
|
|
1097
1127
|
const isTheme = isThemeProject(options.config);
|
|
@@ -1150,19 +1180,19 @@ ${tips}
|
|
|
1150
1180
|
}
|
|
1151
1181
|
|
|
1152
1182
|
// src/vite/resolve-dev-config.ts
|
|
1153
|
-
import { existsSync as
|
|
1154
|
-
import { resolve as
|
|
1183
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1184
|
+
import { resolve as resolve9 } from "path";
|
|
1155
1185
|
var CONSUMER_DEV_CONFIG = "vite.config.dev.mjs";
|
|
1156
1186
|
function resolveDevViteConfigPath(projectRoot) {
|
|
1157
|
-
const consumerConfig =
|
|
1158
|
-
if (
|
|
1187
|
+
const consumerConfig = resolve9(projectRoot, CONSUMER_DEV_CONFIG);
|
|
1188
|
+
if (existsSync6(consumerConfig)) {
|
|
1159
1189
|
return consumerConfig;
|
|
1160
1190
|
}
|
|
1161
1191
|
return kitPath("vite", "vite.config.dev.mjs");
|
|
1162
1192
|
}
|
|
1163
1193
|
function resolveProjectViteBin(projectRoot) {
|
|
1164
|
-
const localBin =
|
|
1165
|
-
if (
|
|
1194
|
+
const localBin = resolve9(projectRoot, "node_modules", ".bin", "vite");
|
|
1195
|
+
if (existsSync6(localBin)) {
|
|
1166
1196
|
return localBin;
|
|
1167
1197
|
}
|
|
1168
1198
|
return "vite";
|
|
@@ -1187,8 +1217,8 @@ async function runDev(ctx, options) {
|
|
|
1187
1217
|
remediation: "Run yarn install or npm install. If this is an older wp init project, add vite and peers to devDependencies (run wp init again or see @woodsportal/hubspot-kit templates)."
|
|
1188
1218
|
});
|
|
1189
1219
|
}
|
|
1190
|
-
const consumerConfig =
|
|
1191
|
-
if (!
|
|
1220
|
+
const consumerConfig = resolve10(ctx.projectRoot, "vite.config.dev.mjs");
|
|
1221
|
+
if (!existsSync7(consumerConfig)) {
|
|
1192
1222
|
throw new WpHsError("vite.config.dev.mjs is missing in the project root.", {
|
|
1193
1223
|
exitCode: ExitCode.USER_ERROR,
|
|
1194
1224
|
remediation: "Add vite.config.dev.mjs (see @woodsportal/hubspot-kit templates/module-hybrid-cdn)."
|
|
@@ -1217,7 +1247,76 @@ async function runDev(ctx, options) {
|
|
|
1217
1247
|
}
|
|
1218
1248
|
|
|
1219
1249
|
// src/cli/commands/deploy.ts
|
|
1250
|
+
import { resolve as resolve13 } from "path";
|
|
1251
|
+
|
|
1252
|
+
// src/lib/project-env-files.ts
|
|
1253
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync } from "fs";
|
|
1220
1254
|
import { resolve as resolve11 } from "path";
|
|
1255
|
+
var ENV_LOCAL_FILE = ".env.local";
|
|
1256
|
+
var ENV_GITIGNORE_LINES = [".env", ".env.*", "!.env*.example", ENV_LOCAL_FILE];
|
|
1257
|
+
function parseEnvFile(content) {
|
|
1258
|
+
const result = {};
|
|
1259
|
+
for (const line of content.split("\n")) {
|
|
1260
|
+
const trimmed = line.trim();
|
|
1261
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1262
|
+
const eq = trimmed.indexOf("=");
|
|
1263
|
+
if (eq <= 0) continue;
|
|
1264
|
+
const key = trimmed.slice(0, eq).trim();
|
|
1265
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
1266
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1267
|
+
value = value.slice(1, -1);
|
|
1268
|
+
}
|
|
1269
|
+
result[key] = value;
|
|
1270
|
+
}
|
|
1271
|
+
return result;
|
|
1272
|
+
}
|
|
1273
|
+
function readEnvLocal(projectRoot) {
|
|
1274
|
+
const path = resolve11(projectRoot, ENV_LOCAL_FILE);
|
|
1275
|
+
if (!existsSync8(path)) return {};
|
|
1276
|
+
return parseEnvFile(readFileSync6(path, "utf8"));
|
|
1277
|
+
}
|
|
1278
|
+
function formatEnvLocal(entries) {
|
|
1279
|
+
const lines = [
|
|
1280
|
+
"# Local secrets \u2014 gitignored. Created by wp init / wp setup (hybrid CDN).",
|
|
1281
|
+
"# PORTAL_CDN_PUBLISH_TOKEN needs repo push on your portal.cdn.json mirror.",
|
|
1282
|
+
""
|
|
1283
|
+
];
|
|
1284
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
1285
|
+
if (value.includes(" ") || value.includes("#")) {
|
|
1286
|
+
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
1287
|
+
} else {
|
|
1288
|
+
lines.push(`${key}=${value}`);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return `${lines.join("\n").trimEnd()}
|
|
1292
|
+
`;
|
|
1293
|
+
}
|
|
1294
|
+
function writeEnvLocal(projectRoot, entries) {
|
|
1295
|
+
const path = resolve11(projectRoot, ENV_LOCAL_FILE);
|
|
1296
|
+
const existing = existsSync8(path) ? parseEnvFile(readFileSync6(path, "utf8")) : {};
|
|
1297
|
+
writeFileSync(path, formatEnvLocal({ ...existing, ...entries }));
|
|
1298
|
+
}
|
|
1299
|
+
function mergeProjectEnv(projectRoot, base = process.env) {
|
|
1300
|
+
const local = readEnvLocal(projectRoot);
|
|
1301
|
+
const merged = { ...base };
|
|
1302
|
+
for (const [key, value] of Object.entries(local)) {
|
|
1303
|
+
if (merged[key] == null || merged[key] === "") {
|
|
1304
|
+
merged[key] = value;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return merged;
|
|
1308
|
+
}
|
|
1309
|
+
function ensureEnvFilesGitignore(projectRoot) {
|
|
1310
|
+
const gitignorePath = resolve11(projectRoot, ".gitignore");
|
|
1311
|
+
const existing = existsSync8(gitignorePath) ? readFileSync6(gitignorePath, "utf8") : "";
|
|
1312
|
+
const toAppend = ENV_GITIGNORE_LINES.filter((line) => !existing.includes(line)).join("\n");
|
|
1313
|
+
if (!toAppend) return;
|
|
1314
|
+
writeFileSync(gitignorePath, `${existing.trimEnd()}
|
|
1315
|
+
|
|
1316
|
+
# Env files (secrets)
|
|
1317
|
+
${toAppend}
|
|
1318
|
+
`);
|
|
1319
|
+
}
|
|
1221
1320
|
|
|
1222
1321
|
// src/cli/commands/publish.ts
|
|
1223
1322
|
async function runPublish(ctx, options) {
|
|
@@ -1228,7 +1327,10 @@ async function runPublish(ctx, options) {
|
|
|
1228
1327
|
});
|
|
1229
1328
|
}
|
|
1230
1329
|
const tier = options.tier ?? DEFAULT_BUILD_TIER;
|
|
1231
|
-
const env =
|
|
1330
|
+
const env = mergeProjectEnv(ctx.projectRoot, {
|
|
1331
|
+
...process.env,
|
|
1332
|
+
WP_HS_PROJECT_ROOT: ctx.projectRoot
|
|
1333
|
+
});
|
|
1232
1334
|
const execOpts = {
|
|
1233
1335
|
cwd: ctx.projectRoot,
|
|
1234
1336
|
env,
|
|
@@ -1275,14 +1377,14 @@ async function runPublish(ctx, options) {
|
|
|
1275
1377
|
}
|
|
1276
1378
|
|
|
1277
1379
|
// src/cli/commands/upload.ts
|
|
1278
|
-
import { resolve as
|
|
1380
|
+
import { resolve as resolve12 } from "path";
|
|
1279
1381
|
async function uploadModuleOrTheme(ctx) {
|
|
1280
1382
|
if (isThemeProject(ctx.config)) {
|
|
1281
1383
|
const theme = ctx.config.theme;
|
|
1282
1384
|
if (!theme) {
|
|
1283
1385
|
throw new WpHsError("theme block missing in woodscli.json", { exitCode: ExitCode.USER_ERROR });
|
|
1284
1386
|
}
|
|
1285
|
-
const srcDir =
|
|
1387
|
+
const srcDir = resolve12(ctx.projectRoot, theme.srcDir);
|
|
1286
1388
|
runCommand("hs", ["cms", "upload", srcDir, theme.uploadPath], {
|
|
1287
1389
|
cwd: ctx.projectRoot,
|
|
1288
1390
|
logger: ctx.logger,
|
|
@@ -1290,7 +1392,7 @@ async function uploadModuleOrTheme(ctx) {
|
|
|
1290
1392
|
});
|
|
1291
1393
|
return `Uploaded theme ${srcDir} \u2192 ${theme.uploadPath}`;
|
|
1292
1394
|
}
|
|
1293
|
-
const outDir =
|
|
1395
|
+
const outDir = resolve12(ctx.projectRoot, ctx.config.module.outDir);
|
|
1294
1396
|
const uploadPath = ctx.config.module.uploadPath;
|
|
1295
1397
|
runCommand("hs", ["cms", "upload", outDir, uploadPath], {
|
|
1296
1398
|
cwd: ctx.projectRoot,
|
|
@@ -1402,7 +1504,7 @@ async function runDeploy(ctx, options) {
|
|
|
1402
1504
|
if (!ctx.global.json) {
|
|
1403
1505
|
printCommandDone(
|
|
1404
1506
|
ctx,
|
|
1405
|
-
`Deploy complete (${
|
|
1507
|
+
`Deploy complete (${resolve13(ctx.projectRoot, ctx.config.module.outDir)} \u2192 ${ctx.config.module.uploadPath})`
|
|
1406
1508
|
);
|
|
1407
1509
|
}
|
|
1408
1510
|
return ExitCode.SUCCESS;
|
|
@@ -1410,8 +1512,8 @@ async function runDeploy(ctx, options) {
|
|
|
1410
1512
|
|
|
1411
1513
|
// src/cli/commands/audit.ts
|
|
1412
1514
|
import { createHash } from "crypto";
|
|
1413
|
-
import { cpSync, existsSync as
|
|
1414
|
-
import { resolve as
|
|
1515
|
+
import { cpSync, existsSync as existsSync9, mkdirSync, readFileSync as readFileSync7, readdirSync, rmSync, statSync } from "fs";
|
|
1516
|
+
import { resolve as resolve14, join as join2 } from "path";
|
|
1415
1517
|
var FORBIDDEN_SHIP_TOKENS = [
|
|
1416
1518
|
"ModuleConfigOverlay",
|
|
1417
1519
|
"module-config-store",
|
|
@@ -1420,10 +1522,10 @@ var FORBIDDEN_SHIP_TOKENS = [
|
|
|
1420
1522
|
"dev.module-config"
|
|
1421
1523
|
];
|
|
1422
1524
|
function hashFile(path) {
|
|
1423
|
-
return createHash("sha256").update(
|
|
1525
|
+
return createHash("sha256").update(readFileSync7(path)).digest("hex");
|
|
1424
1526
|
}
|
|
1425
1527
|
function walkFiles(dir) {
|
|
1426
|
-
if (!
|
|
1528
|
+
if (!existsSync9(dir)) return [];
|
|
1427
1529
|
const entries = readdirSync(dir);
|
|
1428
1530
|
const files = [];
|
|
1429
1531
|
for (const entry of entries) {
|
|
@@ -1439,14 +1541,14 @@ function walkFiles(dir) {
|
|
|
1439
1541
|
async function runAuditShipBundle(ctx) {
|
|
1440
1542
|
const root = ctx.projectRoot;
|
|
1441
1543
|
const targets = [
|
|
1442
|
-
|
|
1443
|
-
|
|
1544
|
+
resolve14(root, "dist/cdn"),
|
|
1545
|
+
resolve14(root, ctx.config.module.outDir)
|
|
1444
1546
|
];
|
|
1445
1547
|
const violations = [];
|
|
1446
1548
|
for (const dir of targets) {
|
|
1447
1549
|
for (const file of walkFiles(dir)) {
|
|
1448
1550
|
if (!/\.(js|css|html)$/i.test(file)) continue;
|
|
1449
|
-
const content =
|
|
1551
|
+
const content = readFileSync7(file, "utf8");
|
|
1450
1552
|
for (const token of FORBIDDEN_SHIP_TOKENS) {
|
|
1451
1553
|
if (content.includes(token)) {
|
|
1452
1554
|
violations.push(`${file}: contains forbidden token "${token}"`);
|
|
@@ -1473,21 +1575,21 @@ async function runAuditBaselineCapture(ctx, options) {
|
|
|
1473
1575
|
const root = ctx.projectRoot;
|
|
1474
1576
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1475
1577
|
const sources = [
|
|
1476
|
-
{ label: "cdn", path:
|
|
1477
|
-
{ label: "hubspot", path:
|
|
1578
|
+
{ label: "cdn", path: resolve14(root, "dist/cdn") },
|
|
1579
|
+
{ label: "hubspot", path: resolve14(root, ctx.config.module.outDir) }
|
|
1478
1580
|
];
|
|
1479
1581
|
if (ctx.global.dryRun) {
|
|
1480
1582
|
ctx.logger.info(`[dry-run] Would capture baseline to ${baseline}`);
|
|
1481
1583
|
return ExitCode.SUCCESS;
|
|
1482
1584
|
}
|
|
1483
1585
|
for (const { label, path } of sources) {
|
|
1484
|
-
if (!
|
|
1586
|
+
if (!existsSync9(path)) {
|
|
1485
1587
|
throw new WpHsError(`Cannot capture baseline \u2014 missing ${path}`, {
|
|
1486
1588
|
exitCode: ExitCode.USER_ERROR,
|
|
1487
1589
|
remediation: "Run wp build before wp audit baseline --capture."
|
|
1488
1590
|
});
|
|
1489
1591
|
}
|
|
1490
|
-
const target =
|
|
1592
|
+
const target = resolve14(baseline, label);
|
|
1491
1593
|
rmSync(target, { recursive: true, force: true });
|
|
1492
1594
|
mkdirSync(baseline, { recursive: true });
|
|
1493
1595
|
cpSync(path, target, { recursive: true });
|
|
@@ -1503,13 +1605,13 @@ async function runAuditParity(ctx, options) {
|
|
|
1503
1605
|
const root = ctx.projectRoot;
|
|
1504
1606
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1505
1607
|
const currentDirs = [
|
|
1506
|
-
{ label: "cdn", path:
|
|
1507
|
-
{ label: "hubspot", path:
|
|
1608
|
+
{ label: "cdn", path: resolve14(root, "dist/cdn") },
|
|
1609
|
+
{ label: "hubspot", path: resolve14(root, ctx.config.module.outDir) }
|
|
1508
1610
|
];
|
|
1509
1611
|
const mismatches = [];
|
|
1510
1612
|
for (const { label, path } of currentDirs) {
|
|
1511
|
-
const baselinePath =
|
|
1512
|
-
if (!
|
|
1613
|
+
const baselinePath = resolve14(baseline, label);
|
|
1614
|
+
if (!existsSync9(baselinePath)) {
|
|
1513
1615
|
ctx.logger.warn(`No baseline at ${baselinePath} \u2014 skipping ${label} parity`);
|
|
1514
1616
|
continue;
|
|
1515
1617
|
}
|
|
@@ -1524,8 +1626,8 @@ async function runAuditParity(ctx, options) {
|
|
|
1524
1626
|
}
|
|
1525
1627
|
for (const file of baselineFiles) {
|
|
1526
1628
|
const rel = file.replace(baselinePath, "");
|
|
1527
|
-
const current =
|
|
1528
|
-
if (!
|
|
1629
|
+
const current = resolve14(path, rel.slice(1));
|
|
1630
|
+
if (!existsSync9(current)) continue;
|
|
1529
1631
|
const bh = hashFile(file);
|
|
1530
1632
|
const ch = hashFile(current);
|
|
1531
1633
|
if (bh !== ch) {
|
|
@@ -1552,13 +1654,13 @@ var SECRET_PATTERNS = [
|
|
|
1552
1654
|
];
|
|
1553
1655
|
async function runAuditSecrets(ctx) {
|
|
1554
1656
|
const root = ctx.projectRoot;
|
|
1555
|
-
const scanRoots = ["src", "public", ".wphs/config"].map((d) =>
|
|
1657
|
+
const scanRoots = ["src", "public", ".wphs/config"].map((d) => resolve14(root, d));
|
|
1556
1658
|
const findings = [];
|
|
1557
1659
|
for (const dir of scanRoots) {
|
|
1558
1660
|
for (const file of walkFiles(dir)) {
|
|
1559
1661
|
if (!/\.(ts|tsx|js|jsx|json|env|yml|yaml|html)$/i.test(file)) continue;
|
|
1560
1662
|
if (file.includes("node_modules")) continue;
|
|
1561
|
-
const content =
|
|
1663
|
+
const content = readFileSync7(file, "utf8");
|
|
1562
1664
|
for (const pattern of SECRET_PATTERNS) {
|
|
1563
1665
|
pattern.lastIndex = 0;
|
|
1564
1666
|
if (pattern.test(content)) {
|
|
@@ -1584,13 +1686,13 @@ async function runAuditSecrets(ctx) {
|
|
|
1584
1686
|
}
|
|
1585
1687
|
|
|
1586
1688
|
// src/cli/commands/init.ts
|
|
1587
|
-
import { cpSync as cpSync2, existsSync as
|
|
1588
|
-
import { basename as basename2, resolve as
|
|
1589
|
-
import * as
|
|
1689
|
+
import { cpSync as cpSync2, existsSync as existsSync13, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
1690
|
+
import { basename as basename2, resolve as resolve18 } from "path";
|
|
1691
|
+
import * as p3 from "@clack/prompts";
|
|
1590
1692
|
|
|
1591
1693
|
// src/lib/init-router-scaffold.ts
|
|
1592
|
-
import { existsSync as
|
|
1593
|
-
import { resolve as
|
|
1694
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1695
|
+
import { resolve as resolve15 } from "path";
|
|
1594
1696
|
var ROUTER_FILES = [
|
|
1595
1697
|
"src/routes",
|
|
1596
1698
|
"src/routeTree.gen.ts",
|
|
@@ -1604,10 +1706,10 @@ function isModuleTemplate(template) {
|
|
|
1604
1706
|
return template === "module-hybrid-cdn" || template === "module-monolith";
|
|
1605
1707
|
}
|
|
1606
1708
|
function writeRouterConfig(targetDir, mode) {
|
|
1607
|
-
const configDir =
|
|
1709
|
+
const configDir = resolve15(targetDir, "src/config");
|
|
1608
1710
|
mkdirSync2(configDir, { recursive: true });
|
|
1609
|
-
|
|
1610
|
-
|
|
1711
|
+
writeFileSync2(
|
|
1712
|
+
resolve15(configDir, "router.ts"),
|
|
1611
1713
|
`/** Router history \u2014 set by \`wp init\`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
|
|
1612
1714
|
export type RouterHistoryMode = 'hash' | 'browser'
|
|
1613
1715
|
|
|
@@ -1616,8 +1718,8 @@ export const ROUTER_HISTORY: RouterHistoryMode = '${mode}'
|
|
|
1616
1718
|
);
|
|
1617
1719
|
}
|
|
1618
1720
|
function writeMountDevPreviewRouter(targetDir) {
|
|
1619
|
-
|
|
1620
|
-
|
|
1721
|
+
writeFileSync2(
|
|
1722
|
+
resolve15(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1621
1723
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1622
1724
|
|
|
1623
1725
|
import { App } from '@/app'
|
|
@@ -1630,8 +1732,8 @@ export async function mountDevPreview() {
|
|
|
1630
1732
|
);
|
|
1631
1733
|
}
|
|
1632
1734
|
function writeMountDevPreviewSinglePage(targetDir) {
|
|
1633
|
-
|
|
1634
|
-
|
|
1735
|
+
writeFileSync2(
|
|
1736
|
+
resolve15(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1635
1737
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1636
1738
|
|
|
1637
1739
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1644,8 +1746,8 @@ export async function mountDevPreview() {
|
|
|
1644
1746
|
);
|
|
1645
1747
|
}
|
|
1646
1748
|
function writeMountPortalRouter(targetDir) {
|
|
1647
|
-
|
|
1648
|
-
|
|
1749
|
+
writeFileSync2(
|
|
1750
|
+
resolve15(targetDir, "src/portal/mount-portal.tsx"),
|
|
1649
1751
|
`import ReactDOM from 'react-dom/client'
|
|
1650
1752
|
|
|
1651
1753
|
import { App } from '@/app'
|
|
@@ -1662,8 +1764,8 @@ export async function mountPortal() {
|
|
|
1662
1764
|
);
|
|
1663
1765
|
}
|
|
1664
1766
|
function writeMountPortalSinglePage(targetDir) {
|
|
1665
|
-
|
|
1666
|
-
|
|
1767
|
+
writeFileSync2(
|
|
1768
|
+
resolve15(targetDir, "src/portal/mount-portal.tsx"),
|
|
1667
1769
|
`import ReactDOM from 'react-dom/client'
|
|
1668
1770
|
|
|
1669
1771
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1680,11 +1782,11 @@ export async function mountPortal() {
|
|
|
1680
1782
|
);
|
|
1681
1783
|
}
|
|
1682
1784
|
function writeStarterSinglePage(targetDir) {
|
|
1683
|
-
const file =
|
|
1684
|
-
if (
|
|
1785
|
+
const file = resolve15(targetDir, "src/features/starter/components/starter-single-page.tsx");
|
|
1786
|
+
if (existsSync10(file)) {
|
|
1685
1787
|
return;
|
|
1686
1788
|
}
|
|
1687
|
-
|
|
1789
|
+
writeFileSync2(
|
|
1688
1790
|
file,
|
|
1689
1791
|
`import { StarterShell } from '@/components/layout/starter-shell'
|
|
1690
1792
|
|
|
@@ -1707,25 +1809,25 @@ export function StarterSinglePage() {
|
|
|
1707
1809
|
);
|
|
1708
1810
|
}
|
|
1709
1811
|
function removePath(targetDir, relativePath) {
|
|
1710
|
-
const full =
|
|
1711
|
-
if (!
|
|
1812
|
+
const full = resolve15(targetDir, relativePath);
|
|
1813
|
+
if (!existsSync10(full)) {
|
|
1712
1814
|
return;
|
|
1713
1815
|
}
|
|
1714
1816
|
rmSync2(full, { recursive: true, force: true });
|
|
1715
1817
|
}
|
|
1716
1818
|
function stripTanStackFromPackage(targetDir) {
|
|
1717
|
-
const pkgPath =
|
|
1718
|
-
if (!
|
|
1819
|
+
const pkgPath = resolve15(targetDir, "package.json");
|
|
1820
|
+
if (!existsSync10(pkgPath)) {
|
|
1719
1821
|
return;
|
|
1720
1822
|
}
|
|
1721
|
-
const pkg = JSON.parse(
|
|
1823
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
1722
1824
|
if (!pkg.devDependencies) {
|
|
1723
1825
|
return;
|
|
1724
1826
|
}
|
|
1725
1827
|
for (const dep of TANSTACK_DEPS) {
|
|
1726
1828
|
delete pkg.devDependencies[dep];
|
|
1727
1829
|
}
|
|
1728
|
-
|
|
1830
|
+
writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1729
1831
|
`);
|
|
1730
1832
|
}
|
|
1731
1833
|
function applyRouterScaffold(targetDir, mode, template) {
|
|
@@ -1745,22 +1847,141 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
1745
1847
|
writeRouterConfig(targetDir, mode);
|
|
1746
1848
|
writeMountDevPreviewRouter(targetDir);
|
|
1747
1849
|
writeMountPortalRouter(targetDir);
|
|
1748
|
-
const featuresIndex =
|
|
1749
|
-
if (
|
|
1750
|
-
let content =
|
|
1850
|
+
const featuresIndex = resolve15(targetDir, "src/features/starter/index.ts");
|
|
1851
|
+
if (existsSync10(featuresIndex)) {
|
|
1852
|
+
let content = readFileSync8(featuresIndex, "utf8");
|
|
1751
1853
|
if (!content.includes("StarterSinglePage")) {
|
|
1752
1854
|
content = content.replace(
|
|
1753
1855
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\n",
|
|
1754
1856
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\nexport { StarterSinglePage } from './components/starter-single-page'\n"
|
|
1755
1857
|
);
|
|
1756
|
-
|
|
1858
|
+
writeFileSync2(featuresIndex, content);
|
|
1757
1859
|
}
|
|
1758
1860
|
}
|
|
1759
1861
|
}
|
|
1760
1862
|
|
|
1863
|
+
// src/lib/cdn-github-setup.ts
|
|
1864
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
1865
|
+
import { resolve as resolve16 } from "path";
|
|
1866
|
+
import * as p2 from "@clack/prompts";
|
|
1867
|
+
var PORTAL_CDN_PUBLISH_TOKEN_ENV = "PORTAL_CDN_PUBLISH_TOKEN";
|
|
1868
|
+
var PORTAL_CDN_JSON = "portal.cdn.json";
|
|
1869
|
+
var PLACEHOLDER_GITHUB = /your-org|your-portal-cdn-mirror/i;
|
|
1870
|
+
function defaultPortalCdnJson() {
|
|
1871
|
+
const examplePath = kitPath("templates", "shared", "portal.cdn.json.example");
|
|
1872
|
+
if (existsSync11(examplePath)) {
|
|
1873
|
+
return JSON.parse(readFileSync9(examplePath, "utf8"));
|
|
1874
|
+
}
|
|
1875
|
+
return {
|
|
1876
|
+
publishMode: "jsdelivr",
|
|
1877
|
+
github: "your-org/your-portal-cdn-mirror",
|
|
1878
|
+
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
1879
|
+
cdnPathInRepo: "dist/cdn",
|
|
1880
|
+
defaultBranch: "main"
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
function readPortalCdnJson(projectRoot) {
|
|
1884
|
+
const path = resolve16(projectRoot, PORTAL_CDN_JSON);
|
|
1885
|
+
if (!existsSync11(path)) return null;
|
|
1886
|
+
try {
|
|
1887
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
1888
|
+
} catch {
|
|
1889
|
+
return null;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
function normalizeGithubSlug(input) {
|
|
1893
|
+
const trimmed = input.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
1894
|
+
const parts = trimmed.split("/").filter(Boolean);
|
|
1895
|
+
if (parts.length !== 2) return null;
|
|
1896
|
+
return `${parts[0]}/${parts[1]}`;
|
|
1897
|
+
}
|
|
1898
|
+
function portalCdnJsonFromGithub(github, base) {
|
|
1899
|
+
const slug = normalizeGithubSlug(github);
|
|
1900
|
+
if (!slug) {
|
|
1901
|
+
throw new Error(`Invalid GitHub repo \u2014 use org/repo (got "${github}")`);
|
|
1902
|
+
}
|
|
1903
|
+
return {
|
|
1904
|
+
publishMode: base?.publishMode ?? "jsdelivr",
|
|
1905
|
+
github: slug,
|
|
1906
|
+
gitRemote: `https://github.com/${slug}.git`,
|
|
1907
|
+
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
1908
|
+
defaultBranch: base?.defaultBranch ?? "main"
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
function hasCdnPublishToken(projectRoot) {
|
|
1912
|
+
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
1913
|
+
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
1914
|
+
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
1915
|
+
}
|
|
1916
|
+
function hasConfiguredCdnGithubMirror(projectRoot) {
|
|
1917
|
+
const config = readPortalCdnJson(projectRoot);
|
|
1918
|
+
const github = config?.github ?? "";
|
|
1919
|
+
return Boolean(github) && !PLACEHOLDER_GITHUB.test(github);
|
|
1920
|
+
}
|
|
1921
|
+
function githubDefaultForPrompt(projectRoot) {
|
|
1922
|
+
const existing = readPortalCdnJson(projectRoot);
|
|
1923
|
+
if (existing?.github && !PLACEHOLDER_GITHUB.test(existing.github)) {
|
|
1924
|
+
return existing.github;
|
|
1925
|
+
}
|
|
1926
|
+
return defaultPortalCdnJson().github;
|
|
1927
|
+
}
|
|
1928
|
+
function needsCdnGitHubSetup(projectRoot) {
|
|
1929
|
+
return !hasConfiguredCdnGithubMirror(projectRoot) || !hasCdnPublishToken(projectRoot);
|
|
1930
|
+
}
|
|
1931
|
+
function applyCdnGitHubSetup(projectRoot, input) {
|
|
1932
|
+
const existing = readPortalCdnJson(projectRoot) ?? defaultPortalCdnJson();
|
|
1933
|
+
const updated = portalCdnJsonFromGithub(input.github, existing);
|
|
1934
|
+
writeFileSync3(resolve16(projectRoot, PORTAL_CDN_JSON), `${JSON.stringify(updated, null, 2)}
|
|
1935
|
+
`);
|
|
1936
|
+
if (input.token?.trim()) {
|
|
1937
|
+
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
1938
|
+
}
|
|
1939
|
+
ensureEnvFilesGitignore(projectRoot);
|
|
1940
|
+
}
|
|
1941
|
+
async function promptCdnGitHubSetup(options = {}) {
|
|
1942
|
+
if (options.skip) return null;
|
|
1943
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
1944
|
+
if (!needsCdnGitHubSetup(projectRoot)) {
|
|
1945
|
+
return null;
|
|
1946
|
+
}
|
|
1947
|
+
let github = options.github?.trim();
|
|
1948
|
+
if (!github && hasConfiguredCdnGithubMirror(projectRoot)) {
|
|
1949
|
+
github = readPortalCdnJson(projectRoot)?.github;
|
|
1950
|
+
}
|
|
1951
|
+
if (!github) {
|
|
1952
|
+
const answer = await p2.text({
|
|
1953
|
+
message: "Public GitHub CDN mirror (org/repo)",
|
|
1954
|
+
placeholder: options.githubDefault ?? "your-org/your-portal-cdn-mirror",
|
|
1955
|
+
initialValue: options.githubDefault,
|
|
1956
|
+
validate: (value) => {
|
|
1957
|
+
const slug = normalizeGithubSlug(value ?? "");
|
|
1958
|
+
return slug ? void 0 : "Enter a public repo as org/repo (jsDelivr serves from it)";
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
if (p2.isCancel(answer)) return null;
|
|
1962
|
+
github = normalizeGithubSlug(answer) ?? void 0;
|
|
1963
|
+
}
|
|
1964
|
+
if (!github) return null;
|
|
1965
|
+
if (hasCdnPublishToken(projectRoot)) {
|
|
1966
|
+
return { github };
|
|
1967
|
+
}
|
|
1968
|
+
const tokenAnswer = await p2.password({
|
|
1969
|
+
message: "GitHub token (repo push on CDN mirror)",
|
|
1970
|
+
validate: (value) => {
|
|
1971
|
+
if (!value?.trim()) return "Required for wp publish \u2014 create a fine-grained PAT with Contents: Read and write";
|
|
1972
|
+
return void 0;
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
if (p2.isCancel(tokenAnswer)) return null;
|
|
1976
|
+
return { github, token: tokenAnswer.trim() };
|
|
1977
|
+
}
|
|
1978
|
+
function formatCdnSetupNote(_projectRoot) {
|
|
1979
|
+
return `CDN config written \u2014 edit ${PORTAL_CDN_JSON} if needed; token saved to ${ENV_LOCAL_FILE} (gitignored)`;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1761
1982
|
// src/lib/project-setup.ts
|
|
1762
|
-
import { copyFileSync, existsSync as
|
|
1763
|
-
import { basename, resolve as
|
|
1983
|
+
import { copyFileSync, existsSync as existsSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
1984
|
+
import { basename, resolve as resolve17 } from "path";
|
|
1764
1985
|
function envFileForTier(tier) {
|
|
1765
1986
|
return tier === "dev" ? ".env.dev" : tier === "stg" ? ".env.stg" : ".env.prod";
|
|
1766
1987
|
}
|
|
@@ -1770,13 +1991,13 @@ function tiersToSetup(config, tier) {
|
|
|
1770
1991
|
}
|
|
1771
1992
|
function firstExisting(paths) {
|
|
1772
1993
|
for (const path of paths) {
|
|
1773
|
-
if (
|
|
1994
|
+
if (existsSync12(path)) return path;
|
|
1774
1995
|
}
|
|
1775
1996
|
return null;
|
|
1776
1997
|
}
|
|
1777
1998
|
function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
1778
|
-
const target =
|
|
1779
|
-
if (
|
|
1999
|
+
const target = resolve17(projectRoot, targetName);
|
|
2000
|
+
if (existsSync12(target)) {
|
|
1780
2001
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1781
2002
|
}
|
|
1782
2003
|
const source = firstExisting(sourceCandidates);
|
|
@@ -1798,12 +2019,12 @@ function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
|
1798
2019
|
};
|
|
1799
2020
|
}
|
|
1800
2021
|
function writeMissingFile(projectRoot, targetName, content, dryRun) {
|
|
1801
|
-
const target =
|
|
1802
|
-
if (
|
|
2022
|
+
const target = resolve17(projectRoot, targetName);
|
|
2023
|
+
if (existsSync12(target)) {
|
|
1803
2024
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1804
2025
|
}
|
|
1805
2026
|
if (!dryRun) {
|
|
1806
|
-
|
|
2027
|
+
writeFileSync4(target, content);
|
|
1807
2028
|
}
|
|
1808
2029
|
return {
|
|
1809
2030
|
action: "copied",
|
|
@@ -1829,8 +2050,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1829
2050
|
const templateDir = kitTemplateDir(config);
|
|
1830
2051
|
results.push(
|
|
1831
2052
|
copyMissingFile(projectRoot, HUBSPOT_CONFIG_FILE, [
|
|
1832
|
-
|
|
1833
|
-
|
|
2053
|
+
resolve17(projectRoot, HUBSPOT_CONFIG_EXAMPLE),
|
|
2054
|
+
resolve17(templateDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1834
2055
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1835
2056
|
], dryRun)
|
|
1836
2057
|
);
|
|
@@ -1839,8 +2060,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1839
2060
|
const envExample = `${envFile}.example`;
|
|
1840
2061
|
results.push(
|
|
1841
2062
|
copyMissingFile(projectRoot, envFile, [
|
|
1842
|
-
|
|
1843
|
-
|
|
2063
|
+
resolve17(projectRoot, envExample),
|
|
2064
|
+
resolve17(sharedDir, envExample)
|
|
1844
2065
|
], dryRun)
|
|
1845
2066
|
);
|
|
1846
2067
|
}
|
|
@@ -1848,8 +2069,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1848
2069
|
const mirror = config.cdn.mirror;
|
|
1849
2070
|
results.push(
|
|
1850
2071
|
copyMissingFile(projectRoot, mirror, [
|
|
1851
|
-
|
|
1852
|
-
|
|
2072
|
+
resolve17(projectRoot, `${mirror}.example`),
|
|
2073
|
+
resolve17(sharedDir, "portal.cdn.json.example")
|
|
1853
2074
|
], dryRun)
|
|
1854
2075
|
);
|
|
1855
2076
|
const buildIdFile = config.cdn.buildIdFile;
|
|
@@ -1859,15 +2080,15 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1859
2080
|
}
|
|
1860
2081
|
for (const file of templateViteFiles(config)) {
|
|
1861
2082
|
results.push(
|
|
1862
|
-
copyMissingFile(projectRoot, file, [
|
|
2083
|
+
copyMissingFile(projectRoot, file, [resolve17(templateDir, file)], dryRun)
|
|
1863
2084
|
);
|
|
1864
2085
|
}
|
|
1865
2086
|
if (isModuleProject(config) && !isThemeProject(config)) {
|
|
1866
2087
|
const adapter = config.dev.adapter;
|
|
1867
|
-
const adapterPath =
|
|
1868
|
-
if (!
|
|
1869
|
-
const templateAdapter =
|
|
1870
|
-
if (
|
|
2088
|
+
const adapterPath = resolve17(projectRoot, adapter);
|
|
2089
|
+
if (!existsSync12(adapterPath)) {
|
|
2090
|
+
const templateAdapter = resolve17(templateDir, "wphs.adapter.ts");
|
|
2091
|
+
if (existsSync12(templateAdapter)) {
|
|
1871
2092
|
results.push(copyMissingFile(projectRoot, adapter, [templateAdapter], dryRun));
|
|
1872
2093
|
} else {
|
|
1873
2094
|
results.push({
|
|
@@ -1884,26 +2105,26 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1884
2105
|
}
|
|
1885
2106
|
function copySharedSetupExamples(targetDir) {
|
|
1886
2107
|
const sharedDir = kitPath("templates", "shared");
|
|
1887
|
-
if (!
|
|
2108
|
+
if (!existsSync12(sharedDir)) return;
|
|
1888
2109
|
for (const name of [
|
|
1889
2110
|
".env.dev.example",
|
|
1890
2111
|
".env.stg.example",
|
|
1891
2112
|
".env.prod.example",
|
|
1892
2113
|
"portal.cdn.json.example"
|
|
1893
2114
|
]) {
|
|
1894
|
-
const src =
|
|
1895
|
-
const dest =
|
|
1896
|
-
if (
|
|
2115
|
+
const src = resolve17(sharedDir, name);
|
|
2116
|
+
const dest = resolve17(targetDir, name);
|
|
2117
|
+
if (existsSync12(src) && !existsSync12(dest)) {
|
|
1897
2118
|
copyFileSync(src, dest);
|
|
1898
2119
|
}
|
|
1899
2120
|
}
|
|
1900
2121
|
const hubspotExample = firstExisting([
|
|
1901
|
-
|
|
2122
|
+
resolve17(sharedDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1902
2123
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1903
2124
|
]);
|
|
1904
2125
|
if (!hubspotExample) return;
|
|
1905
|
-
const destHubspot =
|
|
1906
|
-
if (!
|
|
2126
|
+
const destHubspot = resolve17(targetDir, HUBSPOT_CONFIG_EXAMPLE);
|
|
2127
|
+
if (!existsSync12(destHubspot) && !existsSync12(resolve17(targetDir, HUBSPOT_CONFIG_FILE))) {
|
|
1907
2128
|
copyFileSync(hubspotExample, destHubspot);
|
|
1908
2129
|
}
|
|
1909
2130
|
}
|
|
@@ -1916,28 +2137,14 @@ function formatSetupHint(results) {
|
|
|
1916
2137
|
if (envCopied) {
|
|
1917
2138
|
return "Edit .env.* files \u2014 set VITE_PUBLIC_REST_API_ENDPOINT and portal IDs for your environment";
|
|
1918
2139
|
}
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
// src/lib/project-name.ts
|
|
1923
|
-
var PROJECT_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
1924
|
-
function normalizeProjectName(input) {
|
|
1925
|
-
return input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
1926
|
-
}
|
|
1927
|
-
function validateProjectName(name) {
|
|
1928
|
-
if (!name) return "Project name is required";
|
|
1929
|
-
if (!PROJECT_NAME_PATTERN.test(name)) {
|
|
1930
|
-
return "Use lowercase letters, numbers, and hyphens (e.g. my-portal-module)";
|
|
2140
|
+
const cdnMirror = results.find((r) => r.path === "portal.cdn.json" && r.action === "copied");
|
|
2141
|
+
if (cdnMirror) {
|
|
2142
|
+
return "Hybrid CDN: set public GitHub mirror in portal.cdn.json; token in .env.local (wp init prompts)";
|
|
1931
2143
|
}
|
|
1932
|
-
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
1933
2144
|
return void 0;
|
|
1934
2145
|
}
|
|
1935
|
-
function moduleOutDirFromSlug(slug) {
|
|
1936
|
-
const pascal = slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1937
|
-
return `dist/${pascal || "HubSpot"}.Module`;
|
|
1938
|
-
}
|
|
1939
2146
|
|
|
1940
|
-
// src/
|
|
2147
|
+
// src/lib/template-scripts.ts
|
|
1941
2148
|
var TEMPLATE_SCRIPTS = {
|
|
1942
2149
|
"module-hybrid-cdn": {
|
|
1943
2150
|
dev: "wp dev",
|
|
@@ -1964,6 +2171,26 @@ var TEMPLATE_SCRIPTS = {
|
|
|
1964
2171
|
doctor: "wp doctor"
|
|
1965
2172
|
}
|
|
1966
2173
|
};
|
|
2174
|
+
|
|
2175
|
+
// src/lib/project-name.ts
|
|
2176
|
+
var PROJECT_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
2177
|
+
function normalizeProjectName(input) {
|
|
2178
|
+
return input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
2179
|
+
}
|
|
2180
|
+
function validateProjectName(name) {
|
|
2181
|
+
if (!name) return "Project name is required";
|
|
2182
|
+
if (!PROJECT_NAME_PATTERN.test(name)) {
|
|
2183
|
+
return "Use lowercase letters, numbers, and hyphens (e.g. my-portal-module)";
|
|
2184
|
+
}
|
|
2185
|
+
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
2186
|
+
return void 0;
|
|
2187
|
+
}
|
|
2188
|
+
function moduleOutDirFromSlug(slug) {
|
|
2189
|
+
const pascal = slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
2190
|
+
return `dist/${pascal || "HubSpot"}.Module`;
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
// src/cli/commands/init.ts
|
|
1967
2194
|
function buildConfigForProject(template, projectName, router) {
|
|
1968
2195
|
const base = { ...DEFAULT_WOODSPORTAL_HS, projectType: template };
|
|
1969
2196
|
if (template === "theme-starter") {
|
|
@@ -1995,7 +2222,7 @@ function buildConfigForProject(template, projectName, router) {
|
|
|
1995
2222
|
return config;
|
|
1996
2223
|
}
|
|
1997
2224
|
function assertTargetDirIsScaffoldable(targetDir) {
|
|
1998
|
-
if (!
|
|
2225
|
+
if (!existsSync13(targetDir)) return;
|
|
1999
2226
|
const entries = readdirSync2(targetDir).filter((entry) => entry !== ".DS_Store");
|
|
2000
2227
|
if (entries.length > 0) {
|
|
2001
2228
|
throw new WpHsError(`Target directory is not empty: ${targetDir}`, {
|
|
@@ -2006,7 +2233,7 @@ function assertTargetDirIsScaffoldable(targetDir) {
|
|
|
2006
2233
|
}
|
|
2007
2234
|
function resolveInitTargetDir(options, interactiveName) {
|
|
2008
2235
|
if (options.directory) {
|
|
2009
|
-
return
|
|
2236
|
+
return resolve18(options.directory);
|
|
2010
2237
|
}
|
|
2011
2238
|
const parentDir = process.cwd();
|
|
2012
2239
|
const slug = options.name ?? interactiveName;
|
|
@@ -2020,7 +2247,7 @@ function resolveInitTargetDir(options, interactiveName) {
|
|
|
2020
2247
|
if (error) {
|
|
2021
2248
|
throw new WpHsError(error, { exitCode: ExitCode.USER_ERROR });
|
|
2022
2249
|
}
|
|
2023
|
-
return
|
|
2250
|
+
return resolve18(parentDir, slug);
|
|
2024
2251
|
}
|
|
2025
2252
|
async function runInit(ctx, options) {
|
|
2026
2253
|
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json;
|
|
@@ -2029,7 +2256,7 @@ async function runInit(ctx, options) {
|
|
|
2029
2256
|
let router = options.router;
|
|
2030
2257
|
if (!nonInteractive) {
|
|
2031
2258
|
if (!template) {
|
|
2032
|
-
const type = await
|
|
2259
|
+
const type = await p3.select({
|
|
2033
2260
|
message: "Project type",
|
|
2034
2261
|
options: [
|
|
2035
2262
|
{
|
|
@@ -2041,22 +2268,22 @@ async function runInit(ctx, options) {
|
|
|
2041
2268
|
{ value: "theme-starter", label: "Theme starter", hint: "CMS theme templates" }
|
|
2042
2269
|
]
|
|
2043
2270
|
});
|
|
2044
|
-
if (
|
|
2271
|
+
if (p3.isCancel(type)) throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2045
2272
|
template = type;
|
|
2046
2273
|
}
|
|
2047
2274
|
if (!options.directory && !projectName) {
|
|
2048
|
-
const nameAnswer = await
|
|
2275
|
+
const nameAnswer = await p3.text({
|
|
2049
2276
|
message: "Project name (folder name)",
|
|
2050
2277
|
placeholder: "my-portal-module",
|
|
2051
2278
|
validate: (value) => validateProjectName(normalizeProjectName(value ?? ""))
|
|
2052
2279
|
});
|
|
2053
|
-
if (
|
|
2280
|
+
if (p3.isCancel(nameAnswer)) {
|
|
2054
2281
|
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2055
2282
|
}
|
|
2056
2283
|
projectName = normalizeProjectName(nameAnswer);
|
|
2057
2284
|
}
|
|
2058
2285
|
if (!router && template && isModuleTemplate(template)) {
|
|
2059
|
-
const routerAnswer = await
|
|
2286
|
+
const routerAnswer = await p3.select({
|
|
2060
2287
|
message: "Client-side routing",
|
|
2061
2288
|
initialValue: "hash",
|
|
2062
2289
|
options: [
|
|
@@ -2077,7 +2304,7 @@ async function runInit(ctx, options) {
|
|
|
2077
2304
|
}
|
|
2078
2305
|
]
|
|
2079
2306
|
});
|
|
2080
|
-
if (
|
|
2307
|
+
if (p3.isCancel(routerAnswer)) {
|
|
2081
2308
|
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2082
2309
|
}
|
|
2083
2310
|
router = routerAnswer;
|
|
@@ -2093,7 +2320,7 @@ async function runInit(ctx, options) {
|
|
|
2093
2320
|
const slug = projectName ?? basename2(targetDir);
|
|
2094
2321
|
assertTargetDirIsScaffoldable(targetDir);
|
|
2095
2322
|
const templateDir = kitPath("templates", template);
|
|
2096
|
-
if (!
|
|
2323
|
+
if (!existsSync13(templateDir)) {
|
|
2097
2324
|
throw new WpHsError(`Template not found: ${template}`, { exitCode: ExitCode.USER_ERROR });
|
|
2098
2325
|
}
|
|
2099
2326
|
if (ctx.global.dryRun) {
|
|
@@ -2104,18 +2331,18 @@ async function runInit(ctx, options) {
|
|
|
2104
2331
|
cpSync2(templateDir, targetDir, { recursive: true });
|
|
2105
2332
|
copySharedSetupExamples(targetDir);
|
|
2106
2333
|
const config = buildConfigForProject(template, slug, router);
|
|
2107
|
-
|
|
2334
|
+
writeFileSync5(resolve18(targetDir, WOODSCLI_CONFIG_FILE), `${JSON.stringify(config, null, 2)}
|
|
2108
2335
|
`);
|
|
2109
|
-
const pkgPath =
|
|
2110
|
-
if (
|
|
2111
|
-
const pkg = JSON.parse(
|
|
2336
|
+
const pkgPath = resolve18(targetDir, "package.json");
|
|
2337
|
+
if (existsSync13(pkgPath)) {
|
|
2338
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
2112
2339
|
pkg.name = slug;
|
|
2113
2340
|
pkg.scripts = { ...pkg.scripts, ...TEMPLATE_SCRIPTS[template] };
|
|
2114
2341
|
pkg.devDependencies = {
|
|
2115
2342
|
...pkg.devDependencies,
|
|
2116
2343
|
...templateDevDependencies()
|
|
2117
2344
|
};
|
|
2118
|
-
|
|
2345
|
+
writeFileSync5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2119
2346
|
`);
|
|
2120
2347
|
}
|
|
2121
2348
|
if (router && isModuleTemplate(template)) {
|
|
@@ -2126,7 +2353,7 @@ async function runInit(ctx, options) {
|
|
|
2126
2353
|
logger: ctx.logger,
|
|
2127
2354
|
dryRun: false
|
|
2128
2355
|
});
|
|
2129
|
-
const gitignorePath =
|
|
2356
|
+
const gitignorePath = resolve18(targetDir, ".gitignore");
|
|
2130
2357
|
const gitignoreExtras = [
|
|
2131
2358
|
"hubspot.config.yml",
|
|
2132
2359
|
".wphs/config/module-config.local.json",
|
|
@@ -2139,149 +2366,81 @@ async function runInit(ctx, options) {
|
|
|
2139
2366
|
".wphs/.kit-bootstrap.tsx",
|
|
2140
2367
|
".wphs/.kit-bootstrap-greenfield.tsx"
|
|
2141
2368
|
];
|
|
2142
|
-
const existing =
|
|
2369
|
+
const existing = existsSync13(gitignorePath) ? readFileSync10(gitignorePath, "utf8") : "";
|
|
2143
2370
|
const toAppend = gitignoreExtras.filter((line) => !existing.includes(line)).join("\n");
|
|
2144
2371
|
if (toAppend) {
|
|
2145
|
-
|
|
2372
|
+
writeFileSync5(gitignorePath, `${existing.trimEnd()}
|
|
2146
2373
|
${toAppend}
|
|
2147
2374
|
`);
|
|
2148
2375
|
}
|
|
2376
|
+
if (template === "module-hybrid-cdn" && !ctx.global.dryRun && needsCdnGitHubSetup(targetDir)) {
|
|
2377
|
+
const githubDefault = githubDefaultForPrompt(targetDir);
|
|
2378
|
+
let cdnSetup = null;
|
|
2379
|
+
if (nonInteractive) {
|
|
2380
|
+
if (options.cdnGithub) {
|
|
2381
|
+
cdnSetup = { github: options.cdnGithub };
|
|
2382
|
+
}
|
|
2383
|
+
} else {
|
|
2384
|
+
p3.log.step("Hybrid CDN \u2014 GitHub mirror (public repo for jsDelivr)");
|
|
2385
|
+
cdnSetup = await promptCdnGitHubSetup({
|
|
2386
|
+
projectRoot: targetDir,
|
|
2387
|
+
githubDefault,
|
|
2388
|
+
github: options.cdnGithub
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
if (cdnSetup) {
|
|
2392
|
+
applyCdnGitHubSetup(targetDir, cdnSetup);
|
|
2393
|
+
if (!nonInteractive) {
|
|
2394
|
+
p3.log.success(formatCdnSetupNote(targetDir));
|
|
2395
|
+
} else {
|
|
2396
|
+
ctx.logger.success(formatCdnSetupNote(targetDir));
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2149
2400
|
if (!nonInteractive) {
|
|
2150
|
-
|
|
2151
|
-
|
|
2401
|
+
p3.log.success(`Scaffolded ${template} in ${targetDir}`);
|
|
2402
|
+
p3.note(`cd ${slug} && yarn install && wp setup && wp doctor && wp dev`, "Getting started");
|
|
2152
2403
|
} else {
|
|
2153
2404
|
ctx.logger.success(`Scaffolded ${template} in ${targetDir}`);
|
|
2154
2405
|
}
|
|
2155
2406
|
return ExitCode.SUCCESS;
|
|
2156
2407
|
}
|
|
2157
2408
|
|
|
2158
|
-
// src/cli/commands/adopt.ts
|
|
2159
|
-
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "fs";
|
|
2160
|
-
import { resolve as resolve16 } from "path";
|
|
2161
|
-
import { pathToFileURL } from "url";
|
|
2162
|
-
import * as p3 from "@clack/prompts";
|
|
2163
|
-
async function loadDetectLayout() {
|
|
2164
|
-
const mod = await import(pathToFileURL(kitPath("scripts", "detect-layout.mjs")).href);
|
|
2165
|
-
return mod.detectProjectLayout;
|
|
2166
|
-
}
|
|
2167
|
-
async function runAdopt(ctx, _options = {}) {
|
|
2168
|
-
const detectProjectLayout = await loadDetectLayout();
|
|
2169
|
-
const layout = detectProjectLayout(ctx.projectRoot);
|
|
2170
|
-
const configPath = resolve16(ctx.projectRoot, WOODSCLI_CONFIG_FILE);
|
|
2171
|
-
if (layout.track === "unknown") {
|
|
2172
|
-
throw new WpHsError("Could not detect module or theme layout.", {
|
|
2173
|
-
exitCode: ExitCode.USER_ERROR,
|
|
2174
|
-
remediation: "Ensure fields.json + module.html (module) or theme.json + templates/ (theme) exist."
|
|
2175
|
-
});
|
|
2176
|
-
}
|
|
2177
|
-
let projectType;
|
|
2178
|
-
if (layout.projectType) {
|
|
2179
|
-
projectType = layout.projectType;
|
|
2180
|
-
} else if (layout.track === "theme") {
|
|
2181
|
-
projectType = "theme-starter";
|
|
2182
|
-
} else if (existsSync11(resolve16(ctx.projectRoot, "vite.config.monolith.mjs"))) {
|
|
2183
|
-
projectType = "module-monolith";
|
|
2184
|
-
} else {
|
|
2185
|
-
projectType = "module-hybrid-cdn";
|
|
2186
|
-
}
|
|
2187
|
-
const config = {
|
|
2188
|
-
...DEFAULT_WOODSPORTAL_HS,
|
|
2189
|
-
projectType,
|
|
2190
|
-
...layout.track === "theme" ? {
|
|
2191
|
-
theme: {
|
|
2192
|
-
name: "my-theme",
|
|
2193
|
-
uploadPath: "themes/my-theme",
|
|
2194
|
-
srcDir: "src/theme",
|
|
2195
|
-
themeJson: "src/theme/theme.json"
|
|
2196
|
-
}
|
|
2197
|
-
} : {}
|
|
2198
|
-
};
|
|
2199
|
-
if (ctx.global.dryRun) {
|
|
2200
|
-
ctx.logger.info(`[dry-run] Would adopt as ${projectType} (${layout.track})`);
|
|
2201
|
-
return ExitCode.SUCCESS;
|
|
2202
|
-
}
|
|
2203
|
-
if (!existsSync11(configPath)) {
|
|
2204
|
-
writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
|
|
2205
|
-
`);
|
|
2206
|
-
ctx.logger.success(`Created ${WOODSCLI_CONFIG_FILE}`);
|
|
2207
|
-
} else if (!layout.projectType) {
|
|
2208
|
-
const existing = JSON.parse(readFileSync9(configPath, "utf8"));
|
|
2209
|
-
writeFileSync4(configPath, `${JSON.stringify({ ...existing, projectType }, null, 2)}
|
|
2210
|
-
`);
|
|
2211
|
-
ctx.logger.info(`Set projectType \u2192 ${projectType}`);
|
|
2212
|
-
}
|
|
2213
|
-
runNodeScript(kitPath("scripts/ensure-wphs-workspace.mjs"), [], {
|
|
2214
|
-
cwd: ctx.projectRoot,
|
|
2215
|
-
logger: ctx.logger,
|
|
2216
|
-
dryRun: false
|
|
2217
|
-
});
|
|
2218
|
-
const adapterPath = resolve16(ctx.projectRoot, "wphs.adapter.ts");
|
|
2219
|
-
if (!existsSync11(adapterPath) && isModuleProject(config)) {
|
|
2220
|
-
writeFileSync4(
|
|
2221
|
-
adapterPath,
|
|
2222
|
-
`import type { HubSpotKitAdapter } from '@woodsportal/hubspot-kit'
|
|
2223
|
-
|
|
2224
|
-
const adapter: HubSpotKitAdapter = {
|
|
2225
|
-
syncLiveData(built) {
|
|
2226
|
-
;(window as Window & { __WPHS_DATA__?: Record<string, unknown> }).__WPHS_DATA__ = built
|
|
2227
|
-
},
|
|
2228
|
-
}
|
|
2229
|
-
|
|
2230
|
-
export default adapter
|
|
2231
|
-
`
|
|
2232
|
-
);
|
|
2233
|
-
ctx.logger.info("Created wphs.adapter.ts stub");
|
|
2234
|
-
}
|
|
2235
|
-
if (!ctx.global.ci && !ctx.global.quiet) {
|
|
2236
|
-
p3.note(
|
|
2237
|
-
[
|
|
2238
|
-
`Review ${WOODSCLI_CONFIG_FILE} upload paths`,
|
|
2239
|
-
"Implement wphs.adapter.ts for your app (if module)",
|
|
2240
|
-
"Run wp setup (copies hubspot.config.yml, .env.*, vite shims)",
|
|
2241
|
-
"Patch main.dev.css @source to .wphs/.kit-module-config/**"
|
|
2242
|
-
].join("\n"),
|
|
2243
|
-
"Manual follow-ups"
|
|
2244
|
-
);
|
|
2245
|
-
}
|
|
2246
|
-
ctx.logger.success(`Adopted ${layout.track} project (${projectType})`);
|
|
2247
|
-
return ExitCode.SUCCESS;
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
2409
|
// src/cli/commands/migrate.ts
|
|
2251
|
-
import { cpSync as cpSync3, existsSync as
|
|
2252
|
-
import { join as join3, resolve as
|
|
2410
|
+
import { cpSync as cpSync3, existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2411
|
+
import { join as join3, resolve as resolve19 } from "path";
|
|
2253
2412
|
var LEGACY_DIRS = [".dev", ".woodsportal"];
|
|
2254
2413
|
var LEGACY_BASELINE = ".wp-hs-baseline";
|
|
2255
2414
|
async function runMigrate(ctx, _options = {}) {
|
|
2256
2415
|
const root = ctx.projectRoot;
|
|
2257
|
-
const wphsRoot =
|
|
2416
|
+
const wphsRoot = resolve19(root, WPHS_WORKSPACE_DIR);
|
|
2258
2417
|
const actions = [];
|
|
2259
2418
|
for (const legacy of LEGACY_DIRS) {
|
|
2260
|
-
const legacyPath =
|
|
2261
|
-
if (!
|
|
2419
|
+
const legacyPath = resolve19(root, legacy);
|
|
2420
|
+
if (!existsSync14(legacyPath)) continue;
|
|
2262
2421
|
actions.push(`Move ${legacy}/ \u2192 .wphs/`);
|
|
2263
2422
|
if (ctx.global.dryRun) continue;
|
|
2264
2423
|
mkdirSync4(join3(wphsRoot, "config"), { recursive: true });
|
|
2265
2424
|
mkdirSync4(join3(wphsRoot, "assets", "branding"), { recursive: true });
|
|
2266
2425
|
const localConfig = join3(legacyPath, "module-config.local.json");
|
|
2267
2426
|
const exampleConfig = join3(legacyPath, "module-config.example.json");
|
|
2268
|
-
if (
|
|
2427
|
+
if (existsSync14(localConfig)) {
|
|
2269
2428
|
cpSync3(localConfig, join3(wphsRoot, "config", "module-config.local.json"));
|
|
2270
2429
|
}
|
|
2271
|
-
if (
|
|
2430
|
+
if (existsSync14(exampleConfig)) {
|
|
2272
2431
|
cpSync3(exampleConfig, join3(wphsRoot, "config", "module-config.example.json"));
|
|
2273
2432
|
}
|
|
2274
2433
|
const branding = join3(legacyPath, "branding-assets");
|
|
2275
|
-
if (
|
|
2434
|
+
if (existsSync14(branding)) {
|
|
2276
2435
|
for (const file of readdirSync3(branding)) {
|
|
2277
2436
|
cpSync3(join3(branding, file), join3(wphsRoot, "assets", "branding", file));
|
|
2278
2437
|
}
|
|
2279
2438
|
}
|
|
2280
2439
|
rmSync3(legacyPath, { recursive: true, force: true });
|
|
2281
2440
|
}
|
|
2282
|
-
const legacyBaseline =
|
|
2441
|
+
const legacyBaseline = resolve19(root, LEGACY_BASELINE);
|
|
2283
2442
|
const targetBaseline = join3(wphsRoot, "audit", "baseline");
|
|
2284
|
-
if (
|
|
2443
|
+
if (existsSync14(legacyBaseline)) {
|
|
2285
2444
|
actions.push(`Move ${LEGACY_BASELINE}/ \u2192 .wphs/audit/baseline/`);
|
|
2286
2445
|
if (!ctx.global.dryRun) {
|
|
2287
2446
|
mkdirSync4(targetBaseline, { recursive: true });
|
|
@@ -2289,26 +2448,26 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2289
2448
|
rmSync3(legacyBaseline, { recursive: true, force: true });
|
|
2290
2449
|
}
|
|
2291
2450
|
}
|
|
2292
|
-
const legacyConfigPath2 =
|
|
2293
|
-
const woodscliPath =
|
|
2294
|
-
if (
|
|
2451
|
+
const legacyConfigPath2 = resolve19(root, LEGACY_WOODSPORTAL_HS_FILE);
|
|
2452
|
+
const woodscliPath = resolve19(root, WOODSCLI_CONFIG_FILE);
|
|
2453
|
+
if (existsSync14(legacyConfigPath2) && !existsSync14(woodscliPath)) {
|
|
2295
2454
|
actions.push(`Rename ${LEGACY_WOODSPORTAL_HS_FILE} \u2192 ${WOODSCLI_CONFIG_FILE}`);
|
|
2296
2455
|
if (!ctx.global.dryRun) {
|
|
2297
2456
|
renameSync(legacyConfigPath2, woodscliPath);
|
|
2298
2457
|
}
|
|
2299
|
-
} else if (
|
|
2458
|
+
} else if (existsSync14(legacyConfigPath2) && existsSync14(woodscliPath)) {
|
|
2300
2459
|
actions.push(`Remove duplicate ${LEGACY_WOODSPORTAL_HS_FILE}`);
|
|
2301
2460
|
if (!ctx.global.dryRun) {
|
|
2302
2461
|
rmSync3(legacyConfigPath2, { force: true });
|
|
2303
2462
|
}
|
|
2304
2463
|
}
|
|
2305
|
-
if (
|
|
2306
|
-
const raw = JSON.parse(
|
|
2464
|
+
if (existsSync14(woodscliPath)) {
|
|
2465
|
+
const raw = JSON.parse(readFileSync11(woodscliPath, "utf8"));
|
|
2307
2466
|
if (raw?.dev?.moduleConfigDir && raw.dev.moduleConfigDir !== WPHS_WORKSPACE_DIR) {
|
|
2308
2467
|
actions.push(`Update dev.moduleConfigDir \u2192 .wphs in project config`);
|
|
2309
2468
|
if (!ctx.global.dryRun) {
|
|
2310
2469
|
raw.dev.moduleConfigDir = WPHS_WORKSPACE_DIR;
|
|
2311
|
-
|
|
2470
|
+
writeFileSync6(woodscliPath, `${JSON.stringify(raw, null, 2)}
|
|
2312
2471
|
`);
|
|
2313
2472
|
}
|
|
2314
2473
|
}
|
|
@@ -2337,10 +2496,10 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2337
2496
|
}
|
|
2338
2497
|
|
|
2339
2498
|
// src/cli/commands/examples.ts
|
|
2340
|
-
import { readFileSync as
|
|
2499
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
2341
2500
|
function loadManifest() {
|
|
2342
2501
|
const manifestPath = kitPath("examples", "manifest.json");
|
|
2343
|
-
const raw = JSON.parse(
|
|
2502
|
+
const raw = JSON.parse(readFileSync12(manifestPath, "utf8"));
|
|
2344
2503
|
return raw.scenarios;
|
|
2345
2504
|
}
|
|
2346
2505
|
async function runExamplesList(ctx, options = {}) {
|
|
@@ -2373,15 +2532,15 @@ async function runExamplesSmoke(ctx, options = {}) {
|
|
|
2373
2532
|
}
|
|
2374
2533
|
|
|
2375
2534
|
// src/cli/commands/fields.ts
|
|
2376
|
-
import { cpSync as cpSync4, existsSync as
|
|
2377
|
-
import { resolve as
|
|
2535
|
+
import { cpSync as cpSync4, existsSync as existsSync15, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
2536
|
+
import { resolve as resolve20 } from "path";
|
|
2378
2537
|
async function runFieldsSync(ctx, options = {}) {
|
|
2379
2538
|
const direction = options.direction ?? "pull";
|
|
2380
|
-
const fieldsPath =
|
|
2381
|
-
const cacheDir =
|
|
2382
|
-
const cachePath =
|
|
2539
|
+
const fieldsPath = resolve20(ctx.projectRoot, ctx.config.dev.fieldsJson);
|
|
2540
|
+
const cacheDir = resolve20(ctx.projectRoot, ctx.config.dev.moduleConfigDir, "cache", "fields-sync");
|
|
2541
|
+
const cachePath = resolve20(cacheDir, "fields.json");
|
|
2383
2542
|
if (direction === "pull") {
|
|
2384
|
-
if (!
|
|
2543
|
+
if (!existsSync15(fieldsPath)) {
|
|
2385
2544
|
throw new WpHsError(`Missing ${ctx.config.dev.fieldsJson}`, {
|
|
2386
2545
|
exitCode: ExitCode.USER_ERROR,
|
|
2387
2546
|
remediation: "Add fields.json or run wp init."
|
|
@@ -2397,7 +2556,7 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
2397
2556
|
ctx.logger.success(`Cached fields \u2192 ${cachePath}`);
|
|
2398
2557
|
return ExitCode.SUCCESS;
|
|
2399
2558
|
}
|
|
2400
|
-
if (!
|
|
2559
|
+
if (!existsSync15(cachePath)) {
|
|
2401
2560
|
throw new WpHsError("No cached fields \u2014 run wp fields sync pull first.", {
|
|
2402
2561
|
exitCode: ExitCode.USER_ERROR
|
|
2403
2562
|
});
|
|
@@ -2406,14 +2565,14 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
2406
2565
|
ctx.logger.info(`[dry-run] Would write ${cachePath} \u2192 ${fieldsPath}`);
|
|
2407
2566
|
return ExitCode.SUCCESS;
|
|
2408
2567
|
}
|
|
2409
|
-
|
|
2568
|
+
writeFileSync7(fieldsPath, readFileSync13(cachePath, "utf8"));
|
|
2410
2569
|
ctx.logger.success(`Restored fields from cache \u2192 ${fieldsPath}`);
|
|
2411
2570
|
return ExitCode.SUCCESS;
|
|
2412
2571
|
}
|
|
2413
2572
|
|
|
2414
2573
|
// src/cli/commands/regression.ts
|
|
2415
|
-
import { existsSync as
|
|
2416
|
-
import { resolve as
|
|
2574
|
+
import { existsSync as existsSync16 } from "fs";
|
|
2575
|
+
import { resolve as resolve21 } from "path";
|
|
2417
2576
|
var LEGACY_SHIM_SCRIPTS = [
|
|
2418
2577
|
"build-cdn.mjs",
|
|
2419
2578
|
"run-vite-build.mjs",
|
|
@@ -2436,21 +2595,21 @@ async function runRegressionSmoke(ctx, options = {}) {
|
|
|
2436
2595
|
if (!validateOk) failures.push("wp config validate failed");
|
|
2437
2596
|
if (options.checkLegacyShims !== false) {
|
|
2438
2597
|
for (const script of LEGACY_SHIM_SCRIPTS) {
|
|
2439
|
-
const shim =
|
|
2598
|
+
const shim = resolve21(ctx.projectRoot, "scripts", script);
|
|
2440
2599
|
const kitScript = kitPath("scripts", script);
|
|
2441
|
-
const shimOk =
|
|
2600
|
+
const shimOk = existsSync16(shim) && existsSync16(kitScript);
|
|
2442
2601
|
checks.push({ name: `shim:${script}`, ok: shimOk });
|
|
2443
2602
|
if (!shimOk) failures.push(`Missing legacy shim or kit script: ${script}`);
|
|
2444
2603
|
}
|
|
2445
2604
|
}
|
|
2446
2605
|
for (const rel of FORBIDDEN_CONSUMER_PATHS) {
|
|
2447
|
-
const forbidden =
|
|
2448
|
-
const absent = !
|
|
2606
|
+
const forbidden = resolve21(ctx.projectRoot, rel);
|
|
2607
|
+
const absent = !existsSync16(forbidden);
|
|
2449
2608
|
checks.push({ name: `no-${rel}`, ok: absent });
|
|
2450
2609
|
if (!absent) failures.push(`Forbidden dev path still present: ${rel}`);
|
|
2451
2610
|
}
|
|
2452
|
-
const woodscliConfig =
|
|
2453
|
-
const configOk =
|
|
2611
|
+
const woodscliConfig = resolve21(ctx.projectRoot, WOODSCLI_CONFIG_FILE);
|
|
2612
|
+
const configOk = existsSync16(woodscliConfig);
|
|
2454
2613
|
checks.push({ name: WOODSCLI_CONFIG_FILE, ok: configOk });
|
|
2455
2614
|
if (!configOk) failures.push(`${WOODSCLI_CONFIG_FILE} missing`);
|
|
2456
2615
|
if (ctx.global.json) {
|
|
@@ -2508,6 +2667,17 @@ async function runSetup(ctx, options = {}) {
|
|
|
2508
2667
|
);
|
|
2509
2668
|
const copied = results.filter((r) => r.action === "copied");
|
|
2510
2669
|
const manual = results.filter((r) => r.action === "manual");
|
|
2670
|
+
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json || ctx.global.dryRun;
|
|
2671
|
+
if (usesCdnPipeline(ctx.config) && needsCdnGitHubSetup(ctx.projectRoot) && !nonInteractive) {
|
|
2672
|
+
const cdnSetup = await promptCdnGitHubSetup({
|
|
2673
|
+
projectRoot: ctx.projectRoot,
|
|
2674
|
+
githubDefault: githubDefaultForPrompt(ctx.projectRoot)
|
|
2675
|
+
});
|
|
2676
|
+
if (cdnSetup) {
|
|
2677
|
+
applyCdnGitHubSetup(ctx.projectRoot, cdnSetup);
|
|
2678
|
+
ctx.logger.success(formatCdnSetupNote(ctx.projectRoot));
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2511
2681
|
if (ctx.global.json) {
|
|
2512
2682
|
ctx.logger.json({
|
|
2513
2683
|
command: "setup",
|
|
@@ -2546,8 +2716,8 @@ async function runSetup(ctx, options = {}) {
|
|
|
2546
2716
|
import * as p4 from "@clack/prompts";
|
|
2547
2717
|
|
|
2548
2718
|
// src/lib/hubspot-portals.ts
|
|
2549
|
-
import { existsSync as
|
|
2550
|
-
import { resolve as
|
|
2719
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
2720
|
+
import { resolve as resolve22 } from "path";
|
|
2551
2721
|
function stripYamlScalar(value) {
|
|
2552
2722
|
const trimmed = value.trim();
|
|
2553
2723
|
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
@@ -2585,14 +2755,14 @@ function parseHubSpotConfigPortals(raw) {
|
|
|
2585
2755
|
return { defaultPortal, portals };
|
|
2586
2756
|
}
|
|
2587
2757
|
function readHubSpotPortals(projectRoot) {
|
|
2588
|
-
const configPath =
|
|
2589
|
-
if (!
|
|
2758
|
+
const configPath = resolve22(projectRoot, HUBSPOT_CONFIG_FILE);
|
|
2759
|
+
if (!existsSync17(configPath)) {
|
|
2590
2760
|
throw new WpHsError(`${HUBSPOT_CONFIG_FILE} not found in project root.`, {
|
|
2591
2761
|
exitCode: ExitCode.USER_ERROR,
|
|
2592
2762
|
remediation: "Run wp setup, then add your portal + PAT."
|
|
2593
2763
|
});
|
|
2594
2764
|
}
|
|
2595
|
-
const raw =
|
|
2765
|
+
const raw = readFileSync14(configPath, "utf8");
|
|
2596
2766
|
const { defaultPortal, portals } = parseHubSpotConfigPortals(raw);
|
|
2597
2767
|
return { configPath, defaultPortal, portals };
|
|
2598
2768
|
}
|
|
@@ -2722,7 +2892,7 @@ async function runWatch(ctx, options) {
|
|
|
2722
2892
|
// src/cli/program.ts
|
|
2723
2893
|
function readVersion() {
|
|
2724
2894
|
try {
|
|
2725
|
-
const pkg = JSON.parse(
|
|
2895
|
+
const pkg = JSON.parse(readFileSync15(resolve23(getKitRoot(), "package.json"), "utf8"));
|
|
2726
2896
|
return pkg.version ?? "0.0.0";
|
|
2727
2897
|
} catch {
|
|
2728
2898
|
return "0.0.0";
|
|
@@ -2781,7 +2951,10 @@ function createProgram() {
|
|
|
2781
2951
|
const global = program2.opts();
|
|
2782
2952
|
await handle(() => runConfigValidate(buildContext(global)));
|
|
2783
2953
|
});
|
|
2784
|
-
program2.command("init").description("Scaffold a new module or theme project").option("-t, --template <type>", "module-hybrid-cdn | module-monolith | theme-starter").option("-n, --name <slug>", "Project name (creates ./<name>/ under cwd)").option("-d, --directory <path>", "Full target directory (skips name \u2192 folder prompt)").option("--router <mode>", "hash (client-frontend default) | browser | none \u2014 modules only").
|
|
2954
|
+
program2.command("init").description("Scaffold a new module or theme project").option("-t, --template <type>", "module-hybrid-cdn | module-monolith | theme-starter").option("-n, --name <slug>", "Project name (creates ./<name>/ under cwd)").option("-d, --directory <path>", "Full target directory (skips name \u2192 folder prompt)").option("--router <mode>", "hash (client-frontend default) | browser | none \u2014 modules only").option(
|
|
2955
|
+
"--cdn-github <org/repo>",
|
|
2956
|
+
"Public GitHub CDN mirror repo (hybrid CDN; use with --ci / -y)"
|
|
2957
|
+
).action(
|
|
2785
2958
|
async (opts) => {
|
|
2786
2959
|
const global = program2.opts();
|
|
2787
2960
|
const logger = createLogger({
|
|
@@ -2802,15 +2975,12 @@ function createProgram() {
|
|
|
2802
2975
|
template: opts.template,
|
|
2803
2976
|
name: opts.name,
|
|
2804
2977
|
directory: opts.directory,
|
|
2805
|
-
router: routerMode
|
|
2978
|
+
router: routerMode,
|
|
2979
|
+
cdnGithub: opts.cdnGithub
|
|
2806
2980
|
})
|
|
2807
2981
|
);
|
|
2808
2982
|
}
|
|
2809
2983
|
);
|
|
2810
|
-
program2.command("adopt").description("Detect existing module/theme repo and add WoodsCLI config + .wphs/").action(async () => {
|
|
2811
|
-
const global = program2.opts();
|
|
2812
|
-
await handle(() => runAdopt(buildContext(global)));
|
|
2813
|
-
});
|
|
2814
2984
|
program2.command("migrate").description("Migrate legacy .dev/ or .wp-hs-baseline/ to .wphs/").action(async () => {
|
|
2815
2985
|
const global = program2.opts();
|
|
2816
2986
|
await handle(() => runMigrate(buildContext(global)));
|