@woodsportal/hubspot-kit 1.0.26 → 1.0.29
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 +242 -35
- package/dist/{chunk-U4KFGTEW.js → chunk-S2YJDWZU.js} +5 -5
- package/dist/chunk-S2YJDWZU.js.map +1 -0
- package/dist/cli.js +192 -256
- 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/dist/chunk-U4KFGTEW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./chunk-GTTIJVAR.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/program.ts
|
|
12
|
-
import { readFileSync as
|
|
12
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
13
13
|
import { resolve as resolve21 } from "path";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
|
|
@@ -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.29";
|
|
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";
|
|
@@ -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,7 @@ async function runDev(ctx, options) {
|
|
|
1217
1247
|
}
|
|
1218
1248
|
|
|
1219
1249
|
// src/cli/commands/deploy.ts
|
|
1220
|
-
import { resolve as
|
|
1250
|
+
import { resolve as resolve12 } from "path";
|
|
1221
1251
|
|
|
1222
1252
|
// src/cli/commands/publish.ts
|
|
1223
1253
|
async function runPublish(ctx, options) {
|
|
@@ -1275,14 +1305,14 @@ async function runPublish(ctx, options) {
|
|
|
1275
1305
|
}
|
|
1276
1306
|
|
|
1277
1307
|
// src/cli/commands/upload.ts
|
|
1278
|
-
import { resolve as
|
|
1308
|
+
import { resolve as resolve11 } from "path";
|
|
1279
1309
|
async function uploadModuleOrTheme(ctx) {
|
|
1280
1310
|
if (isThemeProject(ctx.config)) {
|
|
1281
1311
|
const theme = ctx.config.theme;
|
|
1282
1312
|
if (!theme) {
|
|
1283
1313
|
throw new WpHsError("theme block missing in woodscli.json", { exitCode: ExitCode.USER_ERROR });
|
|
1284
1314
|
}
|
|
1285
|
-
const srcDir =
|
|
1315
|
+
const srcDir = resolve11(ctx.projectRoot, theme.srcDir);
|
|
1286
1316
|
runCommand("hs", ["cms", "upload", srcDir, theme.uploadPath], {
|
|
1287
1317
|
cwd: ctx.projectRoot,
|
|
1288
1318
|
logger: ctx.logger,
|
|
@@ -1290,7 +1320,7 @@ async function uploadModuleOrTheme(ctx) {
|
|
|
1290
1320
|
});
|
|
1291
1321
|
return `Uploaded theme ${srcDir} \u2192 ${theme.uploadPath}`;
|
|
1292
1322
|
}
|
|
1293
|
-
const outDir =
|
|
1323
|
+
const outDir = resolve11(ctx.projectRoot, ctx.config.module.outDir);
|
|
1294
1324
|
const uploadPath = ctx.config.module.uploadPath;
|
|
1295
1325
|
runCommand("hs", ["cms", "upload", outDir, uploadPath], {
|
|
1296
1326
|
cwd: ctx.projectRoot,
|
|
@@ -1402,7 +1432,7 @@ async function runDeploy(ctx, options) {
|
|
|
1402
1432
|
if (!ctx.global.json) {
|
|
1403
1433
|
printCommandDone(
|
|
1404
1434
|
ctx,
|
|
1405
|
-
`Deploy complete (${
|
|
1435
|
+
`Deploy complete (${resolve12(ctx.projectRoot, ctx.config.module.outDir)} \u2192 ${ctx.config.module.uploadPath})`
|
|
1406
1436
|
);
|
|
1407
1437
|
}
|
|
1408
1438
|
return ExitCode.SUCCESS;
|
|
@@ -1410,8 +1440,8 @@ async function runDeploy(ctx, options) {
|
|
|
1410
1440
|
|
|
1411
1441
|
// src/cli/commands/audit.ts
|
|
1412
1442
|
import { createHash } from "crypto";
|
|
1413
|
-
import { cpSync, existsSync as
|
|
1414
|
-
import { resolve as
|
|
1443
|
+
import { cpSync, existsSync as existsSync8, mkdirSync, readFileSync as readFileSync6, readdirSync, rmSync, statSync } from "fs";
|
|
1444
|
+
import { resolve as resolve13, join as join2 } from "path";
|
|
1415
1445
|
var FORBIDDEN_SHIP_TOKENS = [
|
|
1416
1446
|
"ModuleConfigOverlay",
|
|
1417
1447
|
"module-config-store",
|
|
@@ -1423,7 +1453,7 @@ function hashFile(path) {
|
|
|
1423
1453
|
return createHash("sha256").update(readFileSync6(path)).digest("hex");
|
|
1424
1454
|
}
|
|
1425
1455
|
function walkFiles(dir) {
|
|
1426
|
-
if (!
|
|
1456
|
+
if (!existsSync8(dir)) return [];
|
|
1427
1457
|
const entries = readdirSync(dir);
|
|
1428
1458
|
const files = [];
|
|
1429
1459
|
for (const entry of entries) {
|
|
@@ -1439,8 +1469,8 @@ function walkFiles(dir) {
|
|
|
1439
1469
|
async function runAuditShipBundle(ctx) {
|
|
1440
1470
|
const root = ctx.projectRoot;
|
|
1441
1471
|
const targets = [
|
|
1442
|
-
|
|
1443
|
-
|
|
1472
|
+
resolve13(root, "dist/cdn"),
|
|
1473
|
+
resolve13(root, ctx.config.module.outDir)
|
|
1444
1474
|
];
|
|
1445
1475
|
const violations = [];
|
|
1446
1476
|
for (const dir of targets) {
|
|
@@ -1473,21 +1503,21 @@ async function runAuditBaselineCapture(ctx, options) {
|
|
|
1473
1503
|
const root = ctx.projectRoot;
|
|
1474
1504
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1475
1505
|
const sources = [
|
|
1476
|
-
{ label: "cdn", path:
|
|
1477
|
-
{ label: "hubspot", path:
|
|
1506
|
+
{ label: "cdn", path: resolve13(root, "dist/cdn") },
|
|
1507
|
+
{ label: "hubspot", path: resolve13(root, ctx.config.module.outDir) }
|
|
1478
1508
|
];
|
|
1479
1509
|
if (ctx.global.dryRun) {
|
|
1480
1510
|
ctx.logger.info(`[dry-run] Would capture baseline to ${baseline}`);
|
|
1481
1511
|
return ExitCode.SUCCESS;
|
|
1482
1512
|
}
|
|
1483
1513
|
for (const { label, path } of sources) {
|
|
1484
|
-
if (!
|
|
1514
|
+
if (!existsSync8(path)) {
|
|
1485
1515
|
throw new WpHsError(`Cannot capture baseline \u2014 missing ${path}`, {
|
|
1486
1516
|
exitCode: ExitCode.USER_ERROR,
|
|
1487
1517
|
remediation: "Run wp build before wp audit baseline --capture."
|
|
1488
1518
|
});
|
|
1489
1519
|
}
|
|
1490
|
-
const target =
|
|
1520
|
+
const target = resolve13(baseline, label);
|
|
1491
1521
|
rmSync(target, { recursive: true, force: true });
|
|
1492
1522
|
mkdirSync(baseline, { recursive: true });
|
|
1493
1523
|
cpSync(path, target, { recursive: true });
|
|
@@ -1503,13 +1533,13 @@ async function runAuditParity(ctx, options) {
|
|
|
1503
1533
|
const root = ctx.projectRoot;
|
|
1504
1534
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1505
1535
|
const currentDirs = [
|
|
1506
|
-
{ label: "cdn", path:
|
|
1507
|
-
{ label: "hubspot", path:
|
|
1536
|
+
{ label: "cdn", path: resolve13(root, "dist/cdn") },
|
|
1537
|
+
{ label: "hubspot", path: resolve13(root, ctx.config.module.outDir) }
|
|
1508
1538
|
];
|
|
1509
1539
|
const mismatches = [];
|
|
1510
1540
|
for (const { label, path } of currentDirs) {
|
|
1511
|
-
const baselinePath =
|
|
1512
|
-
if (!
|
|
1541
|
+
const baselinePath = resolve13(baseline, label);
|
|
1542
|
+
if (!existsSync8(baselinePath)) {
|
|
1513
1543
|
ctx.logger.warn(`No baseline at ${baselinePath} \u2014 skipping ${label} parity`);
|
|
1514
1544
|
continue;
|
|
1515
1545
|
}
|
|
@@ -1524,8 +1554,8 @@ async function runAuditParity(ctx, options) {
|
|
|
1524
1554
|
}
|
|
1525
1555
|
for (const file of baselineFiles) {
|
|
1526
1556
|
const rel = file.replace(baselinePath, "");
|
|
1527
|
-
const current =
|
|
1528
|
-
if (!
|
|
1557
|
+
const current = resolve13(path, rel.slice(1));
|
|
1558
|
+
if (!existsSync8(current)) continue;
|
|
1529
1559
|
const bh = hashFile(file);
|
|
1530
1560
|
const ch = hashFile(current);
|
|
1531
1561
|
if (bh !== ch) {
|
|
@@ -1552,7 +1582,7 @@ var SECRET_PATTERNS = [
|
|
|
1552
1582
|
];
|
|
1553
1583
|
async function runAuditSecrets(ctx) {
|
|
1554
1584
|
const root = ctx.projectRoot;
|
|
1555
|
-
const scanRoots = ["src", "public", ".wphs/config"].map((d) =>
|
|
1585
|
+
const scanRoots = ["src", "public", ".wphs/config"].map((d) => resolve13(root, d));
|
|
1556
1586
|
const findings = [];
|
|
1557
1587
|
for (const dir of scanRoots) {
|
|
1558
1588
|
for (const file of walkFiles(dir)) {
|
|
@@ -1584,13 +1614,13 @@ async function runAuditSecrets(ctx) {
|
|
|
1584
1614
|
}
|
|
1585
1615
|
|
|
1586
1616
|
// src/cli/commands/init.ts
|
|
1587
|
-
import { cpSync as cpSync2, existsSync as
|
|
1588
|
-
import { basename as basename2, resolve as
|
|
1617
|
+
import { cpSync as cpSync2, existsSync as existsSync11, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
|
|
1618
|
+
import { basename as basename2, resolve as resolve16 } from "path";
|
|
1589
1619
|
import * as p2 from "@clack/prompts";
|
|
1590
1620
|
|
|
1591
1621
|
// src/lib/init-router-scaffold.ts
|
|
1592
|
-
import { existsSync as
|
|
1593
|
-
import { resolve as
|
|
1622
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, rmSync as rmSync2, writeFileSync } from "fs";
|
|
1623
|
+
import { resolve as resolve14 } from "path";
|
|
1594
1624
|
var ROUTER_FILES = [
|
|
1595
1625
|
"src/routes",
|
|
1596
1626
|
"src/routeTree.gen.ts",
|
|
@@ -1604,10 +1634,10 @@ function isModuleTemplate(template) {
|
|
|
1604
1634
|
return template === "module-hybrid-cdn" || template === "module-monolith";
|
|
1605
1635
|
}
|
|
1606
1636
|
function writeRouterConfig(targetDir, mode) {
|
|
1607
|
-
const configDir =
|
|
1637
|
+
const configDir = resolve14(targetDir, "src/config");
|
|
1608
1638
|
mkdirSync2(configDir, { recursive: true });
|
|
1609
1639
|
writeFileSync(
|
|
1610
|
-
|
|
1640
|
+
resolve14(configDir, "router.ts"),
|
|
1611
1641
|
`/** Router history \u2014 set by \`wp init\`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
|
|
1612
1642
|
export type RouterHistoryMode = 'hash' | 'browser'
|
|
1613
1643
|
|
|
@@ -1617,7 +1647,7 @@ export const ROUTER_HISTORY: RouterHistoryMode = '${mode}'
|
|
|
1617
1647
|
}
|
|
1618
1648
|
function writeMountDevPreviewRouter(targetDir) {
|
|
1619
1649
|
writeFileSync(
|
|
1620
|
-
|
|
1650
|
+
resolve14(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1621
1651
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1622
1652
|
|
|
1623
1653
|
import { App } from '@/app'
|
|
@@ -1631,7 +1661,7 @@ export async function mountDevPreview() {
|
|
|
1631
1661
|
}
|
|
1632
1662
|
function writeMountDevPreviewSinglePage(targetDir) {
|
|
1633
1663
|
writeFileSync(
|
|
1634
|
-
|
|
1664
|
+
resolve14(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1635
1665
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1636
1666
|
|
|
1637
1667
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1645,7 +1675,7 @@ export async function mountDevPreview() {
|
|
|
1645
1675
|
}
|
|
1646
1676
|
function writeMountPortalRouter(targetDir) {
|
|
1647
1677
|
writeFileSync(
|
|
1648
|
-
|
|
1678
|
+
resolve14(targetDir, "src/portal/mount-portal.tsx"),
|
|
1649
1679
|
`import ReactDOM from 'react-dom/client'
|
|
1650
1680
|
|
|
1651
1681
|
import { App } from '@/app'
|
|
@@ -1663,7 +1693,7 @@ export async function mountPortal() {
|
|
|
1663
1693
|
}
|
|
1664
1694
|
function writeMountPortalSinglePage(targetDir) {
|
|
1665
1695
|
writeFileSync(
|
|
1666
|
-
|
|
1696
|
+
resolve14(targetDir, "src/portal/mount-portal.tsx"),
|
|
1667
1697
|
`import ReactDOM from 'react-dom/client'
|
|
1668
1698
|
|
|
1669
1699
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1680,8 +1710,8 @@ export async function mountPortal() {
|
|
|
1680
1710
|
);
|
|
1681
1711
|
}
|
|
1682
1712
|
function writeStarterSinglePage(targetDir) {
|
|
1683
|
-
const file =
|
|
1684
|
-
if (
|
|
1713
|
+
const file = resolve14(targetDir, "src/features/starter/components/starter-single-page.tsx");
|
|
1714
|
+
if (existsSync9(file)) {
|
|
1685
1715
|
return;
|
|
1686
1716
|
}
|
|
1687
1717
|
writeFileSync(
|
|
@@ -1707,15 +1737,15 @@ export function StarterSinglePage() {
|
|
|
1707
1737
|
);
|
|
1708
1738
|
}
|
|
1709
1739
|
function removePath(targetDir, relativePath) {
|
|
1710
|
-
const full =
|
|
1711
|
-
if (!
|
|
1740
|
+
const full = resolve14(targetDir, relativePath);
|
|
1741
|
+
if (!existsSync9(full)) {
|
|
1712
1742
|
return;
|
|
1713
1743
|
}
|
|
1714
1744
|
rmSync2(full, { recursive: true, force: true });
|
|
1715
1745
|
}
|
|
1716
1746
|
function stripTanStackFromPackage(targetDir) {
|
|
1717
|
-
const pkgPath =
|
|
1718
|
-
if (!
|
|
1747
|
+
const pkgPath = resolve14(targetDir, "package.json");
|
|
1748
|
+
if (!existsSync9(pkgPath)) {
|
|
1719
1749
|
return;
|
|
1720
1750
|
}
|
|
1721
1751
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -1745,8 +1775,8 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
1745
1775
|
writeRouterConfig(targetDir, mode);
|
|
1746
1776
|
writeMountDevPreviewRouter(targetDir);
|
|
1747
1777
|
writeMountPortalRouter(targetDir);
|
|
1748
|
-
const featuresIndex =
|
|
1749
|
-
if (
|
|
1778
|
+
const featuresIndex = resolve14(targetDir, "src/features/starter/index.ts");
|
|
1779
|
+
if (existsSync9(featuresIndex)) {
|
|
1750
1780
|
let content = readFileSync7(featuresIndex, "utf8");
|
|
1751
1781
|
if (!content.includes("StarterSinglePage")) {
|
|
1752
1782
|
content = content.replace(
|
|
@@ -1759,8 +1789,8 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
1759
1789
|
}
|
|
1760
1790
|
|
|
1761
1791
|
// src/lib/project-setup.ts
|
|
1762
|
-
import { copyFileSync, existsSync as
|
|
1763
|
-
import { basename, resolve as
|
|
1792
|
+
import { copyFileSync, existsSync as existsSync10, writeFileSync as writeFileSync2 } from "fs";
|
|
1793
|
+
import { basename, resolve as resolve15 } from "path";
|
|
1764
1794
|
function envFileForTier(tier) {
|
|
1765
1795
|
return tier === "dev" ? ".env.dev" : tier === "stg" ? ".env.stg" : ".env.prod";
|
|
1766
1796
|
}
|
|
@@ -1770,13 +1800,13 @@ function tiersToSetup(config, tier) {
|
|
|
1770
1800
|
}
|
|
1771
1801
|
function firstExisting(paths) {
|
|
1772
1802
|
for (const path of paths) {
|
|
1773
|
-
if (
|
|
1803
|
+
if (existsSync10(path)) return path;
|
|
1774
1804
|
}
|
|
1775
1805
|
return null;
|
|
1776
1806
|
}
|
|
1777
1807
|
function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
1778
|
-
const target =
|
|
1779
|
-
if (
|
|
1808
|
+
const target = resolve15(projectRoot, targetName);
|
|
1809
|
+
if (existsSync10(target)) {
|
|
1780
1810
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1781
1811
|
}
|
|
1782
1812
|
const source = firstExisting(sourceCandidates);
|
|
@@ -1798,8 +1828,8 @@ function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
|
1798
1828
|
};
|
|
1799
1829
|
}
|
|
1800
1830
|
function writeMissingFile(projectRoot, targetName, content, dryRun) {
|
|
1801
|
-
const target =
|
|
1802
|
-
if (
|
|
1831
|
+
const target = resolve15(projectRoot, targetName);
|
|
1832
|
+
if (existsSync10(target)) {
|
|
1803
1833
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1804
1834
|
}
|
|
1805
1835
|
if (!dryRun) {
|
|
@@ -1829,8 +1859,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1829
1859
|
const templateDir = kitTemplateDir(config);
|
|
1830
1860
|
results.push(
|
|
1831
1861
|
copyMissingFile(projectRoot, HUBSPOT_CONFIG_FILE, [
|
|
1832
|
-
|
|
1833
|
-
|
|
1862
|
+
resolve15(projectRoot, HUBSPOT_CONFIG_EXAMPLE),
|
|
1863
|
+
resolve15(templateDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1834
1864
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1835
1865
|
], dryRun)
|
|
1836
1866
|
);
|
|
@@ -1839,8 +1869,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1839
1869
|
const envExample = `${envFile}.example`;
|
|
1840
1870
|
results.push(
|
|
1841
1871
|
copyMissingFile(projectRoot, envFile, [
|
|
1842
|
-
|
|
1843
|
-
|
|
1872
|
+
resolve15(projectRoot, envExample),
|
|
1873
|
+
resolve15(sharedDir, envExample)
|
|
1844
1874
|
], dryRun)
|
|
1845
1875
|
);
|
|
1846
1876
|
}
|
|
@@ -1848,8 +1878,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1848
1878
|
const mirror = config.cdn.mirror;
|
|
1849
1879
|
results.push(
|
|
1850
1880
|
copyMissingFile(projectRoot, mirror, [
|
|
1851
|
-
|
|
1852
|
-
|
|
1881
|
+
resolve15(projectRoot, `${mirror}.example`),
|
|
1882
|
+
resolve15(sharedDir, "portal.cdn.json.example")
|
|
1853
1883
|
], dryRun)
|
|
1854
1884
|
);
|
|
1855
1885
|
const buildIdFile = config.cdn.buildIdFile;
|
|
@@ -1859,15 +1889,15 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1859
1889
|
}
|
|
1860
1890
|
for (const file of templateViteFiles(config)) {
|
|
1861
1891
|
results.push(
|
|
1862
|
-
copyMissingFile(projectRoot, file, [
|
|
1892
|
+
copyMissingFile(projectRoot, file, [resolve15(templateDir, file)], dryRun)
|
|
1863
1893
|
);
|
|
1864
1894
|
}
|
|
1865
1895
|
if (isModuleProject(config) && !isThemeProject(config)) {
|
|
1866
1896
|
const adapter = config.dev.adapter;
|
|
1867
|
-
const adapterPath =
|
|
1868
|
-
if (!
|
|
1869
|
-
const templateAdapter =
|
|
1870
|
-
if (
|
|
1897
|
+
const adapterPath = resolve15(projectRoot, adapter);
|
|
1898
|
+
if (!existsSync10(adapterPath)) {
|
|
1899
|
+
const templateAdapter = resolve15(templateDir, "wphs.adapter.ts");
|
|
1900
|
+
if (existsSync10(templateAdapter)) {
|
|
1871
1901
|
results.push(copyMissingFile(projectRoot, adapter, [templateAdapter], dryRun));
|
|
1872
1902
|
} else {
|
|
1873
1903
|
results.push({
|
|
@@ -1884,26 +1914,26 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1884
1914
|
}
|
|
1885
1915
|
function copySharedSetupExamples(targetDir) {
|
|
1886
1916
|
const sharedDir = kitPath("templates", "shared");
|
|
1887
|
-
if (!
|
|
1917
|
+
if (!existsSync10(sharedDir)) return;
|
|
1888
1918
|
for (const name of [
|
|
1889
1919
|
".env.dev.example",
|
|
1890
1920
|
".env.stg.example",
|
|
1891
1921
|
".env.prod.example",
|
|
1892
1922
|
"portal.cdn.json.example"
|
|
1893
1923
|
]) {
|
|
1894
|
-
const src =
|
|
1895
|
-
const dest =
|
|
1896
|
-
if (
|
|
1924
|
+
const src = resolve15(sharedDir, name);
|
|
1925
|
+
const dest = resolve15(targetDir, name);
|
|
1926
|
+
if (existsSync10(src) && !existsSync10(dest)) {
|
|
1897
1927
|
copyFileSync(src, dest);
|
|
1898
1928
|
}
|
|
1899
1929
|
}
|
|
1900
1930
|
const hubspotExample = firstExisting([
|
|
1901
|
-
|
|
1931
|
+
resolve15(sharedDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1902
1932
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1903
1933
|
]);
|
|
1904
1934
|
if (!hubspotExample) return;
|
|
1905
|
-
const destHubspot =
|
|
1906
|
-
if (!
|
|
1935
|
+
const destHubspot = resolve15(targetDir, HUBSPOT_CONFIG_EXAMPLE);
|
|
1936
|
+
if (!existsSync10(destHubspot) && !existsSync10(resolve15(targetDir, HUBSPOT_CONFIG_FILE))) {
|
|
1907
1937
|
copyFileSync(hubspotExample, destHubspot);
|
|
1908
1938
|
}
|
|
1909
1939
|
}
|
|
@@ -1919,25 +1949,7 @@ function formatSetupHint(results) {
|
|
|
1919
1949
|
return void 0;
|
|
1920
1950
|
}
|
|
1921
1951
|
|
|
1922
|
-
// src/lib/
|
|
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)";
|
|
1931
|
-
}
|
|
1932
|
-
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
1933
|
-
return void 0;
|
|
1934
|
-
}
|
|
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
|
-
|
|
1940
|
-
// src/cli/commands/init.ts
|
|
1952
|
+
// src/lib/template-scripts.ts
|
|
1941
1953
|
var TEMPLATE_SCRIPTS = {
|
|
1942
1954
|
"module-hybrid-cdn": {
|
|
1943
1955
|
dev: "wp dev",
|
|
@@ -1964,6 +1976,26 @@ var TEMPLATE_SCRIPTS = {
|
|
|
1964
1976
|
doctor: "wp doctor"
|
|
1965
1977
|
}
|
|
1966
1978
|
};
|
|
1979
|
+
|
|
1980
|
+
// src/lib/project-name.ts
|
|
1981
|
+
var PROJECT_NAME_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
1982
|
+
function normalizeProjectName(input) {
|
|
1983
|
+
return input.trim().toLowerCase().replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
1984
|
+
}
|
|
1985
|
+
function validateProjectName(name) {
|
|
1986
|
+
if (!name) return "Project name is required";
|
|
1987
|
+
if (!PROJECT_NAME_PATTERN.test(name)) {
|
|
1988
|
+
return "Use lowercase letters, numbers, and hyphens (e.g. my-portal-module)";
|
|
1989
|
+
}
|
|
1990
|
+
if (name.length > 64) return "Project name is too long (max 64 characters)";
|
|
1991
|
+
return void 0;
|
|
1992
|
+
}
|
|
1993
|
+
function moduleOutDirFromSlug(slug) {
|
|
1994
|
+
const pascal = slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1995
|
+
return `dist/${pascal || "HubSpot"}.Module`;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
// src/cli/commands/init.ts
|
|
1967
1999
|
function buildConfigForProject(template, projectName, router) {
|
|
1968
2000
|
const base = { ...DEFAULT_WOODSPORTAL_HS, projectType: template };
|
|
1969
2001
|
if (template === "theme-starter") {
|
|
@@ -1995,7 +2027,7 @@ function buildConfigForProject(template, projectName, router) {
|
|
|
1995
2027
|
return config;
|
|
1996
2028
|
}
|
|
1997
2029
|
function assertTargetDirIsScaffoldable(targetDir) {
|
|
1998
|
-
if (!
|
|
2030
|
+
if (!existsSync11(targetDir)) return;
|
|
1999
2031
|
const entries = readdirSync2(targetDir).filter((entry) => entry !== ".DS_Store");
|
|
2000
2032
|
if (entries.length > 0) {
|
|
2001
2033
|
throw new WpHsError(`Target directory is not empty: ${targetDir}`, {
|
|
@@ -2006,7 +2038,7 @@ function assertTargetDirIsScaffoldable(targetDir) {
|
|
|
2006
2038
|
}
|
|
2007
2039
|
function resolveInitTargetDir(options, interactiveName) {
|
|
2008
2040
|
if (options.directory) {
|
|
2009
|
-
return
|
|
2041
|
+
return resolve16(options.directory);
|
|
2010
2042
|
}
|
|
2011
2043
|
const parentDir = process.cwd();
|
|
2012
2044
|
const slug = options.name ?? interactiveName;
|
|
@@ -2020,7 +2052,7 @@ function resolveInitTargetDir(options, interactiveName) {
|
|
|
2020
2052
|
if (error) {
|
|
2021
2053
|
throw new WpHsError(error, { exitCode: ExitCode.USER_ERROR });
|
|
2022
2054
|
}
|
|
2023
|
-
return
|
|
2055
|
+
return resolve16(parentDir, slug);
|
|
2024
2056
|
}
|
|
2025
2057
|
async function runInit(ctx, options) {
|
|
2026
2058
|
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json;
|
|
@@ -2093,7 +2125,7 @@ async function runInit(ctx, options) {
|
|
|
2093
2125
|
const slug = projectName ?? basename2(targetDir);
|
|
2094
2126
|
assertTargetDirIsScaffoldable(targetDir);
|
|
2095
2127
|
const templateDir = kitPath("templates", template);
|
|
2096
|
-
if (!
|
|
2128
|
+
if (!existsSync11(templateDir)) {
|
|
2097
2129
|
throw new WpHsError(`Template not found: ${template}`, { exitCode: ExitCode.USER_ERROR });
|
|
2098
2130
|
}
|
|
2099
2131
|
if (ctx.global.dryRun) {
|
|
@@ -2104,10 +2136,10 @@ async function runInit(ctx, options) {
|
|
|
2104
2136
|
cpSync2(templateDir, targetDir, { recursive: true });
|
|
2105
2137
|
copySharedSetupExamples(targetDir);
|
|
2106
2138
|
const config = buildConfigForProject(template, slug, router);
|
|
2107
|
-
writeFileSync3(
|
|
2139
|
+
writeFileSync3(resolve16(targetDir, WOODSCLI_CONFIG_FILE), `${JSON.stringify(config, null, 2)}
|
|
2108
2140
|
`);
|
|
2109
|
-
const pkgPath =
|
|
2110
|
-
if (
|
|
2141
|
+
const pkgPath = resolve16(targetDir, "package.json");
|
|
2142
|
+
if (existsSync11(pkgPath)) {
|
|
2111
2143
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
2112
2144
|
pkg.name = slug;
|
|
2113
2145
|
pkg.scripts = { ...pkg.scripts, ...TEMPLATE_SCRIPTS[template] };
|
|
@@ -2126,7 +2158,7 @@ async function runInit(ctx, options) {
|
|
|
2126
2158
|
logger: ctx.logger,
|
|
2127
2159
|
dryRun: false
|
|
2128
2160
|
});
|
|
2129
|
-
const gitignorePath =
|
|
2161
|
+
const gitignorePath = resolve16(targetDir, ".gitignore");
|
|
2130
2162
|
const gitignoreExtras = [
|
|
2131
2163
|
"hubspot.config.yml",
|
|
2132
2164
|
".wphs/config/module-config.local.json",
|
|
@@ -2139,7 +2171,7 @@ async function runInit(ctx, options) {
|
|
|
2139
2171
|
".wphs/.kit-bootstrap.tsx",
|
|
2140
2172
|
".wphs/.kit-bootstrap-greenfield.tsx"
|
|
2141
2173
|
];
|
|
2142
|
-
const existing =
|
|
2174
|
+
const existing = existsSync11(gitignorePath) ? readFileSync8(gitignorePath, "utf8") : "";
|
|
2143
2175
|
const toAppend = gitignoreExtras.filter((line) => !existing.includes(line)).join("\n");
|
|
2144
2176
|
if (toAppend) {
|
|
2145
2177
|
writeFileSync3(gitignorePath, `${existing.trimEnd()}
|
|
@@ -2155,100 +2187,8 @@ ${toAppend}
|
|
|
2155
2187
|
return ExitCode.SUCCESS;
|
|
2156
2188
|
}
|
|
2157
2189
|
|
|
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
2190
|
// src/cli/commands/migrate.ts
|
|
2251
|
-
import { cpSync as cpSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as
|
|
2191
|
+
import { cpSync as cpSync3, existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, readdirSync as readdirSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2252
2192
|
import { join as join3, resolve as resolve17 } from "path";
|
|
2253
2193
|
var LEGACY_DIRS = [".dev", ".woodsportal"];
|
|
2254
2194
|
var LEGACY_BASELINE = ".wp-hs-baseline";
|
|
@@ -2303,12 +2243,12 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2303
2243
|
}
|
|
2304
2244
|
}
|
|
2305
2245
|
if (existsSync12(woodscliPath)) {
|
|
2306
|
-
const raw = JSON.parse(
|
|
2246
|
+
const raw = JSON.parse(readFileSync9(woodscliPath, "utf8"));
|
|
2307
2247
|
if (raw?.dev?.moduleConfigDir && raw.dev.moduleConfigDir !== WPHS_WORKSPACE_DIR) {
|
|
2308
2248
|
actions.push(`Update dev.moduleConfigDir \u2192 .wphs in project config`);
|
|
2309
2249
|
if (!ctx.global.dryRun) {
|
|
2310
2250
|
raw.dev.moduleConfigDir = WPHS_WORKSPACE_DIR;
|
|
2311
|
-
|
|
2251
|
+
writeFileSync4(woodscliPath, `${JSON.stringify(raw, null, 2)}
|
|
2312
2252
|
`);
|
|
2313
2253
|
}
|
|
2314
2254
|
}
|
|
@@ -2337,10 +2277,10 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2337
2277
|
}
|
|
2338
2278
|
|
|
2339
2279
|
// src/cli/commands/examples.ts
|
|
2340
|
-
import { readFileSync as
|
|
2280
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
2341
2281
|
function loadManifest() {
|
|
2342
2282
|
const manifestPath = kitPath("examples", "manifest.json");
|
|
2343
|
-
const raw = JSON.parse(
|
|
2283
|
+
const raw = JSON.parse(readFileSync10(manifestPath, "utf8"));
|
|
2344
2284
|
return raw.scenarios;
|
|
2345
2285
|
}
|
|
2346
2286
|
async function runExamplesList(ctx, options = {}) {
|
|
@@ -2373,7 +2313,7 @@ async function runExamplesSmoke(ctx, options = {}) {
|
|
|
2373
2313
|
}
|
|
2374
2314
|
|
|
2375
2315
|
// src/cli/commands/fields.ts
|
|
2376
|
-
import { cpSync as cpSync4, existsSync as existsSync13, readFileSync as
|
|
2316
|
+
import { cpSync as cpSync4, existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
|
|
2377
2317
|
import { resolve as resolve18 } from "path";
|
|
2378
2318
|
async function runFieldsSync(ctx, options = {}) {
|
|
2379
2319
|
const direction = options.direction ?? "pull";
|
|
@@ -2406,7 +2346,7 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
2406
2346
|
ctx.logger.info(`[dry-run] Would write ${cachePath} \u2192 ${fieldsPath}`);
|
|
2407
2347
|
return ExitCode.SUCCESS;
|
|
2408
2348
|
}
|
|
2409
|
-
|
|
2349
|
+
writeFileSync5(fieldsPath, readFileSync11(cachePath, "utf8"));
|
|
2410
2350
|
ctx.logger.success(`Restored fields from cache \u2192 ${fieldsPath}`);
|
|
2411
2351
|
return ExitCode.SUCCESS;
|
|
2412
2352
|
}
|
|
@@ -2543,10 +2483,10 @@ async function runSetup(ctx, options = {}) {
|
|
|
2543
2483
|
}
|
|
2544
2484
|
|
|
2545
2485
|
// src/cli/commands/portal.ts
|
|
2546
|
-
import * as
|
|
2486
|
+
import * as p3 from "@clack/prompts";
|
|
2547
2487
|
|
|
2548
2488
|
// src/lib/hubspot-portals.ts
|
|
2549
|
-
import { existsSync as existsSync15, readFileSync as
|
|
2489
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
2550
2490
|
import { resolve as resolve20 } from "path";
|
|
2551
2491
|
function stripYamlScalar(value) {
|
|
2552
2492
|
const trimmed = value.trim();
|
|
@@ -2592,13 +2532,13 @@ function readHubSpotPortals(projectRoot) {
|
|
|
2592
2532
|
remediation: "Run wp setup, then add your portal + PAT."
|
|
2593
2533
|
});
|
|
2594
2534
|
}
|
|
2595
|
-
const raw =
|
|
2535
|
+
const raw = readFileSync12(configPath, "utf8");
|
|
2596
2536
|
const { defaultPortal, portals } = parseHubSpotConfigPortals(raw);
|
|
2597
2537
|
return { configPath, defaultPortal, portals };
|
|
2598
2538
|
}
|
|
2599
2539
|
function resolvePortalName(portals, nameOrId) {
|
|
2600
2540
|
const needle = nameOrId.trim();
|
|
2601
|
-
return portals.find((
|
|
2541
|
+
return portals.find((p4) => p4.name === needle) ?? portals.find((p4) => p4.portalId === needle) ?? null;
|
|
2602
2542
|
}
|
|
2603
2543
|
|
|
2604
2544
|
// src/cli/commands/portal.ts
|
|
@@ -2653,7 +2593,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
2653
2593
|
remediation: "wp portal use <name> \xB7 wp portal list"
|
|
2654
2594
|
});
|
|
2655
2595
|
}
|
|
2656
|
-
const answer = await
|
|
2596
|
+
const answer = await p3.select({
|
|
2657
2597
|
message: "Set default HubSpot portal",
|
|
2658
2598
|
options: data.portals.map((portal2) => ({
|
|
2659
2599
|
value: portal2.name,
|
|
@@ -2661,7 +2601,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
2661
2601
|
hint: portal2.name === data.defaultPortal ? `current default \xB7 ${portal2.portalId ?? "no id"}` : portal2.portalId ?? void 0
|
|
2662
2602
|
}))
|
|
2663
2603
|
});
|
|
2664
|
-
if (
|
|
2604
|
+
if (p3.isCancel(answer)) {
|
|
2665
2605
|
throw new WpHsError("Portal selection cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2666
2606
|
}
|
|
2667
2607
|
targetName = answer;
|
|
@@ -2722,7 +2662,7 @@ async function runWatch(ctx, options) {
|
|
|
2722
2662
|
// src/cli/program.ts
|
|
2723
2663
|
function readVersion() {
|
|
2724
2664
|
try {
|
|
2725
|
-
const pkg = JSON.parse(
|
|
2665
|
+
const pkg = JSON.parse(readFileSync13(resolve21(getKitRoot(), "package.json"), "utf8"));
|
|
2726
2666
|
return pkg.version ?? "0.0.0";
|
|
2727
2667
|
} catch {
|
|
2728
2668
|
return "0.0.0";
|
|
@@ -2807,10 +2747,6 @@ function createProgram() {
|
|
|
2807
2747
|
);
|
|
2808
2748
|
}
|
|
2809
2749
|
);
|
|
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
2750
|
program2.command("migrate").description("Migrate legacy .dev/ or .wp-hs-baseline/ to .wphs/").action(async () => {
|
|
2815
2751
|
const global = program2.opts();
|
|
2816
2752
|
await handle(() => runMigrate(buildContext(global)));
|