@woodsportal/hubspot-kit 1.0.41-dev.6 → 1.0.42-dev.0
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 +6 -0
- package/dist/cli.js +452 -404
- package/dist/cli.js.map +1 -1
- package/dist/ensure-wphs-workspace-5S3GTO6K.js +234 -0
- package/dist/ensure-wphs-workspace-5S3GTO6K.js.map +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -26,7 +26,6 @@ import {
|
|
|
26
26
|
isWpHsError,
|
|
27
27
|
runCommand,
|
|
28
28
|
runCommandAsync,
|
|
29
|
-
runNodeScript,
|
|
30
29
|
runNodeScriptAsync,
|
|
31
30
|
runNodeScriptLongRunning
|
|
32
31
|
} from "./chunk-4GOJPOW3.js";
|
|
@@ -276,9 +275,20 @@ function defaultBaselineDir(projectRoot, config) {
|
|
|
276
275
|
return resolve2(projectRoot, workspace, "audit", "baseline");
|
|
277
276
|
}
|
|
278
277
|
|
|
278
|
+
// src/lib/exit-cli.ts
|
|
279
|
+
async function exitCli(code) {
|
|
280
|
+
await new Promise((resolve23) => {
|
|
281
|
+
setImmediate(resolve23);
|
|
282
|
+
});
|
|
283
|
+
await new Promise((resolve23) => {
|
|
284
|
+
setTimeout(resolve23, 0);
|
|
285
|
+
});
|
|
286
|
+
process.exit(code);
|
|
287
|
+
}
|
|
288
|
+
|
|
279
289
|
// src/cli/commands/doctor.ts
|
|
280
|
-
import { existsSync as
|
|
281
|
-
import { join as join2, resolve as
|
|
290
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
291
|
+
import { join as join2, resolve as resolve9 } from "path";
|
|
282
292
|
|
|
283
293
|
// src/lib/cli-spinner.ts
|
|
284
294
|
import * as p from "@clack/prompts";
|
|
@@ -526,7 +536,7 @@ function doctorHubSpotChecks(projectRoot) {
|
|
|
526
536
|
ok: assessment.hsCliInstalled,
|
|
527
537
|
message: assessment.hsCliInstalled ? `HubSpot CLI ${assessment.hsCliVersion ?? ""}`.trim() : "HubSpot CLI not installed",
|
|
528
538
|
remediation: assessment.hsCliInstalled ? void 0 : "npm i -g @hubspot/cli",
|
|
529
|
-
severity: "
|
|
539
|
+
severity: "warn"
|
|
530
540
|
},
|
|
531
541
|
{
|
|
532
542
|
name: "hubspot-config",
|
|
@@ -545,6 +555,215 @@ function doctorHubSpotChecks(projectRoot) {
|
|
|
545
555
|
];
|
|
546
556
|
}
|
|
547
557
|
|
|
558
|
+
// src/lib/cdn-github-setup.ts
|
|
559
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
560
|
+
import { resolve as resolve5 } from "path";
|
|
561
|
+
import * as p2 from "@clack/prompts";
|
|
562
|
+
|
|
563
|
+
// src/lib/project-env-files.ts
|
|
564
|
+
import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
565
|
+
import { resolve as resolve4 } from "path";
|
|
566
|
+
var ENV_LOCAL_FILE = ".env.local";
|
|
567
|
+
var ENV_GITIGNORE_LINES = [".env", ".env.*", "!.env*.example", ENV_LOCAL_FILE];
|
|
568
|
+
function parseEnvFile(content) {
|
|
569
|
+
const result = {};
|
|
570
|
+
for (const line of content.split("\n")) {
|
|
571
|
+
const trimmed = line.trim();
|
|
572
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
573
|
+
const eq = trimmed.indexOf("=");
|
|
574
|
+
if (eq <= 0) continue;
|
|
575
|
+
const key = trimmed.slice(0, eq).trim();
|
|
576
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
577
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
578
|
+
value = value.slice(1, -1);
|
|
579
|
+
}
|
|
580
|
+
result[key] = value;
|
|
581
|
+
}
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
function readEnvLocal(projectRoot) {
|
|
585
|
+
const path = resolve4(projectRoot, ENV_LOCAL_FILE);
|
|
586
|
+
if (!existsSync3(path)) return {};
|
|
587
|
+
return parseEnvFile(readFileSync5(path, "utf8"));
|
|
588
|
+
}
|
|
589
|
+
function formatEnvLocal(entries) {
|
|
590
|
+
const lines = [
|
|
591
|
+
"# Local secrets \u2014 gitignored. Created by wp init / wp setup (hybrid CDN).",
|
|
592
|
+
"# PORTAL_CDN_PUBLISH_TOKEN needs repo push on your portal.cdn.json mirror.",
|
|
593
|
+
""
|
|
594
|
+
];
|
|
595
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
596
|
+
if (value.includes(" ") || value.includes("#")) {
|
|
597
|
+
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
598
|
+
} else {
|
|
599
|
+
lines.push(`${key}=${value}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
return `${lines.join("\n").trimEnd()}
|
|
603
|
+
`;
|
|
604
|
+
}
|
|
605
|
+
function writeEnvLocal(projectRoot, entries) {
|
|
606
|
+
const path = resolve4(projectRoot, ENV_LOCAL_FILE);
|
|
607
|
+
const existing = existsSync3(path) ? parseEnvFile(readFileSync5(path, "utf8")) : {};
|
|
608
|
+
writeFileSync2(path, formatEnvLocal({ ...existing, ...entries }));
|
|
609
|
+
}
|
|
610
|
+
function mergeProjectEnv(projectRoot, base = process.env) {
|
|
611
|
+
const local = readEnvLocal(projectRoot);
|
|
612
|
+
const merged = { ...base };
|
|
613
|
+
for (const [key, value] of Object.entries(local)) {
|
|
614
|
+
if (merged[key] == null || merged[key] === "") {
|
|
615
|
+
merged[key] = value;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return merged;
|
|
619
|
+
}
|
|
620
|
+
function ensureEnvFilesGitignore(projectRoot) {
|
|
621
|
+
const gitignorePath = resolve4(projectRoot, ".gitignore");
|
|
622
|
+
const existing = existsSync3(gitignorePath) ? readFileSync5(gitignorePath, "utf8") : "";
|
|
623
|
+
const toAppend = ENV_GITIGNORE_LINES.filter((line) => !existing.includes(line)).join("\n");
|
|
624
|
+
if (!toAppend) return;
|
|
625
|
+
writeFileSync2(gitignorePath, `${existing.trimEnd()}
|
|
626
|
+
|
|
627
|
+
# Env files (secrets)
|
|
628
|
+
${toAppend}
|
|
629
|
+
`);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// src/lib/cdn-github-setup.ts
|
|
633
|
+
var PORTAL_CDN_PUBLISH_TOKEN_ENV = "PORTAL_CDN_PUBLISH_TOKEN";
|
|
634
|
+
var PORTAL_CDN_JSON = "portal.cdn.json";
|
|
635
|
+
var PLACEHOLDER_GITHUB = /your-org|your-portal-cdn-mirror/i;
|
|
636
|
+
function isPlaceholderGithubSlug(github) {
|
|
637
|
+
return PLACEHOLDER_GITHUB.test(github);
|
|
638
|
+
}
|
|
639
|
+
function defaultPortalCdnJson() {
|
|
640
|
+
const examplePath = kitPath("templates", "shared", "portal.cdn.json.example");
|
|
641
|
+
if (existsSync4(examplePath)) {
|
|
642
|
+
return JSON.parse(readFileSync6(examplePath, "utf8"));
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
publishMode: "jsdelivr",
|
|
646
|
+
github: "your-org/your-portal-cdn-mirror",
|
|
647
|
+
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
648
|
+
cdnPathInRepo: "dist/cdn",
|
|
649
|
+
defaultBranch: "main"
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function readPortalCdnJson(projectRoot) {
|
|
653
|
+
const path = resolve5(projectRoot, PORTAL_CDN_JSON);
|
|
654
|
+
if (!existsSync4(path)) return null;
|
|
655
|
+
try {
|
|
656
|
+
return JSON.parse(readFileSync6(path, "utf8"));
|
|
657
|
+
} catch {
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
function normalizeGithubSlug(input) {
|
|
662
|
+
const trimmed = input.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
663
|
+
const parts = trimmed.split("/").filter(Boolean);
|
|
664
|
+
if (parts.length !== 2) return null;
|
|
665
|
+
return `${parts[0]}/${parts[1]}`;
|
|
666
|
+
}
|
|
667
|
+
function portalCdnJsonFromGithub(github, base) {
|
|
668
|
+
const slug = normalizeGithubSlug(github);
|
|
669
|
+
if (!slug) {
|
|
670
|
+
throw new Error(`Invalid GitHub repo \u2014 use org/repo (got "${github}")`);
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
publishMode: base?.publishMode ?? "jsdelivr",
|
|
674
|
+
github: slug,
|
|
675
|
+
gitRemote: `https://github.com/${slug}.git`,
|
|
676
|
+
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
677
|
+
defaultBranch: base?.defaultBranch ?? "main"
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
function hasCdnPublishToken(projectRoot) {
|
|
681
|
+
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
682
|
+
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
683
|
+
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
684
|
+
}
|
|
685
|
+
function hasConfiguredCdnGithubMirror(projectRoot) {
|
|
686
|
+
const config = readPortalCdnJson(projectRoot);
|
|
687
|
+
const github = config?.github ?? "";
|
|
688
|
+
return Boolean(github) && !PLACEHOLDER_GITHUB.test(github);
|
|
689
|
+
}
|
|
690
|
+
function githubDefaultForPrompt(projectRoot) {
|
|
691
|
+
const existing = readPortalCdnJson(projectRoot);
|
|
692
|
+
if (existing?.github && !PLACEHOLDER_GITHUB.test(existing.github)) {
|
|
693
|
+
return existing.github;
|
|
694
|
+
}
|
|
695
|
+
return defaultPortalCdnJson().github;
|
|
696
|
+
}
|
|
697
|
+
function needsCdnGitHubSetup(projectRoot) {
|
|
698
|
+
return !hasConfiguredCdnGithubMirror(projectRoot) || !hasCdnPublishToken(projectRoot);
|
|
699
|
+
}
|
|
700
|
+
function applyCdnGitHubSetup(projectRoot, input) {
|
|
701
|
+
const existing = readPortalCdnJson(projectRoot) ?? defaultPortalCdnJson();
|
|
702
|
+
const updated = portalCdnJsonFromGithub(input.github, existing);
|
|
703
|
+
writeFileSync3(resolve5(projectRoot, PORTAL_CDN_JSON), `${JSON.stringify(updated, null, 2)}
|
|
704
|
+
`);
|
|
705
|
+
if (input.token?.trim()) {
|
|
706
|
+
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
707
|
+
}
|
|
708
|
+
ensureEnvFilesGitignore(projectRoot);
|
|
709
|
+
}
|
|
710
|
+
async function promptCdnGitHubSetup(options = {}) {
|
|
711
|
+
if (options.skip) return null;
|
|
712
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
713
|
+
if (!needsCdnGitHubSetup(projectRoot)) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
let github = options.github?.trim();
|
|
717
|
+
if (!github && hasConfiguredCdnGithubMirror(projectRoot)) {
|
|
718
|
+
github = readPortalCdnJson(projectRoot)?.github;
|
|
719
|
+
}
|
|
720
|
+
if (!github) {
|
|
721
|
+
const answer = await p2.text({
|
|
722
|
+
message: "Public GitHub CDN mirror (org/repo)",
|
|
723
|
+
placeholder: options.githubDefault ?? "your-org/your-portal-cdn-mirror",
|
|
724
|
+
initialValue: options.githubDefault,
|
|
725
|
+
validate: (value) => {
|
|
726
|
+
const slug = normalizeGithubSlug(value ?? "");
|
|
727
|
+
return slug ? void 0 : "Enter a public repo as org/repo (jsDelivr serves from it)";
|
|
728
|
+
}
|
|
729
|
+
});
|
|
730
|
+
if (p2.isCancel(answer)) return null;
|
|
731
|
+
github = normalizeGithubSlug(answer) ?? void 0;
|
|
732
|
+
}
|
|
733
|
+
if (!github) return null;
|
|
734
|
+
if (hasCdnPublishToken(projectRoot)) {
|
|
735
|
+
return { github };
|
|
736
|
+
}
|
|
737
|
+
const tokenAnswer = await p2.password({
|
|
738
|
+
message: "GitHub token (repo push on CDN mirror)",
|
|
739
|
+
validate: (value) => {
|
|
740
|
+
if (!value?.trim()) return "Required for wp publish \u2014 create a fine-grained PAT with Contents: Read and write";
|
|
741
|
+
return void 0;
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
if (p2.isCancel(tokenAnswer)) return null;
|
|
745
|
+
return { github, token: tokenAnswer.trim() };
|
|
746
|
+
}
|
|
747
|
+
function formatCdnSetupNote(_projectRoot) {
|
|
748
|
+
return `CDN config written \u2014 edit ${PORTAL_CDN_JSON} if needed; token saved to ${ENV_LOCAL_FILE} (gitignored)`;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/lib/wphs-workspace.ts
|
|
752
|
+
var ensureModulePromise;
|
|
753
|
+
function loadEnsureModule() {
|
|
754
|
+
if (!ensureModulePromise) {
|
|
755
|
+
ensureModulePromise = Promise.resolve().then(async () => {
|
|
756
|
+
const mod = await import("./ensure-wphs-workspace-5S3GTO6K.js");
|
|
757
|
+
return mod;
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
return ensureModulePromise;
|
|
761
|
+
}
|
|
762
|
+
async function ensureProjectWphsWorkspace(projectRoot) {
|
|
763
|
+
const { ensureWphsWorkspace } = await loadEnsureModule();
|
|
764
|
+
return ensureWphsWorkspace(projectRoot, getKitRoot());
|
|
765
|
+
}
|
|
766
|
+
|
|
548
767
|
// src/lib/template-dev-deps.ts
|
|
549
768
|
var VITE_DEV_PEER_DEPS = {
|
|
550
769
|
vite: "^6.1.0",
|
|
@@ -667,14 +886,14 @@ function validateHubSpotFieldsJson(fields) {
|
|
|
667
886
|
}
|
|
668
887
|
|
|
669
888
|
// src/lib/developer-slug.ts
|
|
670
|
-
import { existsSync as
|
|
671
|
-
import { resolve as
|
|
672
|
-
import * as
|
|
889
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
890
|
+
import { resolve as resolve6 } from "path";
|
|
891
|
+
import * as p3 from "@clack/prompts";
|
|
673
892
|
var SLUG_MAX_LEN = 12;
|
|
674
893
|
var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,10}[a-z0-9])?$/;
|
|
675
894
|
var DEVELOPER_CONFIG_FILE = "developer.json";
|
|
676
895
|
function developerConfigPath(projectRoot) {
|
|
677
|
-
return
|
|
896
|
+
return resolve6(projectRoot, ".wphs", "config", DEVELOPER_CONFIG_FILE);
|
|
678
897
|
}
|
|
679
898
|
function sanitizeDevSlug(input) {
|
|
680
899
|
if (!input?.trim()) return void 0;
|
|
@@ -686,9 +905,9 @@ function readDeveloperSlug(projectRoot) {
|
|
|
686
905
|
const fromEnv = sanitizeDevSlug(process.env.PORTAL_CDN_DEV_SLUG) ?? sanitizeDevSlug(process.env.MODULE_CDN_DEV_SLUG);
|
|
687
906
|
if (fromEnv) return fromEnv;
|
|
688
907
|
const path = developerConfigPath(projectRoot);
|
|
689
|
-
if (!
|
|
908
|
+
if (!existsSync5(path)) return void 0;
|
|
690
909
|
try {
|
|
691
|
-
const raw = JSON.parse(
|
|
910
|
+
const raw = JSON.parse(readFileSync7(path, "utf8"));
|
|
692
911
|
return sanitizeDevSlug(raw.slug);
|
|
693
912
|
} catch {
|
|
694
913
|
return void 0;
|
|
@@ -700,8 +919,8 @@ function writeDeveloperSlug(projectRoot, slug) {
|
|
|
700
919
|
throw new Error("Invalid CDN dev slug \u2014 use lowercase letters, numbers, and hyphens (max 12 chars)");
|
|
701
920
|
}
|
|
702
921
|
const path = developerConfigPath(projectRoot);
|
|
703
|
-
mkdirSync2(
|
|
704
|
-
|
|
922
|
+
mkdirSync2(resolve6(path, ".."), { recursive: true });
|
|
923
|
+
writeFileSync4(path, `${JSON.stringify({ slug: normalized }, null, 2)}
|
|
705
924
|
`);
|
|
706
925
|
}
|
|
707
926
|
function needsDeveloperSlugSetup(projectRoot) {
|
|
@@ -711,7 +930,7 @@ async function promptDeveloperSlug(projectRoot) {
|
|
|
711
930
|
if (!needsDeveloperSlugSetup(projectRoot)) {
|
|
712
931
|
return readDeveloperSlug(projectRoot);
|
|
713
932
|
}
|
|
714
|
-
const slug = await
|
|
933
|
+
const slug = await p3.text({
|
|
715
934
|
message: "CDN dev slug (unique per developer for shared CDN mirror \u2014 e.g. your first name)",
|
|
716
935
|
placeholder: "manab",
|
|
717
936
|
validate: (value) => {
|
|
@@ -721,7 +940,7 @@ async function promptDeveloperSlug(projectRoot) {
|
|
|
721
940
|
return void 0;
|
|
722
941
|
}
|
|
723
942
|
});
|
|
724
|
-
if (
|
|
943
|
+
if (p3.isCancel(slug) || typeof slug !== "string") {
|
|
725
944
|
return void 0;
|
|
726
945
|
}
|
|
727
946
|
writeDeveloperSlug(projectRoot, slug);
|
|
@@ -763,37 +982,37 @@ function resolveModuleBuildIdMetaForCli(projectRoot, env = process.env) {
|
|
|
763
982
|
}
|
|
764
983
|
|
|
765
984
|
// src/lib/upload-only-module.ts
|
|
766
|
-
import { existsSync as
|
|
767
|
-
import { resolve as
|
|
985
|
+
import { existsSync as existsSync6 } from "fs";
|
|
986
|
+
import { resolve as resolve7 } from "path";
|
|
768
987
|
function isUploadOnlyPreexistingProject(projectRoot, moduleArtifactDirs) {
|
|
769
988
|
if (moduleArtifactDirs.length === 0) return false;
|
|
770
|
-
if (
|
|
771
|
-
if (
|
|
772
|
-
return moduleArtifactDirs.some((dir) =>
|
|
989
|
+
if (existsSync6(resolve7(projectRoot, "package.json"))) return false;
|
|
990
|
+
if (existsSync6(resolve7(projectRoot, "src"))) return false;
|
|
991
|
+
return moduleArtifactDirs.some((dir) => existsSync6(resolve7(dir, "fields.json")));
|
|
773
992
|
}
|
|
774
993
|
|
|
775
994
|
// src/vite/resolve-dev-config.ts
|
|
776
|
-
import { existsSync as
|
|
995
|
+
import { existsSync as existsSync7 } from "fs";
|
|
777
996
|
import { platform } from "os";
|
|
778
|
-
import { resolve as
|
|
997
|
+
import { resolve as resolve8 } from "path";
|
|
779
998
|
var VITE_JS_SEGMENTS = ["node_modules", "vite", "bin", "vite.js"];
|
|
780
999
|
function isProjectViteInstalled(projectRoot) {
|
|
781
|
-
return
|
|
1000
|
+
return existsSync7(resolve8(projectRoot, ...VITE_JS_SEGMENTS));
|
|
782
1001
|
}
|
|
783
1002
|
function resolveProjectViteSpawn(projectRoot) {
|
|
784
|
-
const viteJs =
|
|
785
|
-
if (
|
|
1003
|
+
const viteJs = resolve8(projectRoot, ...VITE_JS_SEGMENTS);
|
|
1004
|
+
if (existsSync7(viteJs)) {
|
|
786
1005
|
return { command: process.execPath, prefixArgs: [viteJs] };
|
|
787
1006
|
}
|
|
788
|
-
const binDir =
|
|
1007
|
+
const binDir = resolve8(projectRoot, "node_modules", ".bin");
|
|
789
1008
|
if (platform() === "win32") {
|
|
790
|
-
const winCmd =
|
|
791
|
-
if (
|
|
1009
|
+
const winCmd = resolve8(binDir, "vite.cmd");
|
|
1010
|
+
if (existsSync7(winCmd)) {
|
|
792
1011
|
return { command: winCmd, prefixArgs: [] };
|
|
793
1012
|
}
|
|
794
1013
|
}
|
|
795
|
-
const posixShim =
|
|
796
|
-
if (
|
|
1014
|
+
const posixShim = resolve8(binDir, "vite");
|
|
1015
|
+
if (existsSync7(posixShim)) {
|
|
797
1016
|
return { command: posixShim, prefixArgs: [] };
|
|
798
1017
|
}
|
|
799
1018
|
return null;
|
|
@@ -880,10 +1099,10 @@ function envFileForTier(tier) {
|
|
|
880
1099
|
return ".env.prod";
|
|
881
1100
|
}
|
|
882
1101
|
function readEnvFileValue(projectRoot, file, key) {
|
|
883
|
-
const path =
|
|
884
|
-
if (!
|
|
1102
|
+
const path = resolve9(projectRoot, file);
|
|
1103
|
+
if (!existsSync8(path)) return void 0;
|
|
885
1104
|
try {
|
|
886
|
-
const content =
|
|
1105
|
+
const content = readFileSync8(path, "utf8");
|
|
887
1106
|
const match = content.match(new RegExp(`^${key}=(.*)$`, "m"));
|
|
888
1107
|
return match?.[1]?.trim();
|
|
889
1108
|
} catch {
|
|
@@ -892,8 +1111,8 @@ function readEnvFileValue(projectRoot, file, key) {
|
|
|
892
1111
|
}
|
|
893
1112
|
function checkEnvFiles(ctx) {
|
|
894
1113
|
const checks = [];
|
|
895
|
-
const localPath =
|
|
896
|
-
const localOk =
|
|
1114
|
+
const localPath = resolve9(ctx.projectRoot, ".env.local");
|
|
1115
|
+
const localOk = existsSync8(localPath);
|
|
897
1116
|
checks.push({
|
|
898
1117
|
name: "env-local",
|
|
899
1118
|
ok: localOk,
|
|
@@ -901,8 +1120,8 @@ function checkEnvFiles(ctx) {
|
|
|
901
1120
|
remediation: localOk ? void 0 : "wp setup or copy .env.local.example \u2192 .env.local",
|
|
902
1121
|
severity: "warn"
|
|
903
1122
|
});
|
|
904
|
-
const legacyLocalDev =
|
|
905
|
-
if (!localOk &&
|
|
1123
|
+
const legacyLocalDev = resolve9(ctx.projectRoot, ".env.dev");
|
|
1124
|
+
if (!localOk && existsSync8(legacyLocalDev)) {
|
|
906
1125
|
const nodeEnv = readEnvFileValue(ctx.projectRoot, ".env.dev", "VITE_NODE_ENV");
|
|
907
1126
|
const hasDevMode = readEnvFileValue(ctx.projectRoot, ".env.dev", "VITE_DEV_MODE") != null;
|
|
908
1127
|
const looksLikeLocal = hasDevMode || nodeEnv === "local" || nodeEnv == null || nodeEnv === "development";
|
|
@@ -918,7 +1137,7 @@ function checkEnvFiles(ctx) {
|
|
|
918
1137
|
}
|
|
919
1138
|
for (const tier of ctx.config.env.tiers) {
|
|
920
1139
|
const file = envFileForTier(tier);
|
|
921
|
-
const ok =
|
|
1140
|
+
const ok = existsSync8(resolve9(ctx.projectRoot, file));
|
|
922
1141
|
if (tier === "stg") {
|
|
923
1142
|
checks.push({
|
|
924
1143
|
name: "env-stg",
|
|
@@ -941,8 +1160,8 @@ function checkEnvFiles(ctx) {
|
|
|
941
1160
|
}
|
|
942
1161
|
function checkLegacyBuildIdFile(ctx) {
|
|
943
1162
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
944
|
-
const legacy =
|
|
945
|
-
if (!
|
|
1163
|
+
const legacy = resolve9(ctx.projectRoot, ".portal-build-id");
|
|
1164
|
+
if (!existsSync8(legacy)) return null;
|
|
946
1165
|
return {
|
|
947
1166
|
name: "legacy-build-id",
|
|
948
1167
|
ok: false,
|
|
@@ -953,8 +1172,8 @@ function checkLegacyBuildIdFile(ctx) {
|
|
|
953
1172
|
}
|
|
954
1173
|
function checkBuildId(ctx) {
|
|
955
1174
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
956
|
-
const file =
|
|
957
|
-
const ok =
|
|
1175
|
+
const file = resolve9(ctx.projectRoot, ctx.config.cdn.buildIdFile);
|
|
1176
|
+
const ok = existsSync8(file);
|
|
958
1177
|
return {
|
|
959
1178
|
name: "build-id",
|
|
960
1179
|
ok,
|
|
@@ -1008,7 +1227,7 @@ function checkDevSlug(ctx) {
|
|
|
1008
1227
|
};
|
|
1009
1228
|
}
|
|
1010
1229
|
function isUploadOnlyProject(ctx) {
|
|
1011
|
-
const moduleDirs = [
|
|
1230
|
+
const moduleDirs = [resolve9(ctx.projectRoot, ctx.config.module.uploadPath)];
|
|
1012
1231
|
return isUploadOnlyPreexistingProject(ctx.projectRoot, moduleDirs);
|
|
1013
1232
|
}
|
|
1014
1233
|
function checkUploadOnlyNote(ctx) {
|
|
@@ -1022,8 +1241,8 @@ function checkUploadOnlyNote(ctx) {
|
|
|
1022
1241
|
};
|
|
1023
1242
|
}
|
|
1024
1243
|
function checkDevViteConfig(ctx) {
|
|
1025
|
-
const path =
|
|
1026
|
-
const ok =
|
|
1244
|
+
const path = resolve9(ctx.projectRoot, "vite.config.dev.mjs");
|
|
1245
|
+
const ok = existsSync8(path);
|
|
1027
1246
|
return {
|
|
1028
1247
|
name: "vite-dev-config",
|
|
1029
1248
|
ok,
|
|
@@ -1034,8 +1253,8 @@ function checkDevViteConfig(ctx) {
|
|
|
1034
1253
|
}
|
|
1035
1254
|
function checkMetaJson(ctx) {
|
|
1036
1255
|
if (isThemeProject(ctx.config)) return null;
|
|
1037
|
-
const path =
|
|
1038
|
-
if (!
|
|
1256
|
+
const path = resolve9(ctx.projectRoot, "public/meta.json");
|
|
1257
|
+
if (!existsSync8(path)) {
|
|
1039
1258
|
return {
|
|
1040
1259
|
name: "meta-json",
|
|
1041
1260
|
ok: false,
|
|
@@ -1045,7 +1264,7 @@ function checkMetaJson(ctx) {
|
|
|
1045
1264
|
};
|
|
1046
1265
|
}
|
|
1047
1266
|
try {
|
|
1048
|
-
const meta = JSON.parse(
|
|
1267
|
+
const meta = JSON.parse(readFileSync8(path, "utf8"));
|
|
1049
1268
|
const hasContentTypes = Array.isArray(meta.content_types) && meta.content_types.length > 0;
|
|
1050
1269
|
return {
|
|
1051
1270
|
name: "meta-json",
|
|
@@ -1067,8 +1286,8 @@ function checkMetaJson(ctx) {
|
|
|
1067
1286
|
}
|
|
1068
1287
|
async function checkCdnMirrorPublic(ctx) {
|
|
1069
1288
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
1070
|
-
const mirrorPath =
|
|
1071
|
-
if (!
|
|
1289
|
+
const mirrorPath = resolve9(ctx.projectRoot, ctx.config.cdn?.mirror ?? "portal.cdn.json");
|
|
1290
|
+
if (!existsSync8(mirrorPath)) {
|
|
1072
1291
|
return {
|
|
1073
1292
|
name: "cdn-mirror-public",
|
|
1074
1293
|
ok: false,
|
|
@@ -1079,7 +1298,7 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1079
1298
|
}
|
|
1080
1299
|
let github;
|
|
1081
1300
|
try {
|
|
1082
|
-
const mirror = JSON.parse(
|
|
1301
|
+
const mirror = JSON.parse(readFileSync8(mirrorPath, "utf8"));
|
|
1083
1302
|
github = mirror.github?.trim();
|
|
1084
1303
|
} catch {
|
|
1085
1304
|
return {
|
|
@@ -1099,6 +1318,15 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1099
1318
|
severity: "error"
|
|
1100
1319
|
};
|
|
1101
1320
|
}
|
|
1321
|
+
if (isPlaceholderGithubSlug(github)) {
|
|
1322
|
+
return {
|
|
1323
|
+
name: "cdn-mirror-public",
|
|
1324
|
+
ok: false,
|
|
1325
|
+
message: `portal.cdn.json still has placeholder github slug (${github})`,
|
|
1326
|
+
remediation: "Run wp setup and set a real public org/repo for jsDelivr",
|
|
1327
|
+
severity: "warn"
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1102
1330
|
try {
|
|
1103
1331
|
const res = await fetch(`https://api.github.com/repos/${github}`, {
|
|
1104
1332
|
headers: { Accept: "application/vnd.github+json" }
|
|
@@ -1131,8 +1359,8 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1131
1359
|
severity: "warn"
|
|
1132
1360
|
};
|
|
1133
1361
|
}
|
|
1134
|
-
const manifestPath =
|
|
1135
|
-
if (!
|
|
1362
|
+
const manifestPath = resolve9(ctx.projectRoot, "dist/cdn/manifest.json");
|
|
1363
|
+
if (!existsSync8(manifestPath)) {
|
|
1136
1364
|
return {
|
|
1137
1365
|
name: "cdn-mirror-public",
|
|
1138
1366
|
ok: true,
|
|
@@ -1141,7 +1369,7 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1141
1369
|
};
|
|
1142
1370
|
}
|
|
1143
1371
|
try {
|
|
1144
|
-
const manifest = JSON.parse(
|
|
1372
|
+
const manifest = JSON.parse(readFileSync8(manifestPath, "utf8"));
|
|
1145
1373
|
const appUrl = manifest.scripts?.find((s) => s.name === "app")?.url;
|
|
1146
1374
|
if (!appUrl) {
|
|
1147
1375
|
return {
|
|
@@ -1194,8 +1422,8 @@ async function checkCdnMirrorPublic(ctx) {
|
|
|
1194
1422
|
}
|
|
1195
1423
|
function checkFieldsJson(ctx) {
|
|
1196
1424
|
if (isThemeProject(ctx.config)) return null;
|
|
1197
|
-
const path =
|
|
1198
|
-
if (!
|
|
1425
|
+
const path = resolve9(ctx.projectRoot, "public/fields.json");
|
|
1426
|
+
if (!existsSync8(path)) {
|
|
1199
1427
|
return {
|
|
1200
1428
|
name: "fields-json",
|
|
1201
1429
|
ok: false,
|
|
@@ -1205,7 +1433,7 @@ function checkFieldsJson(ctx) {
|
|
|
1205
1433
|
};
|
|
1206
1434
|
}
|
|
1207
1435
|
try {
|
|
1208
|
-
const fields = JSON.parse(
|
|
1436
|
+
const fields = JSON.parse(readFileSync8(path, "utf8"));
|
|
1209
1437
|
const issues = validateHubSpotFieldsJson(fields);
|
|
1210
1438
|
if (issues.length === 0) {
|
|
1211
1439
|
return {
|
|
@@ -1235,11 +1463,11 @@ function checkFieldsJson(ctx) {
|
|
|
1235
1463
|
}
|
|
1236
1464
|
}
|
|
1237
1465
|
function readProjectFile(projectRoot, relativePath) {
|
|
1238
|
-
const path =
|
|
1239
|
-
if (!
|
|
1466
|
+
const path = resolve9(projectRoot, relativePath);
|
|
1467
|
+
if (!existsSync8(path)) {
|
|
1240
1468
|
return null;
|
|
1241
1469
|
}
|
|
1242
|
-
return
|
|
1470
|
+
return readFileSync8(path, "utf8");
|
|
1243
1471
|
}
|
|
1244
1472
|
function checkCssPipeline(ctx) {
|
|
1245
1473
|
if (isThemeProject(ctx.config) || isUploadOnlyProject(ctx)) {
|
|
@@ -1306,9 +1534,9 @@ function checkCssPipeline(ctx) {
|
|
|
1306
1534
|
}
|
|
1307
1535
|
const moduleOutDir = ctx.config.module?.outDir;
|
|
1308
1536
|
if (moduleOutDir && usesCdnPipeline(ctx.config)) {
|
|
1309
|
-
const moduleCssPath =
|
|
1310
|
-
if (
|
|
1311
|
-
const css =
|
|
1537
|
+
const moduleCssPath = resolve9(ctx.projectRoot, moduleOutDir, "module.css");
|
|
1538
|
+
if (existsSync8(moduleCssPath)) {
|
|
1539
|
+
const css = readFileSync8(moduleCssPath, "utf8");
|
|
1312
1540
|
const hasDwUtilities = /dw\\:/.test(css) || /\.dw:/.test(css);
|
|
1313
1541
|
checks.push({
|
|
1314
1542
|
name: "css-module-dist-prefixed",
|
|
@@ -1323,8 +1551,8 @@ function checkCssPipeline(ctx) {
|
|
|
1323
1551
|
}
|
|
1324
1552
|
function checkHybridModuleHtml(ctx) {
|
|
1325
1553
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return null;
|
|
1326
|
-
const path =
|
|
1327
|
-
if (!
|
|
1554
|
+
const path = resolve9(ctx.projectRoot, "public/module.html");
|
|
1555
|
+
if (!existsSync8(path)) {
|
|
1328
1556
|
return {
|
|
1329
1557
|
name: "module-html",
|
|
1330
1558
|
ok: false,
|
|
@@ -1333,7 +1561,7 @@ function checkHybridModuleHtml(ctx) {
|
|
|
1333
1561
|
severity: "error"
|
|
1334
1562
|
};
|
|
1335
1563
|
}
|
|
1336
|
-
const html =
|
|
1564
|
+
const html = readFileSync8(path, "utf8");
|
|
1337
1565
|
const needs = ["__PORTAL_CDN_BASE__", "require_js", "hubSpotData", 'id="app"'];
|
|
1338
1566
|
const missing = needs.filter((token) => !html.includes(token));
|
|
1339
1567
|
const usesUndeclaredCdnFields = /module\.portal_(cdn_base_url|vendor_script|app_script|app_css)/.test(
|
|
@@ -1351,8 +1579,8 @@ function checkHybridModuleHtml(ctx) {
|
|
|
1351
1579
|
function checkCdnViteConfigs(ctx) {
|
|
1352
1580
|
if (isThemeProject(ctx.config) || !usesCdnPipeline(ctx.config)) return [];
|
|
1353
1581
|
return ["vite.config.cdn-vendor.mjs", "vite.config.cdn-app.mjs"].map((file) => {
|
|
1354
|
-
const path =
|
|
1355
|
-
const ok =
|
|
1582
|
+
const path = resolve9(ctx.projectRoot, file);
|
|
1583
|
+
const ok = existsSync8(path);
|
|
1356
1584
|
return {
|
|
1357
1585
|
name: file.replace(/\./g, "-"),
|
|
1358
1586
|
ok,
|
|
@@ -1364,8 +1592,8 @@ function checkCdnViteConfigs(ctx) {
|
|
|
1364
1592
|
}
|
|
1365
1593
|
function checkDevEntry(ctx) {
|
|
1366
1594
|
const checks = [];
|
|
1367
|
-
const indexHtml =
|
|
1368
|
-
const hasIndex =
|
|
1595
|
+
const indexHtml = resolve9(ctx.projectRoot, "index.html");
|
|
1596
|
+
const hasIndex = existsSync8(indexHtml);
|
|
1369
1597
|
checks.push({
|
|
1370
1598
|
name: "index-html",
|
|
1371
1599
|
ok: hasIndex,
|
|
@@ -1373,8 +1601,8 @@ function checkDevEntry(ctx) {
|
|
|
1373
1601
|
remediation: hasIndex ? void 0 : "Re-run wp init or copy index.html from kit templates",
|
|
1374
1602
|
severity: "error"
|
|
1375
1603
|
});
|
|
1376
|
-
const localDev =
|
|
1377
|
-
const hasEntry =
|
|
1604
|
+
const localDev = resolve9(ctx.projectRoot, ctx.config.entries.localDev);
|
|
1605
|
+
const hasEntry = existsSync8(localDev);
|
|
1378
1606
|
checks.push({
|
|
1379
1607
|
name: "local-dev-entry",
|
|
1380
1608
|
ok: hasEntry,
|
|
@@ -1384,7 +1612,7 @@ function checkDevEntry(ctx) {
|
|
|
1384
1612
|
});
|
|
1385
1613
|
if (hasEntry) {
|
|
1386
1614
|
try {
|
|
1387
|
-
const source =
|
|
1615
|
+
const source = readFileSync8(localDev, "utf8");
|
|
1388
1616
|
const usesKitNodeBootstrap = source.includes("@woodsportal/hubspot-kit/dev/bootstrap-greenfield") || source.includes("@woodsportal/hubspot-kit/dev/bootstrap");
|
|
1389
1617
|
const usesWphsShim = source.includes(".wphs/.kit-bootstrap-greenfield") || source.includes(".wphs/.kit-bootstrap") || source.includes("@wphs/bootstrap-greenfield") || source.includes("@wphs/bootstrap");
|
|
1390
1618
|
checks.push({
|
|
@@ -1401,7 +1629,7 @@ function checkDevEntry(ctx) {
|
|
|
1401
1629
|
}
|
|
1402
1630
|
function checkKitScripts() {
|
|
1403
1631
|
const script = kitPath("scripts", "build-cdn.mjs");
|
|
1404
|
-
const ok =
|
|
1632
|
+
const ok = existsSync8(script);
|
|
1405
1633
|
return {
|
|
1406
1634
|
name: "kit-scripts",
|
|
1407
1635
|
ok,
|
|
@@ -1410,12 +1638,38 @@ function checkKitScripts() {
|
|
|
1410
1638
|
severity: "error"
|
|
1411
1639
|
};
|
|
1412
1640
|
}
|
|
1641
|
+
function checkWphsScaffold(scaffoldError) {
|
|
1642
|
+
if (!scaffoldError) return null;
|
|
1643
|
+
return {
|
|
1644
|
+
name: "wphs-scaffold",
|
|
1645
|
+
ok: false,
|
|
1646
|
+
message: `.wphs workspace scaffold failed \u2014 ${scaffoldError}`,
|
|
1647
|
+
remediation: "Run wp doctor --verbose or wp setup; check write permissions on the project root (Windows: enable Developer Mode if junction creation fails)",
|
|
1648
|
+
severity: "error"
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
async function runWphsScaffold(ctx) {
|
|
1652
|
+
if (ctx.global.dryRun) return void 0;
|
|
1653
|
+
try {
|
|
1654
|
+
const paths = await ensureProjectWphsWorkspace(ctx.projectRoot);
|
|
1655
|
+
if (ctx.global.verbose) {
|
|
1656
|
+
ctx.logger.info(`.wphs workspace ready at ${paths.workspaceRoot}`);
|
|
1657
|
+
}
|
|
1658
|
+
return void 0;
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1661
|
+
if (ctx.global.verbose) {
|
|
1662
|
+
ctx.logger.error(`Could not scaffold .wphs workspace: ${message}`);
|
|
1663
|
+
}
|
|
1664
|
+
return message;
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1413
1667
|
function checkWphsWorkspace(ctx) {
|
|
1414
1668
|
const workspace = ctx.config.dev.moduleConfigDir;
|
|
1415
1669
|
const checks = [];
|
|
1416
1670
|
for (const legacy of [".dev", ".woodsportal"]) {
|
|
1417
|
-
const legacyPath =
|
|
1418
|
-
if (
|
|
1671
|
+
const legacyPath = resolve9(ctx.projectRoot, legacy);
|
|
1672
|
+
if (existsSync8(legacyPath)) {
|
|
1419
1673
|
checks.push({
|
|
1420
1674
|
name: `legacy-${legacy}`,
|
|
1421
1675
|
ok: false,
|
|
@@ -1425,7 +1679,7 @@ function checkWphsWorkspace(ctx) {
|
|
|
1425
1679
|
});
|
|
1426
1680
|
}
|
|
1427
1681
|
}
|
|
1428
|
-
if (
|
|
1682
|
+
if (existsSync8(resolve9(ctx.projectRoot, ".wp-hs-baseline"))) {
|
|
1429
1683
|
checks.push({
|
|
1430
1684
|
name: "legacy-baseline",
|
|
1431
1685
|
ok: false,
|
|
@@ -1444,15 +1698,15 @@ function checkWphsWorkspace(ctx) {
|
|
|
1444
1698
|
});
|
|
1445
1699
|
return checks;
|
|
1446
1700
|
}
|
|
1447
|
-
const wphsRoot =
|
|
1701
|
+
const wphsRoot = resolve9(ctx.projectRoot, workspace);
|
|
1448
1702
|
const required = ["config", "assets/branding", "fixtures", "cache", "audit/baseline", "state"];
|
|
1449
1703
|
for (const sub of required) {
|
|
1450
|
-
const ok =
|
|
1704
|
+
const ok = existsSync8(join2(wphsRoot, sub));
|
|
1451
1705
|
checks.push({
|
|
1452
1706
|
name: `wphs-${sub.replace(/\//g, "-")}`,
|
|
1453
1707
|
ok,
|
|
1454
1708
|
message: ok ? `.wphs/${sub}/ present` : `.wphs/${sub}/ missing`,
|
|
1455
|
-
remediation: ok ? void 0 : "Run wp
|
|
1709
|
+
remediation: ok ? void 0 : "Run wp setup or wp doctor --verbose; check write permissions on the project root",
|
|
1456
1710
|
severity: "error"
|
|
1457
1711
|
});
|
|
1458
1712
|
}
|
|
@@ -1460,8 +1714,8 @@ function checkWphsWorkspace(ctx) {
|
|
|
1460
1714
|
}
|
|
1461
1715
|
function checkAdapter(ctx) {
|
|
1462
1716
|
if (isThemeProject(ctx.config)) return null;
|
|
1463
|
-
const adapterPath =
|
|
1464
|
-
const ok =
|
|
1717
|
+
const adapterPath = resolve9(ctx.projectRoot, ctx.config.dev.adapter);
|
|
1718
|
+
const ok = existsSync8(adapterPath);
|
|
1465
1719
|
return {
|
|
1466
1720
|
name: "wphs-adapter",
|
|
1467
1721
|
ok,
|
|
@@ -1472,11 +1726,11 @@ function checkAdapter(ctx) {
|
|
|
1472
1726
|
}
|
|
1473
1727
|
function checkModuleConfigUiDeps(ctx) {
|
|
1474
1728
|
if (isThemeProject(ctx.config)) return [];
|
|
1475
|
-
const pkgPath =
|
|
1476
|
-
if (!
|
|
1729
|
+
const pkgPath = resolve9(ctx.projectRoot, "package.json");
|
|
1730
|
+
if (!existsSync8(pkgPath)) return [];
|
|
1477
1731
|
let deps = {};
|
|
1478
1732
|
try {
|
|
1479
|
-
const pkg = JSON.parse(
|
|
1733
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
1480
1734
|
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
1481
1735
|
} catch {
|
|
1482
1736
|
return [];
|
|
@@ -1503,7 +1757,7 @@ function checkNodeLoaderInterop() {
|
|
|
1503
1757
|
};
|
|
1504
1758
|
}
|
|
1505
1759
|
function checkViteInstalled(ctx) {
|
|
1506
|
-
if (isThemeProject(ctx.config) && !
|
|
1760
|
+
if (isThemeProject(ctx.config) && !existsSync8(resolve9(ctx.projectRoot, "vite.config.dev.mjs"))) {
|
|
1507
1761
|
return null;
|
|
1508
1762
|
}
|
|
1509
1763
|
const ok = isProjectViteInstalled(ctx.projectRoot);
|
|
@@ -1517,19 +1771,12 @@ function checkViteInstalled(ctx) {
|
|
|
1517
1771
|
}
|
|
1518
1772
|
async function gatherDoctorChecks(ctx) {
|
|
1519
1773
|
const checks = [];
|
|
1774
|
+
let wphsScaffoldError;
|
|
1520
1775
|
await runCliTasks(ctx, [
|
|
1521
1776
|
{
|
|
1522
1777
|
title: "Preparing .wphs workspace",
|
|
1523
1778
|
task: async () => {
|
|
1524
|
-
|
|
1525
|
-
try {
|
|
1526
|
-
runNodeScript(kitPath("scripts", "ensure-wphs-workspace.mjs"), [], {
|
|
1527
|
-
cwd: ctx.projectRoot,
|
|
1528
|
-
logger: ctx.logger,
|
|
1529
|
-
dryRun: false
|
|
1530
|
-
});
|
|
1531
|
-
} catch {
|
|
1532
|
-
}
|
|
1779
|
+
wphsScaffoldError = await runWphsScaffold(ctx);
|
|
1533
1780
|
}
|
|
1534
1781
|
},
|
|
1535
1782
|
{
|
|
@@ -1553,7 +1800,11 @@ async function gatherDoctorChecks(ctx) {
|
|
|
1553
1800
|
{
|
|
1554
1801
|
title: "Checking woodscli.json and project layout",
|
|
1555
1802
|
task: async () => {
|
|
1556
|
-
|
|
1803
|
+
wphsScaffoldError = await runWphsScaffold(ctx) ?? wphsScaffoldError;
|
|
1804
|
+
checks.push(...checkConfig(ctx));
|
|
1805
|
+
const scaffoldCheck = checkWphsScaffold(wphsScaffoldError);
|
|
1806
|
+
if (scaffoldCheck) checks.push(scaffoldCheck);
|
|
1807
|
+
checks.push(...checkWphsWorkspace(ctx));
|
|
1557
1808
|
const fieldsCheck = checkFieldsJson(ctx);
|
|
1558
1809
|
if (fieldsCheck) checks.push(fieldsCheck);
|
|
1559
1810
|
const metaCheck = checkMetaJson(ctx);
|
|
@@ -1660,7 +1911,7 @@ async function runConfigValidate(ctx) {
|
|
|
1660
1911
|
}
|
|
1661
1912
|
|
|
1662
1913
|
// src/cli/commands/build.ts
|
|
1663
|
-
import { resolve as
|
|
1914
|
+
import { resolve as resolve10 } from "path";
|
|
1664
1915
|
|
|
1665
1916
|
// src/lib/cli-banner.ts
|
|
1666
1917
|
import pc4 from "picocolors";
|
|
@@ -1789,7 +2040,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1789
2040
|
title: `Building monolith module (${tier})`,
|
|
1790
2041
|
task: async () => {
|
|
1791
2042
|
await run("run-vite-build.mjs", [
|
|
1792
|
-
|
|
2043
|
+
resolve10(ctx.projectRoot, "vite.config.monolith.mjs"),
|
|
1793
2044
|
...tierToViteModeFlag(tier)
|
|
1794
2045
|
]);
|
|
1795
2046
|
await run("run-hubspot-module-postbuild.mjs");
|
|
@@ -1803,7 +2054,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1803
2054
|
task: async () => {
|
|
1804
2055
|
if (options.cdnEsm) {
|
|
1805
2056
|
await run("run-vite-build.mjs", [
|
|
1806
|
-
|
|
2057
|
+
resolve10(ctx.projectRoot, "vite.config.cdn-esm.mjs"),
|
|
1807
2058
|
...tierToViteModeFlag(tier)
|
|
1808
2059
|
]);
|
|
1809
2060
|
} else {
|
|
@@ -1818,7 +2069,7 @@ function buildTasks(ctx, options, run) {
|
|
|
1818
2069
|
title: `Building HubSpot module (${tier})`,
|
|
1819
2070
|
task: async () => {
|
|
1820
2071
|
await run("run-vite-build.mjs", [
|
|
1821
|
-
|
|
2072
|
+
resolve10(ctx.projectRoot, "vite.config.hubspot.mjs"),
|
|
1822
2073
|
...tierToViteModeFlag(tier)
|
|
1823
2074
|
]);
|
|
1824
2075
|
await run("run-hubspot-module-postbuild.mjs");
|
|
@@ -1915,7 +2166,7 @@ async function runBuild(ctx, options) {
|
|
|
1915
2166
|
command: "build",
|
|
1916
2167
|
tier,
|
|
1917
2168
|
ok: true,
|
|
1918
|
-
outDir:
|
|
2169
|
+
outDir: resolve10(ctx.projectRoot, ctx.config.module.outDir)
|
|
1919
2170
|
});
|
|
1920
2171
|
} else if (!options.quiet) {
|
|
1921
2172
|
printCommandDone(ctx, "Build complete");
|
|
@@ -1966,86 +2217,17 @@ async function runDev(ctx, options) {
|
|
|
1966
2217
|
}
|
|
1967
2218
|
|
|
1968
2219
|
// src/cli/commands/deploy.ts
|
|
1969
|
-
import { resolve as
|
|
1970
|
-
|
|
1971
|
-
// src/lib/project-env-files.ts
|
|
1972
|
-
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
|
|
1973
|
-
import { resolve as resolve9 } from "path";
|
|
1974
|
-
var ENV_LOCAL_FILE = ".env.local";
|
|
1975
|
-
var ENV_GITIGNORE_LINES = [".env", ".env.*", "!.env*.example", ENV_LOCAL_FILE];
|
|
1976
|
-
function parseEnvFile(content) {
|
|
1977
|
-
const result = {};
|
|
1978
|
-
for (const line of content.split("\n")) {
|
|
1979
|
-
const trimmed = line.trim();
|
|
1980
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1981
|
-
const eq = trimmed.indexOf("=");
|
|
1982
|
-
if (eq <= 0) continue;
|
|
1983
|
-
const key = trimmed.slice(0, eq).trim();
|
|
1984
|
-
let value = trimmed.slice(eq + 1).trim();
|
|
1985
|
-
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1986
|
-
value = value.slice(1, -1);
|
|
1987
|
-
}
|
|
1988
|
-
result[key] = value;
|
|
1989
|
-
}
|
|
1990
|
-
return result;
|
|
1991
|
-
}
|
|
1992
|
-
function readEnvLocal(projectRoot) {
|
|
1993
|
-
const path = resolve9(projectRoot, ENV_LOCAL_FILE);
|
|
1994
|
-
if (!existsSync7(path)) return {};
|
|
1995
|
-
return parseEnvFile(readFileSync7(path, "utf8"));
|
|
1996
|
-
}
|
|
1997
|
-
function formatEnvLocal(entries) {
|
|
1998
|
-
const lines = [
|
|
1999
|
-
"# Local secrets \u2014 gitignored. Created by wp init / wp setup (hybrid CDN).",
|
|
2000
|
-
"# PORTAL_CDN_PUBLISH_TOKEN needs repo push on your portal.cdn.json mirror.",
|
|
2001
|
-
""
|
|
2002
|
-
];
|
|
2003
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
2004
|
-
if (value.includes(" ") || value.includes("#")) {
|
|
2005
|
-
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
2006
|
-
} else {
|
|
2007
|
-
lines.push(`${key}=${value}`);
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
return `${lines.join("\n").trimEnd()}
|
|
2011
|
-
`;
|
|
2012
|
-
}
|
|
2013
|
-
function writeEnvLocal(projectRoot, entries) {
|
|
2014
|
-
const path = resolve9(projectRoot, ENV_LOCAL_FILE);
|
|
2015
|
-
const existing = existsSync7(path) ? parseEnvFile(readFileSync7(path, "utf8")) : {};
|
|
2016
|
-
writeFileSync3(path, formatEnvLocal({ ...existing, ...entries }));
|
|
2017
|
-
}
|
|
2018
|
-
function mergeProjectEnv(projectRoot, base = process.env) {
|
|
2019
|
-
const local = readEnvLocal(projectRoot);
|
|
2020
|
-
const merged = { ...base };
|
|
2021
|
-
for (const [key, value] of Object.entries(local)) {
|
|
2022
|
-
if (merged[key] == null || merged[key] === "") {
|
|
2023
|
-
merged[key] = value;
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
return merged;
|
|
2027
|
-
}
|
|
2028
|
-
function ensureEnvFilesGitignore(projectRoot) {
|
|
2029
|
-
const gitignorePath = resolve9(projectRoot, ".gitignore");
|
|
2030
|
-
const existing = existsSync7(gitignorePath) ? readFileSync7(gitignorePath, "utf8") : "";
|
|
2031
|
-
const toAppend = ENV_GITIGNORE_LINES.filter((line) => !existing.includes(line)).join("\n");
|
|
2032
|
-
if (!toAppend) return;
|
|
2033
|
-
writeFileSync3(gitignorePath, `${existing.trimEnd()}
|
|
2034
|
-
|
|
2035
|
-
# Env files (secrets)
|
|
2036
|
-
${toAppend}
|
|
2037
|
-
`);
|
|
2038
|
-
}
|
|
2220
|
+
import { resolve as resolve12 } from "path";
|
|
2039
2221
|
|
|
2040
2222
|
// src/cli/commands/upload.ts
|
|
2041
|
-
import { resolve as
|
|
2223
|
+
import { resolve as resolve11 } from "path";
|
|
2042
2224
|
async function uploadModuleOrTheme(ctx) {
|
|
2043
2225
|
if (isThemeProject(ctx.config)) {
|
|
2044
2226
|
const theme = ctx.config.theme;
|
|
2045
2227
|
if (!theme) {
|
|
2046
2228
|
throw new WpHsError("theme block missing in woodscli.json", { exitCode: ExitCode.USER_ERROR });
|
|
2047
2229
|
}
|
|
2048
|
-
const srcDir =
|
|
2230
|
+
const srcDir = resolve11(ctx.projectRoot, theme.srcDir);
|
|
2049
2231
|
await runCommandAsync("hs", ["cms", "upload", srcDir, theme.uploadPath], {
|
|
2050
2232
|
cwd: ctx.projectRoot,
|
|
2051
2233
|
projectRoot: ctx.projectRoot,
|
|
@@ -2055,7 +2237,7 @@ async function uploadModuleOrTheme(ctx) {
|
|
|
2055
2237
|
});
|
|
2056
2238
|
return `Uploaded theme ${srcDir} \u2192 ${theme.uploadPath}`;
|
|
2057
2239
|
}
|
|
2058
|
-
const outDir =
|
|
2240
|
+
const outDir = resolve11(ctx.projectRoot, ctx.config.module.outDir);
|
|
2059
2241
|
const uploadPath = ctx.config.module.uploadPath;
|
|
2060
2242
|
await runCommandAsync("hs", ["cms", "upload", outDir, uploadPath], {
|
|
2061
2243
|
cwd: ctx.projectRoot,
|
|
@@ -2232,7 +2414,7 @@ async function runDeploy(ctx, options) {
|
|
|
2232
2414
|
if (!ctx.global.json) {
|
|
2233
2415
|
printCommandDone(
|
|
2234
2416
|
ctx,
|
|
2235
|
-
`Deploy complete (${
|
|
2417
|
+
`Deploy complete (${resolve12(ctx.projectRoot, ctx.config.module.outDir)} \u2192 ${ctx.config.module.uploadPath})`
|
|
2236
2418
|
);
|
|
2237
2419
|
}
|
|
2238
2420
|
return ExitCode.SUCCESS;
|
|
@@ -2305,8 +2487,8 @@ async function runPublish(ctx, options) {
|
|
|
2305
2487
|
|
|
2306
2488
|
// src/cli/commands/audit.ts
|
|
2307
2489
|
import { createHash } from "crypto";
|
|
2308
|
-
import { cpSync, existsSync as
|
|
2309
|
-
import { resolve as
|
|
2490
|
+
import { cpSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync, rmSync, statSync } from "fs";
|
|
2491
|
+
import { resolve as resolve13, join as join3 } from "path";
|
|
2310
2492
|
var FORBIDDEN_SHIP_TOKENS = [
|
|
2311
2493
|
"ModuleConfigOverlay",
|
|
2312
2494
|
"module-config-store",
|
|
@@ -2317,10 +2499,10 @@ var FORBIDDEN_SHIP_TOKENS = [
|
|
|
2317
2499
|
var FORBIDDEN_FIXTURE_MENU_TOKENS = ["User Home", "Primary Company Deals"];
|
|
2318
2500
|
var FORBIDDEN_PREVIEW_FIXTURE_TOKENS = ["preview.editor@example.com", "CMS_EDITOR_PREVIEW"];
|
|
2319
2501
|
function hashFile(path) {
|
|
2320
|
-
return createHash("sha256").update(
|
|
2502
|
+
return createHash("sha256").update(readFileSync9(path)).digest("hex");
|
|
2321
2503
|
}
|
|
2322
2504
|
function walkFiles(dir) {
|
|
2323
|
-
if (!
|
|
2505
|
+
if (!existsSync9(dir)) return [];
|
|
2324
2506
|
const entries = readdirSync(dir);
|
|
2325
2507
|
const files = [];
|
|
2326
2508
|
for (const entry of entries) {
|
|
@@ -2336,14 +2518,14 @@ function walkFiles(dir) {
|
|
|
2336
2518
|
async function runAuditShipBundle(ctx) {
|
|
2337
2519
|
const root = ctx.projectRoot;
|
|
2338
2520
|
const targets = [
|
|
2339
|
-
|
|
2340
|
-
|
|
2521
|
+
resolve13(root, "dist/cdn"),
|
|
2522
|
+
resolve13(root, ctx.config.module.outDir)
|
|
2341
2523
|
];
|
|
2342
2524
|
const violations = [];
|
|
2343
2525
|
for (const dir of targets) {
|
|
2344
2526
|
for (const file of walkFiles(dir)) {
|
|
2345
2527
|
if (!/\.(js|css|html)$/i.test(file)) continue;
|
|
2346
|
-
const content =
|
|
2528
|
+
const content = readFileSync9(file, "utf8");
|
|
2347
2529
|
for (const token of FORBIDDEN_SHIP_TOKENS) {
|
|
2348
2530
|
if (content.includes(token)) {
|
|
2349
2531
|
violations.push(`${file}: contains forbidden token "${token}"`);
|
|
@@ -2383,21 +2565,21 @@ async function runAuditBaselineCapture(ctx, options) {
|
|
|
2383
2565
|
const root = ctx.projectRoot;
|
|
2384
2566
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
2385
2567
|
const sources = [
|
|
2386
|
-
{ label: "cdn", path:
|
|
2387
|
-
{ label: "hubspot", path:
|
|
2568
|
+
{ label: "cdn", path: resolve13(root, "dist/cdn") },
|
|
2569
|
+
{ label: "hubspot", path: resolve13(root, ctx.config.module.outDir) }
|
|
2388
2570
|
];
|
|
2389
2571
|
if (ctx.global.dryRun) {
|
|
2390
2572
|
ctx.logger.info(`[dry-run] Would capture baseline to ${baseline}`);
|
|
2391
2573
|
return ExitCode.SUCCESS;
|
|
2392
2574
|
}
|
|
2393
2575
|
for (const { label, path } of sources) {
|
|
2394
|
-
if (!
|
|
2576
|
+
if (!existsSync9(path)) {
|
|
2395
2577
|
throw new WpHsError(`Cannot capture baseline \u2014 missing ${path}`, {
|
|
2396
2578
|
exitCode: ExitCode.USER_ERROR,
|
|
2397
2579
|
remediation: "Run wp build before wp audit baseline --capture."
|
|
2398
2580
|
});
|
|
2399
2581
|
}
|
|
2400
|
-
const target =
|
|
2582
|
+
const target = resolve13(baseline, label);
|
|
2401
2583
|
rmSync(target, { recursive: true, force: true });
|
|
2402
2584
|
mkdirSync3(baseline, { recursive: true });
|
|
2403
2585
|
cpSync(path, target, { recursive: true });
|
|
@@ -2413,13 +2595,13 @@ async function runAuditParity(ctx, options) {
|
|
|
2413
2595
|
const root = ctx.projectRoot;
|
|
2414
2596
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
2415
2597
|
const currentDirs = [
|
|
2416
|
-
{ label: "cdn", path:
|
|
2417
|
-
{ label: "hubspot", path:
|
|
2598
|
+
{ label: "cdn", path: resolve13(root, "dist/cdn") },
|
|
2599
|
+
{ label: "hubspot", path: resolve13(root, ctx.config.module.outDir) }
|
|
2418
2600
|
];
|
|
2419
2601
|
const mismatches = [];
|
|
2420
2602
|
for (const { label, path } of currentDirs) {
|
|
2421
|
-
const baselinePath =
|
|
2422
|
-
if (!
|
|
2603
|
+
const baselinePath = resolve13(baseline, label);
|
|
2604
|
+
if (!existsSync9(baselinePath)) {
|
|
2423
2605
|
ctx.logger.warn(`No baseline at ${baselinePath} \u2014 skipping ${label} parity`);
|
|
2424
2606
|
continue;
|
|
2425
2607
|
}
|
|
@@ -2434,8 +2616,8 @@ async function runAuditParity(ctx, options) {
|
|
|
2434
2616
|
}
|
|
2435
2617
|
for (const file of baselineFiles) {
|
|
2436
2618
|
const rel = file.replace(baselinePath, "");
|
|
2437
|
-
const current =
|
|
2438
|
-
if (!
|
|
2619
|
+
const current = resolve13(path, rel.slice(1));
|
|
2620
|
+
if (!existsSync9(current)) continue;
|
|
2439
2621
|
const bh = hashFile(file);
|
|
2440
2622
|
const ch = hashFile(current);
|
|
2441
2623
|
if (bh !== ch) {
|
|
@@ -2462,13 +2644,13 @@ var SECRET_PATTERNS = [
|
|
|
2462
2644
|
];
|
|
2463
2645
|
async function runAuditSecrets(ctx) {
|
|
2464
2646
|
const root = ctx.projectRoot;
|
|
2465
|
-
const scanRoots = ["src", "public", ".wphs/config"].map((d) =>
|
|
2647
|
+
const scanRoots = ["src", "public", ".wphs/config"].map((d) => resolve13(root, d));
|
|
2466
2648
|
const findings = [];
|
|
2467
2649
|
for (const dir of scanRoots) {
|
|
2468
2650
|
for (const file of walkFiles(dir)) {
|
|
2469
2651
|
if (!/\.(ts|tsx|js|jsx|json|env|yml|yaml|html)$/i.test(file)) continue;
|
|
2470
2652
|
if (file.includes("node_modules")) continue;
|
|
2471
|
-
const content =
|
|
2653
|
+
const content = readFileSync9(file, "utf8");
|
|
2472
2654
|
for (const pattern of SECRET_PATTERNS) {
|
|
2473
2655
|
pattern.lastIndex = 0;
|
|
2474
2656
|
if (pattern.test(content)) {
|
|
@@ -2499,8 +2681,8 @@ import { basename as basename2, resolve as resolve17 } from "path";
|
|
|
2499
2681
|
import * as p5 from "@clack/prompts";
|
|
2500
2682
|
|
|
2501
2683
|
// src/lib/init-router-scaffold.ts
|
|
2502
|
-
import { existsSync as
|
|
2503
|
-
import { resolve as
|
|
2684
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync10, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
2685
|
+
import { resolve as resolve14 } from "path";
|
|
2504
2686
|
var ROUTER_FILES = [
|
|
2505
2687
|
"src/routes",
|
|
2506
2688
|
"src/routeTree.gen.ts",
|
|
@@ -2514,10 +2696,10 @@ function isModuleTemplate(template) {
|
|
|
2514
2696
|
return template === "module-hybrid-cdn" || template === "module-monolith";
|
|
2515
2697
|
}
|
|
2516
2698
|
function writeRouterConfig(targetDir, mode) {
|
|
2517
|
-
const configDir =
|
|
2699
|
+
const configDir = resolve14(targetDir, "src/config");
|
|
2518
2700
|
mkdirSync4(configDir, { recursive: true });
|
|
2519
|
-
|
|
2520
|
-
|
|
2701
|
+
writeFileSync5(
|
|
2702
|
+
resolve14(configDir, "router.ts"),
|
|
2521
2703
|
`/** Router history \u2014 set by \`wp init\`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
|
|
2522
2704
|
export type RouterHistoryMode = 'hash' | 'browser'
|
|
2523
2705
|
|
|
@@ -2526,8 +2708,8 @@ export const ROUTER_HISTORY: RouterHistoryMode = '${mode}'
|
|
|
2526
2708
|
);
|
|
2527
2709
|
}
|
|
2528
2710
|
function writeMountDevPreviewRouter(targetDir) {
|
|
2529
|
-
|
|
2530
|
-
|
|
2711
|
+
writeFileSync5(
|
|
2712
|
+
resolve14(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2531
2713
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2532
2714
|
|
|
2533
2715
|
import { App } from '@/app'
|
|
@@ -2540,8 +2722,8 @@ export async function mountDevPreview() {
|
|
|
2540
2722
|
);
|
|
2541
2723
|
}
|
|
2542
2724
|
function writeMountDevPreviewSinglePage(targetDir) {
|
|
2543
|
-
|
|
2544
|
-
|
|
2725
|
+
writeFileSync5(
|
|
2726
|
+
resolve14(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2545
2727
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2546
2728
|
|
|
2547
2729
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -2554,8 +2736,8 @@ export async function mountDevPreview() {
|
|
|
2554
2736
|
);
|
|
2555
2737
|
}
|
|
2556
2738
|
function writeMountPortalRouter(targetDir) {
|
|
2557
|
-
|
|
2558
|
-
|
|
2739
|
+
writeFileSync5(
|
|
2740
|
+
resolve14(targetDir, "src/portal/mount-portal.tsx"),
|
|
2559
2741
|
`import ReactDOM from 'react-dom/client'
|
|
2560
2742
|
|
|
2561
2743
|
import { App } from '@/app'
|
|
@@ -2572,8 +2754,8 @@ export async function mountPortal() {
|
|
|
2572
2754
|
);
|
|
2573
2755
|
}
|
|
2574
2756
|
function writeMountPortalSinglePage(targetDir) {
|
|
2575
|
-
|
|
2576
|
-
|
|
2757
|
+
writeFileSync5(
|
|
2758
|
+
resolve14(targetDir, "src/portal/mount-portal.tsx"),
|
|
2577
2759
|
`import ReactDOM from 'react-dom/client'
|
|
2578
2760
|
|
|
2579
2761
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -2590,11 +2772,11 @@ export async function mountPortal() {
|
|
|
2590
2772
|
);
|
|
2591
2773
|
}
|
|
2592
2774
|
function writeStarterSinglePage(targetDir) {
|
|
2593
|
-
const file =
|
|
2594
|
-
if (
|
|
2775
|
+
const file = resolve14(targetDir, "src/features/starter/components/starter-single-page.tsx");
|
|
2776
|
+
if (existsSync10(file)) {
|
|
2595
2777
|
return;
|
|
2596
2778
|
}
|
|
2597
|
-
|
|
2779
|
+
writeFileSync5(
|
|
2598
2780
|
file,
|
|
2599
2781
|
`import { StarterShell } from '@/components/layout/starter-shell'
|
|
2600
2782
|
|
|
@@ -2617,25 +2799,25 @@ export function StarterSinglePage() {
|
|
|
2617
2799
|
);
|
|
2618
2800
|
}
|
|
2619
2801
|
function removePath(targetDir, relativePath) {
|
|
2620
|
-
const full =
|
|
2621
|
-
if (!
|
|
2802
|
+
const full = resolve14(targetDir, relativePath);
|
|
2803
|
+
if (!existsSync10(full)) {
|
|
2622
2804
|
return;
|
|
2623
2805
|
}
|
|
2624
2806
|
rmSync2(full, { recursive: true, force: true });
|
|
2625
2807
|
}
|
|
2626
2808
|
function stripTanStackFromPackage(targetDir) {
|
|
2627
|
-
const pkgPath =
|
|
2628
|
-
if (!
|
|
2809
|
+
const pkgPath = resolve14(targetDir, "package.json");
|
|
2810
|
+
if (!existsSync10(pkgPath)) {
|
|
2629
2811
|
return;
|
|
2630
2812
|
}
|
|
2631
|
-
const pkg = JSON.parse(
|
|
2813
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
2632
2814
|
if (!pkg.devDependencies) {
|
|
2633
2815
|
return;
|
|
2634
2816
|
}
|
|
2635
2817
|
for (const dep of TANSTACK_DEPS) {
|
|
2636
2818
|
delete pkg.devDependencies[dep];
|
|
2637
2819
|
}
|
|
2638
|
-
|
|
2820
|
+
writeFileSync5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2639
2821
|
`);
|
|
2640
2822
|
}
|
|
2641
2823
|
function applyRouterScaffold(targetDir, mode, template) {
|
|
@@ -2655,22 +2837,22 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
2655
2837
|
writeRouterConfig(targetDir, mode);
|
|
2656
2838
|
writeMountDevPreviewRouter(targetDir);
|
|
2657
2839
|
writeMountPortalRouter(targetDir);
|
|
2658
|
-
const featuresIndex =
|
|
2659
|
-
if (
|
|
2660
|
-
let content =
|
|
2840
|
+
const featuresIndex = resolve14(targetDir, "src/features/starter/index.ts");
|
|
2841
|
+
if (existsSync10(featuresIndex)) {
|
|
2842
|
+
let content = readFileSync10(featuresIndex, "utf8");
|
|
2661
2843
|
if (!content.includes("StarterSinglePage")) {
|
|
2662
2844
|
content = content.replace(
|
|
2663
2845
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\n",
|
|
2664
2846
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\nexport { StarterSinglePage } from './components/starter-single-page'\n"
|
|
2665
2847
|
);
|
|
2666
|
-
|
|
2848
|
+
writeFileSync5(featuresIndex, content);
|
|
2667
2849
|
}
|
|
2668
2850
|
}
|
|
2669
2851
|
}
|
|
2670
2852
|
|
|
2671
2853
|
// src/lib/init-auth-scaffold.ts
|
|
2672
|
-
import { cpSync as cpSync2, existsSync as
|
|
2673
|
-
import { resolve as
|
|
2854
|
+
import { cpSync as cpSync2, existsSync as existsSync11, readFileSync as readFileSync11, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2855
|
+
import { resolve as resolve15 } from "path";
|
|
2674
2856
|
|
|
2675
2857
|
// src/lib/template-auth-deps.ts
|
|
2676
2858
|
var AUTH_SCAFFOLD_DEPS = {
|
|
@@ -2690,12 +2872,12 @@ function mergeAuthDevDependencies(existing) {
|
|
|
2690
2872
|
// src/lib/init-auth-scaffold.ts
|
|
2691
2873
|
var AUTH_SCAFFOLD_ROOT = kitPath("templates", "scaffold", "auth");
|
|
2692
2874
|
function readTierManifest() {
|
|
2693
|
-
const path =
|
|
2694
|
-
return JSON.parse(
|
|
2875
|
+
const path = resolve15(AUTH_SCAFFOLD_ROOT, "tier-manifest.json");
|
|
2876
|
+
return JSON.parse(readFileSync11(path, "utf8"));
|
|
2695
2877
|
}
|
|
2696
2878
|
function readNavManifest() {
|
|
2697
|
-
const path =
|
|
2698
|
-
return JSON.parse(
|
|
2879
|
+
const path = resolve15(AUTH_SCAFFOLD_ROOT, "nav-manifest.json");
|
|
2880
|
+
return JSON.parse(readFileSync11(path, "utf8"));
|
|
2699
2881
|
}
|
|
2700
2882
|
function layersForMode(mode) {
|
|
2701
2883
|
const manifest = readTierManifest();
|
|
@@ -2706,8 +2888,8 @@ function layersForMode(mode) {
|
|
|
2706
2888
|
return entry.layers;
|
|
2707
2889
|
}
|
|
2708
2890
|
function copyLayer(targetDir, layerName) {
|
|
2709
|
-
const layerRoot =
|
|
2710
|
-
if (!
|
|
2891
|
+
const layerRoot = resolve15(AUTH_SCAFFOLD_ROOT, layerName);
|
|
2892
|
+
if (!existsSync11(layerRoot)) {
|
|
2711
2893
|
throw new Error(`Auth scaffold layer not found: ${layerName}`);
|
|
2712
2894
|
}
|
|
2713
2895
|
cpSync2(layerRoot, targetDir, { recursive: true, force: true });
|
|
@@ -2724,8 +2906,8 @@ function copyNavLayer(targetDir, nav) {
|
|
|
2724
2906
|
copyLayer(targetDir, layerForNavMode(nav));
|
|
2725
2907
|
}
|
|
2726
2908
|
function patchNavScaffoldFile(targetDir, nav) {
|
|
2727
|
-
const navScaffoldPath =
|
|
2728
|
-
if (!
|
|
2909
|
+
const navScaffoldPath = resolve15(targetDir, "src/config/nav-scaffold.ts");
|
|
2910
|
+
if (!existsSync11(navScaffoldPath)) {
|
|
2729
2911
|
return;
|
|
2730
2912
|
}
|
|
2731
2913
|
const content = `/** Set by wp init --nav ${nav}. */
|
|
@@ -2733,11 +2915,11 @@ export const NAV_SCAFFOLD_MODE = '${nav}' as const
|
|
|
2733
2915
|
|
|
2734
2916
|
export type NavScaffoldMode = typeof NAV_SCAFFOLD_MODE
|
|
2735
2917
|
`;
|
|
2736
|
-
|
|
2918
|
+
writeFileSync6(navScaffoldPath, content);
|
|
2737
2919
|
}
|
|
2738
2920
|
function patchAuthTierFile(targetDir, mode) {
|
|
2739
|
-
const authTierPath =
|
|
2740
|
-
if (!
|
|
2921
|
+
const authTierPath = resolve15(targetDir, "src/config/auth-tier.ts");
|
|
2922
|
+
if (!existsSync11(authTierPath)) {
|
|
2741
2923
|
return;
|
|
2742
2924
|
}
|
|
2743
2925
|
const content = `/** Set by wp init --auth ${mode}. */
|
|
@@ -2745,7 +2927,7 @@ export const AUTH_SCAFFOLD_TIER = '${mode}' as const
|
|
|
2745
2927
|
|
|
2746
2928
|
export type AuthScaffoldTier = typeof AUTH_SCAFFOLD_TIER
|
|
2747
2929
|
`;
|
|
2748
|
-
|
|
2930
|
+
writeFileSync6(authTierPath, content);
|
|
2749
2931
|
}
|
|
2750
2932
|
function patchStarterOnlyBlocks(targetDir, mode) {
|
|
2751
2933
|
const hasSso = mode === "sso" || mode === "full";
|
|
@@ -2759,11 +2941,11 @@ function patchStarterOnlyBlocks(targetDir, mode) {
|
|
|
2759
2941
|
"src/features/auth/auth-route-paths.ts"
|
|
2760
2942
|
];
|
|
2761
2943
|
for (const relative of filesToPatch) {
|
|
2762
|
-
const filePath =
|
|
2763
|
-
if (!
|
|
2944
|
+
const filePath = resolve15(targetDir, relative);
|
|
2945
|
+
if (!existsSync11(filePath)) {
|
|
2764
2946
|
continue;
|
|
2765
2947
|
}
|
|
2766
|
-
let content =
|
|
2948
|
+
let content = readFileSync11(filePath, "utf8");
|
|
2767
2949
|
content = stripMarkedBlock(content, "WPHS_AUTH_SSO", hasSso);
|
|
2768
2950
|
content = stripMarkedBlock(content, "WPHS_AUTH_FULL", hasFull);
|
|
2769
2951
|
if (!hasFull && relative === "src/integrations/configure-woodsportal-sdk.ts") {
|
|
@@ -2772,7 +2954,7 @@ function patchStarterOnlyBlocks(targetDir, mode) {
|
|
|
2772
2954
|
""
|
|
2773
2955
|
);
|
|
2774
2956
|
}
|
|
2775
|
-
|
|
2957
|
+
writeFileSync6(filePath, content);
|
|
2776
2958
|
}
|
|
2777
2959
|
}
|
|
2778
2960
|
function stripMarkedBlock(content, marker, keep) {
|
|
@@ -2800,11 +2982,11 @@ var LOWER_TIER_AUTH_GUARD_FILES = [
|
|
|
2800
2982
|
function assertLowerTierAuthScaffold(targetDir, mode) {
|
|
2801
2983
|
const violations = [];
|
|
2802
2984
|
for (const relative of LOWER_TIER_AUTH_GUARD_FILES) {
|
|
2803
|
-
const filePath =
|
|
2804
|
-
if (!
|
|
2985
|
+
const filePath = resolve15(targetDir, relative);
|
|
2986
|
+
if (!existsSync11(filePath)) {
|
|
2805
2987
|
continue;
|
|
2806
2988
|
}
|
|
2807
|
-
const content =
|
|
2989
|
+
const content = readFileSync11(filePath, "utf8");
|
|
2808
2990
|
if (content.includes("WPHS_AUTH_")) {
|
|
2809
2991
|
violations.push(`${relative}: leftover WPHS_AUTH_* strip markers`);
|
|
2810
2992
|
}
|
|
@@ -2817,7 +2999,7 @@ function assertLowerTierAuthScaffold(targetDir, mode) {
|
|
|
2817
2999
|
}
|
|
2818
3000
|
}
|
|
2819
3001
|
}
|
|
2820
|
-
if (
|
|
3002
|
+
if (existsSync11(resolve15(targetDir, "src/routes/_auth/two-fa.tsx"))) {
|
|
2821
3003
|
violations.push("src/routes/_auth/two-fa.tsx: MFA route must not exist on --auth " + mode);
|
|
2822
3004
|
}
|
|
2823
3005
|
if (violations.length > 0) {
|
|
@@ -2833,14 +3015,14 @@ function assertFullTierAuthScaffold(targetDir) {
|
|
|
2833
3015
|
"src/features/auth/mfa-pending-storage.ts",
|
|
2834
3016
|
"src/routes/_auth/two-fa.tsx"
|
|
2835
3017
|
];
|
|
2836
|
-
const missing = required.filter((relative) => !
|
|
3018
|
+
const missing = required.filter((relative) => !existsSync11(resolve15(targetDir, relative)));
|
|
2837
3019
|
if (missing.length > 0) {
|
|
2838
3020
|
throw new WpHsError(`Auth scaffold tier "full" is missing required files:
|
|
2839
3021
|
- ${missing.join("\n- ")}`, {
|
|
2840
3022
|
exitCode: ExitCode.INTERNAL
|
|
2841
3023
|
});
|
|
2842
3024
|
}
|
|
2843
|
-
const bridge =
|
|
3025
|
+
const bridge = readFileSync11(resolve15(targetDir, "src/features/auth/login-session-bridge.ts"), "utf8");
|
|
2844
3026
|
if (bridge.includes("WPHS_AUTH_")) {
|
|
2845
3027
|
throw new WpHsError('Auth scaffold tier "full" left WPHS_AUTH_* markers in login-session-bridge.ts', {
|
|
2846
3028
|
exitCode: ExitCode.INTERNAL
|
|
@@ -2853,18 +3035,18 @@ function assertFullTierAuthScaffold(targetDir) {
|
|
|
2853
3035
|
}
|
|
2854
3036
|
}
|
|
2855
3037
|
function patchPackageJson(targetDir) {
|
|
2856
|
-
const pkgPath =
|
|
2857
|
-
if (!
|
|
3038
|
+
const pkgPath = resolve15(targetDir, "package.json");
|
|
3039
|
+
if (!existsSync11(pkgPath)) {
|
|
2858
3040
|
return;
|
|
2859
3041
|
}
|
|
2860
|
-
const pkg = JSON.parse(
|
|
3042
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
|
|
2861
3043
|
pkg.devDependencies = mergeAuthDevDependencies(pkg.devDependencies);
|
|
2862
|
-
|
|
3044
|
+
writeFileSync6(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2863
3045
|
`);
|
|
2864
3046
|
}
|
|
2865
3047
|
function writeAuthMountDevPreview(targetDir) {
|
|
2866
|
-
|
|
2867
|
-
|
|
3048
|
+
writeFileSync6(
|
|
3049
|
+
resolve15(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
2868
3050
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
2869
3051
|
|
|
2870
3052
|
import { App } from '@/app'
|
|
@@ -2901,25 +3083,25 @@ function validateNavAuthCombo(auth, nav) {
|
|
|
2901
3083
|
}
|
|
2902
3084
|
}
|
|
2903
3085
|
function assertNavScaffold(targetDir, nav) {
|
|
2904
|
-
const mainLayoutPath =
|
|
2905
|
-
if (!
|
|
3086
|
+
const mainLayoutPath = resolve15(targetDir, "src/components/Layouts/MainLayout.tsx");
|
|
3087
|
+
if (!existsSync11(mainLayoutPath)) {
|
|
2906
3088
|
throw new WpHsError("Auth scaffold is missing MainLayout.tsx after nav layer copy", {
|
|
2907
3089
|
exitCode: ExitCode.INTERNAL
|
|
2908
3090
|
});
|
|
2909
3091
|
}
|
|
2910
|
-
const sidebarPath =
|
|
3092
|
+
const sidebarPath = resolve15(targetDir, "src/components/layout/portal-sidebar.tsx");
|
|
2911
3093
|
if (nav === "sidebar") {
|
|
2912
|
-
if (!
|
|
3094
|
+
if (!existsSync11(sidebarPath)) {
|
|
2913
3095
|
throw new WpHsError('Nav scaffold mode "sidebar" is missing portal-sidebar.tsx', {
|
|
2914
3096
|
exitCode: ExitCode.INTERNAL
|
|
2915
3097
|
});
|
|
2916
3098
|
}
|
|
2917
|
-
} else if (
|
|
3099
|
+
} else if (existsSync11(sidebarPath)) {
|
|
2918
3100
|
throw new WpHsError('Nav scaffold mode "top" must not include portal-sidebar.tsx', {
|
|
2919
3101
|
exitCode: ExitCode.INTERNAL
|
|
2920
3102
|
});
|
|
2921
3103
|
}
|
|
2922
|
-
const navScaffold =
|
|
3104
|
+
const navScaffold = readFileSync11(resolve15(targetDir, "src/config/nav-scaffold.ts"), "utf8");
|
|
2923
3105
|
if (!navScaffold.includes(`'${nav}'`)) {
|
|
2924
3106
|
throw new WpHsError(`nav-scaffold.ts was not patched for mode "${nav}"`, {
|
|
2925
3107
|
exitCode: ExitCode.INTERNAL
|
|
@@ -2955,11 +3137,11 @@ function authTierRank(tier) {
|
|
|
2955
3137
|
return AUTH_TIER_ORDER.indexOf(tier);
|
|
2956
3138
|
}
|
|
2957
3139
|
function readCurrentAuthTier(targetDir) {
|
|
2958
|
-
const authTierPath =
|
|
2959
|
-
if (!
|
|
3140
|
+
const authTierPath = resolve15(targetDir, "src/config/auth-tier.ts");
|
|
3141
|
+
if (!existsSync11(authTierPath)) {
|
|
2960
3142
|
return "none";
|
|
2961
3143
|
}
|
|
2962
|
-
const content =
|
|
3144
|
+
const content = readFileSync11(authTierPath, "utf8");
|
|
2963
3145
|
const match = content.match(/AUTH_SCAFFOLD_TIER = '(starter|sso|full)'/);
|
|
2964
3146
|
return match?.[1] ?? "none";
|
|
2965
3147
|
}
|
|
@@ -3008,8 +3190,8 @@ var SIDEBAR_NAV_FILES = [
|
|
|
3008
3190
|
"src/components/layout/portal-sidebar-nav.tsx"
|
|
3009
3191
|
];
|
|
3010
3192
|
function applyNavScaffoldUpgrade(targetDir, nav) {
|
|
3011
|
-
const authTierPath =
|
|
3012
|
-
if (!
|
|
3193
|
+
const authTierPath = resolve15(targetDir, "src/config/auth-tier.ts");
|
|
3194
|
+
if (!existsSync11(authTierPath)) {
|
|
3013
3195
|
throw new WpHsError("Nav scaffold requires portal auth (missing src/config/auth-tier.ts).", {
|
|
3014
3196
|
exitCode: ExitCode.USER_ERROR,
|
|
3015
3197
|
remediation: "Run wp scaffold auth upgrade --tier starter first."
|
|
@@ -3017,8 +3199,8 @@ function applyNavScaffoldUpgrade(targetDir, nav) {
|
|
|
3017
3199
|
}
|
|
3018
3200
|
if (nav === "top") {
|
|
3019
3201
|
for (const relative of SIDEBAR_NAV_FILES) {
|
|
3020
|
-
const filePath =
|
|
3021
|
-
if (
|
|
3202
|
+
const filePath = resolve15(targetDir, relative);
|
|
3203
|
+
if (existsSync11(filePath)) {
|
|
3022
3204
|
rmSync3(filePath);
|
|
3023
3205
|
}
|
|
3024
3206
|
}
|
|
@@ -3052,125 +3234,6 @@ function moduleOutDirFromSlug(slug) {
|
|
|
3052
3234
|
return `dist/${pascal || "HubSpot"}.Module`;
|
|
3053
3235
|
}
|
|
3054
3236
|
|
|
3055
|
-
// src/lib/cdn-github-setup.ts
|
|
3056
|
-
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
|
|
3057
|
-
import { resolve as resolve15 } from "path";
|
|
3058
|
-
import * as p3 from "@clack/prompts";
|
|
3059
|
-
var PORTAL_CDN_PUBLISH_TOKEN_ENV = "PORTAL_CDN_PUBLISH_TOKEN";
|
|
3060
|
-
var PORTAL_CDN_JSON = "portal.cdn.json";
|
|
3061
|
-
var PLACEHOLDER_GITHUB = /your-org|your-portal-cdn-mirror/i;
|
|
3062
|
-
function defaultPortalCdnJson() {
|
|
3063
|
-
const examplePath = kitPath("templates", "shared", "portal.cdn.json.example");
|
|
3064
|
-
if (existsSync11(examplePath)) {
|
|
3065
|
-
return JSON.parse(readFileSync11(examplePath, "utf8"));
|
|
3066
|
-
}
|
|
3067
|
-
return {
|
|
3068
|
-
publishMode: "jsdelivr",
|
|
3069
|
-
github: "your-org/your-portal-cdn-mirror",
|
|
3070
|
-
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
3071
|
-
cdnPathInRepo: "dist/cdn",
|
|
3072
|
-
defaultBranch: "main"
|
|
3073
|
-
};
|
|
3074
|
-
}
|
|
3075
|
-
function readPortalCdnJson(projectRoot) {
|
|
3076
|
-
const path = resolve15(projectRoot, PORTAL_CDN_JSON);
|
|
3077
|
-
if (!existsSync11(path)) return null;
|
|
3078
|
-
try {
|
|
3079
|
-
return JSON.parse(readFileSync11(path, "utf8"));
|
|
3080
|
-
} catch {
|
|
3081
|
-
return null;
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
function normalizeGithubSlug(input) {
|
|
3085
|
-
const trimmed = input.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
3086
|
-
const parts = trimmed.split("/").filter(Boolean);
|
|
3087
|
-
if (parts.length !== 2) return null;
|
|
3088
|
-
return `${parts[0]}/${parts[1]}`;
|
|
3089
|
-
}
|
|
3090
|
-
function portalCdnJsonFromGithub(github, base) {
|
|
3091
|
-
const slug = normalizeGithubSlug(github);
|
|
3092
|
-
if (!slug) {
|
|
3093
|
-
throw new Error(`Invalid GitHub repo \u2014 use org/repo (got "${github}")`);
|
|
3094
|
-
}
|
|
3095
|
-
return {
|
|
3096
|
-
publishMode: base?.publishMode ?? "jsdelivr",
|
|
3097
|
-
github: slug,
|
|
3098
|
-
gitRemote: `https://github.com/${slug}.git`,
|
|
3099
|
-
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
3100
|
-
defaultBranch: base?.defaultBranch ?? "main"
|
|
3101
|
-
};
|
|
3102
|
-
}
|
|
3103
|
-
function hasCdnPublishToken(projectRoot) {
|
|
3104
|
-
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
3105
|
-
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
3106
|
-
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
3107
|
-
}
|
|
3108
|
-
function hasConfiguredCdnGithubMirror(projectRoot) {
|
|
3109
|
-
const config = readPortalCdnJson(projectRoot);
|
|
3110
|
-
const github = config?.github ?? "";
|
|
3111
|
-
return Boolean(github) && !PLACEHOLDER_GITHUB.test(github);
|
|
3112
|
-
}
|
|
3113
|
-
function githubDefaultForPrompt(projectRoot) {
|
|
3114
|
-
const existing = readPortalCdnJson(projectRoot);
|
|
3115
|
-
if (existing?.github && !PLACEHOLDER_GITHUB.test(existing.github)) {
|
|
3116
|
-
return existing.github;
|
|
3117
|
-
}
|
|
3118
|
-
return defaultPortalCdnJson().github;
|
|
3119
|
-
}
|
|
3120
|
-
function needsCdnGitHubSetup(projectRoot) {
|
|
3121
|
-
return !hasConfiguredCdnGithubMirror(projectRoot) || !hasCdnPublishToken(projectRoot);
|
|
3122
|
-
}
|
|
3123
|
-
function applyCdnGitHubSetup(projectRoot, input) {
|
|
3124
|
-
const existing = readPortalCdnJson(projectRoot) ?? defaultPortalCdnJson();
|
|
3125
|
-
const updated = portalCdnJsonFromGithub(input.github, existing);
|
|
3126
|
-
writeFileSync6(resolve15(projectRoot, PORTAL_CDN_JSON), `${JSON.stringify(updated, null, 2)}
|
|
3127
|
-
`);
|
|
3128
|
-
if (input.token?.trim()) {
|
|
3129
|
-
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
3130
|
-
}
|
|
3131
|
-
ensureEnvFilesGitignore(projectRoot);
|
|
3132
|
-
}
|
|
3133
|
-
async function promptCdnGitHubSetup(options = {}) {
|
|
3134
|
-
if (options.skip) return null;
|
|
3135
|
-
const projectRoot = options.projectRoot ?? process.cwd();
|
|
3136
|
-
if (!needsCdnGitHubSetup(projectRoot)) {
|
|
3137
|
-
return null;
|
|
3138
|
-
}
|
|
3139
|
-
let github = options.github?.trim();
|
|
3140
|
-
if (!github && hasConfiguredCdnGithubMirror(projectRoot)) {
|
|
3141
|
-
github = readPortalCdnJson(projectRoot)?.github;
|
|
3142
|
-
}
|
|
3143
|
-
if (!github) {
|
|
3144
|
-
const answer = await p3.text({
|
|
3145
|
-
message: "Public GitHub CDN mirror (org/repo)",
|
|
3146
|
-
placeholder: options.githubDefault ?? "your-org/your-portal-cdn-mirror",
|
|
3147
|
-
initialValue: options.githubDefault,
|
|
3148
|
-
validate: (value) => {
|
|
3149
|
-
const slug = normalizeGithubSlug(value ?? "");
|
|
3150
|
-
return slug ? void 0 : "Enter a public repo as org/repo (jsDelivr serves from it)";
|
|
3151
|
-
}
|
|
3152
|
-
});
|
|
3153
|
-
if (p3.isCancel(answer)) return null;
|
|
3154
|
-
github = normalizeGithubSlug(answer) ?? void 0;
|
|
3155
|
-
}
|
|
3156
|
-
if (!github) return null;
|
|
3157
|
-
if (hasCdnPublishToken(projectRoot)) {
|
|
3158
|
-
return { github };
|
|
3159
|
-
}
|
|
3160
|
-
const tokenAnswer = await p3.password({
|
|
3161
|
-
message: "GitHub token (repo push on CDN mirror)",
|
|
3162
|
-
validate: (value) => {
|
|
3163
|
-
if (!value?.trim()) return "Required for wp publish \u2014 create a fine-grained PAT with Contents: Read and write";
|
|
3164
|
-
return void 0;
|
|
3165
|
-
}
|
|
3166
|
-
});
|
|
3167
|
-
if (p3.isCancel(tokenAnswer)) return null;
|
|
3168
|
-
return { github, token: tokenAnswer.trim() };
|
|
3169
|
-
}
|
|
3170
|
-
function formatCdnSetupNote(_projectRoot) {
|
|
3171
|
-
return `CDN config written \u2014 edit ${PORTAL_CDN_JSON} if needed; token saved to ${ENV_LOCAL_FILE} (gitignored)`;
|
|
3172
|
-
}
|
|
3173
|
-
|
|
3174
3237
|
// src/lib/init-wizard.ts
|
|
3175
3238
|
function cancelIfNeeded(value) {
|
|
3176
3239
|
if (p4.isCancel(value)) {
|
|
@@ -3696,11 +3759,7 @@ async function runInit(ctx, options) {
|
|
|
3696
3759
|
if (isModuleTemplate(template)) {
|
|
3697
3760
|
applyAuthScaffold(targetDir, auth, router, template, nav);
|
|
3698
3761
|
}
|
|
3699
|
-
|
|
3700
|
-
cwd: targetDir,
|
|
3701
|
-
logger: ctx.logger,
|
|
3702
|
-
dryRun: false
|
|
3703
|
-
});
|
|
3762
|
+
await ensureProjectWphsWorkspace(targetDir);
|
|
3704
3763
|
const gitignorePath = resolve17(targetDir, ".gitignore");
|
|
3705
3764
|
const gitignoreExtras = [
|
|
3706
3765
|
"hubspot.config.yml",
|
|
@@ -3863,11 +3922,7 @@ ${baseline}
|
|
|
3863
3922
|
remediation: "Run: wp migrate --yes"
|
|
3864
3923
|
});
|
|
3865
3924
|
}
|
|
3866
|
-
|
|
3867
|
-
cwd: root,
|
|
3868
|
-
logger: ctx.logger,
|
|
3869
|
-
dryRun: false
|
|
3870
|
-
});
|
|
3925
|
+
await ensureProjectWphsWorkspace(root);
|
|
3871
3926
|
for (const action of actions) ctx.logger.success(action);
|
|
3872
3927
|
return ExitCode.SUCCESS;
|
|
3873
3928
|
}
|
|
@@ -3896,8 +3951,8 @@ async function runExamplesSmoke(ctx, options = {}) {
|
|
|
3896
3951
|
const tier = options.tier ?? "static";
|
|
3897
3952
|
const args = ["--tier", tier];
|
|
3898
3953
|
if (options.scenario) args.push("--scenario", options.scenario);
|
|
3899
|
-
const { runNodeScript
|
|
3900
|
-
|
|
3954
|
+
const { runNodeScript } = await import("./exec-VY6BGWF3.js");
|
|
3955
|
+
runNodeScript(kitPath("examples", "smoke", "run-matrix.mjs"), args, {
|
|
3901
3956
|
cwd: ctx.projectRoot,
|
|
3902
3957
|
logger: ctx.logger,
|
|
3903
3958
|
dryRun: ctx.global.dryRun
|
|
@@ -4020,14 +4075,7 @@ async function runSetup(ctx, options = {}) {
|
|
|
4020
4075
|
title: "Preparing .wphs workspace",
|
|
4021
4076
|
task: async () => {
|
|
4022
4077
|
if (ctx.global.dryRun) return;
|
|
4023
|
-
|
|
4024
|
-
runNodeScript(kitPath("scripts", "ensure-wphs-workspace.mjs"), [], {
|
|
4025
|
-
cwd: ctx.projectRoot,
|
|
4026
|
-
logger: ctx.logger,
|
|
4027
|
-
dryRun: false
|
|
4028
|
-
});
|
|
4029
|
-
} catch {
|
|
4030
|
-
}
|
|
4078
|
+
await ensureProjectWphsWorkspace(ctx.projectRoot);
|
|
4031
4079
|
}
|
|
4032
4080
|
},
|
|
4033
4081
|
{
|
|
@@ -4608,7 +4656,7 @@ function attachGlobals(command) {
|
|
|
4608
4656
|
async function handle(fn) {
|
|
4609
4657
|
try {
|
|
4610
4658
|
const code = await fn();
|
|
4611
|
-
|
|
4659
|
+
await exitCli(code);
|
|
4612
4660
|
} catch (error) {
|
|
4613
4661
|
if (isWpHsError(error)) {
|
|
4614
4662
|
process.stderr.write(`Error: ${error.message}
|
|
@@ -4617,11 +4665,11 @@ async function handle(fn) {
|
|
|
4617
4665
|
process.stderr.write(`\u2192 ${error.remediation}
|
|
4618
4666
|
`);
|
|
4619
4667
|
}
|
|
4620
|
-
|
|
4668
|
+
await exitCli(error.exitCode);
|
|
4621
4669
|
}
|
|
4622
4670
|
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}
|
|
4623
4671
|
`);
|
|
4624
|
-
|
|
4672
|
+
await exitCli(ExitCode.INTERNAL);
|
|
4625
4673
|
}
|
|
4626
4674
|
}
|
|
4627
4675
|
function createProgram() {
|