@woodsportal/hubspot-kit 1.0.29 → 1.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cli.js +389 -155
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/templates/shared/.env.prod.example +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "./chunk-GTTIJVAR.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/program.ts
|
|
12
|
-
import { readFileSync as
|
|
13
|
-
import { resolve as
|
|
12
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
13
|
+
import { resolve as resolve23 } from "path";
|
|
14
14
|
import { Command } from "commander";
|
|
15
15
|
|
|
16
16
|
// src/lib/config.ts
|
|
@@ -520,7 +520,7 @@ var VITE_DEV_PEER_DEPS = {
|
|
|
520
520
|
tailwindcss: "^4.0.6"
|
|
521
521
|
};
|
|
522
522
|
var KIT_DEV_DEP = "@woodsportal/hubspot-kit";
|
|
523
|
-
var KIT_DEV_DEP_VERSION = "^1.0.
|
|
523
|
+
var KIT_DEV_DEP_VERSION = "^1.0.30";
|
|
524
524
|
var MODULE_CONFIG_UI_DEPS = [
|
|
525
525
|
"@dnd-kit/core",
|
|
526
526
|
"@dnd-kit/sortable",
|
|
@@ -1109,7 +1109,7 @@ function formatProjectLabel(config) {
|
|
|
1109
1109
|
return `${config.module.name} \xB7 ${config.projectType}${routerNote}`;
|
|
1110
1110
|
}
|
|
1111
1111
|
function section(color, title, body) {
|
|
1112
|
-
const bold = (
|
|
1112
|
+
const bold = (text3) => color ? pc3.bold(text3) : text3;
|
|
1113
1113
|
return ` ${bold(title)}
|
|
1114
1114
|
${body}`;
|
|
1115
1115
|
}
|
|
@@ -1118,10 +1118,10 @@ function printWoodsCliDevBanner(options) {
|
|
|
1118
1118
|
return;
|
|
1119
1119
|
}
|
|
1120
1120
|
const color = useCliColor();
|
|
1121
|
-
const cyan = (
|
|
1122
|
-
const dim = (
|
|
1123
|
-
const bold = (
|
|
1124
|
-
const yellow = (
|
|
1121
|
+
const cyan = (text3) => color ? pc3.cyan(text3) : text3;
|
|
1122
|
+
const dim = (text3) => color ? pc3.dim(text3) : text3;
|
|
1123
|
+
const bold = (text3) => color ? pc3.bold(text3) : text3;
|
|
1124
|
+
const yellow = (text3) => color ? pc3.yellow(text3) : text3;
|
|
1125
1125
|
const version = options.version ?? readKitVersion();
|
|
1126
1126
|
const project = formatProjectLabel(options.config);
|
|
1127
1127
|
const isTheme = isThemeProject(options.config);
|
|
@@ -1247,7 +1247,76 @@ async function runDev(ctx, options) {
|
|
|
1247
1247
|
}
|
|
1248
1248
|
|
|
1249
1249
|
// src/cli/commands/deploy.ts
|
|
1250
|
-
import { resolve as
|
|
1250
|
+
import { resolve as resolve13 } from "path";
|
|
1251
|
+
|
|
1252
|
+
// src/lib/project-env-files.ts
|
|
1253
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync } from "fs";
|
|
1254
|
+
import { resolve as resolve11 } from "path";
|
|
1255
|
+
var ENV_LOCAL_FILE = ".env.local";
|
|
1256
|
+
var ENV_GITIGNORE_LINES = [".env", ".env.*", "!.env*.example", ENV_LOCAL_FILE];
|
|
1257
|
+
function parseEnvFile(content) {
|
|
1258
|
+
const result = {};
|
|
1259
|
+
for (const line of content.split("\n")) {
|
|
1260
|
+
const trimmed = line.trim();
|
|
1261
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1262
|
+
const eq = trimmed.indexOf("=");
|
|
1263
|
+
if (eq <= 0) continue;
|
|
1264
|
+
const key = trimmed.slice(0, eq).trim();
|
|
1265
|
+
let value = trimmed.slice(eq + 1).trim();
|
|
1266
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1267
|
+
value = value.slice(1, -1);
|
|
1268
|
+
}
|
|
1269
|
+
result[key] = value;
|
|
1270
|
+
}
|
|
1271
|
+
return result;
|
|
1272
|
+
}
|
|
1273
|
+
function readEnvLocal(projectRoot) {
|
|
1274
|
+
const path = resolve11(projectRoot, ENV_LOCAL_FILE);
|
|
1275
|
+
if (!existsSync8(path)) return {};
|
|
1276
|
+
return parseEnvFile(readFileSync6(path, "utf8"));
|
|
1277
|
+
}
|
|
1278
|
+
function formatEnvLocal(entries) {
|
|
1279
|
+
const lines = [
|
|
1280
|
+
"# Local secrets \u2014 gitignored. Created by wp init / wp setup (hybrid CDN).",
|
|
1281
|
+
"# PORTAL_CDN_PUBLISH_TOKEN needs repo push on your portal.cdn.json mirror.",
|
|
1282
|
+
""
|
|
1283
|
+
];
|
|
1284
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
1285
|
+
if (value.includes(" ") || value.includes("#")) {
|
|
1286
|
+
lines.push(`${key}="${value.replace(/"/g, '\\"')}"`);
|
|
1287
|
+
} else {
|
|
1288
|
+
lines.push(`${key}=${value}`);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
return `${lines.join("\n").trimEnd()}
|
|
1292
|
+
`;
|
|
1293
|
+
}
|
|
1294
|
+
function writeEnvLocal(projectRoot, entries) {
|
|
1295
|
+
const path = resolve11(projectRoot, ENV_LOCAL_FILE);
|
|
1296
|
+
const existing = existsSync8(path) ? parseEnvFile(readFileSync6(path, "utf8")) : {};
|
|
1297
|
+
writeFileSync(path, formatEnvLocal({ ...existing, ...entries }));
|
|
1298
|
+
}
|
|
1299
|
+
function mergeProjectEnv(projectRoot, base = process.env) {
|
|
1300
|
+
const local = readEnvLocal(projectRoot);
|
|
1301
|
+
const merged = { ...base };
|
|
1302
|
+
for (const [key, value] of Object.entries(local)) {
|
|
1303
|
+
if (merged[key] == null || merged[key] === "") {
|
|
1304
|
+
merged[key] = value;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
return merged;
|
|
1308
|
+
}
|
|
1309
|
+
function ensureEnvFilesGitignore(projectRoot) {
|
|
1310
|
+
const gitignorePath = resolve11(projectRoot, ".gitignore");
|
|
1311
|
+
const existing = existsSync8(gitignorePath) ? readFileSync6(gitignorePath, "utf8") : "";
|
|
1312
|
+
const toAppend = ENV_GITIGNORE_LINES.filter((line) => !existing.includes(line)).join("\n");
|
|
1313
|
+
if (!toAppend) return;
|
|
1314
|
+
writeFileSync(gitignorePath, `${existing.trimEnd()}
|
|
1315
|
+
|
|
1316
|
+
# Env files (secrets)
|
|
1317
|
+
${toAppend}
|
|
1318
|
+
`);
|
|
1319
|
+
}
|
|
1251
1320
|
|
|
1252
1321
|
// src/cli/commands/publish.ts
|
|
1253
1322
|
async function runPublish(ctx, options) {
|
|
@@ -1258,7 +1327,10 @@ async function runPublish(ctx, options) {
|
|
|
1258
1327
|
});
|
|
1259
1328
|
}
|
|
1260
1329
|
const tier = options.tier ?? DEFAULT_BUILD_TIER;
|
|
1261
|
-
const env =
|
|
1330
|
+
const env = mergeProjectEnv(ctx.projectRoot, {
|
|
1331
|
+
...process.env,
|
|
1332
|
+
WP_HS_PROJECT_ROOT: ctx.projectRoot
|
|
1333
|
+
});
|
|
1262
1334
|
const execOpts = {
|
|
1263
1335
|
cwd: ctx.projectRoot,
|
|
1264
1336
|
env,
|
|
@@ -1305,14 +1377,14 @@ async function runPublish(ctx, options) {
|
|
|
1305
1377
|
}
|
|
1306
1378
|
|
|
1307
1379
|
// src/cli/commands/upload.ts
|
|
1308
|
-
import { resolve as
|
|
1380
|
+
import { resolve as resolve12 } from "path";
|
|
1309
1381
|
async function uploadModuleOrTheme(ctx) {
|
|
1310
1382
|
if (isThemeProject(ctx.config)) {
|
|
1311
1383
|
const theme = ctx.config.theme;
|
|
1312
1384
|
if (!theme) {
|
|
1313
1385
|
throw new WpHsError("theme block missing in woodscli.json", { exitCode: ExitCode.USER_ERROR });
|
|
1314
1386
|
}
|
|
1315
|
-
const srcDir =
|
|
1387
|
+
const srcDir = resolve12(ctx.projectRoot, theme.srcDir);
|
|
1316
1388
|
runCommand("hs", ["cms", "upload", srcDir, theme.uploadPath], {
|
|
1317
1389
|
cwd: ctx.projectRoot,
|
|
1318
1390
|
logger: ctx.logger,
|
|
@@ -1320,7 +1392,7 @@ async function uploadModuleOrTheme(ctx) {
|
|
|
1320
1392
|
});
|
|
1321
1393
|
return `Uploaded theme ${srcDir} \u2192 ${theme.uploadPath}`;
|
|
1322
1394
|
}
|
|
1323
|
-
const outDir =
|
|
1395
|
+
const outDir = resolve12(ctx.projectRoot, ctx.config.module.outDir);
|
|
1324
1396
|
const uploadPath = ctx.config.module.uploadPath;
|
|
1325
1397
|
runCommand("hs", ["cms", "upload", outDir, uploadPath], {
|
|
1326
1398
|
cwd: ctx.projectRoot,
|
|
@@ -1432,7 +1504,7 @@ async function runDeploy(ctx, options) {
|
|
|
1432
1504
|
if (!ctx.global.json) {
|
|
1433
1505
|
printCommandDone(
|
|
1434
1506
|
ctx,
|
|
1435
|
-
`Deploy complete (${
|
|
1507
|
+
`Deploy complete (${resolve13(ctx.projectRoot, ctx.config.module.outDir)} \u2192 ${ctx.config.module.uploadPath})`
|
|
1436
1508
|
);
|
|
1437
1509
|
}
|
|
1438
1510
|
return ExitCode.SUCCESS;
|
|
@@ -1440,8 +1512,8 @@ async function runDeploy(ctx, options) {
|
|
|
1440
1512
|
|
|
1441
1513
|
// src/cli/commands/audit.ts
|
|
1442
1514
|
import { createHash } from "crypto";
|
|
1443
|
-
import { cpSync, existsSync as
|
|
1444
|
-
import { resolve as
|
|
1515
|
+
import { cpSync, existsSync as existsSync9, mkdirSync, readFileSync as readFileSync7, readdirSync, rmSync, statSync } from "fs";
|
|
1516
|
+
import { resolve as resolve14, join as join2 } from "path";
|
|
1445
1517
|
var FORBIDDEN_SHIP_TOKENS = [
|
|
1446
1518
|
"ModuleConfigOverlay",
|
|
1447
1519
|
"module-config-store",
|
|
@@ -1450,10 +1522,10 @@ var FORBIDDEN_SHIP_TOKENS = [
|
|
|
1450
1522
|
"dev.module-config"
|
|
1451
1523
|
];
|
|
1452
1524
|
function hashFile(path) {
|
|
1453
|
-
return createHash("sha256").update(
|
|
1525
|
+
return createHash("sha256").update(readFileSync7(path)).digest("hex");
|
|
1454
1526
|
}
|
|
1455
1527
|
function walkFiles(dir) {
|
|
1456
|
-
if (!
|
|
1528
|
+
if (!existsSync9(dir)) return [];
|
|
1457
1529
|
const entries = readdirSync(dir);
|
|
1458
1530
|
const files = [];
|
|
1459
1531
|
for (const entry of entries) {
|
|
@@ -1469,14 +1541,14 @@ function walkFiles(dir) {
|
|
|
1469
1541
|
async function runAuditShipBundle(ctx) {
|
|
1470
1542
|
const root = ctx.projectRoot;
|
|
1471
1543
|
const targets = [
|
|
1472
|
-
|
|
1473
|
-
|
|
1544
|
+
resolve14(root, "dist/cdn"),
|
|
1545
|
+
resolve14(root, ctx.config.module.outDir)
|
|
1474
1546
|
];
|
|
1475
1547
|
const violations = [];
|
|
1476
1548
|
for (const dir of targets) {
|
|
1477
1549
|
for (const file of walkFiles(dir)) {
|
|
1478
1550
|
if (!/\.(js|css|html)$/i.test(file)) continue;
|
|
1479
|
-
const content =
|
|
1551
|
+
const content = readFileSync7(file, "utf8");
|
|
1480
1552
|
for (const token of FORBIDDEN_SHIP_TOKENS) {
|
|
1481
1553
|
if (content.includes(token)) {
|
|
1482
1554
|
violations.push(`${file}: contains forbidden token "${token}"`);
|
|
@@ -1503,21 +1575,21 @@ async function runAuditBaselineCapture(ctx, options) {
|
|
|
1503
1575
|
const root = ctx.projectRoot;
|
|
1504
1576
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1505
1577
|
const sources = [
|
|
1506
|
-
{ label: "cdn", path:
|
|
1507
|
-
{ label: "hubspot", path:
|
|
1578
|
+
{ label: "cdn", path: resolve14(root, "dist/cdn") },
|
|
1579
|
+
{ label: "hubspot", path: resolve14(root, ctx.config.module.outDir) }
|
|
1508
1580
|
];
|
|
1509
1581
|
if (ctx.global.dryRun) {
|
|
1510
1582
|
ctx.logger.info(`[dry-run] Would capture baseline to ${baseline}`);
|
|
1511
1583
|
return ExitCode.SUCCESS;
|
|
1512
1584
|
}
|
|
1513
1585
|
for (const { label, path } of sources) {
|
|
1514
|
-
if (!
|
|
1586
|
+
if (!existsSync9(path)) {
|
|
1515
1587
|
throw new WpHsError(`Cannot capture baseline \u2014 missing ${path}`, {
|
|
1516
1588
|
exitCode: ExitCode.USER_ERROR,
|
|
1517
1589
|
remediation: "Run wp build before wp audit baseline --capture."
|
|
1518
1590
|
});
|
|
1519
1591
|
}
|
|
1520
|
-
const target =
|
|
1592
|
+
const target = resolve14(baseline, label);
|
|
1521
1593
|
rmSync(target, { recursive: true, force: true });
|
|
1522
1594
|
mkdirSync(baseline, { recursive: true });
|
|
1523
1595
|
cpSync(path, target, { recursive: true });
|
|
@@ -1533,13 +1605,13 @@ async function runAuditParity(ctx, options) {
|
|
|
1533
1605
|
const root = ctx.projectRoot;
|
|
1534
1606
|
const baseline = options.baselineDir ?? defaultBaselineDir(root, ctx.config);
|
|
1535
1607
|
const currentDirs = [
|
|
1536
|
-
{ label: "cdn", path:
|
|
1537
|
-
{ label: "hubspot", path:
|
|
1608
|
+
{ label: "cdn", path: resolve14(root, "dist/cdn") },
|
|
1609
|
+
{ label: "hubspot", path: resolve14(root, ctx.config.module.outDir) }
|
|
1538
1610
|
];
|
|
1539
1611
|
const mismatches = [];
|
|
1540
1612
|
for (const { label, path } of currentDirs) {
|
|
1541
|
-
const baselinePath =
|
|
1542
|
-
if (!
|
|
1613
|
+
const baselinePath = resolve14(baseline, label);
|
|
1614
|
+
if (!existsSync9(baselinePath)) {
|
|
1543
1615
|
ctx.logger.warn(`No baseline at ${baselinePath} \u2014 skipping ${label} parity`);
|
|
1544
1616
|
continue;
|
|
1545
1617
|
}
|
|
@@ -1554,8 +1626,8 @@ async function runAuditParity(ctx, options) {
|
|
|
1554
1626
|
}
|
|
1555
1627
|
for (const file of baselineFiles) {
|
|
1556
1628
|
const rel = file.replace(baselinePath, "");
|
|
1557
|
-
const current =
|
|
1558
|
-
if (!
|
|
1629
|
+
const current = resolve14(path, rel.slice(1));
|
|
1630
|
+
if (!existsSync9(current)) continue;
|
|
1559
1631
|
const bh = hashFile(file);
|
|
1560
1632
|
const ch = hashFile(current);
|
|
1561
1633
|
if (bh !== ch) {
|
|
@@ -1582,13 +1654,13 @@ var SECRET_PATTERNS = [
|
|
|
1582
1654
|
];
|
|
1583
1655
|
async function runAuditSecrets(ctx) {
|
|
1584
1656
|
const root = ctx.projectRoot;
|
|
1585
|
-
const scanRoots = ["src", "public", ".wphs/config"].map((d) =>
|
|
1657
|
+
const scanRoots = ["src", "public", ".wphs/config"].map((d) => resolve14(root, d));
|
|
1586
1658
|
const findings = [];
|
|
1587
1659
|
for (const dir of scanRoots) {
|
|
1588
1660
|
for (const file of walkFiles(dir)) {
|
|
1589
1661
|
if (!/\.(ts|tsx|js|jsx|json|env|yml|yaml|html)$/i.test(file)) continue;
|
|
1590
1662
|
if (file.includes("node_modules")) continue;
|
|
1591
|
-
const content =
|
|
1663
|
+
const content = readFileSync7(file, "utf8");
|
|
1592
1664
|
for (const pattern of SECRET_PATTERNS) {
|
|
1593
1665
|
pattern.lastIndex = 0;
|
|
1594
1666
|
if (pattern.test(content)) {
|
|
@@ -1614,13 +1686,13 @@ async function runAuditSecrets(ctx) {
|
|
|
1614
1686
|
}
|
|
1615
1687
|
|
|
1616
1688
|
// src/cli/commands/init.ts
|
|
1617
|
-
import { cpSync as cpSync2, existsSync as
|
|
1618
|
-
import { basename as basename2, resolve as
|
|
1619
|
-
import * as
|
|
1689
|
+
import { cpSync as cpSync2, existsSync as existsSync13, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
|
|
1690
|
+
import { basename as basename2, resolve as resolve18 } from "path";
|
|
1691
|
+
import * as p3 from "@clack/prompts";
|
|
1620
1692
|
|
|
1621
1693
|
// src/lib/init-router-scaffold.ts
|
|
1622
|
-
import { existsSync as
|
|
1623
|
-
import { resolve as
|
|
1694
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1695
|
+
import { resolve as resolve15 } from "path";
|
|
1624
1696
|
var ROUTER_FILES = [
|
|
1625
1697
|
"src/routes",
|
|
1626
1698
|
"src/routeTree.gen.ts",
|
|
@@ -1634,10 +1706,10 @@ function isModuleTemplate(template) {
|
|
|
1634
1706
|
return template === "module-hybrid-cdn" || template === "module-monolith";
|
|
1635
1707
|
}
|
|
1636
1708
|
function writeRouterConfig(targetDir, mode) {
|
|
1637
|
-
const configDir =
|
|
1709
|
+
const configDir = resolve15(targetDir, "src/config");
|
|
1638
1710
|
mkdirSync2(configDir, { recursive: true });
|
|
1639
|
-
|
|
1640
|
-
|
|
1711
|
+
writeFileSync2(
|
|
1712
|
+
resolve15(configDir, "router.ts"),
|
|
1641
1713
|
`/** Router history \u2014 set by \`wp init\`. Hash matches woodsportal-client-frontend (HubSpot CMS embeds). */
|
|
1642
1714
|
export type RouterHistoryMode = 'hash' | 'browser'
|
|
1643
1715
|
|
|
@@ -1646,8 +1718,8 @@ export const ROUTER_HISTORY: RouterHistoryMode = '${mode}'
|
|
|
1646
1718
|
);
|
|
1647
1719
|
}
|
|
1648
1720
|
function writeMountDevPreviewRouter(targetDir) {
|
|
1649
|
-
|
|
1650
|
-
|
|
1721
|
+
writeFileSync2(
|
|
1722
|
+
resolve15(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1651
1723
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1652
1724
|
|
|
1653
1725
|
import { App } from '@/app'
|
|
@@ -1660,8 +1732,8 @@ export async function mountDevPreview() {
|
|
|
1660
1732
|
);
|
|
1661
1733
|
}
|
|
1662
1734
|
function writeMountDevPreviewSinglePage(targetDir) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1735
|
+
writeFileSync2(
|
|
1736
|
+
resolve15(targetDir, "src/portal/mount-dev-preview.tsx"),
|
|
1665
1737
|
`import { mountGreenfieldDev } from '../../.wphs/.kit-bootstrap-greenfield.tsx'
|
|
1666
1738
|
|
|
1667
1739
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1674,8 +1746,8 @@ export async function mountDevPreview() {
|
|
|
1674
1746
|
);
|
|
1675
1747
|
}
|
|
1676
1748
|
function writeMountPortalRouter(targetDir) {
|
|
1677
|
-
|
|
1678
|
-
|
|
1749
|
+
writeFileSync2(
|
|
1750
|
+
resolve15(targetDir, "src/portal/mount-portal.tsx"),
|
|
1679
1751
|
`import ReactDOM from 'react-dom/client'
|
|
1680
1752
|
|
|
1681
1753
|
import { App } from '@/app'
|
|
@@ -1692,8 +1764,8 @@ export async function mountPortal() {
|
|
|
1692
1764
|
);
|
|
1693
1765
|
}
|
|
1694
1766
|
function writeMountPortalSinglePage(targetDir) {
|
|
1695
|
-
|
|
1696
|
-
|
|
1767
|
+
writeFileSync2(
|
|
1768
|
+
resolve15(targetDir, "src/portal/mount-portal.tsx"),
|
|
1697
1769
|
`import ReactDOM from 'react-dom/client'
|
|
1698
1770
|
|
|
1699
1771
|
import { StarterSinglePage } from '@/features/starter'
|
|
@@ -1710,11 +1782,11 @@ export async function mountPortal() {
|
|
|
1710
1782
|
);
|
|
1711
1783
|
}
|
|
1712
1784
|
function writeStarterSinglePage(targetDir) {
|
|
1713
|
-
const file =
|
|
1714
|
-
if (
|
|
1785
|
+
const file = resolve15(targetDir, "src/features/starter/components/starter-single-page.tsx");
|
|
1786
|
+
if (existsSync10(file)) {
|
|
1715
1787
|
return;
|
|
1716
1788
|
}
|
|
1717
|
-
|
|
1789
|
+
writeFileSync2(
|
|
1718
1790
|
file,
|
|
1719
1791
|
`import { StarterShell } from '@/components/layout/starter-shell'
|
|
1720
1792
|
|
|
@@ -1737,25 +1809,25 @@ export function StarterSinglePage() {
|
|
|
1737
1809
|
);
|
|
1738
1810
|
}
|
|
1739
1811
|
function removePath(targetDir, relativePath) {
|
|
1740
|
-
const full =
|
|
1741
|
-
if (!
|
|
1812
|
+
const full = resolve15(targetDir, relativePath);
|
|
1813
|
+
if (!existsSync10(full)) {
|
|
1742
1814
|
return;
|
|
1743
1815
|
}
|
|
1744
1816
|
rmSync2(full, { recursive: true, force: true });
|
|
1745
1817
|
}
|
|
1746
1818
|
function stripTanStackFromPackage(targetDir) {
|
|
1747
|
-
const pkgPath =
|
|
1748
|
-
if (!
|
|
1819
|
+
const pkgPath = resolve15(targetDir, "package.json");
|
|
1820
|
+
if (!existsSync10(pkgPath)) {
|
|
1749
1821
|
return;
|
|
1750
1822
|
}
|
|
1751
|
-
const pkg = JSON.parse(
|
|
1823
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
1752
1824
|
if (!pkg.devDependencies) {
|
|
1753
1825
|
return;
|
|
1754
1826
|
}
|
|
1755
1827
|
for (const dep of TANSTACK_DEPS) {
|
|
1756
1828
|
delete pkg.devDependencies[dep];
|
|
1757
1829
|
}
|
|
1758
|
-
|
|
1830
|
+
writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1759
1831
|
`);
|
|
1760
1832
|
}
|
|
1761
1833
|
function applyRouterScaffold(targetDir, mode, template) {
|
|
@@ -1775,22 +1847,141 @@ function applyRouterScaffold(targetDir, mode, template) {
|
|
|
1775
1847
|
writeRouterConfig(targetDir, mode);
|
|
1776
1848
|
writeMountDevPreviewRouter(targetDir);
|
|
1777
1849
|
writeMountPortalRouter(targetDir);
|
|
1778
|
-
const featuresIndex =
|
|
1779
|
-
if (
|
|
1780
|
-
let content =
|
|
1850
|
+
const featuresIndex = resolve15(targetDir, "src/features/starter/index.ts");
|
|
1851
|
+
if (existsSync10(featuresIndex)) {
|
|
1852
|
+
let content = readFileSync8(featuresIndex, "utf8");
|
|
1781
1853
|
if (!content.includes("StarterSinglePage")) {
|
|
1782
1854
|
content = content.replace(
|
|
1783
1855
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\n",
|
|
1784
1856
|
"export type { StarterPreviewValues } from './hooks/use-starter-preview'\nexport { StarterSinglePage } from './components/starter-single-page'\n"
|
|
1785
1857
|
);
|
|
1786
|
-
|
|
1858
|
+
writeFileSync2(featuresIndex, content);
|
|
1787
1859
|
}
|
|
1788
1860
|
}
|
|
1789
1861
|
}
|
|
1790
1862
|
|
|
1863
|
+
// src/lib/cdn-github-setup.ts
|
|
1864
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "fs";
|
|
1865
|
+
import { resolve as resolve16 } from "path";
|
|
1866
|
+
import * as p2 from "@clack/prompts";
|
|
1867
|
+
var PORTAL_CDN_PUBLISH_TOKEN_ENV = "PORTAL_CDN_PUBLISH_TOKEN";
|
|
1868
|
+
var PORTAL_CDN_JSON = "portal.cdn.json";
|
|
1869
|
+
var PLACEHOLDER_GITHUB = /your-org|your-portal-cdn-mirror/i;
|
|
1870
|
+
function defaultPortalCdnJson() {
|
|
1871
|
+
const examplePath = kitPath("templates", "shared", "portal.cdn.json.example");
|
|
1872
|
+
if (existsSync11(examplePath)) {
|
|
1873
|
+
return JSON.parse(readFileSync9(examplePath, "utf8"));
|
|
1874
|
+
}
|
|
1875
|
+
return {
|
|
1876
|
+
publishMode: "jsdelivr",
|
|
1877
|
+
github: "your-org/your-portal-cdn-mirror",
|
|
1878
|
+
gitRemote: "https://github.com/your-org/your-portal-cdn-mirror.git",
|
|
1879
|
+
cdnPathInRepo: "dist/cdn",
|
|
1880
|
+
defaultBranch: "main"
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
function readPortalCdnJson(projectRoot) {
|
|
1884
|
+
const path = resolve16(projectRoot, PORTAL_CDN_JSON);
|
|
1885
|
+
if (!existsSync11(path)) return null;
|
|
1886
|
+
try {
|
|
1887
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
1888
|
+
} catch {
|
|
1889
|
+
return null;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
function normalizeGithubSlug(input) {
|
|
1893
|
+
const trimmed = input.trim().replace(/^https:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
1894
|
+
const parts = trimmed.split("/").filter(Boolean);
|
|
1895
|
+
if (parts.length !== 2) return null;
|
|
1896
|
+
return `${parts[0]}/${parts[1]}`;
|
|
1897
|
+
}
|
|
1898
|
+
function portalCdnJsonFromGithub(github, base) {
|
|
1899
|
+
const slug = normalizeGithubSlug(github);
|
|
1900
|
+
if (!slug) {
|
|
1901
|
+
throw new Error(`Invalid GitHub repo \u2014 use org/repo (got "${github}")`);
|
|
1902
|
+
}
|
|
1903
|
+
return {
|
|
1904
|
+
publishMode: base?.publishMode ?? "jsdelivr",
|
|
1905
|
+
github: slug,
|
|
1906
|
+
gitRemote: `https://github.com/${slug}.git`,
|
|
1907
|
+
cdnPathInRepo: base?.cdnPathInRepo ?? "dist/cdn",
|
|
1908
|
+
defaultBranch: base?.defaultBranch ?? "main"
|
|
1909
|
+
};
|
|
1910
|
+
}
|
|
1911
|
+
function hasCdnPublishToken(projectRoot) {
|
|
1912
|
+
if (process.env[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim()) return true;
|
|
1913
|
+
if (process.env.GITHUB_TOKEN?.trim()) return true;
|
|
1914
|
+
return Boolean(readEnvLocal(projectRoot)[PORTAL_CDN_PUBLISH_TOKEN_ENV]?.trim());
|
|
1915
|
+
}
|
|
1916
|
+
function hasConfiguredCdnGithubMirror(projectRoot) {
|
|
1917
|
+
const config = readPortalCdnJson(projectRoot);
|
|
1918
|
+
const github = config?.github ?? "";
|
|
1919
|
+
return Boolean(github) && !PLACEHOLDER_GITHUB.test(github);
|
|
1920
|
+
}
|
|
1921
|
+
function githubDefaultForPrompt(projectRoot) {
|
|
1922
|
+
const existing = readPortalCdnJson(projectRoot);
|
|
1923
|
+
if (existing?.github && !PLACEHOLDER_GITHUB.test(existing.github)) {
|
|
1924
|
+
return existing.github;
|
|
1925
|
+
}
|
|
1926
|
+
return defaultPortalCdnJson().github;
|
|
1927
|
+
}
|
|
1928
|
+
function needsCdnGitHubSetup(projectRoot) {
|
|
1929
|
+
return !hasConfiguredCdnGithubMirror(projectRoot) || !hasCdnPublishToken(projectRoot);
|
|
1930
|
+
}
|
|
1931
|
+
function applyCdnGitHubSetup(projectRoot, input) {
|
|
1932
|
+
const existing = readPortalCdnJson(projectRoot) ?? defaultPortalCdnJson();
|
|
1933
|
+
const updated = portalCdnJsonFromGithub(input.github, existing);
|
|
1934
|
+
writeFileSync3(resolve16(projectRoot, PORTAL_CDN_JSON), `${JSON.stringify(updated, null, 2)}
|
|
1935
|
+
`);
|
|
1936
|
+
if (input.token?.trim()) {
|
|
1937
|
+
writeEnvLocal(projectRoot, { [PORTAL_CDN_PUBLISH_TOKEN_ENV]: input.token.trim() });
|
|
1938
|
+
}
|
|
1939
|
+
ensureEnvFilesGitignore(projectRoot);
|
|
1940
|
+
}
|
|
1941
|
+
async function promptCdnGitHubSetup(options = {}) {
|
|
1942
|
+
if (options.skip) return null;
|
|
1943
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
1944
|
+
if (!needsCdnGitHubSetup(projectRoot)) {
|
|
1945
|
+
return null;
|
|
1946
|
+
}
|
|
1947
|
+
let github = options.github?.trim();
|
|
1948
|
+
if (!github && hasConfiguredCdnGithubMirror(projectRoot)) {
|
|
1949
|
+
github = readPortalCdnJson(projectRoot)?.github;
|
|
1950
|
+
}
|
|
1951
|
+
if (!github) {
|
|
1952
|
+
const answer = await p2.text({
|
|
1953
|
+
message: "Public GitHub CDN mirror (org/repo)",
|
|
1954
|
+
placeholder: options.githubDefault ?? "your-org/your-portal-cdn-mirror",
|
|
1955
|
+
initialValue: options.githubDefault,
|
|
1956
|
+
validate: (value) => {
|
|
1957
|
+
const slug = normalizeGithubSlug(value ?? "");
|
|
1958
|
+
return slug ? void 0 : "Enter a public repo as org/repo (jsDelivr serves from it)";
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
if (p2.isCancel(answer)) return null;
|
|
1962
|
+
github = normalizeGithubSlug(answer) ?? void 0;
|
|
1963
|
+
}
|
|
1964
|
+
if (!github) return null;
|
|
1965
|
+
if (hasCdnPublishToken(projectRoot)) {
|
|
1966
|
+
return { github };
|
|
1967
|
+
}
|
|
1968
|
+
const tokenAnswer = await p2.password({
|
|
1969
|
+
message: "GitHub token (repo push on CDN mirror)",
|
|
1970
|
+
validate: (value) => {
|
|
1971
|
+
if (!value?.trim()) return "Required for wp publish \u2014 create a fine-grained PAT with Contents: Read and write";
|
|
1972
|
+
return void 0;
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
if (p2.isCancel(tokenAnswer)) return null;
|
|
1976
|
+
return { github, token: tokenAnswer.trim() };
|
|
1977
|
+
}
|
|
1978
|
+
function formatCdnSetupNote(_projectRoot) {
|
|
1979
|
+
return `CDN config written \u2014 edit ${PORTAL_CDN_JSON} if needed; token saved to ${ENV_LOCAL_FILE} (gitignored)`;
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1791
1982
|
// src/lib/project-setup.ts
|
|
1792
|
-
import { copyFileSync, existsSync as
|
|
1793
|
-
import { basename, resolve as
|
|
1983
|
+
import { copyFileSync, existsSync as existsSync12, writeFileSync as writeFileSync4 } from "fs";
|
|
1984
|
+
import { basename, resolve as resolve17 } from "path";
|
|
1794
1985
|
function envFileForTier(tier) {
|
|
1795
1986
|
return tier === "dev" ? ".env.dev" : tier === "stg" ? ".env.stg" : ".env.prod";
|
|
1796
1987
|
}
|
|
@@ -1800,13 +1991,13 @@ function tiersToSetup(config, tier) {
|
|
|
1800
1991
|
}
|
|
1801
1992
|
function firstExisting(paths) {
|
|
1802
1993
|
for (const path of paths) {
|
|
1803
|
-
if (
|
|
1994
|
+
if (existsSync12(path)) return path;
|
|
1804
1995
|
}
|
|
1805
1996
|
return null;
|
|
1806
1997
|
}
|
|
1807
1998
|
function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
1808
|
-
const target =
|
|
1809
|
-
if (
|
|
1999
|
+
const target = resolve17(projectRoot, targetName);
|
|
2000
|
+
if (existsSync12(target)) {
|
|
1810
2001
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1811
2002
|
}
|
|
1812
2003
|
const source = firstExisting(sourceCandidates);
|
|
@@ -1828,12 +2019,12 @@ function copyMissingFile(projectRoot, targetName, sourceCandidates, dryRun) {
|
|
|
1828
2019
|
};
|
|
1829
2020
|
}
|
|
1830
2021
|
function writeMissingFile(projectRoot, targetName, content, dryRun) {
|
|
1831
|
-
const target =
|
|
1832
|
-
if (
|
|
2022
|
+
const target = resolve17(projectRoot, targetName);
|
|
2023
|
+
if (existsSync12(target)) {
|
|
1833
2024
|
return { action: "skipped", path: targetName, detail: "already present" };
|
|
1834
2025
|
}
|
|
1835
2026
|
if (!dryRun) {
|
|
1836
|
-
|
|
2027
|
+
writeFileSync4(target, content);
|
|
1837
2028
|
}
|
|
1838
2029
|
return {
|
|
1839
2030
|
action: "copied",
|
|
@@ -1859,8 +2050,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1859
2050
|
const templateDir = kitTemplateDir(config);
|
|
1860
2051
|
results.push(
|
|
1861
2052
|
copyMissingFile(projectRoot, HUBSPOT_CONFIG_FILE, [
|
|
1862
|
-
|
|
1863
|
-
|
|
2053
|
+
resolve17(projectRoot, HUBSPOT_CONFIG_EXAMPLE),
|
|
2054
|
+
resolve17(templateDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1864
2055
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1865
2056
|
], dryRun)
|
|
1866
2057
|
);
|
|
@@ -1869,8 +2060,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1869
2060
|
const envExample = `${envFile}.example`;
|
|
1870
2061
|
results.push(
|
|
1871
2062
|
copyMissingFile(projectRoot, envFile, [
|
|
1872
|
-
|
|
1873
|
-
|
|
2063
|
+
resolve17(projectRoot, envExample),
|
|
2064
|
+
resolve17(sharedDir, envExample)
|
|
1874
2065
|
], dryRun)
|
|
1875
2066
|
);
|
|
1876
2067
|
}
|
|
@@ -1878,8 +2069,8 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1878
2069
|
const mirror = config.cdn.mirror;
|
|
1879
2070
|
results.push(
|
|
1880
2071
|
copyMissingFile(projectRoot, mirror, [
|
|
1881
|
-
|
|
1882
|
-
|
|
2072
|
+
resolve17(projectRoot, `${mirror}.example`),
|
|
2073
|
+
resolve17(sharedDir, "portal.cdn.json.example")
|
|
1883
2074
|
], dryRun)
|
|
1884
2075
|
);
|
|
1885
2076
|
const buildIdFile = config.cdn.buildIdFile;
|
|
@@ -1889,15 +2080,15 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1889
2080
|
}
|
|
1890
2081
|
for (const file of templateViteFiles(config)) {
|
|
1891
2082
|
results.push(
|
|
1892
|
-
copyMissingFile(projectRoot, file, [
|
|
2083
|
+
copyMissingFile(projectRoot, file, [resolve17(templateDir, file)], dryRun)
|
|
1893
2084
|
);
|
|
1894
2085
|
}
|
|
1895
2086
|
if (isModuleProject(config) && !isThemeProject(config)) {
|
|
1896
2087
|
const adapter = config.dev.adapter;
|
|
1897
|
-
const adapterPath =
|
|
1898
|
-
if (!
|
|
1899
|
-
const templateAdapter =
|
|
1900
|
-
if (
|
|
2088
|
+
const adapterPath = resolve17(projectRoot, adapter);
|
|
2089
|
+
if (!existsSync12(adapterPath)) {
|
|
2090
|
+
const templateAdapter = resolve17(templateDir, "wphs.adapter.ts");
|
|
2091
|
+
if (existsSync12(templateAdapter)) {
|
|
1901
2092
|
results.push(copyMissingFile(projectRoot, adapter, [templateAdapter], dryRun));
|
|
1902
2093
|
} else {
|
|
1903
2094
|
results.push({
|
|
@@ -1914,26 +2105,26 @@ function applyProjectSetup(ctx, options = {}) {
|
|
|
1914
2105
|
}
|
|
1915
2106
|
function copySharedSetupExamples(targetDir) {
|
|
1916
2107
|
const sharedDir = kitPath("templates", "shared");
|
|
1917
|
-
if (!
|
|
2108
|
+
if (!existsSync12(sharedDir)) return;
|
|
1918
2109
|
for (const name of [
|
|
1919
2110
|
".env.dev.example",
|
|
1920
2111
|
".env.stg.example",
|
|
1921
2112
|
".env.prod.example",
|
|
1922
2113
|
"portal.cdn.json.example"
|
|
1923
2114
|
]) {
|
|
1924
|
-
const src =
|
|
1925
|
-
const dest =
|
|
1926
|
-
if (
|
|
2115
|
+
const src = resolve17(sharedDir, name);
|
|
2116
|
+
const dest = resolve17(targetDir, name);
|
|
2117
|
+
if (existsSync12(src) && !existsSync12(dest)) {
|
|
1927
2118
|
copyFileSync(src, dest);
|
|
1928
2119
|
}
|
|
1929
2120
|
}
|
|
1930
2121
|
const hubspotExample = firstExisting([
|
|
1931
|
-
|
|
2122
|
+
resolve17(sharedDir, HUBSPOT_CONFIG_EXAMPLE),
|
|
1932
2123
|
kitPath("templates", HUBSPOT_CONFIG_EXAMPLE)
|
|
1933
2124
|
]);
|
|
1934
2125
|
if (!hubspotExample) return;
|
|
1935
|
-
const destHubspot =
|
|
1936
|
-
if (!
|
|
2126
|
+
const destHubspot = resolve17(targetDir, HUBSPOT_CONFIG_EXAMPLE);
|
|
2127
|
+
if (!existsSync12(destHubspot) && !existsSync12(resolve17(targetDir, HUBSPOT_CONFIG_FILE))) {
|
|
1937
2128
|
copyFileSync(hubspotExample, destHubspot);
|
|
1938
2129
|
}
|
|
1939
2130
|
}
|
|
@@ -1946,6 +2137,10 @@ function formatSetupHint(results) {
|
|
|
1946
2137
|
if (envCopied) {
|
|
1947
2138
|
return "Edit .env.* files \u2014 set VITE_PUBLIC_REST_API_ENDPOINT and portal IDs for your environment";
|
|
1948
2139
|
}
|
|
2140
|
+
const cdnMirror = results.find((r) => r.path === "portal.cdn.json" && r.action === "copied");
|
|
2141
|
+
if (cdnMirror) {
|
|
2142
|
+
return "Hybrid CDN: set public GitHub mirror in portal.cdn.json; token in .env.local (wp init prompts)";
|
|
2143
|
+
}
|
|
1949
2144
|
return void 0;
|
|
1950
2145
|
}
|
|
1951
2146
|
|
|
@@ -2027,7 +2222,7 @@ function buildConfigForProject(template, projectName, router) {
|
|
|
2027
2222
|
return config;
|
|
2028
2223
|
}
|
|
2029
2224
|
function assertTargetDirIsScaffoldable(targetDir) {
|
|
2030
|
-
if (!
|
|
2225
|
+
if (!existsSync13(targetDir)) return;
|
|
2031
2226
|
const entries = readdirSync2(targetDir).filter((entry) => entry !== ".DS_Store");
|
|
2032
2227
|
if (entries.length > 0) {
|
|
2033
2228
|
throw new WpHsError(`Target directory is not empty: ${targetDir}`, {
|
|
@@ -2038,7 +2233,7 @@ function assertTargetDirIsScaffoldable(targetDir) {
|
|
|
2038
2233
|
}
|
|
2039
2234
|
function resolveInitTargetDir(options, interactiveName) {
|
|
2040
2235
|
if (options.directory) {
|
|
2041
|
-
return
|
|
2236
|
+
return resolve18(options.directory);
|
|
2042
2237
|
}
|
|
2043
2238
|
const parentDir = process.cwd();
|
|
2044
2239
|
const slug = options.name ?? interactiveName;
|
|
@@ -2052,7 +2247,7 @@ function resolveInitTargetDir(options, interactiveName) {
|
|
|
2052
2247
|
if (error) {
|
|
2053
2248
|
throw new WpHsError(error, { exitCode: ExitCode.USER_ERROR });
|
|
2054
2249
|
}
|
|
2055
|
-
return
|
|
2250
|
+
return resolve18(parentDir, slug);
|
|
2056
2251
|
}
|
|
2057
2252
|
async function runInit(ctx, options) {
|
|
2058
2253
|
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json;
|
|
@@ -2061,7 +2256,7 @@ async function runInit(ctx, options) {
|
|
|
2061
2256
|
let router = options.router;
|
|
2062
2257
|
if (!nonInteractive) {
|
|
2063
2258
|
if (!template) {
|
|
2064
|
-
const type = await
|
|
2259
|
+
const type = await p3.select({
|
|
2065
2260
|
message: "Project type",
|
|
2066
2261
|
options: [
|
|
2067
2262
|
{
|
|
@@ -2073,22 +2268,22 @@ async function runInit(ctx, options) {
|
|
|
2073
2268
|
{ value: "theme-starter", label: "Theme starter", hint: "CMS theme templates" }
|
|
2074
2269
|
]
|
|
2075
2270
|
});
|
|
2076
|
-
if (
|
|
2271
|
+
if (p3.isCancel(type)) throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2077
2272
|
template = type;
|
|
2078
2273
|
}
|
|
2079
2274
|
if (!options.directory && !projectName) {
|
|
2080
|
-
const nameAnswer = await
|
|
2275
|
+
const nameAnswer = await p3.text({
|
|
2081
2276
|
message: "Project name (folder name)",
|
|
2082
2277
|
placeholder: "my-portal-module",
|
|
2083
2278
|
validate: (value) => validateProjectName(normalizeProjectName(value ?? ""))
|
|
2084
2279
|
});
|
|
2085
|
-
if (
|
|
2280
|
+
if (p3.isCancel(nameAnswer)) {
|
|
2086
2281
|
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2087
2282
|
}
|
|
2088
2283
|
projectName = normalizeProjectName(nameAnswer);
|
|
2089
2284
|
}
|
|
2090
2285
|
if (!router && template && isModuleTemplate(template)) {
|
|
2091
|
-
const routerAnswer = await
|
|
2286
|
+
const routerAnswer = await p3.select({
|
|
2092
2287
|
message: "Client-side routing",
|
|
2093
2288
|
initialValue: "hash",
|
|
2094
2289
|
options: [
|
|
@@ -2109,7 +2304,7 @@ async function runInit(ctx, options) {
|
|
|
2109
2304
|
}
|
|
2110
2305
|
]
|
|
2111
2306
|
});
|
|
2112
|
-
if (
|
|
2307
|
+
if (p3.isCancel(routerAnswer)) {
|
|
2113
2308
|
throw new WpHsError("Init cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2114
2309
|
}
|
|
2115
2310
|
router = routerAnswer;
|
|
@@ -2125,7 +2320,7 @@ async function runInit(ctx, options) {
|
|
|
2125
2320
|
const slug = projectName ?? basename2(targetDir);
|
|
2126
2321
|
assertTargetDirIsScaffoldable(targetDir);
|
|
2127
2322
|
const templateDir = kitPath("templates", template);
|
|
2128
|
-
if (!
|
|
2323
|
+
if (!existsSync13(templateDir)) {
|
|
2129
2324
|
throw new WpHsError(`Template not found: ${template}`, { exitCode: ExitCode.USER_ERROR });
|
|
2130
2325
|
}
|
|
2131
2326
|
if (ctx.global.dryRun) {
|
|
@@ -2136,18 +2331,18 @@ async function runInit(ctx, options) {
|
|
|
2136
2331
|
cpSync2(templateDir, targetDir, { recursive: true });
|
|
2137
2332
|
copySharedSetupExamples(targetDir);
|
|
2138
2333
|
const config = buildConfigForProject(template, slug, router);
|
|
2139
|
-
|
|
2334
|
+
writeFileSync5(resolve18(targetDir, WOODSCLI_CONFIG_FILE), `${JSON.stringify(config, null, 2)}
|
|
2140
2335
|
`);
|
|
2141
|
-
const pkgPath =
|
|
2142
|
-
if (
|
|
2143
|
-
const pkg = JSON.parse(
|
|
2336
|
+
const pkgPath = resolve18(targetDir, "package.json");
|
|
2337
|
+
if (existsSync13(pkgPath)) {
|
|
2338
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf8"));
|
|
2144
2339
|
pkg.name = slug;
|
|
2145
2340
|
pkg.scripts = { ...pkg.scripts, ...TEMPLATE_SCRIPTS[template] };
|
|
2146
2341
|
pkg.devDependencies = {
|
|
2147
2342
|
...pkg.devDependencies,
|
|
2148
2343
|
...templateDevDependencies()
|
|
2149
2344
|
};
|
|
2150
|
-
|
|
2345
|
+
writeFileSync5(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
2151
2346
|
`);
|
|
2152
2347
|
}
|
|
2153
2348
|
if (router && isModuleTemplate(template)) {
|
|
@@ -2158,7 +2353,7 @@ async function runInit(ctx, options) {
|
|
|
2158
2353
|
logger: ctx.logger,
|
|
2159
2354
|
dryRun: false
|
|
2160
2355
|
});
|
|
2161
|
-
const gitignorePath =
|
|
2356
|
+
const gitignorePath = resolve18(targetDir, ".gitignore");
|
|
2162
2357
|
const gitignoreExtras = [
|
|
2163
2358
|
"hubspot.config.yml",
|
|
2164
2359
|
".wphs/config/module-config.local.json",
|
|
@@ -2171,16 +2366,40 @@ async function runInit(ctx, options) {
|
|
|
2171
2366
|
".wphs/.kit-bootstrap.tsx",
|
|
2172
2367
|
".wphs/.kit-bootstrap-greenfield.tsx"
|
|
2173
2368
|
];
|
|
2174
|
-
const existing =
|
|
2369
|
+
const existing = existsSync13(gitignorePath) ? readFileSync10(gitignorePath, "utf8") : "";
|
|
2175
2370
|
const toAppend = gitignoreExtras.filter((line) => !existing.includes(line)).join("\n");
|
|
2176
2371
|
if (toAppend) {
|
|
2177
|
-
|
|
2372
|
+
writeFileSync5(gitignorePath, `${existing.trimEnd()}
|
|
2178
2373
|
${toAppend}
|
|
2179
2374
|
`);
|
|
2180
2375
|
}
|
|
2376
|
+
if (template === "module-hybrid-cdn" && !ctx.global.dryRun && needsCdnGitHubSetup(targetDir)) {
|
|
2377
|
+
const githubDefault = githubDefaultForPrompt(targetDir);
|
|
2378
|
+
let cdnSetup = null;
|
|
2379
|
+
if (nonInteractive) {
|
|
2380
|
+
if (options.cdnGithub) {
|
|
2381
|
+
cdnSetup = { github: options.cdnGithub };
|
|
2382
|
+
}
|
|
2383
|
+
} else {
|
|
2384
|
+
p3.log.step("Hybrid CDN \u2014 GitHub mirror (public repo for jsDelivr)");
|
|
2385
|
+
cdnSetup = await promptCdnGitHubSetup({
|
|
2386
|
+
projectRoot: targetDir,
|
|
2387
|
+
githubDefault,
|
|
2388
|
+
github: options.cdnGithub
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
if (cdnSetup) {
|
|
2392
|
+
applyCdnGitHubSetup(targetDir, cdnSetup);
|
|
2393
|
+
if (!nonInteractive) {
|
|
2394
|
+
p3.log.success(formatCdnSetupNote(targetDir));
|
|
2395
|
+
} else {
|
|
2396
|
+
ctx.logger.success(formatCdnSetupNote(targetDir));
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2181
2400
|
if (!nonInteractive) {
|
|
2182
|
-
|
|
2183
|
-
|
|
2401
|
+
p3.log.success(`Scaffolded ${template} in ${targetDir}`);
|
|
2402
|
+
p3.note(`cd ${slug} && yarn install && wp setup && wp doctor && wp dev`, "Getting started");
|
|
2184
2403
|
} else {
|
|
2185
2404
|
ctx.logger.success(`Scaffolded ${template} in ${targetDir}`);
|
|
2186
2405
|
}
|
|
@@ -2188,40 +2407,40 @@ ${toAppend}
|
|
|
2188
2407
|
}
|
|
2189
2408
|
|
|
2190
2409
|
// src/cli/commands/migrate.ts
|
|
2191
|
-
import { cpSync as cpSync3, existsSync as
|
|
2192
|
-
import { join as join3, resolve as
|
|
2410
|
+
import { cpSync as cpSync3, existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync11, readdirSync as readdirSync3, renameSync, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
2411
|
+
import { join as join3, resolve as resolve19 } from "path";
|
|
2193
2412
|
var LEGACY_DIRS = [".dev", ".woodsportal"];
|
|
2194
2413
|
var LEGACY_BASELINE = ".wp-hs-baseline";
|
|
2195
2414
|
async function runMigrate(ctx, _options = {}) {
|
|
2196
2415
|
const root = ctx.projectRoot;
|
|
2197
|
-
const wphsRoot =
|
|
2416
|
+
const wphsRoot = resolve19(root, WPHS_WORKSPACE_DIR);
|
|
2198
2417
|
const actions = [];
|
|
2199
2418
|
for (const legacy of LEGACY_DIRS) {
|
|
2200
|
-
const legacyPath =
|
|
2201
|
-
if (!
|
|
2419
|
+
const legacyPath = resolve19(root, legacy);
|
|
2420
|
+
if (!existsSync14(legacyPath)) continue;
|
|
2202
2421
|
actions.push(`Move ${legacy}/ \u2192 .wphs/`);
|
|
2203
2422
|
if (ctx.global.dryRun) continue;
|
|
2204
2423
|
mkdirSync4(join3(wphsRoot, "config"), { recursive: true });
|
|
2205
2424
|
mkdirSync4(join3(wphsRoot, "assets", "branding"), { recursive: true });
|
|
2206
2425
|
const localConfig = join3(legacyPath, "module-config.local.json");
|
|
2207
2426
|
const exampleConfig = join3(legacyPath, "module-config.example.json");
|
|
2208
|
-
if (
|
|
2427
|
+
if (existsSync14(localConfig)) {
|
|
2209
2428
|
cpSync3(localConfig, join3(wphsRoot, "config", "module-config.local.json"));
|
|
2210
2429
|
}
|
|
2211
|
-
if (
|
|
2430
|
+
if (existsSync14(exampleConfig)) {
|
|
2212
2431
|
cpSync3(exampleConfig, join3(wphsRoot, "config", "module-config.example.json"));
|
|
2213
2432
|
}
|
|
2214
2433
|
const branding = join3(legacyPath, "branding-assets");
|
|
2215
|
-
if (
|
|
2434
|
+
if (existsSync14(branding)) {
|
|
2216
2435
|
for (const file of readdirSync3(branding)) {
|
|
2217
2436
|
cpSync3(join3(branding, file), join3(wphsRoot, "assets", "branding", file));
|
|
2218
2437
|
}
|
|
2219
2438
|
}
|
|
2220
2439
|
rmSync3(legacyPath, { recursive: true, force: true });
|
|
2221
2440
|
}
|
|
2222
|
-
const legacyBaseline =
|
|
2441
|
+
const legacyBaseline = resolve19(root, LEGACY_BASELINE);
|
|
2223
2442
|
const targetBaseline = join3(wphsRoot, "audit", "baseline");
|
|
2224
|
-
if (
|
|
2443
|
+
if (existsSync14(legacyBaseline)) {
|
|
2225
2444
|
actions.push(`Move ${LEGACY_BASELINE}/ \u2192 .wphs/audit/baseline/`);
|
|
2226
2445
|
if (!ctx.global.dryRun) {
|
|
2227
2446
|
mkdirSync4(targetBaseline, { recursive: true });
|
|
@@ -2229,26 +2448,26 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2229
2448
|
rmSync3(legacyBaseline, { recursive: true, force: true });
|
|
2230
2449
|
}
|
|
2231
2450
|
}
|
|
2232
|
-
const legacyConfigPath2 =
|
|
2233
|
-
const woodscliPath =
|
|
2234
|
-
if (
|
|
2451
|
+
const legacyConfigPath2 = resolve19(root, LEGACY_WOODSPORTAL_HS_FILE);
|
|
2452
|
+
const woodscliPath = resolve19(root, WOODSCLI_CONFIG_FILE);
|
|
2453
|
+
if (existsSync14(legacyConfigPath2) && !existsSync14(woodscliPath)) {
|
|
2235
2454
|
actions.push(`Rename ${LEGACY_WOODSPORTAL_HS_FILE} \u2192 ${WOODSCLI_CONFIG_FILE}`);
|
|
2236
2455
|
if (!ctx.global.dryRun) {
|
|
2237
2456
|
renameSync(legacyConfigPath2, woodscliPath);
|
|
2238
2457
|
}
|
|
2239
|
-
} else if (
|
|
2458
|
+
} else if (existsSync14(legacyConfigPath2) && existsSync14(woodscliPath)) {
|
|
2240
2459
|
actions.push(`Remove duplicate ${LEGACY_WOODSPORTAL_HS_FILE}`);
|
|
2241
2460
|
if (!ctx.global.dryRun) {
|
|
2242
2461
|
rmSync3(legacyConfigPath2, { force: true });
|
|
2243
2462
|
}
|
|
2244
2463
|
}
|
|
2245
|
-
if (
|
|
2246
|
-
const raw = JSON.parse(
|
|
2464
|
+
if (existsSync14(woodscliPath)) {
|
|
2465
|
+
const raw = JSON.parse(readFileSync11(woodscliPath, "utf8"));
|
|
2247
2466
|
if (raw?.dev?.moduleConfigDir && raw.dev.moduleConfigDir !== WPHS_WORKSPACE_DIR) {
|
|
2248
2467
|
actions.push(`Update dev.moduleConfigDir \u2192 .wphs in project config`);
|
|
2249
2468
|
if (!ctx.global.dryRun) {
|
|
2250
2469
|
raw.dev.moduleConfigDir = WPHS_WORKSPACE_DIR;
|
|
2251
|
-
|
|
2470
|
+
writeFileSync6(woodscliPath, `${JSON.stringify(raw, null, 2)}
|
|
2252
2471
|
`);
|
|
2253
2472
|
}
|
|
2254
2473
|
}
|
|
@@ -2277,10 +2496,10 @@ async function runMigrate(ctx, _options = {}) {
|
|
|
2277
2496
|
}
|
|
2278
2497
|
|
|
2279
2498
|
// src/cli/commands/examples.ts
|
|
2280
|
-
import { readFileSync as
|
|
2499
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
2281
2500
|
function loadManifest() {
|
|
2282
2501
|
const manifestPath = kitPath("examples", "manifest.json");
|
|
2283
|
-
const raw = JSON.parse(
|
|
2502
|
+
const raw = JSON.parse(readFileSync12(manifestPath, "utf8"));
|
|
2284
2503
|
return raw.scenarios;
|
|
2285
2504
|
}
|
|
2286
2505
|
async function runExamplesList(ctx, options = {}) {
|
|
@@ -2313,15 +2532,15 @@ async function runExamplesSmoke(ctx, options = {}) {
|
|
|
2313
2532
|
}
|
|
2314
2533
|
|
|
2315
2534
|
// src/cli/commands/fields.ts
|
|
2316
|
-
import { cpSync as cpSync4, existsSync as
|
|
2317
|
-
import { resolve as
|
|
2535
|
+
import { cpSync as cpSync4, existsSync as existsSync15, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "fs";
|
|
2536
|
+
import { resolve as resolve20 } from "path";
|
|
2318
2537
|
async function runFieldsSync(ctx, options = {}) {
|
|
2319
2538
|
const direction = options.direction ?? "pull";
|
|
2320
|
-
const fieldsPath =
|
|
2321
|
-
const cacheDir =
|
|
2322
|
-
const cachePath =
|
|
2539
|
+
const fieldsPath = resolve20(ctx.projectRoot, ctx.config.dev.fieldsJson);
|
|
2540
|
+
const cacheDir = resolve20(ctx.projectRoot, ctx.config.dev.moduleConfigDir, "cache", "fields-sync");
|
|
2541
|
+
const cachePath = resolve20(cacheDir, "fields.json");
|
|
2323
2542
|
if (direction === "pull") {
|
|
2324
|
-
if (!
|
|
2543
|
+
if (!existsSync15(fieldsPath)) {
|
|
2325
2544
|
throw new WpHsError(`Missing ${ctx.config.dev.fieldsJson}`, {
|
|
2326
2545
|
exitCode: ExitCode.USER_ERROR,
|
|
2327
2546
|
remediation: "Add fields.json or run wp init."
|
|
@@ -2337,7 +2556,7 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
2337
2556
|
ctx.logger.success(`Cached fields \u2192 ${cachePath}`);
|
|
2338
2557
|
return ExitCode.SUCCESS;
|
|
2339
2558
|
}
|
|
2340
|
-
if (!
|
|
2559
|
+
if (!existsSync15(cachePath)) {
|
|
2341
2560
|
throw new WpHsError("No cached fields \u2014 run wp fields sync pull first.", {
|
|
2342
2561
|
exitCode: ExitCode.USER_ERROR
|
|
2343
2562
|
});
|
|
@@ -2346,14 +2565,14 @@ async function runFieldsSync(ctx, options = {}) {
|
|
|
2346
2565
|
ctx.logger.info(`[dry-run] Would write ${cachePath} \u2192 ${fieldsPath}`);
|
|
2347
2566
|
return ExitCode.SUCCESS;
|
|
2348
2567
|
}
|
|
2349
|
-
|
|
2568
|
+
writeFileSync7(fieldsPath, readFileSync13(cachePath, "utf8"));
|
|
2350
2569
|
ctx.logger.success(`Restored fields from cache \u2192 ${fieldsPath}`);
|
|
2351
2570
|
return ExitCode.SUCCESS;
|
|
2352
2571
|
}
|
|
2353
2572
|
|
|
2354
2573
|
// src/cli/commands/regression.ts
|
|
2355
|
-
import { existsSync as
|
|
2356
|
-
import { resolve as
|
|
2574
|
+
import { existsSync as existsSync16 } from "fs";
|
|
2575
|
+
import { resolve as resolve21 } from "path";
|
|
2357
2576
|
var LEGACY_SHIM_SCRIPTS = [
|
|
2358
2577
|
"build-cdn.mjs",
|
|
2359
2578
|
"run-vite-build.mjs",
|
|
@@ -2376,21 +2595,21 @@ async function runRegressionSmoke(ctx, options = {}) {
|
|
|
2376
2595
|
if (!validateOk) failures.push("wp config validate failed");
|
|
2377
2596
|
if (options.checkLegacyShims !== false) {
|
|
2378
2597
|
for (const script of LEGACY_SHIM_SCRIPTS) {
|
|
2379
|
-
const shim =
|
|
2598
|
+
const shim = resolve21(ctx.projectRoot, "scripts", script);
|
|
2380
2599
|
const kitScript = kitPath("scripts", script);
|
|
2381
|
-
const shimOk =
|
|
2600
|
+
const shimOk = existsSync16(shim) && existsSync16(kitScript);
|
|
2382
2601
|
checks.push({ name: `shim:${script}`, ok: shimOk });
|
|
2383
2602
|
if (!shimOk) failures.push(`Missing legacy shim or kit script: ${script}`);
|
|
2384
2603
|
}
|
|
2385
2604
|
}
|
|
2386
2605
|
for (const rel of FORBIDDEN_CONSUMER_PATHS) {
|
|
2387
|
-
const forbidden =
|
|
2388
|
-
const absent = !
|
|
2606
|
+
const forbidden = resolve21(ctx.projectRoot, rel);
|
|
2607
|
+
const absent = !existsSync16(forbidden);
|
|
2389
2608
|
checks.push({ name: `no-${rel}`, ok: absent });
|
|
2390
2609
|
if (!absent) failures.push(`Forbidden dev path still present: ${rel}`);
|
|
2391
2610
|
}
|
|
2392
|
-
const woodscliConfig =
|
|
2393
|
-
const configOk =
|
|
2611
|
+
const woodscliConfig = resolve21(ctx.projectRoot, WOODSCLI_CONFIG_FILE);
|
|
2612
|
+
const configOk = existsSync16(woodscliConfig);
|
|
2394
2613
|
checks.push({ name: WOODSCLI_CONFIG_FILE, ok: configOk });
|
|
2395
2614
|
if (!configOk) failures.push(`${WOODSCLI_CONFIG_FILE} missing`);
|
|
2396
2615
|
if (ctx.global.json) {
|
|
@@ -2448,6 +2667,17 @@ async function runSetup(ctx, options = {}) {
|
|
|
2448
2667
|
);
|
|
2449
2668
|
const copied = results.filter((r) => r.action === "copied");
|
|
2450
2669
|
const manual = results.filter((r) => r.action === "manual");
|
|
2670
|
+
const nonInteractive = ctx.global.ci || ctx.global.yes || ctx.global.json || ctx.global.dryRun;
|
|
2671
|
+
if (usesCdnPipeline(ctx.config) && needsCdnGitHubSetup(ctx.projectRoot) && !nonInteractive) {
|
|
2672
|
+
const cdnSetup = await promptCdnGitHubSetup({
|
|
2673
|
+
projectRoot: ctx.projectRoot,
|
|
2674
|
+
githubDefault: githubDefaultForPrompt(ctx.projectRoot)
|
|
2675
|
+
});
|
|
2676
|
+
if (cdnSetup) {
|
|
2677
|
+
applyCdnGitHubSetup(ctx.projectRoot, cdnSetup);
|
|
2678
|
+
ctx.logger.success(formatCdnSetupNote(ctx.projectRoot));
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2451
2681
|
if (ctx.global.json) {
|
|
2452
2682
|
ctx.logger.json({
|
|
2453
2683
|
command: "setup",
|
|
@@ -2483,11 +2713,11 @@ async function runSetup(ctx, options = {}) {
|
|
|
2483
2713
|
}
|
|
2484
2714
|
|
|
2485
2715
|
// src/cli/commands/portal.ts
|
|
2486
|
-
import * as
|
|
2716
|
+
import * as p4 from "@clack/prompts";
|
|
2487
2717
|
|
|
2488
2718
|
// src/lib/hubspot-portals.ts
|
|
2489
|
-
import { existsSync as
|
|
2490
|
-
import { resolve as
|
|
2719
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
2720
|
+
import { resolve as resolve22 } from "path";
|
|
2491
2721
|
function stripYamlScalar(value) {
|
|
2492
2722
|
const trimmed = value.trim();
|
|
2493
2723
|
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
@@ -2525,20 +2755,20 @@ function parseHubSpotConfigPortals(raw) {
|
|
|
2525
2755
|
return { defaultPortal, portals };
|
|
2526
2756
|
}
|
|
2527
2757
|
function readHubSpotPortals(projectRoot) {
|
|
2528
|
-
const configPath =
|
|
2529
|
-
if (!
|
|
2758
|
+
const configPath = resolve22(projectRoot, HUBSPOT_CONFIG_FILE);
|
|
2759
|
+
if (!existsSync17(configPath)) {
|
|
2530
2760
|
throw new WpHsError(`${HUBSPOT_CONFIG_FILE} not found in project root.`, {
|
|
2531
2761
|
exitCode: ExitCode.USER_ERROR,
|
|
2532
2762
|
remediation: "Run wp setup, then add your portal + PAT."
|
|
2533
2763
|
});
|
|
2534
2764
|
}
|
|
2535
|
-
const raw =
|
|
2765
|
+
const raw = readFileSync14(configPath, "utf8");
|
|
2536
2766
|
const { defaultPortal, portals } = parseHubSpotConfigPortals(raw);
|
|
2537
2767
|
return { configPath, defaultPortal, portals };
|
|
2538
2768
|
}
|
|
2539
2769
|
function resolvePortalName(portals, nameOrId) {
|
|
2540
2770
|
const needle = nameOrId.trim();
|
|
2541
|
-
return portals.find((
|
|
2771
|
+
return portals.find((p5) => p5.name === needle) ?? portals.find((p5) => p5.portalId === needle) ?? null;
|
|
2542
2772
|
}
|
|
2543
2773
|
|
|
2544
2774
|
// src/cli/commands/portal.ts
|
|
@@ -2593,7 +2823,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
2593
2823
|
remediation: "wp portal use <name> \xB7 wp portal list"
|
|
2594
2824
|
});
|
|
2595
2825
|
}
|
|
2596
|
-
const answer = await
|
|
2826
|
+
const answer = await p4.select({
|
|
2597
2827
|
message: "Set default HubSpot portal",
|
|
2598
2828
|
options: data.portals.map((portal2) => ({
|
|
2599
2829
|
value: portal2.name,
|
|
@@ -2601,7 +2831,7 @@ async function runPortalUse(ctx, options = {}) {
|
|
|
2601
2831
|
hint: portal2.name === data.defaultPortal ? `current default \xB7 ${portal2.portalId ?? "no id"}` : portal2.portalId ?? void 0
|
|
2602
2832
|
}))
|
|
2603
2833
|
});
|
|
2604
|
-
if (
|
|
2834
|
+
if (p4.isCancel(answer)) {
|
|
2605
2835
|
throw new WpHsError("Portal selection cancelled", { exitCode: ExitCode.USER_ERROR });
|
|
2606
2836
|
}
|
|
2607
2837
|
targetName = answer;
|
|
@@ -2662,7 +2892,7 @@ async function runWatch(ctx, options) {
|
|
|
2662
2892
|
// src/cli/program.ts
|
|
2663
2893
|
function readVersion() {
|
|
2664
2894
|
try {
|
|
2665
|
-
const pkg = JSON.parse(
|
|
2895
|
+
const pkg = JSON.parse(readFileSync15(resolve23(getKitRoot(), "package.json"), "utf8"));
|
|
2666
2896
|
return pkg.version ?? "0.0.0";
|
|
2667
2897
|
} catch {
|
|
2668
2898
|
return "0.0.0";
|
|
@@ -2721,7 +2951,10 @@ function createProgram() {
|
|
|
2721
2951
|
const global = program2.opts();
|
|
2722
2952
|
await handle(() => runConfigValidate(buildContext(global)));
|
|
2723
2953
|
});
|
|
2724
|
-
program2.command("init").description("Scaffold a new module or theme project").option("-t, --template <type>", "module-hybrid-cdn | module-monolith | theme-starter").option("-n, --name <slug>", "Project name (creates ./<name>/ under cwd)").option("-d, --directory <path>", "Full target directory (skips name \u2192 folder prompt)").option("--router <mode>", "hash (client-frontend default) | browser | none \u2014 modules only").
|
|
2954
|
+
program2.command("init").description("Scaffold a new module or theme project").option("-t, --template <type>", "module-hybrid-cdn | module-monolith | theme-starter").option("-n, --name <slug>", "Project name (creates ./<name>/ under cwd)").option("-d, --directory <path>", "Full target directory (skips name \u2192 folder prompt)").option("--router <mode>", "hash (client-frontend default) | browser | none \u2014 modules only").option(
|
|
2955
|
+
"--cdn-github <org/repo>",
|
|
2956
|
+
"Public GitHub CDN mirror repo (hybrid CDN; use with --ci / -y)"
|
|
2957
|
+
).action(
|
|
2725
2958
|
async (opts) => {
|
|
2726
2959
|
const global = program2.opts();
|
|
2727
2960
|
const logger = createLogger({
|
|
@@ -2742,7 +2975,8 @@ function createProgram() {
|
|
|
2742
2975
|
template: opts.template,
|
|
2743
2976
|
name: opts.name,
|
|
2744
2977
|
directory: opts.directory,
|
|
2745
|
-
router: routerMode
|
|
2978
|
+
router: routerMode,
|
|
2979
|
+
cdnGithub: opts.cdnGithub
|
|
2746
2980
|
})
|
|
2747
2981
|
);
|
|
2748
2982
|
}
|