appfunnel 0.7.0 → 0.8.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/dist/index.js +65 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/appfunnel.config.ts +2 -0
- package/templates/default/src/components/ConsentDrawer.tsx +70 -0
- package/templates/default/src/components/Header.tsx +37 -0
- package/templates/default/src/components/paywall/PaymentCheckoutDialog.tsx +76 -0
- package/templates/default/src/pages/birthday.tsx +66 -0
- package/templates/default/src/pages/download.tsx +67 -0
- package/templates/default/src/pages/email.tsx +95 -0
- package/templates/default/src/pages/intro.tsx +109 -0
- package/templates/default/src/pages/multi-select.tsx +79 -0
- package/templates/default/src/pages/name.tsx +48 -0
- package/templates/default/src/pages/paywall.tsx +191 -0
- package/templates/default/src/pages/single-select.tsx +61 -0
- package/templates/default/src/pages/upsell.tsx +158 -0
- package/templates/default/template.json +1 -1
- package/templates/default/src/pages/index.tsx +0 -37
- package/templates/default/src/pages/loading.tsx +0 -76
- package/templates/default/src/pages/result.tsx +0 -38
package/dist/index.js
CHANGED
|
@@ -21,9 +21,7 @@ function formatError(err) {
|
|
|
21
21
|
return lines.join("\n");
|
|
22
22
|
}
|
|
23
23
|
function formatWarning(code, message, hint) {
|
|
24
|
-
const lines = [
|
|
25
|
-
`${pc.yellow("WARNING")} ${pc.dim(`[${code}]`)}: ${message}`
|
|
26
|
-
];
|
|
24
|
+
const lines = [`${pc.yellow("WARNING")} ${pc.dim(`[${code}]`)}: ${message}`];
|
|
27
25
|
if (hint) {
|
|
28
26
|
lines.push(` ${pc.dim("Hint:")} ${hint}`);
|
|
29
27
|
}
|
|
@@ -330,10 +328,10 @@ function formatStoreName(store) {
|
|
|
330
328
|
const test = store.isTestMode ? pc4.yellow(" (test)") : "";
|
|
331
329
|
return `${store.name || store.type}${test}`;
|
|
332
330
|
}
|
|
333
|
-
async function initCommand() {
|
|
331
|
+
async function initCommand(nameArg) {
|
|
334
332
|
const creds = requireAuth();
|
|
335
333
|
const apiOpts = { token: creds.token };
|
|
336
|
-
const name = await input({
|
|
334
|
+
const name = nameArg?.trim() || await input({
|
|
337
335
|
message: "Funnel name",
|
|
338
336
|
validate: (value) => {
|
|
339
337
|
if (!value.trim()) return "Name is required";
|
|
@@ -344,6 +342,12 @@ async function initCommand() {
|
|
|
344
342
|
return true;
|
|
345
343
|
}
|
|
346
344
|
});
|
|
345
|
+
if (nameArg) {
|
|
346
|
+
if (!/^[a-z0-9-]+$/.test(name))
|
|
347
|
+
throw new Error("Name must be lowercase letters, numbers, and hyphens only");
|
|
348
|
+
if (existsSync(join2(process.cwd(), name)))
|
|
349
|
+
throw new Error(`Directory '${name}' already exists`);
|
|
350
|
+
}
|
|
347
351
|
const dir = join2(process.cwd(), name.trim());
|
|
348
352
|
const projectId = await promptForProject(creds.token);
|
|
349
353
|
const projects = await fetchProjects(creds.token);
|
|
@@ -476,7 +480,7 @@ ${itemsStr},
|
|
|
476
480
|
}
|
|
477
481
|
writeFileSync2(configPath, config);
|
|
478
482
|
}
|
|
479
|
-
const sdkVersion = `^${"0.
|
|
483
|
+
const sdkVersion = `^${"0.8.0"}`;
|
|
480
484
|
writeFileSync2(
|
|
481
485
|
join2(dir, "package.json"),
|
|
482
486
|
JSON.stringify(
|
|
@@ -722,7 +726,7 @@ var init_config = __esm({
|
|
|
722
726
|
import { readFileSync as readFileSync4 } from "fs";
|
|
723
727
|
import { join as join4 } from "path";
|
|
724
728
|
function checkVersionCompatibility(cwd) {
|
|
725
|
-
const cliVersion = "0.
|
|
729
|
+
const cliVersion = "0.8.0";
|
|
726
730
|
const sdkVersion = getSdkVersion(cwd);
|
|
727
731
|
const [cliMajor, cliMinor] = cliVersion.split(".").map(Number);
|
|
728
732
|
const [sdkMajor, sdkMinor] = sdkVersion.split(".").map(Number);
|
|
@@ -879,7 +883,7 @@ var init_pages = __esm({
|
|
|
879
883
|
import { join as join6 } from "path";
|
|
880
884
|
import { existsSync as existsSync4 } from "fs";
|
|
881
885
|
function generateEntrySource(options) {
|
|
882
|
-
const { config, pages, pagesDir, funnelTsxPath, isDev } = options;
|
|
886
|
+
const { config, pages, pagesDir, funnelTsxPath, isDev, translations } = options;
|
|
883
887
|
const pageKeys = Object.keys(pages);
|
|
884
888
|
const mergedPages = {};
|
|
885
889
|
const mergedRoutes = {};
|
|
@@ -937,6 +941,8 @@ ${pageImports}
|
|
|
937
941
|
|
|
938
942
|
${priceDataCode}
|
|
939
943
|
|
|
944
|
+
const translations = ${translations ? JSON.stringify(translations) : "undefined"}
|
|
945
|
+
|
|
940
946
|
const config = ${JSON.stringify(fullConfig, null, 2)}
|
|
941
947
|
|
|
942
948
|
const keyToSlug = ${JSON.stringify(
|
|
@@ -945,7 +951,7 @@ const keyToSlug = ${JSON.stringify(
|
|
|
945
951
|
const slugToKey = ${JSON.stringify(slugMap)}
|
|
946
952
|
|
|
947
953
|
const DEV_CAMPAIGN_SLUG = 'campaign'
|
|
948
|
-
const DEFAULT_INITIAL = '${config.initialPageKey
|
|
954
|
+
const DEFAULT_INITIAL = '${config.initialPageKey}'
|
|
949
955
|
|
|
950
956
|
/**
|
|
951
957
|
* Parse the URL to extract basePath, campaignSlug, and initial page.
|
|
@@ -1093,6 +1099,7 @@ function App() {
|
|
|
1093
1099
|
campaignSlug={campaignSlug}
|
|
1094
1100
|
priceData={priceData}
|
|
1095
1101
|
sessionData={sessionData}
|
|
1102
|
+
translations={translations}
|
|
1096
1103
|
>
|
|
1097
1104
|
<FunnelWrapper>
|
|
1098
1105
|
<PageRenderer />
|
|
@@ -1142,7 +1149,24 @@ var init_html = __esm({
|
|
|
1142
1149
|
|
|
1143
1150
|
// src/vite/plugin.ts
|
|
1144
1151
|
import { resolve as resolve2, join as join7 } from "path";
|
|
1145
|
-
import { existsSync as existsSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync6 } from "fs";
|
|
1152
|
+
import { existsSync as existsSync5, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync6, readdirSync as readdirSync3 } from "fs";
|
|
1153
|
+
function loadTranslations(cwd) {
|
|
1154
|
+
const localesDir = join7(cwd, "locales");
|
|
1155
|
+
if (!existsSync5(localesDir)) return void 0;
|
|
1156
|
+
const translations = {};
|
|
1157
|
+
let hasAny = false;
|
|
1158
|
+
for (const file of readdirSync3(localesDir)) {
|
|
1159
|
+
if (!file.endsWith(".json")) continue;
|
|
1160
|
+
const locale = file.replace(/\.json$/, "");
|
|
1161
|
+
try {
|
|
1162
|
+
const content = readFileSync6(join7(localesDir, file), "utf-8");
|
|
1163
|
+
translations[locale] = JSON.parse(content);
|
|
1164
|
+
hasAny = true;
|
|
1165
|
+
} catch {
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return hasAny ? translations : void 0;
|
|
1169
|
+
}
|
|
1146
1170
|
function appfunnelPlugin(options) {
|
|
1147
1171
|
const { cwd, config, isDev } = options;
|
|
1148
1172
|
let pages = options.pages;
|
|
@@ -1150,6 +1174,7 @@ function appfunnelPlugin(options) {
|
|
|
1150
1174
|
const funnelTsxPath = resolve2(cwd, "src", "funnel.tsx");
|
|
1151
1175
|
const appfunnelDir = join7(cwd, APPFUNNEL_DIR);
|
|
1152
1176
|
const htmlPath = join7(appfunnelDir, "index.html");
|
|
1177
|
+
const localesDir = join7(cwd, "locales");
|
|
1153
1178
|
function getEntrySource() {
|
|
1154
1179
|
return generateEntrySource({
|
|
1155
1180
|
config,
|
|
@@ -1157,7 +1182,8 @@ function appfunnelPlugin(options) {
|
|
|
1157
1182
|
pagesDir,
|
|
1158
1183
|
funnelTsxPath,
|
|
1159
1184
|
isDev,
|
|
1160
|
-
priceData: options.priceData
|
|
1185
|
+
priceData: options.priceData,
|
|
1186
|
+
translations: loadTranslations(cwd)
|
|
1161
1187
|
});
|
|
1162
1188
|
}
|
|
1163
1189
|
return {
|
|
@@ -1171,7 +1197,8 @@ function appfunnelPlugin(options) {
|
|
|
1171
1197
|
resolve: {
|
|
1172
1198
|
alias: {
|
|
1173
1199
|
"@": resolve2(cwd, "src")
|
|
1174
|
-
}
|
|
1200
|
+
},
|
|
1201
|
+
dedupe: ["react", "react-dom"]
|
|
1175
1202
|
},
|
|
1176
1203
|
esbuild: {
|
|
1177
1204
|
jsx: "automatic"
|
|
@@ -1233,6 +1260,18 @@ function appfunnelPlugin(options) {
|
|
|
1233
1260
|
}
|
|
1234
1261
|
});
|
|
1235
1262
|
}
|
|
1263
|
+
if (existsSync5(localesDir)) {
|
|
1264
|
+
watcher.add(localesDir);
|
|
1265
|
+
watcher.on("change", (file) => {
|
|
1266
|
+
if (file.startsWith(localesDir) && file.endsWith(".json")) {
|
|
1267
|
+
const mod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ENTRY_ID);
|
|
1268
|
+
if (mod) {
|
|
1269
|
+
devServer.moduleGraph.invalidateModule(mod);
|
|
1270
|
+
}
|
|
1271
|
+
devServer.ws.send({ type: "full-reload" });
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1236
1275
|
return () => {
|
|
1237
1276
|
devServer.middlewares.use(async (req, res, next) => {
|
|
1238
1277
|
const url = req.url?.split("?")[0] || "";
|
|
@@ -1411,7 +1450,7 @@ __export(build_exports, {
|
|
|
1411
1450
|
});
|
|
1412
1451
|
import { resolve as resolve3, join as join9 } from "path";
|
|
1413
1452
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
1414
|
-
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, statSync, readdirSync as
|
|
1453
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, statSync, readdirSync as readdirSync4 } from "fs";
|
|
1415
1454
|
import pc7 from "picocolors";
|
|
1416
1455
|
async function buildCommand() {
|
|
1417
1456
|
const cwd = process.cwd();
|
|
@@ -1427,6 +1466,14 @@ async function buildCommand() {
|
|
|
1427
1466
|
"Run 'appfunnel dev' first to select a project, or add projectId manually."
|
|
1428
1467
|
);
|
|
1429
1468
|
}
|
|
1469
|
+
if (!config.initialPageKey) {
|
|
1470
|
+
s.stop();
|
|
1471
|
+
throw new CLIError(
|
|
1472
|
+
"MISSING_INITIAL_PAGE_KEY",
|
|
1473
|
+
"Missing initialPageKey in appfunnel.config.ts.",
|
|
1474
|
+
"Set initialPageKey to the page key (filename without .tsx) of your first page."
|
|
1475
|
+
);
|
|
1476
|
+
}
|
|
1430
1477
|
const pageKeys = scanPages(cwd);
|
|
1431
1478
|
const pages = await extractPageDefinitions(cwd, pageKeys);
|
|
1432
1479
|
s.stop();
|
|
@@ -1622,7 +1669,7 @@ function validateConditionVariables(condition, pageKey, allVariables) {
|
|
|
1622
1669
|
function collectAssets(outDir) {
|
|
1623
1670
|
const assets = [];
|
|
1624
1671
|
function walk(dir, prefix = "") {
|
|
1625
|
-
for (const entry of
|
|
1672
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
1626
1673
|
const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1627
1674
|
const fullPath = join9(dir, entry.name);
|
|
1628
1675
|
if (entry.isDirectory()) {
|
|
@@ -1642,7 +1689,7 @@ function formatSize(bytes) {
|
|
|
1642
1689
|
}
|
|
1643
1690
|
function getSdkVersion2(cwd) {
|
|
1644
1691
|
try {
|
|
1645
|
-
const pkg = JSON.parse(readFileSync8(join9(cwd, "node_modules", "@appfunnel", "sdk", "package.json"), "utf-8"));
|
|
1692
|
+
const pkg = JSON.parse(readFileSync8(join9(cwd, "node_modules", "@appfunnel-dev", "sdk", "package.json"), "utf-8"));
|
|
1646
1693
|
return pkg.version;
|
|
1647
1694
|
} catch {
|
|
1648
1695
|
return "0.0.0";
|
|
@@ -1818,10 +1865,10 @@ init_errors();
|
|
|
1818
1865
|
import { Command } from "commander";
|
|
1819
1866
|
import pc9 from "picocolors";
|
|
1820
1867
|
var program = new Command();
|
|
1821
|
-
program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.
|
|
1822
|
-
program.command("init").description("Create a new AppFunnel project").action(async () => {
|
|
1868
|
+
program.name("appfunnel").description("Build and publish headless AppFunnel projects").version("0.8.0");
|
|
1869
|
+
program.command("init").argument("[name]", "Project directory name").description("Create a new AppFunnel project").action(async (name) => {
|
|
1823
1870
|
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
1824
|
-
await initCommand2();
|
|
1871
|
+
await initCommand2(name);
|
|
1825
1872
|
});
|
|
1826
1873
|
program.command("login").description("Authenticate with AppFunnel").action(async () => {
|
|
1827
1874
|
const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|