@c15t/cli 1.8.3 → 2.0.0-rc.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/760.mjs +1720 -0
- package/dist/__tests__/auth/config-store.test.d.ts +2 -0
- package/dist/__tests__/auth/config-store.test.d.ts.map +1 -0
- package/dist/__tests__/constants.test.d.ts +2 -0
- package/dist/__tests__/constants.test.d.ts.map +1 -0
- package/dist/__tests__/detection/layout.test.d.ts +2 -0
- package/dist/__tests__/detection/layout.test.d.ts.map +1 -0
- package/dist/__tests__/mocks/prompts.d.ts +25 -0
- package/dist/__tests__/mocks/prompts.d.ts.map +1 -0
- package/dist/__tests__/utils/validation.test.d.ts +2 -0
- package/dist/__tests__/utils/validation.test.d.ts.map +1 -0
- package/dist/auth/config-store.d.ts +63 -0
- package/dist/auth/config-store.d.ts.map +1 -0
- package/dist/auth/device-flow.d.ts +41 -0
- package/dist/auth/device-flow.d.ts.map +1 -0
- package/dist/auth/index.d.ts +7 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/types.d.ts +90 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/commands/auth/index.d.ts +21 -0
- package/dist/commands/auth/index.d.ts.map +1 -0
- package/dist/commands/generate/index.d.ts +23 -2
- package/dist/commands/generate/index.d.ts.map +1 -1
- package/dist/commands/generate/options/c15t-mode.d.ts.map +1 -1
- package/dist/commands/generate/options/custom-mode.d.ts +3 -1
- package/dist/commands/generate/options/custom-mode.d.ts.map +1 -1
- package/dist/commands/generate/options/offline-mode.d.ts.map +1 -1
- package/dist/commands/generate/options/self-hosted-mode.d.ts.map +1 -1
- package/dist/commands/generate/options/shared/backend-options.d.ts +26 -0
- package/dist/commands/generate/options/shared/backend-options.d.ts.map +1 -0
- package/dist/commands/generate/options/shared/dev-tools.d.ts +9 -0
- package/dist/commands/generate/options/shared/dev-tools.d.ts.map +1 -0
- package/dist/commands/generate/options/shared/frontend-ui-options.d.ts +44 -0
- package/dist/commands/generate/options/shared/frontend-ui-options.d.ts.map +1 -0
- package/dist/commands/generate/options/shared/index.d.ts +9 -0
- package/dist/commands/generate/options/shared/index.d.ts.map +1 -0
- package/dist/commands/generate/options/shared/ssr.d.ts +21 -0
- package/dist/commands/generate/options/shared/ssr.d.ts.map +1 -0
- package/dist/commands/generate/options/utils/generate-files.d.ts +10 -2
- package/dist/commands/generate/options/utils/generate-files.d.ts.map +1 -1
- package/dist/commands/generate/preflight.d.ts +31 -0
- package/dist/commands/generate/preflight.d.ts.map +1 -0
- package/dist/commands/generate/prompts/expanded-theme.d.ts +46 -0
- package/dist/commands/generate/prompts/expanded-theme.d.ts.map +1 -0
- package/dist/commands/generate/prompts/index.d.ts +10 -0
- package/dist/commands/generate/prompts/index.d.ts.map +1 -0
- package/dist/commands/generate/prompts/instance.d.ts +33 -0
- package/dist/commands/generate/prompts/instance.d.ts.map +1 -0
- package/dist/commands/generate/prompts/mode-select.d.ts +42 -0
- package/dist/commands/generate/prompts/mode-select.d.ts.map +1 -0
- package/dist/commands/generate/prompts/scripts.d.ts +66 -0
- package/dist/commands/generate/prompts/scripts.d.ts.map +1 -0
- package/dist/commands/generate/prompts/theme.d.ts +46 -0
- package/dist/commands/generate/prompts/theme.d.ts.map +1 -0
- package/dist/commands/generate/prompts/ui-style.d.ts +36 -0
- package/dist/commands/generate/prompts/ui-style.d.ts.map +1 -0
- package/dist/commands/generate/summary.d.ts +37 -0
- package/dist/commands/generate/summary.d.ts.map +1 -0
- package/dist/commands/generate/templates/config.d.ts +7 -6
- package/dist/commands/generate/templates/config.d.ts.map +1 -1
- package/dist/commands/generate/templates/css.d.ts +12 -0
- package/dist/commands/generate/templates/css.d.ts.map +1 -0
- package/dist/commands/generate/templates/index.d.ts +1 -1
- package/dist/commands/generate/templates/index.d.ts.map +1 -1
- package/dist/commands/generate/templates/layout.d.ts +7 -0
- package/dist/commands/generate/templates/layout.d.ts.map +1 -1
- package/dist/commands/generate/templates/next/app/layout.d.ts +9 -2
- package/dist/commands/generate/templates/next/app/layout.d.ts.map +1 -1
- package/dist/commands/generate/templates/next/index.d.ts +7 -0
- package/dist/commands/generate/templates/next/index.d.ts.map +1 -1
- package/dist/commands/generate/templates/next/pages/layout.d.ts +3 -1
- package/dist/commands/generate/templates/next/pages/layout.d.ts.map +1 -1
- package/dist/commands/generate/templates/shared/components.d.ts +69 -0
- package/dist/commands/generate/templates/shared/components.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/directory.d.ts +71 -0
- package/dist/commands/generate/templates/shared/directory.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/expanded-components.d.ts +50 -0
- package/dist/commands/generate/templates/shared/expanded-components.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/framework-config.d.ts +18 -0
- package/dist/commands/generate/templates/shared/framework-config.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/layout-pipeline.d.ts +47 -0
- package/dist/commands/generate/templates/shared/layout-pipeline.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/module-specifier.d.ts +68 -0
- package/dist/commands/generate/templates/shared/module-specifier.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/options.d.ts +37 -20
- package/dist/commands/generate/templates/shared/options.d.ts.map +1 -1
- package/dist/commands/generate/templates/shared/scripts.d.ts +62 -0
- package/dist/commands/generate/templates/shared/scripts.d.ts.map +1 -0
- package/dist/commands/generate/templates/shared/server-components.d.ts +38 -0
- package/dist/commands/generate/templates/shared/server-components.d.ts.map +1 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/instances/index.d.ts +22 -0
- package/dist/commands/instances/index.d.ts.map +1 -0
- package/dist/commands/self-host/migrate/migrator-result.d.ts +1 -1
- package/dist/commands/self-host/migrate/migrator-result.d.ts.map +1 -1
- package/dist/commands/self-host/migrate/orm-result.d.ts +1 -1
- package/dist/commands/self-host/migrate/orm-result.d.ts.map +1 -1
- package/dist/commands/self-host/migrate/read-config.d.ts +1 -1
- package/dist/commands/self-host/migrate/read-config.d.ts.map +1 -1
- package/dist/constants.d.ts +101 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/context/framework-detection.d.ts +1 -0
- package/dist/context/framework-detection.d.ts.map +1 -1
- package/dist/context/types.d.ts +3 -3
- package/dist/context/types.d.ts.map +1 -1
- package/dist/core/context.d.ts +24 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/errors.d.ts +214 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/logger.d.ts +50 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/parser.d.ts +35 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/telemetry.d.ts +78 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/detection/framework.d.ts +31 -0
- package/dist/detection/framework.d.ts.map +1 -0
- package/dist/detection/index.d.ts +7 -0
- package/dist/detection/index.d.ts.map +1 -0
- package/dist/detection/layout.d.ts +36 -0
- package/dist/detection/layout.d.ts.map +1 -0
- package/dist/detection/package-manager.d.ts +31 -0
- package/dist/detection/package-manager.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +2066 -1836
- package/dist/machines/generate/actions.d.ts +116 -0
- package/dist/machines/generate/actions.d.ts.map +1 -0
- package/dist/machines/generate/actors/dependencies.d.ts +43 -0
- package/dist/machines/generate/actors/dependencies.d.ts.map +1 -0
- package/dist/machines/generate/actors/file-generation.d.ts +54 -0
- package/dist/machines/generate/actors/file-generation.d.ts.map +1 -0
- package/dist/machines/generate/actors/preflight.d.ts +46 -0
- package/dist/machines/generate/actors/preflight.d.ts.map +1 -0
- package/dist/machines/generate/actors/prompts.d.ts +122 -0
- package/dist/machines/generate/actors/prompts.d.ts.map +1 -0
- package/dist/machines/generate/guards.d.ts +180 -0
- package/dist/machines/generate/guards.d.ts.map +1 -0
- package/dist/machines/generate/machine.d.ts +250 -0
- package/dist/machines/generate/machine.d.ts.map +1 -0
- package/dist/machines/generate/runner.d.ts +40 -0
- package/dist/machines/generate/runner.d.ts.map +1 -0
- package/dist/machines/generate/types.d.ts +189 -0
- package/dist/machines/generate/types.d.ts.map +1 -0
- package/dist/machines/index.d.ts +20 -0
- package/dist/machines/index.d.ts.map +1 -0
- package/dist/machines/persistence.d.ts +65 -0
- package/dist/machines/persistence.d.ts.map +1 -0
- package/dist/machines/telemetry-plugin.d.ts +54 -0
- package/dist/machines/telemetry-plugin.d.ts.map +1 -0
- package/dist/machines/types.d.ts +104 -0
- package/dist/machines/types.d.ts.map +1 -0
- package/dist/mcp/client.d.ts +61 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +6 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/types.d.ts +84 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/types.d.ts +238 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/formatter.d.ts +71 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/fs.d.ts +80 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/spinner.d.ts +60 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/telemetry.d.ts +4 -0
- package/dist/utils/telemetry.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +68 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/package.json +63 -64
- package/LICENSE.md +0 -595
- package/dist/commands/generate/options/utils/shared-frontend.d.ts +0 -14
- package/dist/commands/generate/options/utils/shared-frontend.d.ts.map +0 -1
- package/dist/commands/generate/templates/backend.d.ts +0 -10
- package/dist/commands/generate/templates/backend.d.ts.map +0 -1
- package/dist/commands/generate/templates/next/app/components.d.ts +0 -44
- package/dist/commands/generate/templates/next/app/components.d.ts.map +0 -1
- package/dist/commands/generate/templates/next/pages/components.d.ts +0 -22
- package/dist/commands/generate/templates/next/pages/components.d.ts.map +0 -1
- package/dist/commands/generate/templates/react/components.d.ts +0 -21
- package/dist/commands/generate/templates/react/components.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,162 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import * as __rspack_external__clack_prompts_3cae1695 from "@clack/prompts";
|
|
3
|
+
import * as __rspack_external_node_fs_promises_153e37e0 from "node:fs/promises";
|
|
4
|
+
import * as __rspack_external_node_path_c5b9b54f from "node:path";
|
|
5
|
+
import * as __rspack_external_picocolors from "picocolors";
|
|
3
6
|
import "dotenv/config";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
7
|
+
import * as __rspack_external_open from "open";
|
|
8
|
+
import * as __rspack_external_xstate from "xstate";
|
|
9
|
+
import * as __rspack_external_node_crypto_9ba42079 from "node:crypto";
|
|
10
|
+
import * as __rspack_external_node_os_74b4b876 from "node:os";
|
|
11
|
+
import * as __rspack_external_posthog_node_1b07bdf4 from "posthog-node";
|
|
12
|
+
import * as __rspack_external_node_child_process_27f17141 from "node:child_process";
|
|
13
|
+
import * as __rspack_external_node_events_0a6aefe7 from "node:events";
|
|
14
|
+
import * as __rspack_external__c15t_logger_04a510d4 from "@c15t/logger";
|
|
15
|
+
import * as __rspack_external__c15t_backend_db_migrator_ebe6d5c7 from "@c15t/backend/db/migrator";
|
|
16
|
+
import * as __rspack_external__c15t_backend_db_schema_e7c5e6a0 from "@c15t/backend/db/schema";
|
|
17
|
+
import * as __rspack_external_c12 from "c12";
|
|
18
|
+
import * as __rspack_external_figlet from "figlet";
|
|
19
|
+
import * as __rspack_external_fs_extra_ce68a66b from "fs-extra";
|
|
20
|
+
import * as __rspack_external_package_manager_detector_detect_94d6a9ae from "package-manager-detector/detect";
|
|
21
|
+
var __webpack_modules__ = {
|
|
22
|
+
"./src/constants.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
23
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
24
|
+
$V: ()=>STORAGE_MODES,
|
|
25
|
+
tl: ()=>URLS
|
|
26
|
+
});
|
|
27
|
+
const URLS = {
|
|
28
|
+
CONSENT_IO: 'https://consent.io',
|
|
29
|
+
DOCS: 'https://c15t.dev/docs',
|
|
30
|
+
GITHUB: 'https://github.com/c15t/c15t',
|
|
31
|
+
DISCORD: 'https://c15t.dev/discord',
|
|
32
|
+
API_DOCS: 'https://c15t.dev/docs/api',
|
|
33
|
+
CLI_DOCS: 'https://c15t.dev/docs/cli'
|
|
34
|
+
};
|
|
35
|
+
const STORAGE_MODES = {
|
|
36
|
+
C15T: 'c15t',
|
|
37
|
+
OFFLINE: 'offline',
|
|
38
|
+
SELF_HOSTED: 'self-hosted',
|
|
39
|
+
CUSTOM: 'custom'
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
"./src/utils/logger.ts" (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
|
43
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
44
|
+
U0: ()=>validLogLevels,
|
|
45
|
+
$e: ()=>formatLogMessage,
|
|
46
|
+
xw: ()=>createCliLogger
|
|
47
|
+
});
|
|
48
|
+
var prompts_ = __webpack_require__("@clack/prompts");
|
|
49
|
+
var external_picocolors_ = __webpack_require__("picocolors");
|
|
50
|
+
const validLogLevels = [
|
|
51
|
+
'error',
|
|
52
|
+
'warn',
|
|
53
|
+
'info',
|
|
54
|
+
'debug'
|
|
55
|
+
];
|
|
56
|
+
const formatArgs = (args)=>{
|
|
57
|
+
if (0 === args.length) return '';
|
|
58
|
+
return `\n${args.map((arg)=>` - ${JSON.stringify(arg, null, 2)}`).join('\n')}`;
|
|
59
|
+
};
|
|
60
|
+
const formatLogMessage = (logLevel, message, args = [])=>{
|
|
61
|
+
const messageStr = 'string' == typeof message ? message : String(message);
|
|
62
|
+
const formattedArgs = formatArgs(args);
|
|
63
|
+
switch(logLevel){
|
|
64
|
+
case 'error':
|
|
65
|
+
return `${external_picocolors_["default"].bgRed(external_picocolors_["default"].black(' error '))} ${messageStr}${formattedArgs}`;
|
|
66
|
+
case 'warn':
|
|
67
|
+
return `${external_picocolors_["default"].bgYellow(external_picocolors_["default"].black(' warning '))} ${messageStr}${formattedArgs}`;
|
|
68
|
+
case 'info':
|
|
69
|
+
return `${external_picocolors_["default"].bgGreen(external_picocolors_["default"].black(' info '))} ${messageStr}${formattedArgs}`;
|
|
70
|
+
case 'debug':
|
|
71
|
+
return `${external_picocolors_["default"].bgBlack(external_picocolors_["default"].white(' debug '))} ${messageStr}${formattedArgs}`;
|
|
72
|
+
case 'success':
|
|
73
|
+
return `${external_picocolors_["default"].bgGreen(external_picocolors_["default"].white(' success '))} ${messageStr}${formattedArgs}`;
|
|
74
|
+
case 'failed':
|
|
75
|
+
return `${external_picocolors_["default"].bgRed(external_picocolors_["default"].white(' failed '))} ${messageStr}${formattedArgs}`;
|
|
76
|
+
default:
|
|
77
|
+
{
|
|
78
|
+
const levelStr = logLevel;
|
|
79
|
+
return `[${levelStr.toUpperCase()}] ${messageStr}${formattedArgs}`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const logMessage = (logLevel, message, ...args)=>{
|
|
84
|
+
const formattedMessage = formatLogMessage(logLevel, message, args);
|
|
85
|
+
switch(logLevel){
|
|
86
|
+
case 'error':
|
|
87
|
+
prompts_.log.error(formattedMessage);
|
|
88
|
+
break;
|
|
89
|
+
case 'warn':
|
|
90
|
+
prompts_.log.warn(formattedMessage);
|
|
91
|
+
break;
|
|
92
|
+
case 'info':
|
|
93
|
+
case 'debug':
|
|
94
|
+
prompts_.log.info(formattedMessage);
|
|
95
|
+
break;
|
|
96
|
+
case 'success':
|
|
97
|
+
case 'failed':
|
|
98
|
+
prompts_.outro(formattedMessage);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
prompts_.log.message(formattedMessage);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
const createCliLogger = (level)=>{
|
|
105
|
+
const baseLogger = (0, __rspack_external__c15t_logger_04a510d4.createLogger)({
|
|
106
|
+
level,
|
|
107
|
+
appName: 'c15t',
|
|
108
|
+
log: (logLevel, message, ...args)=>{
|
|
109
|
+
logMessage(logLevel, message, ...args);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
const extendedLogger = baseLogger;
|
|
113
|
+
extendedLogger.message = (message)=>{
|
|
114
|
+
prompts_.log.message(message);
|
|
115
|
+
};
|
|
116
|
+
extendedLogger.note = (message, ...args)=>{
|
|
117
|
+
const messageStr = 'string' == typeof message ? message : String(message);
|
|
118
|
+
const title = args.length > 0 && 'string' == typeof args[0] ? args[0] : void 0;
|
|
119
|
+
prompts_.note(messageStr, title, {
|
|
120
|
+
format: (line)=>line
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
extendedLogger.success = (message, ...args)=>{
|
|
124
|
+
logMessage('success', message, ...args);
|
|
125
|
+
};
|
|
126
|
+
extendedLogger.failed = (message, ...args)=>{
|
|
127
|
+
logMessage('failed', message, ...args);
|
|
128
|
+
process.exit(0);
|
|
129
|
+
};
|
|
130
|
+
extendedLogger.outro = (message)=>{
|
|
131
|
+
prompts_.outro(message);
|
|
132
|
+
};
|
|
133
|
+
return extendedLogger;
|
|
134
|
+
};
|
|
135
|
+
},
|
|
136
|
+
"@clack/prompts" (module) {
|
|
137
|
+
module.exports = __rspack_external__clack_prompts_3cae1695;
|
|
138
|
+
},
|
|
139
|
+
"node:fs/promises" (module) {
|
|
140
|
+
module.exports = __rspack_external_node_fs_promises_153e37e0;
|
|
141
|
+
},
|
|
142
|
+
"node:path" (module) {
|
|
143
|
+
module.exports = __rspack_external_node_path_c5b9b54f;
|
|
144
|
+
},
|
|
145
|
+
picocolors (module) {
|
|
146
|
+
module.exports = __rspack_external_picocolors;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var __webpack_module_cache__ = {};
|
|
150
|
+
function __webpack_require__(moduleId) {
|
|
151
|
+
var cachedModule = __webpack_module_cache__[moduleId];
|
|
152
|
+
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
153
|
+
var module = __webpack_module_cache__[moduleId] = {
|
|
154
|
+
exports: {}
|
|
155
|
+
};
|
|
156
|
+
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
157
|
+
return module.exports;
|
|
158
|
+
}
|
|
159
|
+
__webpack_require__.m = __webpack_modules__;
|
|
22
160
|
(()=>{
|
|
23
161
|
__webpack_require__.d = (exports, definition)=>{
|
|
24
162
|
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, {
|
|
@@ -27,31 +165,58 @@ var __webpack_require__ = {};
|
|
|
27
165
|
});
|
|
28
166
|
};
|
|
29
167
|
})();
|
|
168
|
+
(()=>{
|
|
169
|
+
__webpack_require__.f = {};
|
|
170
|
+
__webpack_require__.e = (chunkId)=>Promise.all(Object.keys(__webpack_require__.f).reduce((promises, key)=>{
|
|
171
|
+
__webpack_require__.f[key](chunkId, promises);
|
|
172
|
+
return promises;
|
|
173
|
+
}, []));
|
|
174
|
+
})();
|
|
175
|
+
(()=>{
|
|
176
|
+
__webpack_require__.u = (chunkId)=>"" + chunkId + ".mjs";
|
|
177
|
+
})();
|
|
30
178
|
(()=>{
|
|
31
179
|
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
32
180
|
})();
|
|
33
181
|
(()=>{
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
182
|
+
var installedChunks = {
|
|
183
|
+
410: 0
|
|
184
|
+
};
|
|
185
|
+
var installChunk = (data)=>{
|
|
186
|
+
var __rspack_esm_ids = data.__rspack_esm_ids;
|
|
187
|
+
var __webpack_modules__ = data.__webpack_modules__;
|
|
188
|
+
var __rspack_esm_runtime = data.__rspack_esm_runtime;
|
|
189
|
+
var moduleId, chunkId, i = 0;
|
|
190
|
+
for(moduleId in __webpack_modules__)if (__webpack_require__.o(__webpack_modules__, moduleId)) __webpack_require__.m[moduleId] = __webpack_modules__[moduleId];
|
|
191
|
+
if (__rspack_esm_runtime) __rspack_esm_runtime(__webpack_require__);
|
|
192
|
+
for(; i < __rspack_esm_ids.length; i++){
|
|
193
|
+
chunkId = __rspack_esm_ids[i];
|
|
194
|
+
if (__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) installedChunks[chunkId][0]();
|
|
195
|
+
installedChunks[__rspack_esm_ids[i]] = 0;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
__webpack_require__.f.j = function(chunkId, promises) {
|
|
199
|
+
var installedChunkData = __webpack_require__.o(installedChunks, chunkId) ? installedChunks[chunkId] : void 0;
|
|
200
|
+
if (0 !== installedChunkData) if (installedChunkData) promises.push(installedChunkData[1]);
|
|
201
|
+
else {
|
|
202
|
+
var promise = import("./" + __webpack_require__.u(chunkId)).then(installChunk, (e)=>{
|
|
203
|
+
if (0 !== installedChunks[chunkId]) installedChunks[chunkId] = void 0;
|
|
204
|
+
throw e;
|
|
205
|
+
});
|
|
206
|
+
var promise = Promise.race([
|
|
207
|
+
promise,
|
|
208
|
+
new Promise((resolve)=>{
|
|
209
|
+
installedChunkData = installedChunks[chunkId] = [
|
|
210
|
+
resolve
|
|
211
|
+
];
|
|
212
|
+
})
|
|
213
|
+
]);
|
|
214
|
+
promises.push(installedChunkData[1] = promise);
|
|
215
|
+
}
|
|
41
216
|
};
|
|
42
217
|
})();
|
|
43
|
-
var
|
|
44
|
-
__webpack_require__
|
|
45
|
-
__webpack_require__.d(prompts_namespaceObject, {
|
|
46
|
-
confirm: ()=>prompts_confirm,
|
|
47
|
-
isCancel: ()=>isCancel,
|
|
48
|
-
log: ()=>prompts_log,
|
|
49
|
-
note: ()=>note,
|
|
50
|
-
outro: ()=>outro,
|
|
51
|
-
select: ()=>prompts_select,
|
|
52
|
-
spinner: ()=>prompts_spinner,
|
|
53
|
-
text: ()=>prompts_text
|
|
54
|
-
});
|
|
218
|
+
var prompts_ = __webpack_require__("@clack/prompts");
|
|
219
|
+
var external_picocolors_ = __webpack_require__("picocolors");
|
|
55
220
|
function showHelpMenu(context, version, commands, flags) {
|
|
56
221
|
const { logger } = context;
|
|
57
222
|
logger.debug('Displaying help menu using command and flag structures.');
|
|
@@ -69,101 +234,12 @@ ${commandLines}
|
|
|
69
234
|
Options:
|
|
70
235
|
${optionLines}
|
|
71
236
|
|
|
72
|
-
Run a command directly (e.g., ${
|
|
237
|
+
Run a command directly (e.g., ${external_picocolors_["default"].cyan('c15t generate')}) or select one interactively when no command is provided.
|
|
73
238
|
|
|
74
239
|
For more help, visit: https://c15t.dev`;
|
|
75
240
|
logger.debug('Help menu content generated.');
|
|
76
241
|
logger.note(helpContent, 'Usage');
|
|
77
242
|
}
|
|
78
|
-
async function detectFramework(projectRoot, logger) {
|
|
79
|
-
try {
|
|
80
|
-
logger?.debug(`Detecting framework in ${projectRoot}`);
|
|
81
|
-
const packageJsonPath = node_path.join(projectRoot, 'package.json');
|
|
82
|
-
const packageJson = JSON.parse(await promises.readFile(packageJsonPath, 'utf-8'));
|
|
83
|
-
const deps = {
|
|
84
|
-
...packageJson.dependencies,
|
|
85
|
-
...packageJson.devDependencies
|
|
86
|
-
};
|
|
87
|
-
const hasReact = 'react' in deps;
|
|
88
|
-
const reactVersion = hasReact ? deps.react : null;
|
|
89
|
-
logger?.debug(`React detected: ${hasReact}${reactVersion ? ` (version: ${reactVersion})` : ''}`);
|
|
90
|
-
let framework = null;
|
|
91
|
-
let frameworkVersion = null;
|
|
92
|
-
let pkg = hasReact ? '@c15t/react' : 'c15t';
|
|
93
|
-
if ('next' in deps) {
|
|
94
|
-
framework = 'Next.js';
|
|
95
|
-
frameworkVersion = deps.next;
|
|
96
|
-
pkg = '@c15t/nextjs';
|
|
97
|
-
} else if ('@remix-run/react' in deps) {
|
|
98
|
-
framework = 'Remix';
|
|
99
|
-
frameworkVersion = deps['@remix-run/react'];
|
|
100
|
-
} else if ('@vitejs/plugin-react' in deps || '@vitejs/plugin-react-swc' in deps) {
|
|
101
|
-
framework = 'Vite + React';
|
|
102
|
-
frameworkVersion = deps['@vitejs/plugin-react'] || deps['@vitejs/plugin-react-swc'];
|
|
103
|
-
} else if ('gatsby' in deps) {
|
|
104
|
-
framework = 'Gatsby';
|
|
105
|
-
frameworkVersion = deps.gatsby;
|
|
106
|
-
} else if (hasReact) {
|
|
107
|
-
framework = 'React';
|
|
108
|
-
frameworkVersion = reactVersion;
|
|
109
|
-
}
|
|
110
|
-
logger?.debug(`Detected framework: ${framework}${frameworkVersion ? ` (version: ${frameworkVersion})` : ''}, package: ${pkg}`);
|
|
111
|
-
return {
|
|
112
|
-
framework,
|
|
113
|
-
frameworkVersion,
|
|
114
|
-
pkg,
|
|
115
|
-
hasReact,
|
|
116
|
-
reactVersion
|
|
117
|
-
};
|
|
118
|
-
} catch (error) {
|
|
119
|
-
logger?.debug(`Framework detection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
120
|
-
return {
|
|
121
|
-
framework: null,
|
|
122
|
-
frameworkVersion: null,
|
|
123
|
-
pkg: 'c15t',
|
|
124
|
-
hasReact: false,
|
|
125
|
-
reactVersion: null
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
async function detectProjectRoot(cwd, logger) {
|
|
130
|
-
let projectRoot = cwd;
|
|
131
|
-
logger?.debug(`Starting project root detection from: ${cwd}`);
|
|
132
|
-
try {
|
|
133
|
-
let prevDir = '';
|
|
134
|
-
let depth = 0;
|
|
135
|
-
const maxDepth = 10;
|
|
136
|
-
while(projectRoot !== prevDir && depth < maxDepth){
|
|
137
|
-
logger?.debug(`Checking directory (depth ${depth}): ${projectRoot}`);
|
|
138
|
-
try {
|
|
139
|
-
const packageJsonPath = node_path.join(projectRoot, 'package.json');
|
|
140
|
-
logger?.debug(`Looking for package.json at: ${packageJsonPath}`);
|
|
141
|
-
await promises.access(packageJsonPath);
|
|
142
|
-
logger?.debug(`Found package.json at: ${projectRoot}`);
|
|
143
|
-
break;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
logger?.debug(`No package.json found in ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
-
prevDir = projectRoot;
|
|
147
|
-
projectRoot = node_path.dirname(projectRoot);
|
|
148
|
-
depth++;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
if (projectRoot === prevDir) {
|
|
152
|
-
logger?.debug('Reached root directory without finding package.json');
|
|
153
|
-
logger?.failed('Could not find project root (no package.json found)');
|
|
154
|
-
}
|
|
155
|
-
if (depth >= maxDepth) {
|
|
156
|
-
logger?.debug('Reached maximum directory depth without finding package.json');
|
|
157
|
-
logger?.failed('Could not find project root (reached maximum directory depth)');
|
|
158
|
-
}
|
|
159
|
-
logger?.debug(`Project root detection complete. Found at: ${projectRoot}`);
|
|
160
|
-
return projectRoot;
|
|
161
|
-
} catch (error) {
|
|
162
|
-
logger?.debug(`Project root detection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
163
|
-
logger?.debug(`Falling back to current directory: ${cwd}`);
|
|
164
|
-
return cwd;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
243
|
const TELEMETRY_DISABLED_ENV = 'C15T_TELEMETRY_DISABLED';
|
|
168
244
|
const TelemetryEventName = {
|
|
169
245
|
CLI_INVOKED: 'cli.invoked',
|
|
@@ -203,7 +279,11 @@ const TelemetryEventName = {
|
|
|
203
279
|
GENERATE_FAILED: 'generate.failed',
|
|
204
280
|
SELF_HOST_STARTED: 'self-host.started',
|
|
205
281
|
SELF_HOST_COMPLETED: 'self-host.completed',
|
|
206
|
-
SELF_HOST_FAILED: 'self-host.failed'
|
|
282
|
+
SELF_HOST_FAILED: 'self-host.failed',
|
|
283
|
+
CLI_STATE_TRANSITION: 'cli.state.transition',
|
|
284
|
+
CLI_STATE_ERROR: 'cli.state.error',
|
|
285
|
+
CLI_STATE_CANCELLED: 'cli.state.cancelled',
|
|
286
|
+
CLI_STATE_COMPLETE: 'cli.state.complete'
|
|
207
287
|
};
|
|
208
288
|
class Telemetry {
|
|
209
289
|
client = null;
|
|
@@ -338,7 +418,7 @@ class Telemetry {
|
|
|
338
418
|
requestTimeout: 3000
|
|
339
419
|
};
|
|
340
420
|
if (this.debug) this.logDebug('Initializing PostHog client with config:', JSON.stringify(clientConfig));
|
|
341
|
-
this.client = new PostHog(this.apiKey, clientConfig);
|
|
421
|
+
this.client = new __rspack_external_posthog_node_1b07bdf4.PostHog(this.apiKey, clientConfig);
|
|
342
422
|
const initTime = Date.now() - startTime;
|
|
343
423
|
if (this.debug) this.logDebug('PostHog client initialized in', initTime, 'ms');
|
|
344
424
|
} catch (error) {
|
|
@@ -353,7 +433,7 @@ class Telemetry {
|
|
|
353
433
|
if (this.debug) this.logDebug('Telemetry disabled due to initialization error:', JSON.stringify(errorDetails, null, 2));
|
|
354
434
|
try {
|
|
355
435
|
if (this.debug) this.logDebug('Attempting fallback PostHog initialization');
|
|
356
|
-
this.client = new PostHog(this.apiKey);
|
|
436
|
+
this.client = new __rspack_external_posthog_node_1b07bdf4.PostHog(this.apiKey);
|
|
357
437
|
this.disabled = false;
|
|
358
438
|
if (this.debug) this.logDebug('PostHog client initialized using fallback method');
|
|
359
439
|
} catch (fallbackError) {
|
|
@@ -363,7 +443,7 @@ class Telemetry {
|
|
|
363
443
|
}
|
|
364
444
|
}
|
|
365
445
|
generateAnonymousId() {
|
|
366
|
-
const machineId =
|
|
446
|
+
const machineId = __rspack_external_node_crypto_9ba42079["default"].createHash('sha256').update(__rspack_external_node_os_74b4b876["default"].hostname() + __rspack_external_node_os_74b4b876["default"].platform() + __rspack_external_node_os_74b4b876["default"].arch() + __rspack_external_node_os_74b4b876["default"].totalmem()).digest('hex');
|
|
367
447
|
return machineId;
|
|
368
448
|
}
|
|
369
449
|
flushSync() {
|
|
@@ -379,10 +459,175 @@ class Telemetry {
|
|
|
379
459
|
function createTelemetry(options) {
|
|
380
460
|
return new Telemetry(options);
|
|
381
461
|
}
|
|
382
|
-
|
|
462
|
+
var external_node_path_ = __webpack_require__("node:path");
|
|
463
|
+
const DEFAULT_PERSIST_FILENAME = '.c15t-state.json';
|
|
464
|
+
function getPersistPath(projectRoot) {
|
|
465
|
+
return external_node_path_["default"].join(projectRoot, DEFAULT_PERSIST_FILENAME);
|
|
466
|
+
}
|
|
467
|
+
const NON_SERIALIZABLE_FIELDS = new Set([
|
|
468
|
+
'cliContext',
|
|
469
|
+
'spinner',
|
|
470
|
+
'_parent',
|
|
471
|
+
'_actorScope',
|
|
472
|
+
'_processingStatus',
|
|
473
|
+
'_systemId',
|
|
474
|
+
'logic',
|
|
475
|
+
'src',
|
|
476
|
+
'system',
|
|
477
|
+
'self',
|
|
478
|
+
'_snapshot'
|
|
479
|
+
]);
|
|
480
|
+
function makeSerializable(obj, seen = new WeakSet()) {
|
|
481
|
+
if (null == obj) return obj;
|
|
482
|
+
if ('object' != typeof obj) return obj;
|
|
483
|
+
if (seen.has(obj)) return '[Circular]';
|
|
484
|
+
seen.add(obj);
|
|
485
|
+
if (Array.isArray(obj)) return obj.map((item)=>makeSerializable(item, seen));
|
|
486
|
+
if (obj instanceof Date) return obj.toISOString();
|
|
487
|
+
if (obj instanceof Map) return Object.fromEntries(Array.from(obj.entries()).map(([k, v])=>[
|
|
488
|
+
k,
|
|
489
|
+
makeSerializable(v, seen)
|
|
490
|
+
]));
|
|
491
|
+
if (obj instanceof Set) return Array.from(obj).map((v)=>makeSerializable(v, seen));
|
|
492
|
+
const result = {};
|
|
493
|
+
for (const [key, value] of Object.entries(obj))if (!NON_SERIALIZABLE_FIELDS.has(key)) {
|
|
494
|
+
if ('function' != typeof value && 'symbol' != typeof value) result[key] = makeSerializable(value, seen);
|
|
495
|
+
}
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
async function saveSnapshot(snapshot, machineId, persistPath) {
|
|
499
|
+
const fs = await import("node:fs/promises");
|
|
500
|
+
const serializableSnapshot = makeSerializable(snapshot);
|
|
501
|
+
const persisted = {
|
|
502
|
+
machineId,
|
|
503
|
+
version: 1,
|
|
504
|
+
savedAt: Date.now(),
|
|
505
|
+
snapshot: serializableSnapshot
|
|
506
|
+
};
|
|
507
|
+
await fs.writeFile(persistPath, JSON.stringify(persisted, null, 2), 'utf-8');
|
|
508
|
+
}
|
|
509
|
+
async function loadSnapshot(persistPath, machineId) {
|
|
510
|
+
const fs = await import("node:fs/promises");
|
|
511
|
+
try {
|
|
512
|
+
const content = await fs.readFile(persistPath, 'utf-8');
|
|
513
|
+
const persisted = JSON.parse(content);
|
|
514
|
+
if (persisted.machineId !== machineId) return null;
|
|
515
|
+
const maxAge = 86400000;
|
|
516
|
+
if (Date.now() - persisted.savedAt > maxAge) {
|
|
517
|
+
await clearSnapshot(persistPath);
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
return persisted.snapshot;
|
|
521
|
+
} catch {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
async function clearSnapshot(persistPath) {
|
|
526
|
+
const fs = await import("node:fs/promises");
|
|
527
|
+
try {
|
|
528
|
+
await fs.unlink(persistPath);
|
|
529
|
+
} catch {}
|
|
530
|
+
}
|
|
531
|
+
async function hasPersistedState(persistPath) {
|
|
532
|
+
const fs = await import("node:fs/promises");
|
|
533
|
+
try {
|
|
534
|
+
await fs.access(persistPath);
|
|
535
|
+
return true;
|
|
536
|
+
} catch {
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
function createPersistenceSubscriber(machineId, persistPath, options = {}) {
|
|
541
|
+
const { persistStates, skipStates = [
|
|
542
|
+
'exited',
|
|
543
|
+
'complete',
|
|
544
|
+
'error'
|
|
545
|
+
] } = options;
|
|
546
|
+
return (snapshot)=>{
|
|
547
|
+
const stateValue = String(snapshot.value);
|
|
548
|
+
if (skipStates.includes(stateValue)) return void clearSnapshot(persistPath).catch(()=>{});
|
|
549
|
+
if (persistStates && !persistStates.includes(stateValue)) return;
|
|
550
|
+
saveSnapshot(snapshot, machineId, persistPath).catch((error)=>{
|
|
551
|
+
console.error('Failed to persist state:', error);
|
|
552
|
+
});
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
function createTelemetrySubscriber(config) {
|
|
556
|
+
const { telemetry, machineId, skipStates = [] } = config;
|
|
557
|
+
let lastState = null;
|
|
558
|
+
let lastStateTime = Date.now();
|
|
559
|
+
const stateHistory = [];
|
|
560
|
+
return (snapshot)=>{
|
|
561
|
+
const currentState = String(snapshot.value);
|
|
562
|
+
const now = Date.now();
|
|
563
|
+
if (currentState === lastState) return;
|
|
564
|
+
if (skipStates.includes(currentState)) return;
|
|
565
|
+
if (null !== lastState) {
|
|
566
|
+
const duration = now - lastStateTime;
|
|
567
|
+
telemetry.trackEvent(TelemetryEventName.CLI_STATE_TRANSITION, {
|
|
568
|
+
machineId,
|
|
569
|
+
fromState: lastState,
|
|
570
|
+
toState: currentState,
|
|
571
|
+
duration
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
stateHistory.push({
|
|
575
|
+
state: currentState,
|
|
576
|
+
timestamp: now
|
|
577
|
+
});
|
|
578
|
+
if ('error' === currentState || 'preflightError' === currentState) {
|
|
579
|
+
const ctx = snapshot.context;
|
|
580
|
+
const errors = ctx?.errors;
|
|
581
|
+
const lastError = errors?.[errors.length - 1];
|
|
582
|
+
telemetry.trackEvent(TelemetryEventName.CLI_STATE_ERROR, {
|
|
583
|
+
machineId,
|
|
584
|
+
state: currentState,
|
|
585
|
+
error: lastError?.error?.message ?? 'Unknown error',
|
|
586
|
+
stateHistory: stateHistory.map((e)=>e.state).join(',')
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
if ('exited' === currentState || 'cancelled' === currentState) telemetry.trackEvent(TelemetryEventName.CLI_STATE_CANCELLED, {
|
|
590
|
+
machineId,
|
|
591
|
+
lastState: lastState ?? 'unknown',
|
|
592
|
+
stateHistory: stateHistory.map((e)=>e.state).join(',')
|
|
593
|
+
});
|
|
594
|
+
if ('complete' === currentState || 'success' === currentState) {
|
|
595
|
+
const totalDuration = now - (stateHistory[0]?.timestamp ?? now);
|
|
596
|
+
telemetry.trackEvent(TelemetryEventName.CLI_STATE_COMPLETE, {
|
|
597
|
+
machineId,
|
|
598
|
+
totalDuration,
|
|
599
|
+
statesVisited: stateHistory.length,
|
|
600
|
+
stateHistory: stateHistory.map((e)=>e.state).join(',')
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
lastState = currentState;
|
|
604
|
+
lastStateTime = now;
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
function combineSubscribers(...subscribers) {
|
|
608
|
+
return (snapshot)=>{
|
|
609
|
+
for (const subscriber of subscribers)try {
|
|
610
|
+
subscriber(snapshot);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.error('Subscriber error:', error);
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function createDebugSubscriber(machineId, logger) {
|
|
617
|
+
let lastState = null;
|
|
618
|
+
return (snapshot)=>{
|
|
619
|
+
const currentState = String(snapshot.value);
|
|
620
|
+
if (currentState !== lastState) {
|
|
621
|
+
const log = logger?.debug ?? console.debug;
|
|
622
|
+
log(`[${machineId}] State: ${lastState ?? 'initial'} -> ${currentState}`);
|
|
623
|
+
lastState = currentState;
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async function runPackageManagerInstall(projectRoot, dependencies, packageManager) {
|
|
383
628
|
if (0 === dependencies.length) return;
|
|
384
|
-
let command
|
|
385
|
-
let args
|
|
629
|
+
let command;
|
|
630
|
+
let args;
|
|
386
631
|
switch(packageManager){
|
|
387
632
|
case 'npm':
|
|
388
633
|
command = 'npm';
|
|
@@ -413,14 +658,40 @@ async function addAndInstallDependenciesViaPM(projectRoot, dependencies, package
|
|
|
413
658
|
];
|
|
414
659
|
break;
|
|
415
660
|
default:
|
|
416
|
-
throw new Error(`Unsupported package manager
|
|
661
|
+
throw new Error(`Unsupported package manager: ${packageManager}`);
|
|
417
662
|
}
|
|
418
|
-
const child = spawn(command, args, {
|
|
663
|
+
const child = (0, __rspack_external_node_child_process_27f17141.spawn)(command, args, {
|
|
419
664
|
cwd: projectRoot,
|
|
420
665
|
stdio: 'inherit'
|
|
421
666
|
});
|
|
422
|
-
await once(child, 'exit');
|
|
667
|
+
const [exitCode] = await (0, __rspack_external_node_events_0a6aefe7.once)(child, 'exit');
|
|
668
|
+
if (0 !== exitCode) throw new Error(`Package manager exited with code ${exitCode}`);
|
|
423
669
|
}
|
|
670
|
+
const dependencyInstallActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
671
|
+
const { cliContext, dependencies } = input;
|
|
672
|
+
const { projectRoot, packageManager, logger } = cliContext;
|
|
673
|
+
if (0 === dependencies.length) return {
|
|
674
|
+
success: true,
|
|
675
|
+
installedDependencies: []
|
|
676
|
+
};
|
|
677
|
+
logger.debug(`Installing dependencies: ${dependencies.join(', ')}`);
|
|
678
|
+
logger.debug(`Using package manager: ${packageManager.name}`);
|
|
679
|
+
try {
|
|
680
|
+
await runPackageManagerInstall(projectRoot, dependencies, packageManager.name);
|
|
681
|
+
return {
|
|
682
|
+
success: true,
|
|
683
|
+
installedDependencies: dependencies
|
|
684
|
+
};
|
|
685
|
+
} catch (error) {
|
|
686
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
687
|
+
logger.error(`Dependency installation failed: ${errorMessage}`);
|
|
688
|
+
return {
|
|
689
|
+
success: false,
|
|
690
|
+
installedDependencies: [],
|
|
691
|
+
error: errorMessage
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
});
|
|
424
695
|
function getManualInstallCommand(dependencies, packageManager) {
|
|
425
696
|
switch(packageManager){
|
|
426
697
|
case 'npm':
|
|
@@ -435,1449 +706,1585 @@ function getManualInstallCommand(dependencies, packageManager) {
|
|
|
435
706
|
return `npm install ${dependencies.join(' ')}`;
|
|
436
707
|
}
|
|
437
708
|
}
|
|
438
|
-
|
|
439
|
-
const {
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
});
|
|
451
|
-
if (handleCancel?.(addDepsSelection)) return {
|
|
452
|
-
installDepsConfirmed: false,
|
|
453
|
-
ranInstall: false
|
|
454
|
-
};
|
|
455
|
-
if (!addDepsSelection) return {
|
|
456
|
-
installDepsConfirmed: false,
|
|
457
|
-
ranInstall: false
|
|
709
|
+
const checkDependenciesActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
710
|
+
const { projectRoot, dependencies } = input;
|
|
711
|
+
const fs = await import("node:fs/promises");
|
|
712
|
+
const path = await import("node:path");
|
|
713
|
+
const installed = [];
|
|
714
|
+
const missing = [];
|
|
715
|
+
try {
|
|
716
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
717
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));
|
|
718
|
+
const allDeps = {
|
|
719
|
+
...packageJson.dependencies,
|
|
720
|
+
...packageJson.devDependencies
|
|
458
721
|
};
|
|
722
|
+
for (const dep of dependencies){
|
|
723
|
+
const depName = dep.startsWith('@') ? dep : dep.split('@')[0];
|
|
724
|
+
if (depName && depName in allDeps) installed.push(dep);
|
|
725
|
+
else missing.push(dep);
|
|
726
|
+
}
|
|
727
|
+
} catch {
|
|
728
|
+
missing.push(...dependencies);
|
|
459
729
|
}
|
|
460
|
-
|
|
730
|
+
return {
|
|
731
|
+
installed,
|
|
732
|
+
missing
|
|
733
|
+
};
|
|
734
|
+
});
|
|
735
|
+
async function readFileForBackup(filePath) {
|
|
736
|
+
const fs = await import("node:fs/promises");
|
|
461
737
|
try {
|
|
462
|
-
await
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
success: true,
|
|
466
|
-
dependencies: dependenciesToAdd.join(','),
|
|
467
|
-
packageManager: context.packageManager.name
|
|
468
|
-
});
|
|
469
|
-
return {
|
|
470
|
-
installDepsConfirmed: true,
|
|
471
|
-
ranInstall: true
|
|
472
|
-
};
|
|
473
|
-
} catch (installError) {
|
|
474
|
-
s.stop(picocolors.yellow('⚠️ Dependency installation failed.'));
|
|
475
|
-
logger.error('Installation Error:', installError);
|
|
476
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_DEPENDENCIES_INSTALLED, {
|
|
477
|
-
success: false,
|
|
478
|
-
error: installError instanceof Error ? installError.message : String(installError),
|
|
479
|
-
dependencies: dependenciesToAdd.join(','),
|
|
480
|
-
packageManager: context.packageManager.name
|
|
481
|
-
});
|
|
482
|
-
const pmCommand = getManualInstallCommand(dependenciesToAdd, context.packageManager.name);
|
|
483
|
-
logger.info(`Please try running '${pmCommand}' manually in ${picocolors.cyan(node_path.relative(context.cwd, context.projectRoot))}.`);
|
|
484
|
-
return {
|
|
485
|
-
installDepsConfirmed: true,
|
|
486
|
-
ranInstall: false
|
|
487
|
-
};
|
|
738
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
739
|
+
} catch {
|
|
740
|
+
return null;
|
|
488
741
|
}
|
|
489
742
|
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (0 === args.length) return '';
|
|
498
|
-
return `\n${args.map((arg)=>` - ${JSON.stringify(arg, null, 2)}`).join('\n')}`;
|
|
499
|
-
};
|
|
500
|
-
const formatLogMessage = (logLevel, message, args = [])=>{
|
|
501
|
-
const messageStr = 'string' == typeof message ? message : String(message);
|
|
502
|
-
const formattedArgs = formatArgs(args);
|
|
503
|
-
switch(logLevel){
|
|
504
|
-
case 'error':
|
|
505
|
-
return `${picocolors.bgRed(picocolors.black(' error '))} ${messageStr}${formattedArgs}`;
|
|
506
|
-
case 'warn':
|
|
507
|
-
return `${picocolors.bgYellow(picocolors.black(' warning '))} ${messageStr}${formattedArgs}`;
|
|
508
|
-
case 'info':
|
|
509
|
-
return `${picocolors.bgGreen(picocolors.black(' info '))} ${messageStr}${formattedArgs}`;
|
|
510
|
-
case 'debug':
|
|
511
|
-
return `${picocolors.bgBlack(picocolors.white(' debug '))} ${messageStr}${formattedArgs}`;
|
|
512
|
-
case 'success':
|
|
513
|
-
return `${picocolors.bgGreen(picocolors.white(' success '))} ${messageStr}${formattedArgs}`;
|
|
514
|
-
case 'failed':
|
|
515
|
-
return `${picocolors.bgRed(picocolors.white(' failed '))} ${messageStr}${formattedArgs}`;
|
|
516
|
-
default:
|
|
517
|
-
{
|
|
518
|
-
const levelStr = logLevel;
|
|
519
|
-
return `[${levelStr.toUpperCase()}] ${messageStr}${formattedArgs}`;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
const logMessage = (logLevel, message, ...args)=>{
|
|
524
|
-
const formattedMessage = formatLogMessage(logLevel, message, args);
|
|
525
|
-
switch(logLevel){
|
|
526
|
-
case 'error':
|
|
527
|
-
prompts_log.error(formattedMessage);
|
|
528
|
-
break;
|
|
529
|
-
case 'warn':
|
|
530
|
-
prompts_log.warn(formattedMessage);
|
|
531
|
-
break;
|
|
532
|
-
case 'info':
|
|
533
|
-
case 'debug':
|
|
534
|
-
prompts_log.info(formattedMessage);
|
|
535
|
-
break;
|
|
536
|
-
case 'success':
|
|
537
|
-
case 'failed':
|
|
538
|
-
outro(formattedMessage);
|
|
539
|
-
break;
|
|
540
|
-
default:
|
|
541
|
-
prompts_log.message(formattedMessage);
|
|
743
|
+
async function fileExists(filePath) {
|
|
744
|
+
const fs = await import("node:fs/promises");
|
|
745
|
+
try {
|
|
746
|
+
await fs.access(filePath);
|
|
747
|
+
return true;
|
|
748
|
+
} catch {
|
|
749
|
+
return false;
|
|
542
750
|
}
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
751
|
+
}
|
|
752
|
+
async function formatGeneratedFiles(projectRoot, files, logger) {
|
|
753
|
+
const fs = await import("node:fs/promises");
|
|
754
|
+
const { execFile } = await import("node:child_process");
|
|
755
|
+
const { promisify } = await import("node:util");
|
|
756
|
+
const execFileAsync = promisify(execFile);
|
|
757
|
+
const codeFiles = files.filter((f)=>f.endsWith('.ts') || f.endsWith('.tsx') || f.endsWith('.js') || f.endsWith('.jsx'));
|
|
758
|
+
if (0 === codeFiles.length) return;
|
|
759
|
+
const formatters = [
|
|
760
|
+
{
|
|
761
|
+
bin: external_node_path_["default"].join(projectRoot, 'node_modules', '.bin', 'prettier'),
|
|
762
|
+
args: [
|
|
763
|
+
'--write'
|
|
764
|
+
]
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
bin: external_node_path_["default"].join(projectRoot, 'node_modules', '.bin', 'biome'),
|
|
768
|
+
args: [
|
|
769
|
+
'format',
|
|
770
|
+
'--write'
|
|
771
|
+
]
|
|
550
772
|
}
|
|
551
|
-
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
note(messageStr, title, {
|
|
560
|
-
format: (line)=>line
|
|
773
|
+
];
|
|
774
|
+
for (const { bin, args } of formatters)try {
|
|
775
|
+
await fs.access(bin);
|
|
776
|
+
await execFileAsync(bin, [
|
|
777
|
+
...args,
|
|
778
|
+
...codeFiles
|
|
779
|
+
], {
|
|
780
|
+
cwd: projectRoot
|
|
561
781
|
});
|
|
782
|
+
logger.debug(`Formatted ${codeFiles.length} files with ${external_node_path_["default"].basename(bin)}`);
|
|
783
|
+
return;
|
|
784
|
+
} catch {}
|
|
785
|
+
logger.debug('No formatter found, skipping formatting');
|
|
786
|
+
}
|
|
787
|
+
const fileGenerationActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
788
|
+
const { cliContext, mode, backendURL, useEnvFile, proxyNextjs, enableSSR, enableDevTools, uiStyle, expandedTheme, selectedScripts } = input;
|
|
789
|
+
const filesCreated = [];
|
|
790
|
+
const filesModified = [];
|
|
791
|
+
const result = {
|
|
792
|
+
filesCreated: [],
|
|
793
|
+
filesModified: [],
|
|
794
|
+
configPath: null,
|
|
795
|
+
layoutPath: null,
|
|
796
|
+
nextConfigPath: null,
|
|
797
|
+
envPath: null
|
|
562
798
|
};
|
|
563
|
-
|
|
564
|
-
|
|
799
|
+
const { projectRoot, framework, cwd, logger } = cliContext;
|
|
800
|
+
framework.pkg;
|
|
801
|
+
const { generateFiles } = await __webpack_require__.e("760").then(__webpack_require__.bind(__webpack_require__, "./src/commands/generate/options/utils/generate-files.ts"));
|
|
802
|
+
const spinnerMock = {
|
|
803
|
+
start: (msg)=>logger.debug(`[spinner] ${msg}`),
|
|
804
|
+
stop: (msg)=>logger.debug(`[spinner] ${msg}`),
|
|
805
|
+
message: (msg)=>logger.debug(`[spinner] ${msg}`)
|
|
565
806
|
};
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
store.getState().showPopup;
|
|
599
|
-
`;
|
|
600
|
-
break;
|
|
601
|
-
case 'offline':
|
|
602
|
-
configContent = `
|
|
603
|
-
import {
|
|
604
|
-
type ConsentManagerOptions,
|
|
605
|
-
configureConsentManager,
|
|
606
|
-
createConsentManagerStore
|
|
607
|
-
} from "c15t";
|
|
608
|
-
|
|
609
|
-
export const consentManager = configureConsentManager({ mode: "offline" });
|
|
610
|
-
export const store = createConsentManagerStore(consentManager, {
|
|
611
|
-
initialGdprTypes: ["necessary", "marketing"], // Optional: Specify which consent categories to show in the banner.
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
store.getState().setConsent("marketing", true); // set consent to marketing
|
|
615
|
-
store.getState().showPopup; // should show popup?
|
|
616
|
-
|
|
617
|
-
`;
|
|
618
|
-
break;
|
|
619
|
-
case 'custom':
|
|
620
|
-
configContent = `import {
|
|
621
|
-
type ConsentManagerOptions,
|
|
622
|
-
configureConsentManager,
|
|
623
|
-
createConsentManagerStore
|
|
624
|
-
} from "c15t";
|
|
625
|
-
|
|
626
|
-
export const consentManager = configureConsentManager({ mode: "custom", endpointHandlers: createCustomHandlers(), });
|
|
627
|
-
export const store = createConsentManagerStore(consentManager, {
|
|
628
|
-
initialGdprTypes: ["necessary", "marketing"], // Optional: Specify which consent categories to show in the banner.
|
|
629
|
-
ignoreGeoLocation: true // Useful for development to always view the banner.
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
store.getState().setConsent("marketing", true); // set consent to marketing
|
|
633
|
-
store.getState().showPopup; // should show popup?
|
|
634
|
-
`;
|
|
635
|
-
break;
|
|
636
|
-
default:
|
|
637
|
-
logger?.failed(`Invalid mode: ${mode}. Valid modes are: ${validModes.join(', ')}`);
|
|
638
|
-
}
|
|
639
|
-
return configContent;
|
|
640
|
-
}
|
|
641
|
-
function getEnvVarName(pkg) {
|
|
642
|
-
return '@c15t/nextjs' === pkg ? 'NEXT_PUBLIC_C15T_URL' : 'PUBLIC_C15T_URL';
|
|
643
|
-
}
|
|
644
|
-
function generateEnvFileContent(backendURL, pkg) {
|
|
645
|
-
const envVarName = getEnvVarName(pkg);
|
|
646
|
-
return `\n${envVarName}=${backendURL}\n`;
|
|
647
|
-
}
|
|
648
|
-
function generateEnvExampleContent(pkg) {
|
|
649
|
-
const envVarName = getEnvVarName(pkg);
|
|
650
|
-
return `\n# c15t Configuration\n${envVarName}=https://your-instance.c15t.dev\n`;
|
|
651
|
-
}
|
|
652
|
-
function generateOptionsText(mode, backendURL, useEnvFile, proxyNextjs) {
|
|
653
|
-
switch(mode){
|
|
654
|
-
case 'c15t':
|
|
655
|
-
if (proxyNextjs) return `{
|
|
656
|
-
mode: 'c15t',
|
|
657
|
-
backendURL: '/api/c15t',
|
|
658
|
-
consentCategories: ['necessary', 'marketing'], // Optional: Specify which consent categories to show in the banner.
|
|
659
|
-
ignoreGeoLocation: true, // Useful for development to always view the banner.
|
|
660
|
-
}`;
|
|
661
|
-
if (useEnvFile) return `{
|
|
662
|
-
mode: 'c15t',
|
|
663
|
-
backendURL: process.env.NEXT_PUBLIC_C15T_URL!,
|
|
664
|
-
consentCategories: ['necessary', 'marketing'], // Optional: Specify which consent categories to show in the banner.
|
|
665
|
-
ignoreGeoLocation: true, // Useful for development to always view the banner.
|
|
666
|
-
}`;
|
|
667
|
-
return `{
|
|
668
|
-
mode: 'c15t',
|
|
669
|
-
backendURL: '${backendURL || 'https://your-instance.c15t.dev'}',
|
|
670
|
-
consentCategories: ['necessary', 'marketing'], // Optional: Specify which consent categories to show in the banner.
|
|
671
|
-
ignoreGeoLocation: true, // Useful for development to always view the banner.
|
|
672
|
-
}`;
|
|
673
|
-
case 'custom':
|
|
674
|
-
return `{
|
|
675
|
-
mode: 'custom',
|
|
676
|
-
endpointHandlers: createCustomHandlers(),
|
|
677
|
-
}`;
|
|
678
|
-
default:
|
|
679
|
-
return `{
|
|
680
|
-
mode: 'offline',
|
|
681
|
-
consentCategories: ['necessary', 'marketing'], // Optional: Specify which consent categories to show in the banner.
|
|
682
|
-
}`;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
function generateConsentManagerTemplate(optionsText) {
|
|
686
|
-
return `import type { ReactNode } from 'react';
|
|
687
|
-
import {
|
|
688
|
-
ConsentManagerDialog,
|
|
689
|
-
ConsentManagerProvider,
|
|
690
|
-
CookieBanner,
|
|
691
|
-
} from '@c15t/nextjs';
|
|
692
|
-
// For client-only apps (non-SSR), you can use:
|
|
693
|
-
// import { ConsentManagerProvider } from '@c15t/nextjs/client';
|
|
694
|
-
import { ConsentManagerClient } from './consent-manager.client';
|
|
695
|
-
|
|
696
|
-
/**
|
|
697
|
-
* Server-side rendered consent management wrapper for Next.js App Router
|
|
698
|
-
*
|
|
699
|
-
* This component provides SSR-compatible consent management by separating
|
|
700
|
-
* server-side configuration from client-side functionality. The server handles
|
|
701
|
-
* initial setup and configuration, while client-side features (callbacks,
|
|
702
|
-
* scripts) are delegated to the ConsentManagerClient component.
|
|
703
|
-
*
|
|
704
|
-
* @param props - Component properties
|
|
705
|
-
* @param props.children - Child components to render within the consent manager context
|
|
706
|
-
*
|
|
707
|
-
* @returns The consent manager provider with banner, dialog, and client wrapper
|
|
708
|
-
*
|
|
709
|
-
* @remarks
|
|
710
|
-
* This split architecture is necessary because certain options like callbacks
|
|
711
|
-
* and scripts cannot be serialized during server-side rendering. For
|
|
712
|
-
* client-only implementations, use \`<ConsentManagerProvider />\` from
|
|
713
|
-
* \`@c15t/nextjs/client\`.
|
|
714
|
-
*
|
|
715
|
-
* @example
|
|
716
|
-
* \`\`\`tsx
|
|
717
|
-
* // In your root layout.tsx
|
|
718
|
-
* import { ConsentManager } from './consent-manager';
|
|
719
|
-
*
|
|
720
|
-
* export default function RootLayout({ children }) {
|
|
721
|
-
* return (
|
|
722
|
-
* <html>
|
|
723
|
-
* <body>
|
|
724
|
-
* <ConsentManager>
|
|
725
|
-
* {children}
|
|
726
|
-
* </ConsentManager>
|
|
727
|
-
* </body>
|
|
728
|
-
* </html>
|
|
729
|
-
* );
|
|
730
|
-
* }
|
|
731
|
-
* \`\`\`
|
|
732
|
-
*/
|
|
733
|
-
export function ConsentManager({ children }: { children: ReactNode }) {
|
|
734
|
-
return (
|
|
735
|
-
<ConsentManagerProvider
|
|
736
|
-
options={${optionsText}}
|
|
737
|
-
>
|
|
738
|
-
<CookieBanner />
|
|
739
|
-
<ConsentManagerDialog />
|
|
740
|
-
<ConsentManagerClient>{children}</ConsentManagerClient>
|
|
741
|
-
</ConsentManagerProvider>
|
|
742
|
-
);
|
|
743
|
-
}
|
|
744
|
-
`;
|
|
745
|
-
}
|
|
746
|
-
function generateConsentManagerClientTemplate() {
|
|
747
|
-
return `'use client';
|
|
748
|
-
|
|
749
|
-
import type { ReactNode } from 'react';
|
|
750
|
-
import { ClientSideOptionsProvider } from '@c15t/nextjs/client';
|
|
751
|
-
|
|
752
|
-
/**
|
|
753
|
-
* Client-side consent manager wrapper for handling scripts and callbacks
|
|
754
|
-
*
|
|
755
|
-
* This component is rendered on the client and provides the ability to:
|
|
756
|
-
* - Load integration scripts (Google Tag Manager, Meta Pixel, TikTok Pixel, etc.)
|
|
757
|
-
* - Handle client-side callbacks (onConsentSet, onError, onBannerFetched)
|
|
758
|
-
* - Manage script lifecycle (onLoad, onDelete)
|
|
759
|
-
*
|
|
760
|
-
* @param props - Component properties
|
|
761
|
-
* @param props.children - Child components to render within the client-side context
|
|
762
|
-
*
|
|
763
|
-
* @returns The client-side options provider with children
|
|
764
|
-
*
|
|
765
|
-
* @see https://c15t.com/docs/frameworks/next/callbacks
|
|
766
|
-
* @see https://c15t.com/docs/frameworks/next/script-loader
|
|
767
|
-
*/
|
|
768
|
-
export function ConsentManagerClient({
|
|
769
|
-
children,
|
|
770
|
-
}: {
|
|
771
|
-
children: ReactNode;
|
|
772
|
-
}) {
|
|
773
|
-
return (
|
|
774
|
-
<ClientSideOptionsProvider
|
|
775
|
-
// 📝 Add your integration scripts here
|
|
776
|
-
// Scripts are loaded when consent is given and removed when consent is revoked
|
|
777
|
-
scripts={[
|
|
778
|
-
// Example:
|
|
779
|
-
// googleTagManager({
|
|
780
|
-
// id: 'GTM-XXXXXX',
|
|
781
|
-
// script: {
|
|
782
|
-
// onLoad: () => console.log('GTM loaded'),
|
|
783
|
-
// },
|
|
784
|
-
// }),
|
|
785
|
-
]}
|
|
786
|
-
// 📝 Add your callbacks here
|
|
787
|
-
// Callbacks allow you to react to consent events
|
|
788
|
-
callbacks={{
|
|
789
|
-
// Example:
|
|
790
|
-
// onConsentSet(response) {
|
|
791
|
-
// console.log('Consent updated:', response);
|
|
792
|
-
// },
|
|
793
|
-
// onError(error) {
|
|
794
|
-
// console.error('Consent error:', error);
|
|
795
|
-
// },
|
|
796
|
-
}}
|
|
797
|
-
>
|
|
798
|
-
{children}
|
|
799
|
-
</ClientSideOptionsProvider>
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
`;
|
|
803
|
-
}
|
|
804
|
-
const HTML_TAG_REGEX = /<html[^>]*>([\s\S]*)<\/html>/;
|
|
805
|
-
const BODY_TAG_REGEX = /<body[^>]*>([\s\S]*)<\/body>/;
|
|
806
|
-
const BODY_OPENING_TAG_REGEX = /<body[^>]*>/;
|
|
807
|
-
const HTML_CONTENT_REGEX = /([\s\S]*<\/html>)/;
|
|
808
|
-
function findAppLayoutFile(project, projectRoot) {
|
|
809
|
-
const layoutPatterns = [
|
|
810
|
-
'app/layout.tsx',
|
|
811
|
-
'src/app/layout.tsx',
|
|
812
|
-
'app/layout.ts',
|
|
813
|
-
'src/app/layout.ts'
|
|
814
|
-
];
|
|
815
|
-
for (const pattern of layoutPatterns){
|
|
816
|
-
const files = project.addSourceFilesAtPaths(`${projectRoot}/${pattern}`);
|
|
817
|
-
if (files.length > 0) return files[0];
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
function getAppDirectory(layoutFilePath) {
|
|
821
|
-
const normalizedPath = node_path.normalize(layoutFilePath);
|
|
822
|
-
const srcAppSegment = node_path.join('src', 'app');
|
|
823
|
-
if (normalizedPath.includes(srcAppSegment)) return node_path.join('src', 'app');
|
|
824
|
-
return 'app';
|
|
825
|
-
}
|
|
826
|
-
function computeRelativeModuleSpecifier(fromFilePath, toFilePath) {
|
|
827
|
-
const fromDir = node_path.dirname(fromFilePath);
|
|
828
|
-
let relativePath = node_path.relative(fromDir, toFilePath);
|
|
829
|
-
relativePath = relativePath.split(node_path.sep).join('/');
|
|
830
|
-
relativePath = relativePath.replace(/\.(tsx?|jsx?)$/, '');
|
|
831
|
-
if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
|
|
832
|
-
return relativePath;
|
|
833
|
-
}
|
|
834
|
-
function addConsentManagerImport(layoutFile, consentManagerFilePath) {
|
|
835
|
-
const layoutFilePath = layoutFile.getFilePath();
|
|
836
|
-
const moduleSpecifier = computeRelativeModuleSpecifier(layoutFilePath, consentManagerFilePath);
|
|
837
|
-
const existingImports = layoutFile.getImportDeclarations();
|
|
838
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>{
|
|
839
|
-
const existingSpec = importDecl.getModuleSpecifierValue();
|
|
840
|
-
return existingSpec === moduleSpecifier || existingSpec.endsWith('consent-manager') || existingSpec.endsWith('consent-manager.tsx');
|
|
841
|
-
});
|
|
842
|
-
if (!hasConsentManagerImport) layoutFile.addImportDeclaration({
|
|
843
|
-
namedImports: [
|
|
844
|
-
'ConsentManager'
|
|
845
|
-
],
|
|
846
|
-
moduleSpecifier
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
function wrapAppJsxContent(originalJsx) {
|
|
850
|
-
const hasHtmlTag = originalJsx.includes('<html') || originalJsx.includes('</html>');
|
|
851
|
-
const hasBodyTag = originalJsx.includes('<body') || originalJsx.includes('</body>');
|
|
852
|
-
const consentWrapper = (content)=>`
|
|
853
|
-
<ConsentManager>
|
|
854
|
-
${content}
|
|
855
|
-
</ConsentManager>
|
|
856
|
-
`;
|
|
857
|
-
if (hasHtmlTag) {
|
|
858
|
-
const htmlMatch = originalJsx.match(HTML_TAG_REGEX);
|
|
859
|
-
const htmlContent = htmlMatch?.[1] || '';
|
|
860
|
-
if (!htmlContent) return consentWrapper(originalJsx);
|
|
861
|
-
const bodyMatch = htmlContent.match(BODY_TAG_REGEX);
|
|
862
|
-
if (!bodyMatch) return originalJsx.replace(HTML_CONTENT_REGEX, `<html>${consentWrapper('$1')}</html>`);
|
|
863
|
-
const bodyContent = bodyMatch[1] || '';
|
|
864
|
-
const bodyOpeningTag = originalJsx.match(BODY_OPENING_TAG_REGEX)?.[0] || '<body>';
|
|
865
|
-
return originalJsx.replace(BODY_TAG_REGEX, `${bodyOpeningTag}${consentWrapper(bodyContent)}</body>`);
|
|
866
|
-
}
|
|
867
|
-
if (hasBodyTag) {
|
|
868
|
-
const bodyMatch = originalJsx.match(BODY_TAG_REGEX);
|
|
869
|
-
const bodyContent = bodyMatch?.[1] || '';
|
|
870
|
-
if (!bodyContent) return consentWrapper(originalJsx);
|
|
871
|
-
const bodyOpeningTag = originalJsx.match(BODY_OPENING_TAG_REGEX)?.[0] || '<body>';
|
|
872
|
-
return originalJsx.replace(BODY_TAG_REGEX, `${bodyOpeningTag}${consentWrapper(bodyContent)}</body>`);
|
|
873
|
-
}
|
|
874
|
-
return consentWrapper(originalJsx);
|
|
875
|
-
}
|
|
876
|
-
async function createConsentManagerComponents(projectRoot, appDir, optionsText) {
|
|
877
|
-
const appDirPath = node_path.join(projectRoot, appDir);
|
|
878
|
-
const consentManagerContent = generateConsentManagerTemplate(optionsText);
|
|
879
|
-
const consentManagerClientContent = generateConsentManagerClientTemplate();
|
|
880
|
-
const consentManagerPath = node_path.join(appDirPath, 'consent-manager.tsx');
|
|
881
|
-
const consentManagerClientPath = node_path.join(appDirPath, 'consent-manager.client.tsx');
|
|
882
|
-
await Promise.all([
|
|
883
|
-
promises.writeFile(consentManagerPath, consentManagerContent, 'utf-8'),
|
|
884
|
-
promises.writeFile(consentManagerClientPath, consentManagerClientContent, 'utf-8')
|
|
885
|
-
]);
|
|
886
|
-
return {
|
|
887
|
-
consentManager: consentManagerPath,
|
|
888
|
-
consentManagerClient: consentManagerClientPath
|
|
889
|
-
};
|
|
890
|
-
}
|
|
891
|
-
async function updateAppLayout({ projectRoot, mode, backendURL, useEnvFile, proxyNextjs }) {
|
|
892
|
-
const project = new Project();
|
|
893
|
-
const layoutFile = findAppLayoutFile(project, projectRoot);
|
|
894
|
-
if (!layoutFile) return {
|
|
895
|
-
updated: false,
|
|
896
|
-
filePath: null,
|
|
897
|
-
alreadyModified: false
|
|
898
|
-
};
|
|
899
|
-
const layoutFilePath = layoutFile.getFilePath();
|
|
900
|
-
const appDir = getAppDirectory(layoutFilePath);
|
|
901
|
-
const existingImports = layoutFile.getImportDeclarations();
|
|
902
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>'./consent-manager' === importDecl.getModuleSpecifierValue() || './consent-manager.tsx' === importDecl.getModuleSpecifierValue());
|
|
903
|
-
if (hasConsentManagerImport) return {
|
|
904
|
-
updated: false,
|
|
905
|
-
filePath: layoutFilePath,
|
|
906
|
-
alreadyModified: true
|
|
907
|
-
};
|
|
908
|
-
const optionsText = generateOptionsText(mode, backendURL, useEnvFile, proxyNextjs);
|
|
909
|
-
const componentFiles = await createConsentManagerComponents(projectRoot, appDir, optionsText);
|
|
910
|
-
addConsentManagerImport(layoutFile, componentFiles.consentManager);
|
|
911
|
-
const returnStatement = layoutFile.getDescendantsOfKind(SyntaxKind.ReturnStatement)[0];
|
|
912
|
-
if (!returnStatement) return {
|
|
913
|
-
updated: false,
|
|
914
|
-
filePath: layoutFilePath,
|
|
915
|
-
alreadyModified: false
|
|
916
|
-
};
|
|
917
|
-
const expression = returnStatement.getExpression();
|
|
918
|
-
if (!expression) return {
|
|
919
|
-
updated: false,
|
|
920
|
-
filePath: layoutFilePath,
|
|
921
|
-
alreadyModified: false
|
|
922
|
-
};
|
|
923
|
-
const originalJsx = expression.getText();
|
|
924
|
-
const newJsx = wrapAppJsxContent(originalJsx);
|
|
925
|
-
returnStatement.replaceWithText(`return ${newJsx}`);
|
|
926
|
-
await layoutFile.save();
|
|
927
|
-
return {
|
|
928
|
-
updated: true,
|
|
929
|
-
filePath: layoutFilePath,
|
|
930
|
-
alreadyModified: false,
|
|
931
|
-
componentFiles
|
|
932
|
-
};
|
|
933
|
-
}
|
|
934
|
-
function components_generateConsentManagerTemplate(optionsText) {
|
|
935
|
-
return `import type { ReactNode } from 'react';
|
|
936
|
-
import {
|
|
937
|
-
ConsentManagerDialog,
|
|
938
|
-
ConsentManagerProvider,
|
|
939
|
-
CookieBanner,
|
|
940
|
-
type InitialDataPromise
|
|
941
|
-
} from '@c15t/nextjs/pages';
|
|
942
|
-
// For client-only apps (non-SSR), you can use:
|
|
943
|
-
// import { ConsentManagerProvider } from '@c15t/nextjs/client';
|
|
944
|
-
|
|
945
|
-
/**
|
|
946
|
-
* Consent management wrapper for Next.js Pages Router
|
|
947
|
-
*
|
|
948
|
-
* This component wraps your app with consent management functionality,
|
|
949
|
-
* including the cookie banner, consent dialog, and provider.
|
|
950
|
-
*
|
|
951
|
-
* @param props - Component properties
|
|
952
|
-
* @param props.children - Child components to render within the consent manager context
|
|
953
|
-
* @param props.initialData - Initial consent data from server-side props (optional)
|
|
954
|
-
*
|
|
955
|
-
* @returns The consent manager provider with banner and dialog
|
|
956
|
-
*
|
|
957
|
-
* @remarks
|
|
958
|
-
* To get initial server-side data on other pages, use:
|
|
959
|
-
* \`\`\`tsx
|
|
960
|
-
* import { withInitialC15TData } from '@c15t/nextjs/pages';
|
|
961
|
-
*
|
|
962
|
-
* export const getServerSideProps = withInitialC15TData('/api/c15t');
|
|
963
|
-
* \`\`\`
|
|
964
|
-
*
|
|
965
|
-
* @example
|
|
966
|
-
* \`\`\`tsx
|
|
967
|
-
* // In your pages/_app.tsx
|
|
968
|
-
* import { ConsentManager } from '../components/consent-manager';
|
|
969
|
-
*
|
|
970
|
-
* export default function MyApp({ Component, pageProps }) {
|
|
971
|
-
* return (
|
|
972
|
-
* <ConsentManager initialData={pageProps.initialC15TData}>
|
|
973
|
-
* <Component {...pageProps} />
|
|
974
|
-
* </ConsentManager>
|
|
975
|
-
* );
|
|
976
|
-
* }
|
|
977
|
-
* \`\`\`
|
|
978
|
-
*/
|
|
979
|
-
export function ConsentManager({
|
|
980
|
-
children,
|
|
981
|
-
initialData,
|
|
982
|
-
}: {
|
|
983
|
-
children: ReactNode;
|
|
984
|
-
initialData?: InitialDataPromise;
|
|
985
|
-
}) {
|
|
986
|
-
return (
|
|
987
|
-
<ConsentManagerProvider
|
|
988
|
-
initialData={initialData}
|
|
989
|
-
options={${optionsText}}
|
|
990
|
-
>
|
|
991
|
-
<CookieBanner />
|
|
992
|
-
<ConsentManagerDialog />
|
|
993
|
-
{children}
|
|
994
|
-
</ConsentManagerProvider>
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
`;
|
|
998
|
-
}
|
|
999
|
-
function findPagesAppFile(project, projectRoot) {
|
|
1000
|
-
const appPatterns = [
|
|
1001
|
-
'pages/_app.tsx',
|
|
1002
|
-
'pages/_app.ts',
|
|
1003
|
-
'src/pages/_app.tsx',
|
|
1004
|
-
'src/pages/_app.ts'
|
|
1005
|
-
];
|
|
1006
|
-
for (const pattern of appPatterns){
|
|
1007
|
-
const files = project.addSourceFilesAtPaths(`${projectRoot}/${pattern}`);
|
|
1008
|
-
if (files.length > 0) return files[0];
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
function getPagesDirectory(appFilePath) {
|
|
1012
|
-
const normalizedPath = node_path.normalize(appFilePath);
|
|
1013
|
-
const srcPagesSegment = node_path.join('src', 'pages');
|
|
1014
|
-
if (normalizedPath.includes(srcPagesSegment)) return node_path.join('src', 'pages');
|
|
1015
|
-
return 'pages';
|
|
1016
|
-
}
|
|
1017
|
-
function layout_computeRelativeModuleSpecifier(fromFilePath, toFilePath) {
|
|
1018
|
-
const fromDir = node_path.dirname(fromFilePath);
|
|
1019
|
-
let relativePath = node_path.relative(fromDir, toFilePath);
|
|
1020
|
-
relativePath = relativePath.split(node_path.sep).join('/');
|
|
1021
|
-
relativePath = relativePath.replace(/\.(tsx?|jsx?)$/, '');
|
|
1022
|
-
if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
|
|
1023
|
-
return relativePath;
|
|
1024
|
-
}
|
|
1025
|
-
function layout_addConsentManagerImport(appFile, consentManagerFilePath) {
|
|
1026
|
-
const appFilePath = appFile.getFilePath();
|
|
1027
|
-
const moduleSpecifier = layout_computeRelativeModuleSpecifier(appFilePath, consentManagerFilePath);
|
|
1028
|
-
const existingImports = appFile.getImportDeclarations();
|
|
1029
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>{
|
|
1030
|
-
const existingSpec = importDecl.getModuleSpecifierValue();
|
|
1031
|
-
return existingSpec === moduleSpecifier || existingSpec.endsWith('consent-manager') || existingSpec.endsWith('consent-manager.tsx');
|
|
1032
|
-
});
|
|
1033
|
-
if (!hasConsentManagerImport) appFile.addImportDeclaration({
|
|
1034
|
-
namedImports: [
|
|
1035
|
-
'ConsentManager'
|
|
1036
|
-
],
|
|
1037
|
-
moduleSpecifier
|
|
1038
|
-
});
|
|
1039
|
-
}
|
|
1040
|
-
function wrapPagesJsxContent(originalJsx) {
|
|
1041
|
-
const trimmedJsx = originalJsx.trim();
|
|
1042
|
-
const hasParentheses = trimmedJsx.startsWith('(') && trimmedJsx.endsWith(')');
|
|
1043
|
-
const cleanJsx = hasParentheses ? trimmedJsx.slice(1, -1).trim() : originalJsx;
|
|
1044
|
-
const wrappedContent = `
|
|
1045
|
-
<ConsentManager initialData={pageProps.initialC15TData}>
|
|
1046
|
-
${cleanJsx}
|
|
1047
|
-
</ConsentManager>
|
|
1048
|
-
`;
|
|
1049
|
-
return `(${wrappedContent})`;
|
|
1050
|
-
}
|
|
1051
|
-
async function createConsentManagerComponent(projectRoot, pagesDir, optionsText) {
|
|
1052
|
-
let componentsDir;
|
|
1053
|
-
componentsDir = pagesDir.includes('src') ? node_path.join('src', 'components') : 'components';
|
|
1054
|
-
const componentsDirPath = node_path.join(projectRoot, componentsDir);
|
|
1055
|
-
await promises.mkdir(componentsDirPath, {
|
|
1056
|
-
recursive: true
|
|
1057
|
-
});
|
|
1058
|
-
const consentManagerContent = components_generateConsentManagerTemplate(optionsText);
|
|
1059
|
-
const consentManagerPath = node_path.join(componentsDirPath, 'consent-manager.tsx');
|
|
1060
|
-
await promises.writeFile(consentManagerPath, consentManagerContent, 'utf-8');
|
|
1061
|
-
return {
|
|
1062
|
-
consentManager: consentManagerPath
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
function addServerSideDataComment(appFile, backendURL, useEnvFile, proxyNextjs) {
|
|
1066
|
-
const existingComments = appFile.getLeadingCommentRanges();
|
|
1067
|
-
let urlExample;
|
|
1068
|
-
urlExample = proxyNextjs ? "'/api/c15t'" : useEnvFile ? 'process.env.NEXT_PUBLIC_C15T_URL!' : `'${backendURL || 'https://your-instance.c15t.dev'}'`;
|
|
1069
|
-
const serverSideComment = `/**
|
|
1070
|
-
* Note: To get the initial server-side data on other pages, add this to each page:
|
|
1071
|
-
*
|
|
1072
|
-
* import { withInitialC15TData } from '@c15t/nextjs/pages';
|
|
1073
|
-
*
|
|
1074
|
-
* export const getServerSideProps = withInitialC15TData(${urlExample});
|
|
1075
|
-
*
|
|
1076
|
-
* This will automatically pass initialC15TData to pageProps.initialC15TData
|
|
1077
|
-
*/`;
|
|
1078
|
-
const hasServerSideComment = existingComments.some((comment)=>comment.getText().includes('withInitialC15TData'));
|
|
1079
|
-
if (!hasServerSideComment) appFile.insertText(0, `${serverSideComment}\n\n`);
|
|
1080
|
-
}
|
|
1081
|
-
function updateAppComponentTyping(appFile) {
|
|
1082
|
-
const exportAssignment = appFile.getExportAssignment(()=>true);
|
|
1083
|
-
if (!exportAssignment) return;
|
|
1084
|
-
const declaration = exportAssignment.getExpression();
|
|
1085
|
-
if (!declaration) return;
|
|
1086
|
-
const text = declaration.getText();
|
|
1087
|
-
if (text.includes('pageProps') && !text.includes('AppProps')) {
|
|
1088
|
-
const hasAppPropsImport = appFile.getImportDeclarations().some((importDecl)=>'next/app' === importDecl.getModuleSpecifierValue() && importDecl.getNamedImports().some((namedImport)=>'AppProps' === namedImport.getName()));
|
|
1089
|
-
if (!hasAppPropsImport) appFile.addImportDeclaration({
|
|
1090
|
-
namedImports: [
|
|
1091
|
-
'AppProps'
|
|
1092
|
-
],
|
|
1093
|
-
moduleSpecifier: 'next/app'
|
|
807
|
+
try {
|
|
808
|
+
const potentialFiles = [
|
|
809
|
+
external_node_path_["default"].join(projectRoot, 'c15t.config.ts'),
|
|
810
|
+
external_node_path_["default"].join(projectRoot, '.env.local'),
|
|
811
|
+
external_node_path_["default"].join(projectRoot, '.env.example'),
|
|
812
|
+
external_node_path_["default"].join(projectRoot, 'next.config.ts'),
|
|
813
|
+
external_node_path_["default"].join(projectRoot, 'next.config.js'),
|
|
814
|
+
external_node_path_["default"].join(projectRoot, 'next.config.mjs')
|
|
815
|
+
];
|
|
816
|
+
for (const filePath of potentialFiles){
|
|
817
|
+
const exists = await fileExists(filePath);
|
|
818
|
+
if (exists) {
|
|
819
|
+
const backup = await readFileForBackup(filePath);
|
|
820
|
+
if (null !== backup) filesModified.push({
|
|
821
|
+
path: filePath,
|
|
822
|
+
backup,
|
|
823
|
+
type: 'modified'
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
const generateResult = await generateFiles({
|
|
828
|
+
context: cliContext,
|
|
829
|
+
mode: mode,
|
|
830
|
+
spinner: spinnerMock,
|
|
831
|
+
backendURL: backendURL ?? void 0,
|
|
832
|
+
useEnvFile,
|
|
833
|
+
proxyNextjs,
|
|
834
|
+
enableSSR,
|
|
835
|
+
enableDevTools,
|
|
836
|
+
uiStyle,
|
|
837
|
+
expandedTheme: expandedTheme ?? void 0,
|
|
838
|
+
selectedScripts
|
|
1094
839
|
});
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const project = new Project();
|
|
1099
|
-
const appFile = findPagesAppFile(project, projectRoot);
|
|
1100
|
-
if (!appFile) return {
|
|
1101
|
-
updated: false,
|
|
1102
|
-
filePath: null,
|
|
1103
|
-
alreadyModified: false
|
|
1104
|
-
};
|
|
1105
|
-
const appFilePath = appFile.getFilePath();
|
|
1106
|
-
const pagesDir = getPagesDirectory(appFilePath);
|
|
1107
|
-
const existingImports = appFile.getImportDeclarations();
|
|
1108
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>{
|
|
1109
|
-
const specifier = importDecl.getModuleSpecifierValue();
|
|
1110
|
-
return specifier.endsWith('/consent-manager') || specifier.endsWith('/consent-manager.tsx') || './consent-manager' === specifier || './consent-manager.tsx' === specifier;
|
|
1111
|
-
});
|
|
1112
|
-
if (hasConsentManagerImport) return {
|
|
1113
|
-
updated: false,
|
|
1114
|
-
filePath: appFilePath,
|
|
1115
|
-
alreadyModified: true
|
|
1116
|
-
};
|
|
1117
|
-
const optionsText = generateOptionsText(mode, backendURL, useEnvFile, proxyNextjs);
|
|
1118
|
-
const componentFiles = await createConsentManagerComponent(projectRoot, pagesDir, optionsText);
|
|
1119
|
-
layout_addConsentManagerImport(appFile, componentFiles.consentManager);
|
|
1120
|
-
updateAppComponentTyping(appFile);
|
|
1121
|
-
addServerSideDataComment(appFile, backendURL, useEnvFile, proxyNextjs);
|
|
1122
|
-
const returnStatement = appFile.getDescendantsOfKind(SyntaxKind.ReturnStatement)[0];
|
|
1123
|
-
if (!returnStatement) return {
|
|
1124
|
-
updated: false,
|
|
1125
|
-
filePath: appFilePath,
|
|
1126
|
-
alreadyModified: false
|
|
1127
|
-
};
|
|
1128
|
-
const expression = returnStatement.getExpression();
|
|
1129
|
-
if (!expression) return {
|
|
1130
|
-
updated: false,
|
|
1131
|
-
filePath: appFilePath,
|
|
1132
|
-
alreadyModified: false
|
|
1133
|
-
};
|
|
1134
|
-
const originalJsx = expression.getText();
|
|
1135
|
-
const newJsx = wrapPagesJsxContent(originalJsx);
|
|
1136
|
-
returnStatement.replaceWithText(`return ${newJsx}`);
|
|
1137
|
-
await appFile.save();
|
|
1138
|
-
return {
|
|
1139
|
-
updated: true,
|
|
1140
|
-
filePath: appFilePath,
|
|
1141
|
-
alreadyModified: false,
|
|
1142
|
-
componentFiles
|
|
1143
|
-
};
|
|
1144
|
-
}
|
|
1145
|
-
function detectNextJsStructure(projectRoot) {
|
|
1146
|
-
const project = new Project();
|
|
1147
|
-
const appLayoutPatterns = [
|
|
1148
|
-
'app/layout.tsx',
|
|
1149
|
-
'src/app/layout.tsx',
|
|
1150
|
-
'app/layout.ts',
|
|
1151
|
-
'src/app/layout.ts'
|
|
1152
|
-
];
|
|
1153
|
-
for (const pattern of appLayoutPatterns){
|
|
1154
|
-
const files = project.addSourceFilesAtPaths(`${projectRoot}/${pattern}`);
|
|
1155
|
-
if (files.length > 0) return 'app';
|
|
1156
|
-
}
|
|
1157
|
-
const pagesAppPatterns = [
|
|
1158
|
-
'pages/_app.tsx',
|
|
1159
|
-
'pages/_app.ts',
|
|
1160
|
-
'src/pages/_app.tsx',
|
|
1161
|
-
'src/pages/_app.ts'
|
|
1162
|
-
];
|
|
1163
|
-
for (const pattern of pagesAppPatterns){
|
|
1164
|
-
const files = project.addSourceFilesAtPaths(`${projectRoot}/${pattern}`);
|
|
1165
|
-
if (files.length > 0) return 'pages';
|
|
1166
|
-
}
|
|
1167
|
-
return null;
|
|
1168
|
-
}
|
|
1169
|
-
async function updateNextLayout(options) {
|
|
1170
|
-
const structureType = detectNextJsStructure(options.projectRoot);
|
|
1171
|
-
if (!structureType) return {
|
|
1172
|
-
updated: false,
|
|
1173
|
-
filePath: null,
|
|
1174
|
-
alreadyModified: false,
|
|
1175
|
-
structureType: null
|
|
1176
|
-
};
|
|
1177
|
-
let result;
|
|
1178
|
-
result = 'app' === structureType ? await updateAppLayout(options) : await updatePagesLayout(options);
|
|
1179
|
-
return {
|
|
1180
|
-
...result,
|
|
1181
|
-
structureType
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
function react_components_generateConsentManagerTemplate(optionsText) {
|
|
1185
|
-
return `import type { ReactNode } from 'react';
|
|
1186
|
-
import {
|
|
1187
|
-
ConsentManagerDialog,
|
|
1188
|
-
ConsentManagerProvider,
|
|
1189
|
-
CookieBanner,
|
|
1190
|
-
} from '@c15t/react';
|
|
1191
|
-
|
|
1192
|
-
/**
|
|
1193
|
-
* Consent management wrapper for React
|
|
1194
|
-
*
|
|
1195
|
-
* This component wraps your app with consent management functionality,
|
|
1196
|
-
* including the cookie banner, consent dialog, and provider.
|
|
1197
|
-
*
|
|
1198
|
-
* @param props - Component properties
|
|
1199
|
-
* @param props.children - Child components to render within the consent manager context
|
|
1200
|
-
*
|
|
1201
|
-
* @returns The consent manager provider with banner and dialog
|
|
1202
|
-
*
|
|
1203
|
-
* @example
|
|
1204
|
-
* \`\`\`tsx
|
|
1205
|
-
* // In your App.tsx
|
|
1206
|
-
* import { ConsentManager } from './consent-manager';
|
|
1207
|
-
*
|
|
1208
|
-
* export default function App() {
|
|
1209
|
-
* return (
|
|
1210
|
-
* <ConsentManager>
|
|
1211
|
-
* <YourApp />
|
|
1212
|
-
* </ConsentManager>
|
|
1213
|
-
* );
|
|
1214
|
-
* }
|
|
1215
|
-
* \`\`\`
|
|
1216
|
-
*/
|
|
1217
|
-
export function ConsentManager({ children }: { children: ReactNode }) {
|
|
1218
|
-
return (
|
|
1219
|
-
<ConsentManagerProvider
|
|
1220
|
-
options={${optionsText}}
|
|
1221
|
-
>
|
|
1222
|
-
<CookieBanner />
|
|
1223
|
-
<ConsentManagerDialog />
|
|
1224
|
-
{children}
|
|
1225
|
-
</ConsentManagerProvider>
|
|
1226
|
-
);
|
|
1227
|
-
}
|
|
1228
|
-
`;
|
|
1229
|
-
}
|
|
1230
|
-
function templates_layout_computeRelativeModuleSpecifier(fromFilePath, toFilePath) {
|
|
1231
|
-
const fromDir = node_path.dirname(fromFilePath);
|
|
1232
|
-
let relativePath = node_path.relative(fromDir, toFilePath);
|
|
1233
|
-
relativePath = relativePath.split(node_path.sep).join('/');
|
|
1234
|
-
relativePath = relativePath.replace(/\.(tsx?|jsx?)$/, '');
|
|
1235
|
-
if (!relativePath.startsWith('.')) relativePath = `./${relativePath}`;
|
|
1236
|
-
return relativePath;
|
|
1237
|
-
}
|
|
1238
|
-
function templates_layout_addConsentManagerImport(layoutFile, consentManagerFilePath) {
|
|
1239
|
-
const layoutFilePath = layoutFile.getFilePath();
|
|
1240
|
-
const moduleSpecifier = templates_layout_computeRelativeModuleSpecifier(layoutFilePath, consentManagerFilePath);
|
|
1241
|
-
const existingImports = layoutFile.getImportDeclarations();
|
|
1242
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>{
|
|
1243
|
-
const existingSpec = importDecl.getModuleSpecifierValue();
|
|
1244
|
-
return existingSpec === moduleSpecifier || existingSpec.endsWith('consent-manager') || existingSpec.endsWith('consent-manager.tsx');
|
|
1245
|
-
});
|
|
1246
|
-
if (!hasConsentManagerImport) layoutFile.addImportDeclaration({
|
|
1247
|
-
namedImports: [
|
|
1248
|
-
'ConsentManager'
|
|
1249
|
-
],
|
|
1250
|
-
moduleSpecifier
|
|
1251
|
-
});
|
|
1252
|
-
}
|
|
1253
|
-
function getSourceDirectory(layoutFilePath) {
|
|
1254
|
-
const normalizedPath = node_path.normalize(layoutFilePath);
|
|
1255
|
-
const segments = normalizedPath.split(node_path.sep);
|
|
1256
|
-
if (segments.includes('src')) return 'src';
|
|
1257
|
-
return '';
|
|
1258
|
-
}
|
|
1259
|
-
function updateGenericReactJsx(layoutFile) {
|
|
1260
|
-
const functionDeclarations = layoutFile.getFunctions();
|
|
1261
|
-
const variableDeclarations = layoutFile.getVariableDeclarations();
|
|
1262
|
-
for (const func of functionDeclarations){
|
|
1263
|
-
const returnStatement = func.getDescendantsOfKind(SyntaxKind.ReturnStatement)[0];
|
|
1264
|
-
if (returnStatement) return wrapReturnStatementWithConsentManager(returnStatement);
|
|
1265
|
-
}
|
|
1266
|
-
for (const varDecl of variableDeclarations){
|
|
1267
|
-
const initializer = varDecl.getInitializer();
|
|
1268
|
-
if (initializer) {
|
|
1269
|
-
const returnStatement = initializer.getDescendantsOfKind(SyntaxKind.ReturnStatement)[0];
|
|
1270
|
-
if (returnStatement) return wrapReturnStatementWithConsentManager(returnStatement);
|
|
840
|
+
if (generateResult.configPath) {
|
|
841
|
+
result.configPath = generateResult.configPath;
|
|
842
|
+
if (!filesModified.some((f)=>f.path === generateResult.configPath)) filesCreated.push(generateResult.configPath);
|
|
1271
843
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
const expression = returnStatement.getExpression();
|
|
1277
|
-
if (!expression) return false;
|
|
1278
|
-
const originalJsx = expression.getText();
|
|
1279
|
-
const newJsx = `(
|
|
1280
|
-
<ConsentManager>
|
|
1281
|
-
${originalJsx}
|
|
1282
|
-
</ConsentManager>
|
|
1283
|
-
)`;
|
|
1284
|
-
returnStatement.replaceWithText(`return ${newJsx}`);
|
|
1285
|
-
return true;
|
|
1286
|
-
}
|
|
1287
|
-
async function layout_createConsentManagerComponent(projectRoot, sourceDir, optionsText) {
|
|
1288
|
-
const targetDir = sourceDir ? node_path.join(projectRoot, sourceDir) : projectRoot;
|
|
1289
|
-
const consentManagerContent = react_components_generateConsentManagerTemplate(optionsText);
|
|
1290
|
-
const consentManagerPath = node_path.join(targetDir, 'consent-manager.tsx');
|
|
1291
|
-
await promises.writeFile(consentManagerPath, consentManagerContent, 'utf-8');
|
|
1292
|
-
return {
|
|
1293
|
-
consentManager: consentManagerPath
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
async function updateGenericReactLayout({ projectRoot, mode, backendURL, useEnvFile, proxyNextjs }) {
|
|
1297
|
-
const layoutPatterns = [
|
|
1298
|
-
'app.tsx',
|
|
1299
|
-
'App.tsx',
|
|
1300
|
-
'app.jsx',
|
|
1301
|
-
'App.jsx',
|
|
1302
|
-
'src/app.tsx',
|
|
1303
|
-
'src/App.tsx',
|
|
1304
|
-
'src/app.jsx',
|
|
1305
|
-
'src/App.jsx',
|
|
1306
|
-
'src/app/app.tsx',
|
|
1307
|
-
'src/app/App.tsx',
|
|
1308
|
-
'src/app/app.jsx',
|
|
1309
|
-
'src/app/App.jsx'
|
|
1310
|
-
];
|
|
1311
|
-
const project = new Project();
|
|
1312
|
-
let layoutFile;
|
|
1313
|
-
for (const pattern of layoutPatterns)try {
|
|
1314
|
-
const files = project.addSourceFilesAtPaths(`${projectRoot}/${pattern}`);
|
|
1315
|
-
if (files.length > 0) {
|
|
1316
|
-
layoutFile = files[0];
|
|
1317
|
-
break;
|
|
844
|
+
if (generateResult.layoutPath) result.layoutPath = generateResult.layoutPath;
|
|
845
|
+
if (generateResult.nextConfigPath) {
|
|
846
|
+
result.nextConfigPath = generateResult.nextConfigPath;
|
|
847
|
+
if (generateResult.nextConfigCreated) filesCreated.push(generateResult.nextConfigPath);
|
|
1318
848
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
const layoutFilePath = layoutFile.getFilePath();
|
|
1326
|
-
const sourceDir = getSourceDirectory(layoutFilePath);
|
|
1327
|
-
const existingImports = layoutFile.getImportDeclarations();
|
|
1328
|
-
const hasConsentManagerImport = existingImports.some((importDecl)=>'./consent-manager' === importDecl.getModuleSpecifierValue() || './consent-manager.tsx' === importDecl.getModuleSpecifierValue());
|
|
1329
|
-
if (hasConsentManagerImport) return {
|
|
1330
|
-
updated: false,
|
|
1331
|
-
filePath: layoutFilePath,
|
|
1332
|
-
alreadyModified: true
|
|
1333
|
-
};
|
|
1334
|
-
try {
|
|
1335
|
-
const optionsText = generateOptionsText(mode, backendURL, useEnvFile, proxyNextjs);
|
|
1336
|
-
const componentFiles = await layout_createConsentManagerComponent(projectRoot, sourceDir, optionsText);
|
|
1337
|
-
templates_layout_addConsentManagerImport(layoutFile, componentFiles.consentManager);
|
|
1338
|
-
const updated = updateGenericReactJsx(layoutFile);
|
|
1339
|
-
if (updated) {
|
|
1340
|
-
await layoutFile.save();
|
|
1341
|
-
return {
|
|
1342
|
-
updated: true,
|
|
1343
|
-
filePath: layoutFilePath,
|
|
1344
|
-
alreadyModified: false,
|
|
1345
|
-
componentFiles
|
|
1346
|
-
};
|
|
849
|
+
if (useEnvFile && backendURL) {
|
|
850
|
+
const envPath = external_node_path_["default"].join(projectRoot, '.env.local');
|
|
851
|
+
const envExamplePath = external_node_path_["default"].join(projectRoot, '.env.example');
|
|
852
|
+
if (!filesModified.some((f)=>f.path === envPath) && await fileExists(envPath)) filesCreated.push(envPath);
|
|
853
|
+
result.envPath = envPath;
|
|
854
|
+
if (!filesModified.some((f)=>f.path === envExamplePath) && await fileExists(envExamplePath)) filesCreated.push(envExamplePath);
|
|
1347
855
|
}
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
856
|
+
result.filesCreated = filesCreated;
|
|
857
|
+
result.filesModified = filesModified;
|
|
858
|
+
const allFiles = [
|
|
859
|
+
...filesCreated,
|
|
860
|
+
...filesModified.map((f)=>f.path),
|
|
861
|
+
generateResult.layoutPath
|
|
862
|
+
].filter((f)=>!!f);
|
|
863
|
+
await formatGeneratedFiles(projectRoot, allFiles, logger);
|
|
864
|
+
return result;
|
|
1353
865
|
} catch (error) {
|
|
1354
|
-
|
|
866
|
+
result.filesCreated = filesCreated;
|
|
867
|
+
result.filesModified = filesModified;
|
|
868
|
+
throw error;
|
|
1355
869
|
}
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
};
|
|
870
|
+
});
|
|
871
|
+
const rollbackActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
872
|
+
const { filesCreated, filesModified } = input;
|
|
873
|
+
const fs = await import("node:fs/promises");
|
|
874
|
+
const errors = [];
|
|
875
|
+
for (const filePath of filesCreated)try {
|
|
876
|
+
await fs.unlink(filePath);
|
|
877
|
+
} catch (error) {
|
|
878
|
+
errors.push(`Failed to delete ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1366
879
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
const configFile = findNextConfigFile(project, projectRoot);
|
|
1372
|
-
if (!configFile) {
|
|
1373
|
-
const newConfigPath = `${projectRoot}/next.config.ts`;
|
|
1374
|
-
const newConfig = createNewNextConfig(backendURL, useEnvFile);
|
|
1375
|
-
const newConfigFile = project.createSourceFile(newConfigPath, newConfig);
|
|
1376
|
-
await newConfigFile.save();
|
|
1377
|
-
return {
|
|
1378
|
-
updated: true,
|
|
1379
|
-
filePath: newConfigPath,
|
|
1380
|
-
alreadyModified: false,
|
|
1381
|
-
created: true
|
|
1382
|
-
};
|
|
880
|
+
for (const mod of filesModified)try {
|
|
881
|
+
await fs.writeFile(mod.path, mod.backup, 'utf-8');
|
|
882
|
+
} catch (error) {
|
|
883
|
+
errors.push(`Failed to restore ${mod.path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1383
884
|
}
|
|
1384
|
-
if (hasC15tRewriteRule(configFile)) return {
|
|
1385
|
-
updated: false,
|
|
1386
|
-
filePath: configFile.getFilePath(),
|
|
1387
|
-
alreadyModified: true,
|
|
1388
|
-
created: false
|
|
1389
|
-
};
|
|
1390
|
-
const updated = await updateExistingConfig(configFile, backendURL, useEnvFile);
|
|
1391
|
-
if (updated) await configFile.save();
|
|
1392
885
|
return {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
alreadyModified: false,
|
|
1396
|
-
created: false
|
|
886
|
+
success: 0 === errors.length,
|
|
887
|
+
errors
|
|
1397
888
|
};
|
|
1398
|
-
}
|
|
1399
|
-
function
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
'next.config.mjs'
|
|
1404
|
-
];
|
|
1405
|
-
for (const pattern of configPatterns){
|
|
1406
|
-
const configPath = `${projectRoot}/${pattern}`;
|
|
1407
|
-
try {
|
|
1408
|
-
const files = project.addSourceFilesAtPaths(configPath);
|
|
1409
|
-
if (files.length > 0) return files[0];
|
|
1410
|
-
} catch {}
|
|
1411
|
-
}
|
|
1412
|
-
}
|
|
1413
|
-
function hasC15tRewriteRule(configFile) {
|
|
1414
|
-
const text = configFile.getFullText();
|
|
1415
|
-
return text.includes('/api/c15t/') || text.includes("'/api/c15t/:path*'");
|
|
1416
|
-
}
|
|
1417
|
-
function generateRewriteDestination(backendURL, useEnvFile) {
|
|
1418
|
-
if (useEnvFile) return {
|
|
1419
|
-
destination: '${process.env.NEXT_PUBLIC_C15T_URL}/:path*',
|
|
1420
|
-
isTemplateLiteral: true
|
|
1421
|
-
};
|
|
1422
|
-
return {
|
|
1423
|
-
destination: `${backendURL || 'https://your-instance.c15t.dev'}/:path*`,
|
|
1424
|
-
isTemplateLiteral: false
|
|
1425
|
-
};
|
|
1426
|
-
}
|
|
1427
|
-
function createNewNextConfig(backendURL, useEnvFile) {
|
|
1428
|
-
const { destination, isTemplateLiteral } = generateRewriteDestination(backendURL, useEnvFile);
|
|
1429
|
-
const destinationValue = isTemplateLiteral ? `\`${destination}\`` : `'${destination}'`;
|
|
1430
|
-
return `import type { NextConfig } from 'next';
|
|
1431
|
-
|
|
1432
|
-
const config: NextConfig = {
|
|
1433
|
-
async rewrites() {
|
|
1434
|
-
return [
|
|
1435
|
-
{
|
|
1436
|
-
source: '/api/c15t/:path*',
|
|
1437
|
-
destination: ${destinationValue},
|
|
1438
|
-
},
|
|
1439
|
-
];
|
|
1440
|
-
},
|
|
1441
|
-
};
|
|
1442
|
-
|
|
1443
|
-
export default config;
|
|
1444
|
-
`;
|
|
1445
|
-
}
|
|
1446
|
-
function createRewriteRule(destination, isTemplateLiteral) {
|
|
1447
|
-
const destinationValue = isTemplateLiteral ? `\`${destination}\`` : `'${destination}'`;
|
|
1448
|
-
return `{
|
|
1449
|
-
source: '/api/c15t/:path*',
|
|
1450
|
-
destination: ${destinationValue},
|
|
1451
|
-
}`;
|
|
1452
|
-
}
|
|
1453
|
-
function updateExistingConfig(configFile, backendURL, useEnvFile) {
|
|
1454
|
-
const { destination, isTemplateLiteral } = generateRewriteDestination(backendURL, useEnvFile);
|
|
1455
|
-
const configObject = findConfigObject(configFile);
|
|
1456
|
-
if (!configObject) return false;
|
|
1457
|
-
const rewritesProperty = configObject.getProperty('rewrites');
|
|
1458
|
-
if (rewritesProperty && Node.isMethodDeclaration(rewritesProperty)) return updateExistingRewrites(rewritesProperty, destination, isTemplateLiteral);
|
|
1459
|
-
if (rewritesProperty && Node.isPropertyAssignment(rewritesProperty)) return updatePropertyAssignmentRewrites(rewritesProperty, destination, isTemplateLiteral);
|
|
1460
|
-
return addNewRewritesMethod(configObject, destination, isTemplateLiteral);
|
|
1461
|
-
}
|
|
1462
|
-
function findConfigObject(configFile) {
|
|
1463
|
-
return findConfigFromExportDefault(configFile) || findConfigFromVariableDeclarations(configFile);
|
|
1464
|
-
}
|
|
1465
|
-
function findConfigFromExportDefault(configFile) {
|
|
1466
|
-
const exportDefault = configFile.getDefaultExportSymbol();
|
|
1467
|
-
if (!exportDefault) return;
|
|
1468
|
-
const declarations = exportDefault.getDeclarations();
|
|
1469
|
-
for (const declaration of declarations)if (Node.isExportAssignment(declaration)) {
|
|
1470
|
-
const result = findConfigFromExpression(declaration.getExpression(), configFile);
|
|
1471
|
-
if (result) return result;
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
function findConfigFromExpression(expression, configFile) {
|
|
1475
|
-
if (Node.isCallExpression(expression)) return findConfigFromCallExpression(expression, configFile);
|
|
1476
|
-
if (Node.isObjectLiteralExpression(expression)) return expression;
|
|
1477
|
-
if (Node.isIdentifier(expression)) return findConfigFromIdentifier(expression.getText(), configFile);
|
|
1478
|
-
}
|
|
1479
|
-
function findConfigFromCallExpression(expression, configFile) {
|
|
1480
|
-
const args = expression.getArguments();
|
|
1481
|
-
if (0 === args.length) return;
|
|
1482
|
-
const firstArg = args[0];
|
|
1483
|
-
if (Node.isCallExpression(firstArg)) {
|
|
1484
|
-
const innerArgs = firstArg.getArguments();
|
|
1485
|
-
if (innerArgs.length > 0 && Node.isIdentifier(innerArgs[0])) return findConfigFromIdentifier(innerArgs[0].getText(), configFile);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
function findConfigFromIdentifier(identifierText, configFile) {
|
|
1489
|
-
const configVar = configFile.getVariableDeclaration(identifierText);
|
|
1490
|
-
const initializer = configVar?.getInitializer();
|
|
1491
|
-
return initializer && Node.isObjectLiteralExpression(initializer) ? initializer : void 0;
|
|
1492
|
-
}
|
|
1493
|
-
function findConfigFromVariableDeclarations(configFile) {
|
|
1494
|
-
const variableDeclarations = configFile.getVariableDeclarations();
|
|
1495
|
-
for (const varDecl of variableDeclarations){
|
|
1496
|
-
const typeNode = varDecl.getTypeNode();
|
|
1497
|
-
if (typeNode?.getText().includes('NextConfig')) {
|
|
1498
|
-
const initializer = varDecl.getInitializer();
|
|
1499
|
-
if (Node.isObjectLiteralExpression(initializer)) return initializer;
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
function updateExistingRewrites(rewritesMethod, destination, isTemplateLiteral) {
|
|
1504
|
-
const body = rewritesMethod.getBody();
|
|
1505
|
-
if (!Node.isBlock(body)) return false;
|
|
1506
|
-
const returnStatement = body.getStatements().find((stmt)=>Node.isReturnStatement(stmt));
|
|
1507
|
-
if (!returnStatement || !Node.isReturnStatement(returnStatement)) return false;
|
|
1508
|
-
const expression = returnStatement.getExpression();
|
|
1509
|
-
if (!expression || !Node.isArrayLiteralExpression(expression)) return false;
|
|
1510
|
-
const newRewrite = createRewriteRule(destination, isTemplateLiteral);
|
|
1511
|
-
const elements = expression.getElements();
|
|
1512
|
-
if (elements.length > 0) expression.insertElement(0, newRewrite);
|
|
1513
|
-
else expression.addElement(newRewrite);
|
|
1514
|
-
return true;
|
|
1515
|
-
}
|
|
1516
|
-
function updatePropertyAssignmentRewrites(rewritesProperty, destination, isTemplateLiteral) {
|
|
1517
|
-
const initializer = rewritesProperty.getInitializer();
|
|
1518
|
-
if (Node.isArrayLiteralExpression(initializer)) {
|
|
1519
|
-
const newRewrite = createRewriteRule(destination, isTemplateLiteral);
|
|
1520
|
-
initializer.insertElement(0, newRewrite);
|
|
889
|
+
});
|
|
890
|
+
async function preflight_fileExists(filePath) {
|
|
891
|
+
try {
|
|
892
|
+
const fs = await import("node:fs/promises");
|
|
893
|
+
await fs.access(filePath);
|
|
1521
894
|
return true;
|
|
895
|
+
} catch {
|
|
896
|
+
return false;
|
|
1522
897
|
}
|
|
1523
|
-
return false;
|
|
1524
|
-
}
|
|
1525
|
-
function addNewRewritesMethod(configObject, destination, isTemplateLiteral) {
|
|
1526
|
-
const destinationValue = isTemplateLiteral ? `\`${destination}\`` : `'${destination}'`;
|
|
1527
|
-
const rewritesMethod = `async rewrites() {
|
|
1528
|
-
return [
|
|
1529
|
-
{
|
|
1530
|
-
source: '/api/c15t/:path*',
|
|
1531
|
-
destination: ${destinationValue},
|
|
1532
|
-
},
|
|
1533
|
-
];
|
|
1534
|
-
}`;
|
|
1535
|
-
configObject.addProperty(rewritesMethod);
|
|
1536
|
-
return true;
|
|
1537
|
-
}
|
|
1538
|
-
async function handleReactLayout(options) {
|
|
1539
|
-
const { projectRoot, mode, backendURL, useEnvFile, proxyNextjs, pkg, spinner, cwd } = options;
|
|
1540
|
-
spinner.start('Updating layout file...');
|
|
1541
|
-
const layoutResult = await updateReactLayout({
|
|
1542
|
-
projectRoot,
|
|
1543
|
-
mode,
|
|
1544
|
-
backendURL,
|
|
1545
|
-
useEnvFile,
|
|
1546
|
-
proxyNextjs,
|
|
1547
|
-
pkg
|
|
1548
|
-
});
|
|
1549
|
-
const spinnerMessage = ()=>{
|
|
1550
|
-
if (layoutResult.alreadyModified) return {
|
|
1551
|
-
message: 'ConsentManager is already imported. Skipped layout file update.',
|
|
1552
|
-
type: 'info'
|
|
1553
|
-
};
|
|
1554
|
-
if (layoutResult.updated) {
|
|
1555
|
-
const typedResult = layoutResult;
|
|
1556
|
-
if (typedResult.componentFiles) {
|
|
1557
|
-
const relativeConsentManager = node_path.relative(cwd, typedResult.componentFiles.consentManager);
|
|
1558
|
-
const relativeLayout = node_path.relative(cwd, layoutResult.filePath || '');
|
|
1559
|
-
if (typedResult.componentFiles.consentManagerClient) {
|
|
1560
|
-
const relativeConsentManagerClient = node_path.relative(cwd, typedResult.componentFiles.consentManagerClient);
|
|
1561
|
-
return {
|
|
1562
|
-
message: `Layout setup complete!\n ${picocolors.green('✓')} Created: ${picocolors.cyan(relativeConsentManager)}\n ${picocolors.green('✓')} Created: ${picocolors.cyan(relativeConsentManagerClient)}\n ${picocolors.green('✓')} Updated: ${picocolors.cyan(relativeLayout)}`,
|
|
1563
|
-
type: 'info'
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1566
|
-
return {
|
|
1567
|
-
message: `Layout setup complete!\n ${picocolors.green('✓')} Created: ${picocolors.cyan(relativeConsentManager)}\n ${picocolors.green('✓')} Updated: ${picocolors.cyan(relativeLayout)}`,
|
|
1568
|
-
type: 'info'
|
|
1569
|
-
};
|
|
1570
|
-
}
|
|
1571
|
-
return {
|
|
1572
|
-
message: `Layout file updated: ${layoutResult.filePath}`,
|
|
1573
|
-
type: 'info'
|
|
1574
|
-
};
|
|
1575
|
-
}
|
|
1576
|
-
return {
|
|
1577
|
-
message: 'Layout file not updated.',
|
|
1578
|
-
type: 'error'
|
|
1579
|
-
};
|
|
1580
|
-
};
|
|
1581
|
-
const { message, type } = spinnerMessage();
|
|
1582
|
-
spinner.stop(formatLogMessage(type, message));
|
|
1583
|
-
return {
|
|
1584
|
-
layoutUpdated: layoutResult.updated,
|
|
1585
|
-
layoutPath: layoutResult.filePath
|
|
1586
|
-
};
|
|
1587
898
|
}
|
|
1588
|
-
async function
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if (configResult.alreadyModified) return {
|
|
1598
|
-
message: 'Next.js config already has c15t rewrite rule. Skipped config update.',
|
|
1599
|
-
type: 'info'
|
|
1600
|
-
};
|
|
1601
|
-
if (configResult.updated && configResult.created) return {
|
|
1602
|
-
message: `Next.js config created: ${configResult.filePath}`,
|
|
1603
|
-
type: 'info'
|
|
899
|
+
async function checkGitStatus(projectRoot) {
|
|
900
|
+
try {
|
|
901
|
+
const gitDir = external_node_path_["default"].join(projectRoot, '.git');
|
|
902
|
+
const isGitRepo = await preflight_fileExists(gitDir);
|
|
903
|
+
if (!isGitRepo) return {
|
|
904
|
+
name: 'Git',
|
|
905
|
+
status: 'warn',
|
|
906
|
+
message: 'Not a git repository',
|
|
907
|
+
hint: 'Consider initializing git to track changes'
|
|
1604
908
|
};
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
909
|
+
return {
|
|
910
|
+
name: 'Git',
|
|
911
|
+
status: 'pass',
|
|
912
|
+
message: 'Git repository detected'
|
|
1608
913
|
};
|
|
914
|
+
} catch {
|
|
1609
915
|
return {
|
|
1610
|
-
|
|
1611
|
-
|
|
916
|
+
name: 'Git',
|
|
917
|
+
status: 'warn',
|
|
918
|
+
message: 'Could not check git status'
|
|
1612
919
|
};
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
async function runPreflightChecks(context) {
|
|
923
|
+
const { projectRoot, framework, packageManager, logger } = context;
|
|
924
|
+
const checks = [];
|
|
925
|
+
logger.debug('Running preflight checks...');
|
|
926
|
+
const packageJsonPath = external_node_path_["default"].join(projectRoot, 'package.json');
|
|
927
|
+
const hasPackageJson = await preflight_fileExists(packageJsonPath);
|
|
928
|
+
checks.push({
|
|
929
|
+
name: 'package.json',
|
|
930
|
+
status: hasPackageJson ? 'pass' : 'fail',
|
|
931
|
+
message: hasPackageJson ? 'Found package.json' : 'No package.json found',
|
|
932
|
+
hint: hasPackageJson ? void 0 : 'Make sure you are in a JavaScript/TypeScript project'
|
|
933
|
+
});
|
|
934
|
+
checks.push({
|
|
935
|
+
name: 'Framework',
|
|
936
|
+
status: framework.framework ? 'pass' : 'warn',
|
|
937
|
+
message: framework.framework ? `Detected ${framework.framework}` : 'No framework detected',
|
|
938
|
+
hint: framework.framework ? void 0 : 'Will use vanilla JavaScript setup'
|
|
939
|
+
});
|
|
940
|
+
if ('c15t' !== framework.pkg) checks.push({
|
|
941
|
+
name: 'React',
|
|
942
|
+
status: framework.hasReact ? 'pass' : 'warn',
|
|
943
|
+
message: framework.hasReact ? `Found React ${framework.reactVersion || ''}` : 'React not detected',
|
|
944
|
+
hint: framework.hasReact ? void 0 : 'c15t works best with React'
|
|
945
|
+
});
|
|
946
|
+
checks.push({
|
|
947
|
+
name: 'Package Manager',
|
|
948
|
+
status: packageManager.name ? 'pass' : 'warn',
|
|
949
|
+
message: packageManager.name ? `Using ${packageManager.name}` : 'No package manager detected',
|
|
950
|
+
hint: packageManager.name ? void 0 : 'Will default to npm'
|
|
951
|
+
});
|
|
952
|
+
const gitCheck = await checkGitStatus(projectRoot);
|
|
953
|
+
checks.push(gitCheck);
|
|
954
|
+
const configPath = external_node_path_["default"].join(projectRoot, 'c15t.config.ts');
|
|
955
|
+
const hasExistingConfig = await preflight_fileExists(configPath);
|
|
956
|
+
checks.push({
|
|
957
|
+
name: 'Existing config',
|
|
958
|
+
status: hasExistingConfig ? 'warn' : 'pass',
|
|
959
|
+
message: hasExistingConfig ? 'c15t.config.ts already exists' : 'No existing configuration',
|
|
960
|
+
hint: hasExistingConfig ? 'Existing config will be overwritten' : void 0
|
|
961
|
+
});
|
|
962
|
+
const hasFailures = checks.some((c)=>'fail' === c.status);
|
|
1616
963
|
return {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
964
|
+
passed: !hasFailures,
|
|
965
|
+
checks,
|
|
966
|
+
projectRoot,
|
|
967
|
+
framework: {
|
|
968
|
+
name: framework.framework,
|
|
969
|
+
version: framework.frameworkVersion,
|
|
970
|
+
pkg: framework.pkg,
|
|
971
|
+
hasReact: framework.hasReact,
|
|
972
|
+
reactVersion: framework.reactVersion,
|
|
973
|
+
tailwindVersion: framework.tailwindVersion
|
|
974
|
+
},
|
|
975
|
+
packageManager
|
|
1620
976
|
};
|
|
1621
977
|
}
|
|
1622
|
-
|
|
1623
|
-
const {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
if (!currentEnvContent.includes(envVarName)) await promises.appendFile(envPath, envContent);
|
|
1638
|
-
} else await promises.writeFile(envPath, envContent);
|
|
1639
|
-
if (envExampleExists) {
|
|
1640
|
-
const currentExampleContent = await promises.readFile(envExamplePath, 'utf-8');
|
|
1641
|
-
if (!currentExampleContent.includes(envVarName)) await promises.appendFile(envExamplePath, envExampleContent);
|
|
1642
|
-
} else await promises.writeFile(envExamplePath, envExampleContent);
|
|
1643
|
-
spinner.stop(formatLogMessage('info', `Environment files added/updated successfully: ${picocolors.cyan(node_path.relative(cwd, envPath))} and ${picocolors.cyan(node_path.relative(cwd, envExamplePath))}`));
|
|
1644
|
-
} catch (error) {
|
|
1645
|
-
spinner.stop(formatLogMessage('error', `Error processing environment files: ${error instanceof Error ? error.message : String(error)}`));
|
|
1646
|
-
throw error;
|
|
978
|
+
const preflightActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
979
|
+
const { cliContext } = input;
|
|
980
|
+
if (!cliContext) throw new Error('CLI context is required for preflight checks');
|
|
981
|
+
return runPreflightChecks(cliContext);
|
|
982
|
+
});
|
|
983
|
+
function getStatusIcon(status) {
|
|
984
|
+
switch(status){
|
|
985
|
+
case 'pass':
|
|
986
|
+
return external_picocolors_["default"].green('✓');
|
|
987
|
+
case 'warn':
|
|
988
|
+
return external_picocolors_["default"].yellow('⚠');
|
|
989
|
+
case 'fail':
|
|
990
|
+
return external_picocolors_["default"].red('✗');
|
|
991
|
+
default:
|
|
992
|
+
return ' ';
|
|
1647
993
|
}
|
|
1648
994
|
}
|
|
1649
|
-
|
|
1650
|
-
const
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
995
|
+
function displayPreflightResults(context, checks) {
|
|
996
|
+
const { logger } = context;
|
|
997
|
+
const lines = [];
|
|
998
|
+
lines.push('');
|
|
999
|
+
lines.push(external_picocolors_["default"].bold('Pre-flight checks:'));
|
|
1000
|
+
for (const check of checks){
|
|
1001
|
+
const icon = getStatusIcon(check.status);
|
|
1002
|
+
lines.push(` ${icon} ${check.message}`);
|
|
1003
|
+
if (check.hint && 'pass' !== check.status) lines.push(` ${external_picocolors_["default"].dim(check.hint)}`);
|
|
1004
|
+
}
|
|
1005
|
+
lines.push('');
|
|
1006
|
+
logger.message(lines.join('\n'));
|
|
1007
|
+
}
|
|
1008
|
+
function displayPreflightFailure(context, checks) {
|
|
1009
|
+
const { logger } = context;
|
|
1010
|
+
const lines = [];
|
|
1011
|
+
lines.push(external_picocolors_["default"].red('Pre-flight checks failed'));
|
|
1012
|
+
lines.push('');
|
|
1013
|
+
const failures = checks.filter((c)=>'fail' === c.status);
|
|
1014
|
+
for (const check of failures){
|
|
1015
|
+
lines.push(` ${external_picocolors_["default"].red('•')} ${check.message}`);
|
|
1016
|
+
if (check.hint) lines.push(` ${check.hint}`);
|
|
1017
|
+
}
|
|
1018
|
+
lines.push('');
|
|
1019
|
+
lines.push('Please fix the issues above and try again.');
|
|
1020
|
+
logger.message(lines.join('\n'));
|
|
1021
|
+
}
|
|
1022
|
+
async function getDevToolsOption({ context, handleCancel, onCancel }) {
|
|
1023
|
+
const isReactProject = '@c15t/react' === context.framework.pkg || '@c15t/nextjs' === context.framework.pkg;
|
|
1024
|
+
context.logger.info("c15t DevTools helps you inspect consent state, scripts, and location overrides during development.");
|
|
1025
|
+
context.logger.info('Learn more: https://c15t.com/docs/dev-tools/overview');
|
|
1026
|
+
const enableDevTools = await prompts_.select({
|
|
1027
|
+
message: 'Install and enable c15t DevTools?',
|
|
1028
|
+
options: [
|
|
1029
|
+
{
|
|
1030
|
+
value: true,
|
|
1031
|
+
label: 'Yes (Recommended)',
|
|
1032
|
+
hint: isReactProject ? 'Adds <C15TDevTools /> to generated consent components' : 'Adds createDevTools() setup to c15t.config.ts'
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
value: false,
|
|
1036
|
+
label: 'No',
|
|
1037
|
+
hint: 'Skip DevTools installation and setup'
|
|
1038
|
+
}
|
|
1039
|
+
],
|
|
1040
|
+
initialValue: true
|
|
1041
|
+
});
|
|
1042
|
+
const cancelled = handleCancel?.(enableDevTools) ?? prompts_.isCancel(enableDevTools);
|
|
1043
|
+
if (cancelled) {
|
|
1044
|
+
if (onCancel) onCancel();
|
|
1045
|
+
context.error.handleCancel('Setup cancelled.', {
|
|
1046
|
+
command: 'onboarding',
|
|
1047
|
+
stage: 'dev_tools_option'
|
|
1674
1048
|
});
|
|
1675
|
-
result.nextConfigUpdated = configResult.nextConfigUpdated;
|
|
1676
|
-
result.nextConfigPath = configResult.nextConfigPath;
|
|
1677
|
-
result.nextConfigCreated = configResult.nextConfigCreated;
|
|
1678
|
-
}
|
|
1679
|
-
if ('c15t' === pkg) {
|
|
1680
|
-
spinner.start('Generating client configuration file...');
|
|
1681
|
-
result.configContent = generateClientConfigContent(mode, backendURL, useEnvFile);
|
|
1682
|
-
result.configPath = node_path.join(projectRoot, 'c15t.config.ts');
|
|
1683
|
-
spinner.stop(formatLogMessage('info', `Client configuration file generated: ${result.configContent}`));
|
|
1684
1049
|
}
|
|
1685
|
-
|
|
1686
|
-
projectRoot,
|
|
1687
|
-
backendURL,
|
|
1688
|
-
pkg,
|
|
1689
|
-
spinner,
|
|
1690
|
-
cwd: context.cwd
|
|
1691
|
-
});
|
|
1692
|
-
return result;
|
|
1050
|
+
return enableDevTools;
|
|
1693
1051
|
}
|
|
1694
|
-
async function
|
|
1695
|
-
context.logger.info(
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
};
|
|
1714
|
-
const useEnvFileSelection = await prompts_confirm({
|
|
1715
|
-
message: 'Store the backendURL in a .env file? (Recommended, URL is public)',
|
|
1052
|
+
async function getSSROption({ context, handleCancel, onCancel }) {
|
|
1053
|
+
context.logger.info('SSR consent prefetch starts data loading on the server for faster banner visibility.');
|
|
1054
|
+
context.logger.info('Tradeoff: this uses Next.js headers() and makes the route dynamic (not fully static).');
|
|
1055
|
+
context.logger.info('On slow backends or cross-region setups, SSR can increase TTFB. Measure both TTFB and banner visibility.');
|
|
1056
|
+
context.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/ssr');
|
|
1057
|
+
const enableSSR = await prompts_.select({
|
|
1058
|
+
message: 'Enable SSR consent prefetch? (faster first banner visibility, dynamic route)',
|
|
1059
|
+
options: [
|
|
1060
|
+
{
|
|
1061
|
+
value: true,
|
|
1062
|
+
label: 'Yes (Recommended)',
|
|
1063
|
+
hint: 'Fetch consent data on server and stream to client'
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
value: false,
|
|
1067
|
+
label: 'No',
|
|
1068
|
+
hint: 'Client-only fetch after hydration (better for fully static pages)'
|
|
1069
|
+
}
|
|
1070
|
+
],
|
|
1716
1071
|
initialValue: true
|
|
1717
1072
|
});
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
useEnvFile = useEnvFileSelection;
|
|
1723
|
-
if ('@c15t/nextjs' === context.framework.pkg) {
|
|
1724
|
-
context.logger.info('Learn more about Next.js Rewrites: https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites');
|
|
1725
|
-
const proxyNextjsSelection = await prompts_confirm({
|
|
1726
|
-
message: 'Proxy requests to your instance with Next.js Rewrites? (Recommended)',
|
|
1727
|
-
initialValue: true
|
|
1728
|
-
});
|
|
1729
|
-
if (handleCancel?.(proxyNextjsSelection)) context.error.handleCancel('Setup cancelled.', {
|
|
1073
|
+
const cancelled = handleCancel?.(enableSSR) ?? prompts_.isCancel(enableSSR);
|
|
1074
|
+
if (cancelled) {
|
|
1075
|
+
if (onCancel) onCancel();
|
|
1076
|
+
context.error.handleCancel('Setup cancelled.', {
|
|
1730
1077
|
command: 'onboarding',
|
|
1731
|
-
stage: '
|
|
1078
|
+
stage: 'ssr_option'
|
|
1732
1079
|
});
|
|
1733
|
-
proxyNextjs = proxyNextjsSelection;
|
|
1734
1080
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1081
|
+
return enableSSR;
|
|
1082
|
+
}
|
|
1083
|
+
function isCancel(value) {
|
|
1084
|
+
return prompts_.isCancel(value);
|
|
1085
|
+
}
|
|
1086
|
+
class PromptCancelledError extends Error {
|
|
1087
|
+
constructor(stage){
|
|
1088
|
+
super(`Prompt cancelled at stage: ${stage}`);
|
|
1089
|
+
this.name = 'PromptCancelledError';
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
const modeSelectionActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1093
|
+
const result = await prompts_.select({
|
|
1094
|
+
message: 'How would you like to store consent decisions?',
|
|
1095
|
+
initialValue: input.initialMode ?? 'c15t',
|
|
1096
|
+
options: [
|
|
1097
|
+
{
|
|
1098
|
+
value: 'c15t',
|
|
1099
|
+
label: 'Hosted c15t (consent.io)',
|
|
1100
|
+
hint: 'Recommended: Fully managed service'
|
|
1101
|
+
},
|
|
1102
|
+
{
|
|
1103
|
+
value: 'offline',
|
|
1104
|
+
label: 'Offline Mode',
|
|
1105
|
+
hint: 'Store in browser, no backend needed'
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
value: 'self-hosted',
|
|
1109
|
+
label: 'Self-Hosted',
|
|
1110
|
+
hint: 'Run your own c15t backend'
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
value: 'custom',
|
|
1114
|
+
label: 'Custom Implementation',
|
|
1115
|
+
hint: 'Full control over storage logic'
|
|
1116
|
+
}
|
|
1117
|
+
]
|
|
1738
1118
|
});
|
|
1739
|
-
|
|
1740
|
-
context.framework.pkg
|
|
1741
|
-
];
|
|
1742
|
-
if (addScriptsSelection) dependenciesToAdd.push("@c15t/scripts");
|
|
1119
|
+
if (isCancel(result)) throw new PromptCancelledError('mode_selection');
|
|
1743
1120
|
return {
|
|
1744
|
-
|
|
1745
|
-
useEnvFile,
|
|
1746
|
-
dependenciesToAdd
|
|
1121
|
+
mode: result
|
|
1747
1122
|
};
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
const {
|
|
1751
|
-
const needsAccount = await
|
|
1123
|
+
});
|
|
1124
|
+
const accountCreationActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1125
|
+
const { cliContext } = input;
|
|
1126
|
+
const needsAccount = await prompts_.confirm({
|
|
1752
1127
|
message: 'Do you need to create a consent.io account?',
|
|
1753
1128
|
initialValue: true
|
|
1754
1129
|
});
|
|
1755
|
-
if (
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
note(`We'll open your browser to create a consent.io account and set up your instance.\nFollow these steps:\n1. Sign up for a consent.io account\n2. Create a new instance in the dashboard\n3. Configure your trusted origins (domains that can connect)\n4. Copy the provided backendURL (e.g., https://your-instance.c15t.dev)`, 'consent.io Setup');
|
|
1761
|
-
const shouldOpen = await
|
|
1130
|
+
if (isCancel(needsAccount)) throw new PromptCancelledError('account_creation');
|
|
1131
|
+
if (!needsAccount) return {
|
|
1132
|
+
needsAccount: false,
|
|
1133
|
+
browserOpened: false
|
|
1134
|
+
};
|
|
1135
|
+
prompts_.note(`We'll open your browser to create a consent.io account and set up your instance.\nFollow these steps:\n1. Sign up for a consent.io account\n2. Create a new instance in the dashboard\n3. Configure your trusted origins (domains that can connect)\n4. Copy the provided backendURL (e.g., https://your-instance.c15t.dev)`, 'consent.io Setup');
|
|
1136
|
+
const shouldOpen = await prompts_.confirm({
|
|
1762
1137
|
message: 'Open browser to sign up for consent.io?',
|
|
1763
1138
|
initialValue: true
|
|
1764
1139
|
});
|
|
1765
|
-
if (
|
|
1766
|
-
|
|
1767
|
-
stage: 'c15t_browser_open'
|
|
1768
|
-
});
|
|
1140
|
+
if (isCancel(shouldOpen)) throw new PromptCancelledError('browser_open');
|
|
1141
|
+
let browserOpened = false;
|
|
1769
1142
|
if (shouldOpen) try {
|
|
1770
|
-
await
|
|
1771
|
-
|
|
1143
|
+
const open = (await import("open")).default;
|
|
1144
|
+
await open('https://consent.io/dashboard/register?ref=cli');
|
|
1145
|
+
browserOpened = true;
|
|
1146
|
+
const enterPressed = await prompts_.text({
|
|
1772
1147
|
message: 'Press Enter once you have created your instance and have the backendURL'
|
|
1773
1148
|
});
|
|
1774
|
-
if (
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
} catch {
|
|
1779
|
-
logger.warn('Failed to open browser automatically. Please visit https://consent.io/dashboard/register manually.');
|
|
1149
|
+
if (isCancel(enterPressed)) throw new PromptCancelledError('url_input_wait');
|
|
1150
|
+
} catch (error) {
|
|
1151
|
+
if (error instanceof PromptCancelledError) throw error;
|
|
1152
|
+
cliContext.logger.warn('Failed to open browser automatically. Please visit https://consent.io/dashboard/register manually.');
|
|
1780
1153
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1154
|
+
return {
|
|
1155
|
+
needsAccount: true,
|
|
1156
|
+
browserOpened
|
|
1157
|
+
};
|
|
1158
|
+
});
|
|
1159
|
+
const backendURLActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1160
|
+
const { initialURL, isC15tMode } = input;
|
|
1161
|
+
const placeholder = isC15tMode ? 'https://your-instance.c15t.dev' : 'https://your-backend.example.com/api/c15t';
|
|
1162
|
+
const result = await prompts_.text({
|
|
1163
|
+
message: isC15tMode ? 'Enter your consent.io instance URL:' : 'Enter your self-hosted backend URL:',
|
|
1164
|
+
placeholder,
|
|
1165
|
+
initialValue: initialURL,
|
|
1787
1166
|
validate: (value)=>{
|
|
1788
1167
|
if (!value || '' === value) return 'URL is required';
|
|
1789
1168
|
try {
|
|
1790
1169
|
const url = new URL(value);
|
|
1791
|
-
if (!url.hostname.endsWith('.c15t.dev')) return 'Please enter a valid *.c15t.dev URL';
|
|
1170
|
+
if (isC15tMode && !url.hostname.endsWith('.c15t.dev')) return 'Please enter a valid *.c15t.dev URL';
|
|
1792
1171
|
} catch {
|
|
1793
1172
|
return 'Please enter a valid URL';
|
|
1794
1173
|
}
|
|
1795
1174
|
}
|
|
1796
1175
|
});
|
|
1797
|
-
if (
|
|
1798
|
-
|
|
1799
|
-
|
|
1176
|
+
if (isCancel(result)) throw new PromptCancelledError('backend_url');
|
|
1177
|
+
return {
|
|
1178
|
+
url: result
|
|
1179
|
+
};
|
|
1180
|
+
});
|
|
1181
|
+
const backendOptionsActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1182
|
+
const { cliContext } = input;
|
|
1183
|
+
const useEnvFile = await prompts_.confirm({
|
|
1184
|
+
message: 'Store the backendURL in a .env file? (Recommended, URL is public)',
|
|
1185
|
+
initialValue: true
|
|
1800
1186
|
});
|
|
1801
|
-
if (
|
|
1802
|
-
|
|
1803
|
-
|
|
1187
|
+
if (isCancel(useEnvFile)) throw new PromptCancelledError('env_file');
|
|
1188
|
+
let proxyNextjs = false;
|
|
1189
|
+
if ('@c15t/nextjs' === cliContext.framework.pkg) {
|
|
1190
|
+
cliContext.logger.info('Learn more about Next.js Rewrites: https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites');
|
|
1191
|
+
const proxyResult = await prompts_.confirm({
|
|
1192
|
+
message: 'Proxy requests to your instance with Next.js Rewrites? (Recommended)',
|
|
1193
|
+
initialValue: true
|
|
1194
|
+
});
|
|
1195
|
+
if (isCancel(proxyResult)) throw new PromptCancelledError('proxy_nextjs');
|
|
1196
|
+
proxyNextjs = proxyResult;
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
useEnvFile: useEnvFile,
|
|
1200
|
+
proxyNextjs
|
|
1201
|
+
};
|
|
1202
|
+
});
|
|
1203
|
+
const frontendOptionsActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1204
|
+
const { cliContext, hasBackend } = input;
|
|
1205
|
+
const pkg = cliContext.framework.pkg;
|
|
1206
|
+
let enableSSR;
|
|
1207
|
+
let enableDevTools = false;
|
|
1208
|
+
let uiStyle = 'prebuilt';
|
|
1209
|
+
let expandedTheme;
|
|
1210
|
+
if ('@c15t/nextjs' === pkg) {
|
|
1211
|
+
if (hasBackend) {
|
|
1212
|
+
const { existsSync } = await import("node:fs");
|
|
1213
|
+
const { join } = await import("node:path");
|
|
1214
|
+
const projectRoot = cliContext.projectRoot;
|
|
1215
|
+
const isAppRouter = [
|
|
1216
|
+
'app/layout.tsx',
|
|
1217
|
+
'src/app/layout.tsx',
|
|
1218
|
+
'app/layout.ts',
|
|
1219
|
+
'src/app/layout.ts'
|
|
1220
|
+
].some((p)=>existsSync(join(projectRoot, p)));
|
|
1221
|
+
if (isAppRouter) enableSSR = await getSSROption({
|
|
1222
|
+
context: cliContext,
|
|
1223
|
+
onCancel: ()=>{
|
|
1224
|
+
throw new PromptCancelledError('ssr_option');
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
cliContext.logger.info('Choose how you want your consent UI components generated.');
|
|
1229
|
+
cliContext.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/customization');
|
|
1230
|
+
const styleResult = await prompts_.select({
|
|
1231
|
+
message: 'UI component style:',
|
|
1232
|
+
options: [
|
|
1233
|
+
{
|
|
1234
|
+
value: 'prebuilt',
|
|
1235
|
+
label: 'Prebuilt (Recommended)',
|
|
1236
|
+
hint: 'Ready-to-use components'
|
|
1237
|
+
},
|
|
1238
|
+
{
|
|
1239
|
+
value: 'expanded',
|
|
1240
|
+
label: 'Compound components',
|
|
1241
|
+
hint: 'Full customization control'
|
|
1242
|
+
}
|
|
1243
|
+
],
|
|
1244
|
+
initialValue: 'prebuilt'
|
|
1245
|
+
});
|
|
1246
|
+
if (isCancel(styleResult)) throw new PromptCancelledError('ui_style');
|
|
1247
|
+
uiStyle = styleResult;
|
|
1248
|
+
const themeResult = await prompts_.select({
|
|
1249
|
+
message: 'Theme preset:',
|
|
1250
|
+
options: [
|
|
1251
|
+
{
|
|
1252
|
+
value: 'none',
|
|
1253
|
+
label: 'None',
|
|
1254
|
+
hint: 'No preset styling'
|
|
1255
|
+
},
|
|
1256
|
+
{
|
|
1257
|
+
value: 'minimal',
|
|
1258
|
+
label: 'Minimal',
|
|
1259
|
+
hint: 'Clean light theme'
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
value: 'dark',
|
|
1263
|
+
label: 'Dark',
|
|
1264
|
+
hint: 'High contrast dark mode'
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
value: 'tailwind',
|
|
1268
|
+
label: 'Tailwind',
|
|
1269
|
+
hint: 'Uses Tailwind utility classes'
|
|
1270
|
+
}
|
|
1271
|
+
],
|
|
1272
|
+
initialValue: 'none'
|
|
1273
|
+
});
|
|
1274
|
+
if (isCancel(themeResult)) throw new PromptCancelledError('expanded_theme');
|
|
1275
|
+
expandedTheme = themeResult;
|
|
1276
|
+
}
|
|
1277
|
+
if ('@c15t/react' === pkg) {
|
|
1278
|
+
cliContext.logger.info('Choose how you want your consent UI components generated.');
|
|
1279
|
+
const styleResult = await prompts_.select({
|
|
1280
|
+
message: 'UI component style:',
|
|
1281
|
+
options: [
|
|
1282
|
+
{
|
|
1283
|
+
value: 'prebuilt',
|
|
1284
|
+
label: 'Prebuilt (Recommended)',
|
|
1285
|
+
hint: 'Ready-to-use components'
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
value: 'expanded',
|
|
1289
|
+
label: 'Compound components',
|
|
1290
|
+
hint: 'Full customization control'
|
|
1291
|
+
}
|
|
1292
|
+
],
|
|
1293
|
+
initialValue: 'prebuilt'
|
|
1294
|
+
});
|
|
1295
|
+
if (isCancel(styleResult)) throw new PromptCancelledError('ui_style');
|
|
1296
|
+
uiStyle = styleResult;
|
|
1297
|
+
const reactThemeResult = await prompts_.select({
|
|
1298
|
+
message: 'Theme preset:',
|
|
1299
|
+
options: [
|
|
1300
|
+
{
|
|
1301
|
+
value: 'none',
|
|
1302
|
+
label: 'None',
|
|
1303
|
+
hint: 'No preset styling'
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
value: 'minimal',
|
|
1307
|
+
label: 'Minimal',
|
|
1308
|
+
hint: 'Clean light theme'
|
|
1309
|
+
},
|
|
1310
|
+
{
|
|
1311
|
+
value: 'dark',
|
|
1312
|
+
label: 'Dark',
|
|
1313
|
+
hint: 'High contrast dark mode'
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
value: 'tailwind',
|
|
1317
|
+
label: 'Tailwind',
|
|
1318
|
+
hint: 'Uses Tailwind utility classes'
|
|
1319
|
+
}
|
|
1320
|
+
],
|
|
1321
|
+
initialValue: 'none'
|
|
1322
|
+
});
|
|
1323
|
+
if (isCancel(reactThemeResult)) throw new PromptCancelledError('expanded_theme');
|
|
1324
|
+
expandedTheme = reactThemeResult;
|
|
1325
|
+
}
|
|
1326
|
+
if ('c15t' === pkg || '@c15t/react' === pkg || '@c15t/nextjs' === pkg) enableDevTools = await getDevToolsOption({
|
|
1327
|
+
context: cliContext,
|
|
1328
|
+
onCancel: ()=>{
|
|
1329
|
+
throw new PromptCancelledError('dev_tools_option');
|
|
1330
|
+
}
|
|
1804
1331
|
});
|
|
1805
|
-
return
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1332
|
+
return {
|
|
1333
|
+
enableSSR,
|
|
1334
|
+
enableDevTools,
|
|
1335
|
+
uiStyle,
|
|
1336
|
+
expandedTheme
|
|
1337
|
+
};
|
|
1338
|
+
});
|
|
1339
|
+
const AVAILABLE_SCRIPTS = [
|
|
1340
|
+
{
|
|
1341
|
+
value: 'google-tag-manager',
|
|
1342
|
+
label: 'Google Tag Manager',
|
|
1343
|
+
hint: "GTM container script"
|
|
1344
|
+
},
|
|
1345
|
+
{
|
|
1346
|
+
value: 'google-tag',
|
|
1347
|
+
label: 'Google Tag (gtag.js)',
|
|
1348
|
+
hint: 'Google Analytics 4'
|
|
1349
|
+
},
|
|
1350
|
+
{
|
|
1351
|
+
value: 'meta-pixel',
|
|
1352
|
+
label: 'Meta Pixel',
|
|
1353
|
+
hint: 'Facebook/Instagram tracking'
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
value: 'posthog',
|
|
1357
|
+
label: 'PostHog',
|
|
1358
|
+
hint: 'Product analytics'
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
value: 'linkedin-insights',
|
|
1362
|
+
label: 'LinkedIn Insight Tag',
|
|
1363
|
+
hint: 'LinkedIn conversion tracking'
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
value: 'tiktok-pixel',
|
|
1367
|
+
label: 'TikTok Pixel',
|
|
1368
|
+
hint: 'TikTok ads tracking'
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
value: 'x-pixel',
|
|
1372
|
+
label: 'X (Twitter) Pixel',
|
|
1373
|
+
hint: 'X/Twitter conversion tracking'
|
|
1374
|
+
},
|
|
1375
|
+
{
|
|
1376
|
+
value: 'microsoft-uet',
|
|
1377
|
+
label: 'Microsoft UET',
|
|
1378
|
+
hint: 'Bing Ads tracking'
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
value: 'databuddy',
|
|
1382
|
+
label: 'Databuddy',
|
|
1383
|
+
hint: 'Data collection'
|
|
1384
|
+
}
|
|
1385
|
+
];
|
|
1386
|
+
const scriptsOptionActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1387
|
+
const { cliContext } = input;
|
|
1388
|
+
cliContext.logger.info("The @c15t/scripts package provides pre-configured third-party scripts with consent management.");
|
|
1389
|
+
const addScripts = await prompts_.confirm({
|
|
1390
|
+
message: "Add @c15t/scripts for third-party script management?",
|
|
1391
|
+
initialValue: true
|
|
1814
1392
|
});
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1393
|
+
if (isCancel(addScripts)) throw new PromptCancelledError("scripts_option");
|
|
1394
|
+
if (!addScripts) return {
|
|
1395
|
+
addScripts: false,
|
|
1396
|
+
selectedScripts: []
|
|
1397
|
+
};
|
|
1398
|
+
const selected = await prompts_.multiselect({
|
|
1399
|
+
message: "Which scripts do you want to add?",
|
|
1400
|
+
options: AVAILABLE_SCRIPTS.map((s)=>({
|
|
1401
|
+
value: s.value,
|
|
1402
|
+
label: s.label,
|
|
1403
|
+
hint: s.hint
|
|
1404
|
+
})),
|
|
1405
|
+
required: false
|
|
1406
|
+
});
|
|
1407
|
+
if (isCancel(selected)) throw new PromptCancelledError("scripts_selection");
|
|
1408
|
+
return {
|
|
1409
|
+
addScripts: true,
|
|
1410
|
+
selectedScripts: selected
|
|
1411
|
+
};
|
|
1412
|
+
});
|
|
1413
|
+
const installConfirmActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1414
|
+
const { dependencies, packageManager } = input;
|
|
1415
|
+
const depList = dependencies.join(', ');
|
|
1416
|
+
const result = await prompts_.confirm({
|
|
1417
|
+
message: `Install dependencies (${depList}) with ${packageManager}?`,
|
|
1418
|
+
initialValue: true
|
|
1822
1419
|
});
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1420
|
+
if (isCancel(result)) throw new PromptCancelledError('install_confirm');
|
|
1421
|
+
return {
|
|
1422
|
+
confirmed: result
|
|
1423
|
+
};
|
|
1424
|
+
});
|
|
1425
|
+
const githubStarActor = (0, __rspack_external_xstate.fromPromise)(async ({ input })=>{
|
|
1426
|
+
const { cliContext } = input;
|
|
1427
|
+
const result = await prompts_.confirm({
|
|
1428
|
+
message: 'Would you like to star c15t on GitHub now?',
|
|
1429
|
+
initialValue: true
|
|
1827
1430
|
});
|
|
1431
|
+
if (isCancel(result)) return {
|
|
1432
|
+
opened: false
|
|
1433
|
+
};
|
|
1434
|
+
if (result) try {
|
|
1435
|
+
const open = (await import("open")).default;
|
|
1436
|
+
await open('https://github.com/c15t/c15t');
|
|
1437
|
+
cliContext.logger.success('GitHub repository opened. Thank you for your support!');
|
|
1438
|
+
return {
|
|
1439
|
+
opened: true
|
|
1440
|
+
};
|
|
1441
|
+
} catch {
|
|
1442
|
+
cliContext.logger.info('You can star us later by visiting: https://github.com/c15t/c15t');
|
|
1443
|
+
}
|
|
1828
1444
|
return {
|
|
1829
|
-
|
|
1830
|
-
usingEnvFile: useEnvFile ?? false,
|
|
1831
|
-
proxyNextjs,
|
|
1832
|
-
installDepsConfirmed,
|
|
1833
|
-
ranInstall
|
|
1445
|
+
opened: false
|
|
1834
1446
|
};
|
|
1447
|
+
});
|
|
1448
|
+
function preflightPassed({ context }) {
|
|
1449
|
+
return context.preflightPassed;
|
|
1835
1450
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1451
|
+
function preflightFailed({ context }) {
|
|
1452
|
+
return !context.preflightPassed;
|
|
1453
|
+
}
|
|
1454
|
+
function hasModeArg({ context }) {
|
|
1455
|
+
return null !== context.modeArg;
|
|
1456
|
+
}
|
|
1457
|
+
function guards_isC15tMode({ context }) {
|
|
1458
|
+
return 'c15t' === context.selectedMode;
|
|
1459
|
+
}
|
|
1460
|
+
function isOfflineMode({ context }) {
|
|
1461
|
+
return 'offline' === context.selectedMode;
|
|
1462
|
+
}
|
|
1463
|
+
function isSelfHostedMode({ context }) {
|
|
1464
|
+
return 'self-hosted' === context.selectedMode;
|
|
1465
|
+
}
|
|
1466
|
+
function isCustomMode({ context }) {
|
|
1467
|
+
return 'custom' === context.selectedMode;
|
|
1468
|
+
}
|
|
1469
|
+
function modeRequiresBackend({ context }) {
|
|
1470
|
+
return 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode;
|
|
1471
|
+
}
|
|
1472
|
+
function modeNoBackend({ context }) {
|
|
1473
|
+
return 'offline' === context.selectedMode || 'custom' === context.selectedMode;
|
|
1474
|
+
}
|
|
1475
|
+
function isNextjs({ context }) {
|
|
1476
|
+
return context.framework?.pkg === '@c15t/nextjs';
|
|
1477
|
+
}
|
|
1478
|
+
function isReact({ context }) {
|
|
1479
|
+
return context.framework?.pkg === '@c15t/react';
|
|
1480
|
+
}
|
|
1481
|
+
function isCore({ context }) {
|
|
1482
|
+
return context.framework?.pkg === 'c15t';
|
|
1483
|
+
}
|
|
1484
|
+
function guards_hasReact({ context }) {
|
|
1485
|
+
return context.framework?.hasReact ?? false;
|
|
1486
|
+
}
|
|
1487
|
+
function hasTailwind({ context }) {
|
|
1488
|
+
return context.framework?.tailwindVersion !== null;
|
|
1489
|
+
}
|
|
1490
|
+
function hasBackendURL({ context }) {
|
|
1491
|
+
return null !== context.backendURL && '' !== context.backendURL;
|
|
1492
|
+
}
|
|
1493
|
+
function isExpandedUIStyle({ context }) {
|
|
1494
|
+
return 'expanded' === context.uiStyle;
|
|
1495
|
+
}
|
|
1496
|
+
function installConfirmed({ context }) {
|
|
1497
|
+
return context.installConfirmed;
|
|
1498
|
+
}
|
|
1499
|
+
function installSucceeded({ context }) {
|
|
1500
|
+
return context.installSucceeded;
|
|
1501
|
+
}
|
|
1502
|
+
function hasFilesToRollback({ context }) {
|
|
1503
|
+
return context.filesCreated.length > 0 || context.filesModified.length > 0;
|
|
1504
|
+
}
|
|
1505
|
+
function hasDependencies({ context }) {
|
|
1506
|
+
return context.dependenciesToAdd.length > 0;
|
|
1507
|
+
}
|
|
1508
|
+
function hasErrors({ context }) {
|
|
1509
|
+
return context.errors.length > 0;
|
|
1510
|
+
}
|
|
1511
|
+
function needsCleanup({ context }) {
|
|
1512
|
+
return !context.cleanupDone && (context.filesCreated.length > 0 || context.filesModified.length > 0);
|
|
1513
|
+
}
|
|
1514
|
+
function shouldPromptSSR({ context }) {
|
|
1515
|
+
return context.framework?.pkg === '@c15t/nextjs' && ('c15t' === context.selectedMode || 'self-hosted' === context.selectedMode);
|
|
1516
|
+
}
|
|
1517
|
+
function shouldPromptUIStyle({ context }) {
|
|
1518
|
+
return context.framework?.pkg === '@c15t/nextjs' || context.framework?.pkg === '@c15t/react';
|
|
1519
|
+
}
|
|
1520
|
+
const guards = {
|
|
1521
|
+
preflightPassed,
|
|
1522
|
+
preflightFailed,
|
|
1523
|
+
hasModeArg,
|
|
1524
|
+
isC15tMode: guards_isC15tMode,
|
|
1525
|
+
isOfflineMode,
|
|
1526
|
+
isSelfHostedMode,
|
|
1527
|
+
isCustomMode,
|
|
1528
|
+
modeRequiresBackend,
|
|
1529
|
+
modeNoBackend,
|
|
1530
|
+
isNextjs,
|
|
1531
|
+
isReact,
|
|
1532
|
+
isCore,
|
|
1533
|
+
hasReact: guards_hasReact,
|
|
1534
|
+
hasTailwind,
|
|
1535
|
+
hasBackendURL,
|
|
1536
|
+
isExpandedUIStyle,
|
|
1537
|
+
installConfirmed,
|
|
1538
|
+
installSucceeded,
|
|
1539
|
+
hasFilesToRollback,
|
|
1540
|
+
hasDependencies,
|
|
1541
|
+
hasErrors,
|
|
1542
|
+
needsCleanup,
|
|
1543
|
+
shouldPromptSSR,
|
|
1544
|
+
shouldPromptUIStyle
|
|
1545
|
+
};
|
|
1546
|
+
function createInitialContext(cliContext, modeArg) {
|
|
1547
|
+
return {
|
|
1548
|
+
cliContext: cliContext ?? null,
|
|
1549
|
+
projectRoot: cliContext?.projectRoot ?? '',
|
|
1550
|
+
framework: null,
|
|
1551
|
+
packageManager: null,
|
|
1552
|
+
preflightPassed: false,
|
|
1553
|
+
preflightChecks: [],
|
|
1554
|
+
selectedMode: null,
|
|
1555
|
+
modeArg: modeArg ?? null,
|
|
1556
|
+
backendURL: null,
|
|
1557
|
+
useEnvFile: true,
|
|
1558
|
+
proxyNextjs: true,
|
|
1559
|
+
enableSSR: false,
|
|
1560
|
+
enableDevTools: false,
|
|
1561
|
+
uiStyle: 'prebuilt',
|
|
1562
|
+
expandedTheme: null,
|
|
1563
|
+
addScripts: false,
|
|
1564
|
+
selectedScripts: [],
|
|
1565
|
+
filesCreated: [],
|
|
1566
|
+
filesModified: [],
|
|
1567
|
+
dependenciesToAdd: [],
|
|
1568
|
+
installConfirmed: false,
|
|
1569
|
+
installAttempted: false,
|
|
1570
|
+
installSucceeded: false,
|
|
1571
|
+
runMigrations: false,
|
|
1572
|
+
cancelReason: null,
|
|
1573
|
+
cleanupDone: false,
|
|
1574
|
+
errors: [],
|
|
1575
|
+
stateHistory: []
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
const generateMachine = (0, __rspack_external_xstate.setup)({
|
|
1579
|
+
types: {
|
|
1580
|
+
context: {},
|
|
1581
|
+
events: {},
|
|
1582
|
+
input: {}
|
|
1583
|
+
},
|
|
1584
|
+
guards: guards,
|
|
1585
|
+
actors: {
|
|
1586
|
+
preflight: preflightActor,
|
|
1587
|
+
modeSelection: modeSelectionActor,
|
|
1588
|
+
accountCreation: accountCreationActor,
|
|
1589
|
+
backendURL: backendURLActor,
|
|
1590
|
+
backendOptions: backendOptionsActor,
|
|
1591
|
+
frontendOptions: frontendOptionsActor,
|
|
1592
|
+
scriptsOption: scriptsOptionActor,
|
|
1593
|
+
fileGeneration: fileGenerationActor,
|
|
1594
|
+
checkDependencies: checkDependenciesActor,
|
|
1595
|
+
installConfirm: installConfirmActor,
|
|
1596
|
+
dependencyInstall: dependencyInstallActor,
|
|
1597
|
+
rollback: rollbackActor,
|
|
1598
|
+
githubStar: githubStarActor
|
|
1599
|
+
}
|
|
1600
|
+
}).createMachine({
|
|
1601
|
+
id: 'generate',
|
|
1602
|
+
initial: 'idle',
|
|
1603
|
+
context: ({ input })=>createInitialContext(input.cliContext, input.modeArg),
|
|
1604
|
+
on: {
|
|
1605
|
+
CANCEL: {
|
|
1606
|
+
target: '.cancelling',
|
|
1607
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1608
|
+
cancelReason: ({ event })=>event.reason ?? 'User cancelled'
|
|
1609
|
+
})
|
|
1610
|
+
}
|
|
1611
|
+
},
|
|
1612
|
+
states: {
|
|
1613
|
+
idle: {
|
|
1614
|
+
on: {
|
|
1615
|
+
START: 'preflight'
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
preflight: {
|
|
1619
|
+
invoke: {
|
|
1620
|
+
src: 'preflight',
|
|
1621
|
+
input: ({ context })=>({
|
|
1622
|
+
cliContext: context.cliContext
|
|
1623
|
+
}),
|
|
1624
|
+
onDone: [
|
|
1625
|
+
{
|
|
1626
|
+
guard: ({ event })=>event.output.passed,
|
|
1627
|
+
target: 'modeSelection',
|
|
1628
|
+
actions: [
|
|
1629
|
+
(0, __rspack_external_xstate.assign)({
|
|
1630
|
+
preflightPassed: ({ event })=>event.output.passed,
|
|
1631
|
+
preflightChecks: ({ event })=>event.output.checks,
|
|
1632
|
+
projectRoot: ({ event })=>event.output.projectRoot,
|
|
1633
|
+
framework: ({ event })=>event.output.framework,
|
|
1634
|
+
packageManager: ({ event })=>event.output.packageManager
|
|
1635
|
+
}),
|
|
1636
|
+
({ context, event })=>{
|
|
1637
|
+
if (context.cliContext) displayPreflightResults(context.cliContext, event.output.checks);
|
|
1638
|
+
}
|
|
1639
|
+
]
|
|
1640
|
+
},
|
|
1641
|
+
{
|
|
1642
|
+
target: 'preflightError',
|
|
1643
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1644
|
+
preflightPassed: false,
|
|
1645
|
+
preflightChecks: ({ event })=>event.output.checks
|
|
1646
|
+
})
|
|
1647
|
+
}
|
|
1648
|
+
],
|
|
1649
|
+
onError: {
|
|
1650
|
+
target: 'error',
|
|
1651
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1652
|
+
errors: ({ context, event })=>[
|
|
1653
|
+
...context.errors,
|
|
1654
|
+
{
|
|
1655
|
+
state: 'preflight',
|
|
1656
|
+
error: event.error,
|
|
1657
|
+
timestamp: Date.now()
|
|
1658
|
+
}
|
|
1659
|
+
]
|
|
1660
|
+
})
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
},
|
|
1664
|
+
preflightError: {
|
|
1665
|
+
entry: ({ context })=>{
|
|
1666
|
+
if (context.cliContext) displayPreflightFailure(context.cliContext, context.preflightChecks);
|
|
1667
|
+
},
|
|
1668
|
+
on: {
|
|
1669
|
+
RETRY: {
|
|
1670
|
+
target: 'preflight',
|
|
1671
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1672
|
+
preflightPassed: false,
|
|
1673
|
+
preflightChecks: [],
|
|
1674
|
+
errors: []
|
|
1675
|
+
})
|
|
1676
|
+
}
|
|
1677
|
+
},
|
|
1678
|
+
after: {
|
|
1679
|
+
100: 'exited'
|
|
1680
|
+
}
|
|
1681
|
+
},
|
|
1682
|
+
modeSelection: {
|
|
1683
|
+
always: [
|
|
1684
|
+
{
|
|
1685
|
+
guard: 'hasModeArg',
|
|
1686
|
+
target: 'routeToMode',
|
|
1687
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1688
|
+
selectedMode: ({ context })=>context.modeArg
|
|
1689
|
+
})
|
|
1690
|
+
}
|
|
1691
|
+
],
|
|
1692
|
+
invoke: {
|
|
1693
|
+
src: 'modeSelection',
|
|
1694
|
+
input: ()=>({}),
|
|
1695
|
+
onDone: {
|
|
1696
|
+
target: 'routeToMode',
|
|
1697
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1698
|
+
selectedMode: ({ event })=>event.output.mode
|
|
1699
|
+
})
|
|
1700
|
+
},
|
|
1701
|
+
onError: {
|
|
1702
|
+
target: 'cancelling',
|
|
1703
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1704
|
+
cancelReason: 'Mode selection cancelled'
|
|
1705
|
+
})
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1709
|
+
routeToMode: {
|
|
1710
|
+
always: [
|
|
1711
|
+
{
|
|
1712
|
+
guard: 'isC15tMode',
|
|
1713
|
+
target: 'c15tMode'
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
guard: 'isOfflineMode',
|
|
1717
|
+
target: 'offlineMode'
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
guard: 'isSelfHostedMode',
|
|
1721
|
+
target: 'selfHostedMode'
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
guard: 'isCustomMode',
|
|
1725
|
+
target: 'customMode'
|
|
1726
|
+
},
|
|
1727
|
+
{
|
|
1728
|
+
target: 'customMode'
|
|
1729
|
+
}
|
|
1730
|
+
]
|
|
1731
|
+
},
|
|
1732
|
+
c15tMode: {
|
|
1733
|
+
initial: 'accountCreation',
|
|
1734
|
+
states: {
|
|
1735
|
+
accountCreation: {
|
|
1736
|
+
invoke: {
|
|
1737
|
+
src: 'accountCreation',
|
|
1738
|
+
input: ({ context })=>({
|
|
1739
|
+
cliContext: context.cliContext
|
|
1740
|
+
}),
|
|
1741
|
+
onDone: 'backendURL',
|
|
1742
|
+
onError: {
|
|
1743
|
+
target: '#generate.cancelling',
|
|
1744
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1745
|
+
cancelReason: 'Account creation cancelled'
|
|
1746
|
+
})
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
},
|
|
1750
|
+
backendURL: {
|
|
1751
|
+
invoke: {
|
|
1752
|
+
src: 'backendURL',
|
|
1753
|
+
input: ()=>({
|
|
1754
|
+
isC15tMode: true
|
|
1755
|
+
}),
|
|
1756
|
+
onDone: {
|
|
1757
|
+
target: '#generate.backendOptions',
|
|
1758
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1759
|
+
backendURL: ({ event })=>event.output.url
|
|
1760
|
+
})
|
|
1761
|
+
},
|
|
1762
|
+
onError: {
|
|
1763
|
+
target: '#generate.cancelling',
|
|
1764
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1765
|
+
cancelReason: 'Backend URL entry cancelled'
|
|
1766
|
+
})
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
offlineMode: {
|
|
1773
|
+
always: 'frontendOptions'
|
|
1774
|
+
},
|
|
1775
|
+
selfHostedMode: {
|
|
1776
|
+
invoke: {
|
|
1777
|
+
src: 'backendURL',
|
|
1778
|
+
input: ()=>({
|
|
1779
|
+
isC15tMode: false
|
|
1780
|
+
}),
|
|
1781
|
+
onDone: {
|
|
1782
|
+
target: 'backendOptions',
|
|
1783
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1784
|
+
backendURL: ({ event })=>event.output.url
|
|
1785
|
+
})
|
|
1786
|
+
},
|
|
1787
|
+
onError: {
|
|
1788
|
+
target: 'cancelling',
|
|
1789
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1790
|
+
cancelReason: 'Backend URL entry cancelled'
|
|
1791
|
+
})
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
},
|
|
1795
|
+
customMode: {
|
|
1796
|
+
always: 'frontendOptions'
|
|
1797
|
+
},
|
|
1798
|
+
backendOptions: {
|
|
1799
|
+
invoke: {
|
|
1800
|
+
src: 'backendOptions',
|
|
1801
|
+
input: ({ context })=>({
|
|
1802
|
+
cliContext: context.cliContext,
|
|
1803
|
+
backendURL: context.backendURL
|
|
1804
|
+
}),
|
|
1805
|
+
onDone: {
|
|
1806
|
+
target: 'frontendOptions',
|
|
1807
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1808
|
+
useEnvFile: ({ event })=>event.output.useEnvFile,
|
|
1809
|
+
proxyNextjs: ({ event })=>event.output.proxyNextjs
|
|
1810
|
+
})
|
|
1811
|
+
},
|
|
1812
|
+
onError: {
|
|
1813
|
+
target: 'cancelling',
|
|
1814
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1815
|
+
cancelReason: 'Backend options cancelled'
|
|
1816
|
+
})
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
},
|
|
1820
|
+
frontendOptions: {
|
|
1821
|
+
invoke: {
|
|
1822
|
+
src: 'frontendOptions',
|
|
1823
|
+
input: ({ context })=>({
|
|
1824
|
+
cliContext: context.cliContext,
|
|
1825
|
+
hasBackend: 'c15t' === context.selectedMode || 'self-hosted' === context.selectedMode
|
|
1826
|
+
}),
|
|
1827
|
+
onDone: {
|
|
1828
|
+
target: "scriptsOptions",
|
|
1829
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1830
|
+
enableSSR: ({ event, context })=>event.output.enableSSR ?? context.enableSSR,
|
|
1831
|
+
enableDevTools: ({ event, context })=>event.output.enableDevTools ?? context.enableDevTools,
|
|
1832
|
+
uiStyle: ({ event })=>event.output.uiStyle,
|
|
1833
|
+
expandedTheme: ({ event })=>event.output.expandedTheme ?? null
|
|
1834
|
+
})
|
|
1835
|
+
},
|
|
1836
|
+
onError: {
|
|
1837
|
+
target: 'cancelling',
|
|
1838
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1839
|
+
cancelReason: 'Frontend options cancelled'
|
|
1840
|
+
})
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
scriptsOptions: {
|
|
1845
|
+
invoke: {
|
|
1846
|
+
src: "scriptsOption",
|
|
1847
|
+
input: ({ context })=>({
|
|
1848
|
+
cliContext: context.cliContext
|
|
1849
|
+
}),
|
|
1850
|
+
onDone: {
|
|
1851
|
+
target: 'fileGeneration',
|
|
1852
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1853
|
+
addScripts: ({ event })=>event.output.addScripts,
|
|
1854
|
+
selectedScripts: ({ event })=>event.output.selectedScripts ?? [],
|
|
1855
|
+
dependenciesToAdd: ({ context, event })=>{
|
|
1856
|
+
const deps = [
|
|
1857
|
+
context.framework?.pkg ?? 'c15t'
|
|
1858
|
+
];
|
|
1859
|
+
if (event.output.addScripts) deps.push("@c15t/scripts");
|
|
1860
|
+
if (context.enableDevTools) deps.push('@c15t/dev-tools');
|
|
1861
|
+
return deps;
|
|
1862
|
+
}
|
|
1863
|
+
})
|
|
1864
|
+
},
|
|
1865
|
+
onError: {
|
|
1866
|
+
target: 'cancelling',
|
|
1867
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1868
|
+
cancelReason: 'Scripts option cancelled'
|
|
1869
|
+
})
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
fileGeneration: {
|
|
1874
|
+
invoke: {
|
|
1875
|
+
src: 'fileGeneration',
|
|
1876
|
+
input: ({ context })=>({
|
|
1877
|
+
cliContext: context.cliContext,
|
|
1878
|
+
mode: context.selectedMode,
|
|
1879
|
+
backendURL: context.backendURL,
|
|
1880
|
+
useEnvFile: context.useEnvFile,
|
|
1881
|
+
proxyNextjs: context.proxyNextjs,
|
|
1882
|
+
enableSSR: context.enableSSR,
|
|
1883
|
+
enableDevTools: context.enableDevTools,
|
|
1884
|
+
uiStyle: context.uiStyle,
|
|
1885
|
+
expandedTheme: context.expandedTheme,
|
|
1886
|
+
selectedScripts: context.selectedScripts
|
|
1887
|
+
}),
|
|
1888
|
+
onDone: {
|
|
1889
|
+
target: 'dependencyCheck',
|
|
1890
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1891
|
+
filesCreated: ({ event })=>event.output.filesCreated,
|
|
1892
|
+
filesModified: ({ event })=>event.output.filesModified
|
|
1893
|
+
})
|
|
1894
|
+
},
|
|
1895
|
+
onError: {
|
|
1896
|
+
target: 'error',
|
|
1897
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1898
|
+
errors: ({ context, event })=>[
|
|
1899
|
+
...context.errors,
|
|
1900
|
+
{
|
|
1901
|
+
state: 'fileGeneration',
|
|
1902
|
+
error: event.error,
|
|
1903
|
+
timestamp: Date.now()
|
|
1904
|
+
}
|
|
1905
|
+
]
|
|
1906
|
+
})
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
},
|
|
1910
|
+
dependencyCheck: {
|
|
1911
|
+
invoke: {
|
|
1912
|
+
src: 'checkDependencies',
|
|
1913
|
+
input: ({ context })=>({
|
|
1914
|
+
projectRoot: context.cliContext.projectRoot,
|
|
1915
|
+
dependencies: context.dependenciesToAdd
|
|
1916
|
+
}),
|
|
1917
|
+
onDone: {
|
|
1918
|
+
target: 'dependencyConfirm',
|
|
1919
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1920
|
+
dependenciesToAdd: ({ event })=>event.output.missing
|
|
1921
|
+
})
|
|
1922
|
+
},
|
|
1923
|
+
onError: {
|
|
1924
|
+
target: 'dependencyConfirm'
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
},
|
|
1928
|
+
dependencyConfirm: {
|
|
1929
|
+
always: [
|
|
1930
|
+
{
|
|
1931
|
+
guard: ({ context })=>0 === context.dependenciesToAdd.length,
|
|
1932
|
+
target: 'summary'
|
|
1933
|
+
}
|
|
1934
|
+
],
|
|
1935
|
+
invoke: {
|
|
1936
|
+
src: 'installConfirm',
|
|
1937
|
+
input: ({ context })=>({
|
|
1938
|
+
dependencies: context.dependenciesToAdd,
|
|
1939
|
+
packageManager: context.packageManager?.name ?? 'npm'
|
|
1940
|
+
}),
|
|
1941
|
+
onDone: [
|
|
1942
|
+
{
|
|
1943
|
+
guard: ({ event })=>event.output.confirmed,
|
|
1944
|
+
target: 'dependencyInstall',
|
|
1945
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1946
|
+
installConfirmed: true
|
|
1947
|
+
})
|
|
1948
|
+
},
|
|
1949
|
+
{
|
|
1950
|
+
target: 'summary',
|
|
1951
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1952
|
+
installConfirmed: false
|
|
1953
|
+
})
|
|
1954
|
+
}
|
|
1955
|
+
],
|
|
1956
|
+
onError: {
|
|
1957
|
+
target: 'summary',
|
|
1958
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1959
|
+
installConfirmed: false
|
|
1960
|
+
})
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
},
|
|
1964
|
+
dependencyInstall: {
|
|
1965
|
+
invoke: {
|
|
1966
|
+
src: 'dependencyInstall',
|
|
1967
|
+
input: ({ context })=>({
|
|
1968
|
+
cliContext: context.cliContext,
|
|
1969
|
+
dependencies: context.dependenciesToAdd
|
|
1970
|
+
}),
|
|
1971
|
+
onDone: {
|
|
1972
|
+
target: 'summary',
|
|
1973
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1974
|
+
installAttempted: true,
|
|
1975
|
+
installSucceeded: ({ event })=>event.output.success
|
|
1976
|
+
})
|
|
1977
|
+
},
|
|
1978
|
+
onError: {
|
|
1979
|
+
target: 'summary',
|
|
1980
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
1981
|
+
installAttempted: true,
|
|
1982
|
+
installSucceeded: false
|
|
1983
|
+
})
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1987
|
+
summary: {
|
|
1988
|
+
entry: ({ context })=>{
|
|
1989
|
+
if (!context.cliContext) return;
|
|
1990
|
+
const { logger, packageManager } = context.cliContext;
|
|
1991
|
+
if ('self-hosted' === context.selectedMode) {
|
|
1992
|
+
logger.info('Setup your backend with the c15t docs:');
|
|
1993
|
+
logger.info('https://c15t.com/docs/self-host/v2');
|
|
1994
|
+
} else if ('custom' === context.selectedMode) logger.info('Configuration Complete! Implement your custom endpoint handlers.');
|
|
1995
|
+
if (context.installConfirmed && !context.installSucceeded) logger.warn('Dependency installation failed. Please check errors and install manually.');
|
|
1996
|
+
else if (!context.installConfirmed && context.dependenciesToAdd.length > 0) {
|
|
1997
|
+
const pmCommand = getManualInstallCommand(context.dependenciesToAdd, packageManager.name);
|
|
1998
|
+
logger.warn(`Run ${pmCommand} to install required dependencies.`);
|
|
1999
|
+
}
|
|
2000
|
+
},
|
|
2001
|
+
after: {
|
|
2002
|
+
100: 'githubStar'
|
|
2003
|
+
}
|
|
2004
|
+
},
|
|
2005
|
+
githubStar: {
|
|
2006
|
+
invoke: {
|
|
2007
|
+
src: 'githubStar',
|
|
2008
|
+
input: ({ context })=>({
|
|
2009
|
+
cliContext: context.cliContext
|
|
2010
|
+
}),
|
|
2011
|
+
onDone: 'complete',
|
|
2012
|
+
onError: 'complete'
|
|
2013
|
+
}
|
|
2014
|
+
},
|
|
2015
|
+
complete: {
|
|
2016
|
+
entry: ({ context })=>{
|
|
2017
|
+
context.cliContext?.logger.success('Setup completed successfully!');
|
|
2018
|
+
},
|
|
2019
|
+
type: 'final'
|
|
2020
|
+
},
|
|
2021
|
+
error: {
|
|
2022
|
+
entry: ({ context })=>{
|
|
2023
|
+
const lastError = context.errors[context.errors.length - 1];
|
|
2024
|
+
context.cliContext?.logger.error(`Error: ${lastError?.error?.message ?? 'Unknown error'}`);
|
|
2025
|
+
},
|
|
2026
|
+
always: [
|
|
2027
|
+
{
|
|
2028
|
+
guard: 'hasFilesToRollback',
|
|
2029
|
+
target: 'rollback'
|
|
2030
|
+
},
|
|
2031
|
+
{
|
|
2032
|
+
target: 'exited'
|
|
2033
|
+
}
|
|
2034
|
+
]
|
|
2035
|
+
},
|
|
2036
|
+
cancelling: {
|
|
2037
|
+
entry: ({ context })=>{
|
|
2038
|
+
context.cliContext?.logger.info(context.cancelReason ?? 'Configuration cancelled.');
|
|
2039
|
+
},
|
|
2040
|
+
always: [
|
|
2041
|
+
{
|
|
2042
|
+
guard: 'hasFilesToRollback',
|
|
2043
|
+
target: 'rollback'
|
|
2044
|
+
},
|
|
2045
|
+
{
|
|
2046
|
+
target: 'exited'
|
|
2047
|
+
}
|
|
2048
|
+
]
|
|
2049
|
+
},
|
|
2050
|
+
rollback: {
|
|
2051
|
+
invoke: {
|
|
2052
|
+
src: 'rollback',
|
|
2053
|
+
input: ({ context })=>({
|
|
2054
|
+
filesCreated: context.filesCreated,
|
|
2055
|
+
filesModified: context.filesModified
|
|
2056
|
+
}),
|
|
2057
|
+
onDone: {
|
|
2058
|
+
target: 'exited',
|
|
2059
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
2060
|
+
filesCreated: [],
|
|
2061
|
+
filesModified: [],
|
|
2062
|
+
cleanupDone: true
|
|
2063
|
+
})
|
|
2064
|
+
},
|
|
2065
|
+
onError: {
|
|
2066
|
+
target: 'exited',
|
|
2067
|
+
actions: (0, __rspack_external_xstate.assign)({
|
|
2068
|
+
cleanupDone: true
|
|
2069
|
+
})
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
},
|
|
2073
|
+
exited: {
|
|
2074
|
+
type: 'final'
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
});
|
|
2078
|
+
async function runGenerateMachine(options) {
|
|
2079
|
+
const { context: cliContext, modeArg, resume = false, debug = false, persist = true } = options;
|
|
2080
|
+
const { logger, telemetry } = cliContext;
|
|
2081
|
+
const startTime = Date.now();
|
|
2082
|
+
const persistPath = getPersistPath(cliContext.projectRoot);
|
|
2083
|
+
const machineId = 'generate';
|
|
2084
|
+
let snapshot;
|
|
2085
|
+
if (resume) {
|
|
2086
|
+
const hasPersisted = await hasPersistedState(persistPath);
|
|
2087
|
+
if (hasPersisted) {
|
|
2088
|
+
snapshot = await loadSnapshot(persistPath, machineId);
|
|
2089
|
+
if (snapshot) logger.info('Resuming from previous state...');
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
const actor = (0, __rspack_external_xstate.createActor)(generateMachine, {
|
|
2093
|
+
input: {
|
|
2094
|
+
cliContext,
|
|
2095
|
+
modeArg
|
|
2096
|
+
},
|
|
2097
|
+
...snapshot ? {
|
|
2098
|
+
snapshot: snapshot
|
|
2099
|
+
} : {}
|
|
2100
|
+
});
|
|
2101
|
+
const subscribers = [];
|
|
2102
|
+
subscribers.push(createTelemetrySubscriber({
|
|
2103
|
+
telemetry,
|
|
2104
|
+
machineId,
|
|
2105
|
+
skipStates: [
|
|
2106
|
+
'routeToMode'
|
|
1849
2107
|
]
|
|
2108
|
+
}));
|
|
2109
|
+
if (debug) subscribers.push(createDebugSubscriber(machineId, logger));
|
|
2110
|
+
if (persist) subscribers.push(createPersistenceSubscriber(machineId, persistPath));
|
|
2111
|
+
const combinedSubscriber = combineSubscribers(...subscribers);
|
|
2112
|
+
actor.subscribe((snapshot)=>combinedSubscriber(snapshot));
|
|
2113
|
+
telemetry.trackEvent(TelemetryEventName.ONBOARDING_STARTED, {
|
|
2114
|
+
resumed: resume && void 0 !== snapshot
|
|
2115
|
+
});
|
|
2116
|
+
telemetry.flushSync();
|
|
2117
|
+
actor.start();
|
|
2118
|
+
if (!snapshot) actor.send({
|
|
2119
|
+
type: 'START'
|
|
2120
|
+
});
|
|
2121
|
+
return new Promise((resolve)=>{
|
|
2122
|
+
actor.subscribe({
|
|
2123
|
+
complete: ()=>{
|
|
2124
|
+
const finalSnapshot = actor.getSnapshot();
|
|
2125
|
+
const finalContext = finalSnapshot.context;
|
|
2126
|
+
const finalState = String(finalSnapshot.value);
|
|
2127
|
+
const duration = Date.now() - startTime;
|
|
2128
|
+
clearSnapshot(persistPath).catch(()=>{});
|
|
2129
|
+
const success = 'complete' === finalState || 'done' === finalSnapshot.status;
|
|
2130
|
+
telemetry.trackEvent(TelemetryEventName.ONBOARDING_COMPLETED, {
|
|
2131
|
+
success,
|
|
2132
|
+
selectedMode: finalContext.selectedMode ?? void 0,
|
|
2133
|
+
installDependencies: finalContext.installSucceeded,
|
|
2134
|
+
duration,
|
|
2135
|
+
finalState
|
|
2136
|
+
});
|
|
2137
|
+
resolve({
|
|
2138
|
+
success,
|
|
2139
|
+
context: finalContext,
|
|
2140
|
+
finalState,
|
|
2141
|
+
duration,
|
|
2142
|
+
errors: finalContext.errors
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
});
|
|
2146
|
+
});
|
|
2147
|
+
}
|
|
2148
|
+
async function generateAction(context) {
|
|
2149
|
+
const { logger, commandArgs, flags } = context;
|
|
2150
|
+
const modeArg = commandArgs[0];
|
|
2151
|
+
const resume = true === flags.resume;
|
|
2152
|
+
const debug = true === flags.debug || 'debug' === flags.logger;
|
|
2153
|
+
logger.debug('Starting generate command with state machine...');
|
|
2154
|
+
logger.debug(`Mode arg: ${modeArg}`);
|
|
2155
|
+
logger.debug(`Resume: ${resume}`);
|
|
2156
|
+
try {
|
|
2157
|
+
const result = await runGenerateMachine({
|
|
2158
|
+
context,
|
|
2159
|
+
modeArg,
|
|
2160
|
+
resume,
|
|
2161
|
+
debug,
|
|
2162
|
+
persist: true
|
|
2163
|
+
});
|
|
2164
|
+
if (!result.success) {
|
|
2165
|
+
if (result.errors.length > 0) process.exitCode = 1;
|
|
2166
|
+
}
|
|
2167
|
+
} catch (error) {
|
|
2168
|
+
logger.error(`Generate command failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2169
|
+
process.exitCode = 1;
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
async function generate(context, mode) {
|
|
2173
|
+
if (mode) context.commandArgs = [
|
|
2174
|
+
mode
|
|
2175
|
+
];
|
|
2176
|
+
return generateAction(context);
|
|
2177
|
+
}
|
|
2178
|
+
var utils_logger = __webpack_require__("./src/utils/logger.ts");
|
|
2179
|
+
async function addAndInstallDependenciesViaPM(projectRoot, dependencies, packageManager) {
|
|
2180
|
+
if (0 === dependencies.length) return;
|
|
2181
|
+
let command = '';
|
|
2182
|
+
let args = [];
|
|
2183
|
+
switch(packageManager){
|
|
2184
|
+
case 'npm':
|
|
2185
|
+
command = 'npm';
|
|
2186
|
+
args = [
|
|
2187
|
+
'install',
|
|
2188
|
+
...dependencies
|
|
2189
|
+
];
|
|
2190
|
+
break;
|
|
2191
|
+
case 'yarn':
|
|
2192
|
+
command = 'yarn';
|
|
2193
|
+
args = [
|
|
2194
|
+
'add',
|
|
2195
|
+
...dependencies
|
|
2196
|
+
];
|
|
2197
|
+
break;
|
|
2198
|
+
case 'pnpm':
|
|
2199
|
+
command = 'pnpm';
|
|
2200
|
+
args = [
|
|
2201
|
+
'add',
|
|
2202
|
+
...dependencies
|
|
2203
|
+
];
|
|
2204
|
+
break;
|
|
2205
|
+
case 'bun':
|
|
2206
|
+
command = 'bun';
|
|
2207
|
+
args = [
|
|
2208
|
+
'add',
|
|
2209
|
+
...dependencies
|
|
2210
|
+
];
|
|
2211
|
+
break;
|
|
2212
|
+
default:
|
|
2213
|
+
throw new Error(`Unsupported package manager for dependency addition: ${packageManager}`);
|
|
2214
|
+
}
|
|
2215
|
+
const child = (0, __rspack_external_node_child_process_27f17141.spawn)(command, args, {
|
|
2216
|
+
cwd: projectRoot,
|
|
2217
|
+
stdio: 'inherit'
|
|
1850
2218
|
});
|
|
1851
|
-
|
|
1852
|
-
clientConfigContent: result.configContent ?? '',
|
|
1853
|
-
installDepsConfirmed,
|
|
1854
|
-
ranInstall
|
|
1855
|
-
};
|
|
2219
|
+
await (0, __rspack_external_node_events_0a6aefe7.once)(child, 'exit');
|
|
1856
2220
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
installDepsConfirmed,
|
|
1878
|
-
ranInstall
|
|
2221
|
+
function dependencies_getManualInstallCommand(dependencies, packageManager) {
|
|
2222
|
+
switch(packageManager){
|
|
2223
|
+
case 'npm':
|
|
2224
|
+
return `npm install ${dependencies.join(' ')}`;
|
|
2225
|
+
case 'yarn':
|
|
2226
|
+
return `yarn add ${dependencies.join(' ')}`;
|
|
2227
|
+
case 'pnpm':
|
|
2228
|
+
return `pnpm add ${dependencies.join(' ')}`;
|
|
2229
|
+
case 'bun':
|
|
2230
|
+
return `bun add ${dependencies.join(' ')}`;
|
|
2231
|
+
default:
|
|
2232
|
+
return `npm install ${dependencies.join(' ')}`;
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
async function installDependencies({ context, dependenciesToAdd, handleCancel, autoInstall = false }) {
|
|
2236
|
+
const { telemetry, logger } = context;
|
|
2237
|
+
const s = prompts_.spinner();
|
|
2238
|
+
if (0 === dependenciesToAdd.length) return {
|
|
2239
|
+
installDepsConfirmed: false,
|
|
2240
|
+
ranInstall: false
|
|
1879
2241
|
};
|
|
2242
|
+
const depsString = dependenciesToAdd.map((d)=>external_picocolors_["default"].cyan(d)).join(', ');
|
|
2243
|
+
if (!autoInstall) {
|
|
2244
|
+
const addDepsSelection = await prompts_.confirm({
|
|
2245
|
+
message: `Add required dependencies using ${external_picocolors_["default"].cyan(context.packageManager.name)}? (${depsString})`,
|
|
2246
|
+
initialValue: true
|
|
2247
|
+
});
|
|
2248
|
+
if (handleCancel?.(addDepsSelection)) return {
|
|
2249
|
+
installDepsConfirmed: false,
|
|
2250
|
+
ranInstall: false
|
|
2251
|
+
};
|
|
2252
|
+
if (!addDepsSelection) return {
|
|
2253
|
+
installDepsConfirmed: false,
|
|
2254
|
+
ranInstall: false
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
s.start(`Running ${external_picocolors_["default"].cyan(context.packageManager.name)} to add and install dependencies... (this might take a moment)`);
|
|
2258
|
+
try {
|
|
2259
|
+
await addAndInstallDependenciesViaPM(context.projectRoot, dependenciesToAdd, context.packageManager.name);
|
|
2260
|
+
s.stop(`✅ Dependencies installed: ${dependenciesToAdd.map((d)=>external_picocolors_["default"].cyan(d)).join(', ')}`);
|
|
2261
|
+
telemetry.trackEvent(TelemetryEventName.ONBOARDING_DEPENDENCIES_INSTALLED, {
|
|
2262
|
+
success: true,
|
|
2263
|
+
dependencies: dependenciesToAdd.join(','),
|
|
2264
|
+
packageManager: context.packageManager.name
|
|
2265
|
+
});
|
|
2266
|
+
return {
|
|
2267
|
+
installDepsConfirmed: true,
|
|
2268
|
+
ranInstall: true
|
|
2269
|
+
};
|
|
2270
|
+
} catch (installError) {
|
|
2271
|
+
s.stop(external_picocolors_["default"].yellow('⚠️ Dependency installation failed.'));
|
|
2272
|
+
logger.error('Installation Error:', installError);
|
|
2273
|
+
telemetry.trackEvent(TelemetryEventName.ONBOARDING_DEPENDENCIES_INSTALLED, {
|
|
2274
|
+
success: false,
|
|
2275
|
+
error: installError instanceof Error ? installError.message : String(installError),
|
|
2276
|
+
dependencies: dependenciesToAdd.join(','),
|
|
2277
|
+
packageManager: context.packageManager.name
|
|
2278
|
+
});
|
|
2279
|
+
const pmCommand = dependencies_getManualInstallCommand(dependenciesToAdd, context.packageManager.name);
|
|
2280
|
+
logger.info(`Please try running '${pmCommand}' manually in ${external_picocolors_["default"].cyan(external_node_path_["default"].relative(context.cwd, context.projectRoot))}.`);
|
|
2281
|
+
return {
|
|
2282
|
+
installDepsConfirmed: true,
|
|
2283
|
+
ranInstall: false
|
|
2284
|
+
};
|
|
2285
|
+
}
|
|
1880
2286
|
}
|
|
2287
|
+
var promises_ = __webpack_require__("node:fs/promises");
|
|
1881
2288
|
const ADAPTER_LABELS = {
|
|
1882
2289
|
kyselyAdapter: 'kysely',
|
|
1883
2290
|
drizzleAdapter: 'drizzle',
|
|
@@ -1981,7 +2388,7 @@ const CONFIG_BUILDERS = {
|
|
|
1981
2388
|
};
|
|
1982
2389
|
async function pathExists(filePath) {
|
|
1983
2390
|
try {
|
|
1984
|
-
await
|
|
2391
|
+
await promises_["default"].access(filePath);
|
|
1985
2392
|
return true;
|
|
1986
2393
|
} catch {
|
|
1987
2394
|
return false;
|
|
@@ -2072,7 +2479,7 @@ function buildMongoPrelude(connection) {
|
|
|
2072
2479
|
}
|
|
2073
2480
|
function buildFileContent(adapter, provider, dbConfig, connection) {
|
|
2074
2481
|
const adapterPath = 'mongoAdapter' === adapter ? 'mongo' : adapter.replace('Adapter', '');
|
|
2075
|
-
const importAdapter = `import { ${adapter} } from '@c15t/backend/
|
|
2482
|
+
const importAdapter = `import { ${adapter} } from '@c15t/backend/db/adapters/${adapterPath}';`;
|
|
2076
2483
|
let extras = {
|
|
2077
2484
|
imports: '',
|
|
2078
2485
|
prelude: ''
|
|
@@ -2085,7 +2492,7 @@ function buildFileContent(adapter, provider, dbConfig, connection) {
|
|
|
2085
2492
|
else if ('prismaAdapter' === adapter) extras = buildPrismaPrelude(provider, connection);
|
|
2086
2493
|
else if ('typeormAdapter' === adapter) extras = buildTypeormPrelude(provider, connection);
|
|
2087
2494
|
else if ('mongoAdapter' === adapter) extras = buildMongoPrelude(connection);
|
|
2088
|
-
return `import { defineConfig } from '@c15t/backend
|
|
2495
|
+
return `import { defineConfig } from '@c15t/backend';
|
|
2089
2496
|
${importAdapter}
|
|
2090
2497
|
${extras.imports ? `${extras.imports}\n` : ''}
|
|
2091
2498
|
${extras.prelude ? `${extras.prelude}\n` : ''}
|
|
@@ -2095,14 +2502,14 @@ export default defineConfig({
|
|
|
2095
2502
|
`;
|
|
2096
2503
|
}
|
|
2097
2504
|
async function promptSelectAdapter() {
|
|
2098
|
-
const selection = await
|
|
2505
|
+
const selection = await prompts_.select({
|
|
2099
2506
|
message: 'Select database adapter:',
|
|
2100
2507
|
options: Object.keys(ADAPTER_LABELS).map((key)=>({
|
|
2101
2508
|
value: key,
|
|
2102
2509
|
label: ADAPTER_LABELS[key]
|
|
2103
2510
|
}))
|
|
2104
2511
|
});
|
|
2105
|
-
if (isCancel(selection)) throw new Cancelled('adapter_select');
|
|
2512
|
+
if (prompts_.isCancel(selection)) throw new Cancelled('adapter_select');
|
|
2106
2513
|
return selection;
|
|
2107
2514
|
}
|
|
2108
2515
|
async function promptSelectProvider(adapter) {
|
|
@@ -2112,14 +2519,14 @@ async function promptSelectProvider(adapter) {
|
|
|
2112
2519
|
const [first] = providers;
|
|
2113
2520
|
return first.value;
|
|
2114
2521
|
}
|
|
2115
|
-
const selection = await
|
|
2522
|
+
const selection = await prompts_.select({
|
|
2116
2523
|
message: 'Select database provider:',
|
|
2117
2524
|
options: providers.map((opt)=>({
|
|
2118
2525
|
value: opt.value,
|
|
2119
2526
|
label: opt.label
|
|
2120
2527
|
}))
|
|
2121
2528
|
});
|
|
2122
|
-
if (isCancel(selection)) throw new Cancelled('provider_select');
|
|
2529
|
+
if (prompts_.isCancel(selection)) throw new Cancelled('provider_select');
|
|
2123
2530
|
return selection;
|
|
2124
2531
|
}
|
|
2125
2532
|
async function promptConnection(adapter, provider) {
|
|
@@ -2127,42 +2534,42 @@ async function promptConnection(adapter, provider) {
|
|
|
2127
2534
|
useEnv: true
|
|
2128
2535
|
};
|
|
2129
2536
|
if ('sqlite' === provider) {
|
|
2130
|
-
const sqliteFile = await
|
|
2537
|
+
const sqliteFile = await prompts_.text({
|
|
2131
2538
|
message: 'SQLite file path:',
|
|
2132
2539
|
initialValue: './db.sqlite'
|
|
2133
2540
|
});
|
|
2134
|
-
if (isCancel(sqliteFile)) throw new Cancelled('sqlite_path');
|
|
2541
|
+
if (prompts_.isCancel(sqliteFile)) throw new Cancelled('sqlite_path');
|
|
2135
2542
|
connection.sqliteFile = String(sqliteFile);
|
|
2136
2543
|
return connection;
|
|
2137
2544
|
}
|
|
2138
|
-
const useEnv = await
|
|
2545
|
+
const useEnv = await prompts_.confirm({
|
|
2139
2546
|
message: 'Store connection string in an environment variable?',
|
|
2140
2547
|
initialValue: true
|
|
2141
2548
|
});
|
|
2142
|
-
if (isCancel(useEnv)) throw new Cancelled('use_env_confirm');
|
|
2549
|
+
if (prompts_.isCancel(useEnv)) throw new Cancelled('use_env_confirm');
|
|
2143
2550
|
connection.useEnv = Boolean(useEnv);
|
|
2144
2551
|
if (connection.useEnv) {
|
|
2145
2552
|
const defaultVar = 'mongoAdapter' === adapter ? 'MONGODB_URI' : 'DATABASE_URL';
|
|
2146
|
-
const envVarName = await
|
|
2553
|
+
const envVarName = await prompts_.text({
|
|
2147
2554
|
message: 'Env var name for connection string:',
|
|
2148
2555
|
initialValue: defaultVar
|
|
2149
2556
|
});
|
|
2150
|
-
if (isCancel(envVarName)) throw new Cancelled('env_var_name');
|
|
2557
|
+
if (prompts_.isCancel(envVarName)) throw new Cancelled('env_var_name');
|
|
2151
2558
|
connection.envVar = String(envVarName);
|
|
2152
2559
|
return connection;
|
|
2153
2560
|
}
|
|
2154
2561
|
const placeholder = 'mongoAdapter' === adapter ? 'mongodb+srv://user:pass@host/db' : 'postgresql://user:pass@host:5432/db';
|
|
2155
|
-
const connectionString = await
|
|
2562
|
+
const connectionString = await prompts_.text({
|
|
2156
2563
|
message: 'Connection string:',
|
|
2157
2564
|
placeholder
|
|
2158
2565
|
});
|
|
2159
|
-
if (isCancel(connectionString)) throw new Cancelled('connection_string');
|
|
2566
|
+
if (prompts_.isCancel(connectionString)) throw new Cancelled('connection_string');
|
|
2160
2567
|
connection.value = String(connectionString);
|
|
2161
2568
|
return connection;
|
|
2162
2569
|
}
|
|
2163
2570
|
async function ensureBackendConfig(context) {
|
|
2164
2571
|
const { cwd, logger } = context;
|
|
2165
|
-
const targetPath =
|
|
2572
|
+
const targetPath = external_node_path_["default"].join(cwd, 'c15t-backend.config.ts');
|
|
2166
2573
|
if (await pathExists(targetPath)) {
|
|
2167
2574
|
logger.debug(`Backend config already exists at ${targetPath}`);
|
|
2168
2575
|
return {
|
|
@@ -2191,8 +2598,8 @@ async function ensureBackendConfig(context) {
|
|
|
2191
2598
|
else if ('mongoAdapter' === adapter) dependencies.push('mongodb');
|
|
2192
2599
|
const dbConfig = buildDatabaseConfig(adapter, provider, connection);
|
|
2193
2600
|
const fileContent = buildFileContent(adapter, String(provider), dbConfig, connection);
|
|
2194
|
-
await
|
|
2195
|
-
context.logger.success(`Created ${
|
|
2601
|
+
await promises_["default"].writeFile(targetPath, fileContent, 'utf8');
|
|
2602
|
+
context.logger.success(`Created ${external_node_path_["default"].relative(cwd, targetPath)}`);
|
|
2196
2603
|
if ('sqlite' !== provider && connection.useEnv && connection.envVar) context.logger.note(`Remember to set ${connection.envVar} in your environment or .env file.`, 'Environment');
|
|
2197
2604
|
return {
|
|
2198
2605
|
path: targetPath,
|
|
@@ -2211,26 +2618,26 @@ async function handleMigrationResult(context, result) {
|
|
|
2211
2618
|
telemetry.trackEvent(TelemetryEventName.MIGRATION_PLANNED, {
|
|
2212
2619
|
success: true
|
|
2213
2620
|
});
|
|
2214
|
-
const saveSQL = await
|
|
2621
|
+
const saveSQL = await prompts_.confirm({
|
|
2215
2622
|
message: 'Save SQL to file?',
|
|
2216
2623
|
initialValue: true
|
|
2217
2624
|
});
|
|
2218
|
-
if (isCancel(saveSQL)) return void telemetry.trackEvent(TelemetryEventName.MIGRATION_FAILED, {
|
|
2625
|
+
if (prompts_.isCancel(saveSQL)) return void telemetry.trackEvent(TelemetryEventName.MIGRATION_FAILED, {
|
|
2219
2626
|
saveSql: false
|
|
2220
2627
|
});
|
|
2221
2628
|
if (saveSQL) {
|
|
2222
2629
|
const sql = result.getSQL?.() ?? '';
|
|
2223
2630
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
2224
2631
|
const filename = `migration-${timestamp}.sql`;
|
|
2225
|
-
const filepath = join(process.cwd(), filename);
|
|
2226
|
-
await writeFile(filepath, sql, 'utf-8');
|
|
2632
|
+
const filepath = (0, external_node_path_.join)(process.cwd(), filename);
|
|
2633
|
+
await (0, promises_.writeFile)(filepath, sql, 'utf-8');
|
|
2227
2634
|
logger.success(`SQL saved to: ${filename}`);
|
|
2228
2635
|
}
|
|
2229
|
-
const execute = await
|
|
2636
|
+
const execute = await prompts_.confirm({
|
|
2230
2637
|
message: 'Execute this migration?',
|
|
2231
2638
|
initialValue: false
|
|
2232
2639
|
});
|
|
2233
|
-
if (isCancel(execute)) return void telemetry.trackEvent(TelemetryEventName.MIGRATION_FAILED, {
|
|
2640
|
+
if (prompts_.isCancel(execute)) return void telemetry.trackEvent(TelemetryEventName.MIGRATION_FAILED, {
|
|
2234
2641
|
execute: false
|
|
2235
2642
|
});
|
|
2236
2643
|
await result.execute();
|
|
@@ -2241,11 +2648,11 @@ async function handleMigrationResult(context, result) {
|
|
|
2241
2648
|
}
|
|
2242
2649
|
async function handleORMResult(context, result) {
|
|
2243
2650
|
const { logger, telemetry, cwd } = context;
|
|
2244
|
-
const filePath =
|
|
2245
|
-
await
|
|
2651
|
+
const filePath = external_node_path_["default"].join(cwd, result.path);
|
|
2652
|
+
await promises_["default"].mkdir(external_node_path_["default"].dirname(filePath), {
|
|
2246
2653
|
recursive: true
|
|
2247
2654
|
});
|
|
2248
|
-
await
|
|
2655
|
+
await promises_["default"].writeFile(filePath, result.code);
|
|
2249
2656
|
logger.info(`Migration file created at ${filePath}`);
|
|
2250
2657
|
telemetry.trackEvent(TelemetryEventName.MIGRATION_COMPLETED, {
|
|
2251
2658
|
success: true,
|
|
@@ -2255,14 +2662,14 @@ async function handleORMResult(context, result) {
|
|
|
2255
2662
|
async function readConfigAndGetDb(context, absoluteConfigPath) {
|
|
2256
2663
|
const { logger } = context;
|
|
2257
2664
|
logger.info(`Loading backend config from ${absoluteConfigPath}`);
|
|
2258
|
-
const resolvedPath =
|
|
2665
|
+
const resolvedPath = external_node_path_["default"].resolve(absoluteConfigPath);
|
|
2259
2666
|
try {
|
|
2260
|
-
await
|
|
2667
|
+
await promises_["default"].access(resolvedPath);
|
|
2261
2668
|
} catch {
|
|
2262
2669
|
throw new Error(`Backend config not found at: ${resolvedPath}`);
|
|
2263
2670
|
}
|
|
2264
2671
|
try {
|
|
2265
|
-
const { config } = await loadConfig({
|
|
2672
|
+
const { config } = await (0, __rspack_external_c12.loadConfig)({
|
|
2266
2673
|
configFile: absoluteConfigPath,
|
|
2267
2674
|
jitiOptions: {
|
|
2268
2675
|
extensions: [
|
|
@@ -2279,8 +2686,9 @@ async function readConfigAndGetDb(context, absoluteConfigPath) {
|
|
|
2279
2686
|
}
|
|
2280
2687
|
});
|
|
2281
2688
|
logger.debug('Imported Config');
|
|
2689
|
+
if (!config || 'object' != typeof config || !('adapter' in config)) throw new Error('Invalid backend config: missing required "adapter" property');
|
|
2282
2690
|
return {
|
|
2283
|
-
db: DB.client(config.adapter)
|
|
2691
|
+
db: __rspack_external__c15t_backend_db_schema_e7c5e6a0.DB.client(config.adapter)
|
|
2284
2692
|
};
|
|
2285
2693
|
} catch (error) {
|
|
2286
2694
|
logger.error('Failed to load backend config', error);
|
|
@@ -2301,285 +2709,13 @@ async function migrate(context) {
|
|
|
2301
2709
|
});
|
|
2302
2710
|
const { db } = await readConfigAndGetDb(context, path);
|
|
2303
2711
|
logger.info('Loaded c15t-backend.config.ts');
|
|
2304
|
-
const result = await migrator({
|
|
2712
|
+
const result = await (0, __rspack_external__c15t_backend_db_migrator_ebe6d5c7.migrator)({
|
|
2305
2713
|
db,
|
|
2306
2714
|
schema: 'latest'
|
|
2307
2715
|
});
|
|
2308
2716
|
if ('path' in result) await handleORMResult(context, result);
|
|
2309
2717
|
else await handleMigrationResult(context, result);
|
|
2310
2718
|
}
|
|
2311
|
-
async function setupSelfHostedMode({ context, spinner, handleCancel }) {
|
|
2312
|
-
const dependenciesToAdd = new Set([
|
|
2313
|
-
context.framework.pkg,
|
|
2314
|
-
'@c15t/backend'
|
|
2315
|
-
]);
|
|
2316
|
-
const targetPath = node_path.join(context.cwd, 'c15t-backend.config.ts');
|
|
2317
|
-
let createBackendConfig = false;
|
|
2318
|
-
if (!await pathExists(targetPath)) {
|
|
2319
|
-
const config = await ensureBackendConfig(context);
|
|
2320
|
-
createBackendConfig = true;
|
|
2321
|
-
for (const dep of config?.dependencies ?? [])dependenciesToAdd.add(dep);
|
|
2322
|
-
}
|
|
2323
|
-
const backendURL = await prompts_text({
|
|
2324
|
-
message: 'Enter the backend URL:',
|
|
2325
|
-
initialValue: 'http://localhost:3000'
|
|
2326
|
-
});
|
|
2327
|
-
if (handleCancel?.(backendURL)) context.error.handleCancel('Setup cancelled.', {
|
|
2328
|
-
command: 'onboarding',
|
|
2329
|
-
stage: 'self_hosted_backend_url_setup'
|
|
2330
|
-
});
|
|
2331
|
-
const { useEnvFile, proxyNextjs, dependenciesToAdd: sharedDependenciesToAdd } = await getSharedFrontendOptions({
|
|
2332
|
-
backendURL: backendURL,
|
|
2333
|
-
context,
|
|
2334
|
-
handleCancel
|
|
2335
|
-
});
|
|
2336
|
-
for (const dep of sharedDependenciesToAdd)dependenciesToAdd.add(dep);
|
|
2337
|
-
await generateFiles({
|
|
2338
|
-
context,
|
|
2339
|
-
mode: 'c15t',
|
|
2340
|
-
backendURL: backendURL,
|
|
2341
|
-
spinner,
|
|
2342
|
-
useEnvFile,
|
|
2343
|
-
proxyNextjs
|
|
2344
|
-
});
|
|
2345
|
-
const { ranInstall, installDepsConfirmed } = await installDependencies({
|
|
2346
|
-
context,
|
|
2347
|
-
dependenciesToAdd: Array.from(dependenciesToAdd),
|
|
2348
|
-
handleCancel
|
|
2349
|
-
});
|
|
2350
|
-
const runMigrations = await prompts_confirm({
|
|
2351
|
-
message: 'Would you like to run migrations?',
|
|
2352
|
-
initialValue: true
|
|
2353
|
-
});
|
|
2354
|
-
if (handleCancel?.(runMigrations)) context.error.handleCancel('Setup cancelled.', {
|
|
2355
|
-
command: 'onboarding',
|
|
2356
|
-
stage: 'self_hosted_run_migrations_setup'
|
|
2357
|
-
});
|
|
2358
|
-
if (runMigrations) await migrate(context);
|
|
2359
|
-
return {
|
|
2360
|
-
backendURL: backendURL,
|
|
2361
|
-
usingEnvFile: useEnvFile ?? false,
|
|
2362
|
-
proxyNextjs,
|
|
2363
|
-
createBackendConfig: createBackendConfig,
|
|
2364
|
-
installDepsConfirmed,
|
|
2365
|
-
ranInstall,
|
|
2366
|
-
runMigrations: runMigrations
|
|
2367
|
-
};
|
|
2368
|
-
}
|
|
2369
|
-
const WINDOWS_PATH_SEPARATOR_REGEX = /\\/g;
|
|
2370
|
-
const FILE_EXTENSION_REGEX = /\.(ts|js|tsx|jsx)$/;
|
|
2371
|
-
async function generate(context, mode) {
|
|
2372
|
-
const { logger, telemetry } = context;
|
|
2373
|
-
logger.debug('Starting generate command...');
|
|
2374
|
-
logger.debug(`Mode: ${mode}`);
|
|
2375
|
-
const handleCancel = (value)=>{
|
|
2376
|
-
if (isCancel(value)) {
|
|
2377
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_EXITED, {
|
|
2378
|
-
reason: 'user_cancelled',
|
|
2379
|
-
command: 'onboarding',
|
|
2380
|
-
stage: 'setup'
|
|
2381
|
-
});
|
|
2382
|
-
context.error.handleCancel('Configuration cancelled.', {
|
|
2383
|
-
command: 'onboarding',
|
|
2384
|
-
stage: 'setup'
|
|
2385
|
-
});
|
|
2386
|
-
}
|
|
2387
|
-
return false;
|
|
2388
|
-
};
|
|
2389
|
-
try {
|
|
2390
|
-
logger.info('Starting onboarding process...');
|
|
2391
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_STARTED, {});
|
|
2392
|
-
telemetry.flushSync();
|
|
2393
|
-
await performOnboarding(context, handleCancel, mode);
|
|
2394
|
-
logger.success('🚀 Setup completed successfully!');
|
|
2395
|
-
} catch (error) {
|
|
2396
|
-
if (!isCancel(error)) telemetry.trackEvent(TelemetryEventName.ONBOARDING_COMPLETED, {
|
|
2397
|
-
success: false,
|
|
2398
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
}
|
|
2402
|
-
async function performOnboarding(context, handleCancel, mode) {
|
|
2403
|
-
const { telemetry, logger, packageManager } = context;
|
|
2404
|
-
const projectRoot = await detectProjectRoot(context.cwd, logger);
|
|
2405
|
-
const { pkg } = await detectFramework(projectRoot, logger);
|
|
2406
|
-
if (!pkg) throw new Error('Error detecting framework');
|
|
2407
|
-
let selectedMode = mode ?? null;
|
|
2408
|
-
logger.debug(`Selected mode: ${selectedMode}`);
|
|
2409
|
-
if (!selectedMode) selectedMode = await handleStorageModeSelection(context, handleCancel);
|
|
2410
|
-
if (!selectedMode) return;
|
|
2411
|
-
const sharedOptions = {
|
|
2412
|
-
context,
|
|
2413
|
-
spinner: prompts_spinner(),
|
|
2414
|
-
handleCancel
|
|
2415
|
-
};
|
|
2416
|
-
let installDepsConfirmed = false;
|
|
2417
|
-
let ranInstall = false;
|
|
2418
|
-
const dependenciesToAdd = [];
|
|
2419
|
-
switch(selectedMode){
|
|
2420
|
-
case 'c15t':
|
|
2421
|
-
{
|
|
2422
|
-
const c15tResult = await setupC15tMode(sharedOptions);
|
|
2423
|
-
installDepsConfirmed = c15tResult.installDepsConfirmed;
|
|
2424
|
-
ranInstall = c15tResult.ranInstall;
|
|
2425
|
-
dependenciesToAdd.push(context.framework.pkg);
|
|
2426
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_C15T_MODE_CONFIGURED, {
|
|
2427
|
-
usingEnvFile: c15tResult.usingEnvFile,
|
|
2428
|
-
proxyNextjs: c15tResult.proxyNextjs
|
|
2429
|
-
});
|
|
2430
|
-
break;
|
|
2431
|
-
}
|
|
2432
|
-
case 'offline':
|
|
2433
|
-
{
|
|
2434
|
-
const offlineResult = await setupOfflineMode({
|
|
2435
|
-
context,
|
|
2436
|
-
spinner: prompts_spinner()
|
|
2437
|
-
});
|
|
2438
|
-
installDepsConfirmed = offlineResult.installDepsConfirmed;
|
|
2439
|
-
ranInstall = offlineResult.ranInstall;
|
|
2440
|
-
dependenciesToAdd.push(context.framework.pkg);
|
|
2441
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_OFFLINE_MODE_CONFIGURED, {});
|
|
2442
|
-
break;
|
|
2443
|
-
}
|
|
2444
|
-
case 'self-hosted':
|
|
2445
|
-
{
|
|
2446
|
-
const selfHostedResult = await setupSelfHostedMode({
|
|
2447
|
-
context,
|
|
2448
|
-
spinner: prompts_spinner(),
|
|
2449
|
-
handleCancel
|
|
2450
|
-
});
|
|
2451
|
-
installDepsConfirmed = selfHostedResult.installDepsConfirmed;
|
|
2452
|
-
ranInstall = selfHostedResult.ranInstall;
|
|
2453
|
-
dependenciesToAdd.push(context.framework.pkg);
|
|
2454
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_SELF_HOSTED_CONFIGURED, {});
|
|
2455
|
-
break;
|
|
2456
|
-
}
|
|
2457
|
-
default:
|
|
2458
|
-
{
|
|
2459
|
-
const customResult = await setupCustomMode({
|
|
2460
|
-
context,
|
|
2461
|
-
spinner: prompts_spinner()
|
|
2462
|
-
});
|
|
2463
|
-
installDepsConfirmed = customResult.installDepsConfirmed;
|
|
2464
|
-
ranInstall = customResult.ranInstall;
|
|
2465
|
-
dependenciesToAdd.push(context.framework.pkg);
|
|
2466
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_CUSTOM_MODE_CONFIGURED, {});
|
|
2467
|
-
break;
|
|
2468
|
-
}
|
|
2469
|
-
}
|
|
2470
|
-
await displayNextSteps({
|
|
2471
|
-
context,
|
|
2472
|
-
projectRoot,
|
|
2473
|
-
storageMode: selectedMode,
|
|
2474
|
-
installDepsConfirmed,
|
|
2475
|
-
ranInstall,
|
|
2476
|
-
dependenciesToAdd,
|
|
2477
|
-
packageManager
|
|
2478
|
-
});
|
|
2479
|
-
await handleGitHubStar(context);
|
|
2480
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_COMPLETED, {
|
|
2481
|
-
success: true,
|
|
2482
|
-
selectedMode,
|
|
2483
|
-
installDependencies: ranInstall
|
|
2484
|
-
});
|
|
2485
|
-
}
|
|
2486
|
-
async function handleStorageModeSelection(context, handleCancel) {
|
|
2487
|
-
const { telemetry } = context;
|
|
2488
|
-
const storageModeSelection = await prompts_select({
|
|
2489
|
-
message: 'How would you like to store consent decisions?',
|
|
2490
|
-
initialValue: 'c15t',
|
|
2491
|
-
options: [
|
|
2492
|
-
{
|
|
2493
|
-
value: 'c15t',
|
|
2494
|
-
label: 'Hosted c15t (consent.io)',
|
|
2495
|
-
hint: 'Recommended: Fully managed service'
|
|
2496
|
-
},
|
|
2497
|
-
{
|
|
2498
|
-
value: 'offline',
|
|
2499
|
-
label: 'Offline Mode',
|
|
2500
|
-
hint: 'Store in browser, no backend needed'
|
|
2501
|
-
},
|
|
2502
|
-
{
|
|
2503
|
-
value: 'self-hosted',
|
|
2504
|
-
label: 'Self-Hosted',
|
|
2505
|
-
hint: 'Run your own c15t backend'
|
|
2506
|
-
},
|
|
2507
|
-
{
|
|
2508
|
-
value: 'custom',
|
|
2509
|
-
label: 'Custom Implementation',
|
|
2510
|
-
hint: 'Full control over storage logic'
|
|
2511
|
-
}
|
|
2512
|
-
]
|
|
2513
|
-
});
|
|
2514
|
-
if (handleCancel(storageModeSelection)) return null;
|
|
2515
|
-
const storageMode = storageModeSelection;
|
|
2516
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_STORAGE_MODE_SELECTED, {
|
|
2517
|
-
storageMode
|
|
2518
|
-
});
|
|
2519
|
-
return storageMode;
|
|
2520
|
-
}
|
|
2521
|
-
async function displayNextSteps(options) {
|
|
2522
|
-
const { context, projectRoot, storageMode, installDepsConfirmed, ranInstall, dependenciesToAdd, packageManager } = options;
|
|
2523
|
-
const { logger, cwd } = context;
|
|
2524
|
-
const { log } = prompts_namespaceObject;
|
|
2525
|
-
const configPath = node_path.join(projectRoot, 'c15t.config.ts');
|
|
2526
|
-
const relativeConfigPath = node_path.relative(cwd, configPath);
|
|
2527
|
-
const importPath = `./${relativeConfigPath.replace(WINDOWS_PATH_SEPARATOR_REGEX, '/').replace(FILE_EXTENSION_REGEX, '')}`;
|
|
2528
|
-
const importStatement = picocolors.cyan(`import { c15tConfig } from '${importPath}';`);
|
|
2529
|
-
switch(storageMode){
|
|
2530
|
-
case 'c15t':
|
|
2531
|
-
break;
|
|
2532
|
-
case 'offline':
|
|
2533
|
-
break;
|
|
2534
|
-
case 'self-hosted':
|
|
2535
|
-
log.step('Setup your backend with the c15t docs:');
|
|
2536
|
-
logger.info('https://c15t.com/docs/self-host/v2');
|
|
2537
|
-
break;
|
|
2538
|
-
case 'custom':
|
|
2539
|
-
{
|
|
2540
|
-
log.step('Configuration Complete! Next Steps:');
|
|
2541
|
-
const steps = `1. Implement your custom endpoint handlers (referenced in ${picocolors.cyan(relativeConfigPath)}).\n 2. Import and use configuration in your app: ${importStatement}`;
|
|
2542
|
-
logger.info(steps);
|
|
2543
|
-
break;
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
if (installDepsConfirmed && !ranInstall) logger.info(` - ${picocolors.yellow('Dependency installation failed.')} Please check errors and install manually.`);
|
|
2547
|
-
else if (!ranInstall && dependenciesToAdd.length > 0) {
|
|
2548
|
-
const pmCommand = getManualInstallCommand(dependenciesToAdd, packageManager.name);
|
|
2549
|
-
logger.warn(` - Run ${picocolors.cyan(pmCommand)} to install required dependencies.`);
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
2552
|
-
async function handleGitHubStar(context) {
|
|
2553
|
-
const { logger, telemetry } = context;
|
|
2554
|
-
logger.note(`${picocolors.bold('✨ Setup complete!')} Your c15t configuration is ready to use. \n
|
|
2555
|
-
|
|
2556
|
-
We're building c15t as an ${picocolors.bold('open source')} project to make consent management more accessible.
|
|
2557
|
-
If you find this useful, we'd really appreciate a GitHub star - it helps others discover the project!`, '🎉 Thanks for using c15t');
|
|
2558
|
-
const shouldOpenGitHub = await prompts_confirm({
|
|
2559
|
-
message: 'Would you like to star c15t on GitHub now?',
|
|
2560
|
-
initialValue: true
|
|
2561
|
-
});
|
|
2562
|
-
if (isCancel(shouldOpenGitHub)) {
|
|
2563
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_GITHUB_STAR, {
|
|
2564
|
-
action: 'cancelled'
|
|
2565
|
-
});
|
|
2566
|
-
return context.error.handleCancel('GitHub star prompt cancelled. Exiting onboarding.', {
|
|
2567
|
-
command: 'onboarding',
|
|
2568
|
-
stage: 'github_star'
|
|
2569
|
-
});
|
|
2570
|
-
}
|
|
2571
|
-
telemetry.trackEvent(TelemetryEventName.ONBOARDING_GITHUB_STAR, {
|
|
2572
|
-
action: shouldOpenGitHub ? 'opened_browser' : 'declined'
|
|
2573
|
-
});
|
|
2574
|
-
if (shouldOpenGitHub) try {
|
|
2575
|
-
note('Your support helps us continue improving c15t.\nThank you for being part of our community!', '⭐ Star Us on GitHub');
|
|
2576
|
-
await open_0('https://github.com/c15t/c15t');
|
|
2577
|
-
logger.success('GitHub repository opened. Thank you for your support!');
|
|
2578
|
-
} catch (error) {
|
|
2579
|
-
logger.debug('Failed to open browser:', error);
|
|
2580
|
-
logger.info(`You can star us later by visiting: ${picocolors.cyan('https://github.com/c15t/c15t')}`);
|
|
2581
|
-
}
|
|
2582
|
-
}
|
|
2583
2719
|
const subcommands = [
|
|
2584
2720
|
{
|
|
2585
2721
|
name: 'generate',
|
|
@@ -2632,14 +2768,14 @@ async function selfHost(context) {
|
|
|
2632
2768
|
label: 'Exit',
|
|
2633
2769
|
hint: 'Return to main menu'
|
|
2634
2770
|
});
|
|
2635
|
-
const selectedSubcommandName = await
|
|
2636
|
-
message:
|
|
2771
|
+
const selectedSubcommandName = await prompts_.select({
|
|
2772
|
+
message: (0, utils_logger.$e)('info', 'Which self-host command would you like to run?'),
|
|
2637
2773
|
options: promptOptions
|
|
2638
2774
|
});
|
|
2639
|
-
if (isCancel(selectedSubcommandName) || 'exit' === selectedSubcommandName) {
|
|
2775
|
+
if (prompts_.isCancel(selectedSubcommandName) || 'exit' === selectedSubcommandName) {
|
|
2640
2776
|
logger.debug('Interactive selection cancelled or exit chosen.');
|
|
2641
2777
|
telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
|
|
2642
|
-
action: isCancel(selectedSubcommandName) ? 'cancelled' : 'exit',
|
|
2778
|
+
action: prompts_.isCancel(selectedSubcommandName) ? 'cancelled' : 'exit',
|
|
2643
2779
|
context: 'self-host'
|
|
2644
2780
|
});
|
|
2645
2781
|
error.handleCancel('Operation cancelled.', {
|
|
@@ -2667,12 +2803,12 @@ async function selfHost(context) {
|
|
|
2667
2803
|
}
|
|
2668
2804
|
async function displayIntro(context, version) {
|
|
2669
2805
|
const { logger } = context;
|
|
2670
|
-
logger.info(`${
|
|
2806
|
+
logger.info(`${external_picocolors_["default"].bold('Welcome!')} Let's get you set up.`);
|
|
2671
2807
|
logger.message('');
|
|
2672
2808
|
let figletText = 'c15t';
|
|
2673
2809
|
try {
|
|
2674
2810
|
figletText = await new Promise((resolve)=>{
|
|
2675
|
-
|
|
2811
|
+
__rspack_external_figlet["default"].text('c15t', {
|
|
2676
2812
|
font: 'Nancyj-Improved',
|
|
2677
2813
|
horizontalLayout: 'default',
|
|
2678
2814
|
verticalLayout: 'default',
|
|
@@ -2712,6 +2848,7 @@ async function displayIntro(context, version) {
|
|
|
2712
2848
|
});
|
|
2713
2849
|
logger.message(coloredLines.join('\n'));
|
|
2714
2850
|
}
|
|
2851
|
+
var constants = __webpack_require__("./src/constants.ts");
|
|
2715
2852
|
function createErrorHandlers(context) {
|
|
2716
2853
|
const { logger, telemetry } = context;
|
|
2717
2854
|
return {
|
|
@@ -2719,7 +2856,7 @@ function createErrorHandlers(context) {
|
|
|
2719
2856
|
logger.error(message, error);
|
|
2720
2857
|
if (error instanceof Error) logger.error(error.message);
|
|
2721
2858
|
else logger.error(String(error));
|
|
2722
|
-
logger.failed(`${
|
|
2859
|
+
logger.failed(`${external_picocolors_["default"].red('Operation failed unexpectedly.')}`);
|
|
2723
2860
|
process.exit(1);
|
|
2724
2861
|
},
|
|
2725
2862
|
handleCancel: (message = 'Operation cancelled.', context)=>{
|
|
@@ -2739,10 +2876,10 @@ function createFileSystem(context) {
|
|
|
2739
2876
|
return {
|
|
2740
2877
|
getPackageInfo: ()=>{
|
|
2741
2878
|
logger.debug('Reading package.json');
|
|
2742
|
-
const packageJsonPath =
|
|
2879
|
+
const packageJsonPath = external_node_path_["default"].join(cwd, 'package.json');
|
|
2743
2880
|
logger.debug(`package.json path: ${packageJsonPath}`);
|
|
2744
2881
|
try {
|
|
2745
|
-
const packageInfo =
|
|
2882
|
+
const packageInfo = __rspack_external_fs_extra_ce68a66b["default"].readJSONSync(packageJsonPath);
|
|
2746
2883
|
logger.debug('Successfully read package.json');
|
|
2747
2884
|
return {
|
|
2748
2885
|
name: packageInfo?.name || 'unknown',
|
|
@@ -2759,6 +2896,99 @@ function createFileSystem(context) {
|
|
|
2759
2896
|
}
|
|
2760
2897
|
};
|
|
2761
2898
|
}
|
|
2899
|
+
async function detectFramework(projectRoot, logger) {
|
|
2900
|
+
try {
|
|
2901
|
+
logger?.debug(`Detecting framework in ${projectRoot}`);
|
|
2902
|
+
const packageJsonPath = external_node_path_["default"].join(projectRoot, 'package.json');
|
|
2903
|
+
const packageJson = JSON.parse(await promises_["default"].readFile(packageJsonPath, 'utf-8'));
|
|
2904
|
+
const deps = {
|
|
2905
|
+
...packageJson.dependencies,
|
|
2906
|
+
...packageJson.devDependencies
|
|
2907
|
+
};
|
|
2908
|
+
const hasReact = 'react' in deps;
|
|
2909
|
+
const reactVersion = hasReact ? deps.react : null;
|
|
2910
|
+
logger?.debug(`React detected: ${hasReact}${reactVersion ? ` (version: ${reactVersion})` : ''}`);
|
|
2911
|
+
const tailwindVersion = deps.tailwindcss || null;
|
|
2912
|
+
logger?.debug(`Tailwind detected: ${!!tailwindVersion}${tailwindVersion ? ` (version: ${tailwindVersion})` : ''}`);
|
|
2913
|
+
let framework = null;
|
|
2914
|
+
let frameworkVersion = null;
|
|
2915
|
+
let pkg = hasReact ? '@c15t/react' : 'c15t';
|
|
2916
|
+
if ('next' in deps) {
|
|
2917
|
+
framework = 'Next.js';
|
|
2918
|
+
frameworkVersion = deps.next;
|
|
2919
|
+
pkg = '@c15t/nextjs';
|
|
2920
|
+
} else if ('@remix-run/react' in deps) {
|
|
2921
|
+
framework = 'Remix';
|
|
2922
|
+
frameworkVersion = deps['@remix-run/react'];
|
|
2923
|
+
} else if ('@vitejs/plugin-react' in deps || '@vitejs/plugin-react-swc' in deps) {
|
|
2924
|
+
framework = 'Vite + React';
|
|
2925
|
+
frameworkVersion = deps['@vitejs/plugin-react'] || deps['@vitejs/plugin-react-swc'];
|
|
2926
|
+
} else if ('gatsby' in deps) {
|
|
2927
|
+
framework = 'Gatsby';
|
|
2928
|
+
frameworkVersion = deps.gatsby;
|
|
2929
|
+
} else if (hasReact) {
|
|
2930
|
+
framework = 'React';
|
|
2931
|
+
frameworkVersion = reactVersion;
|
|
2932
|
+
}
|
|
2933
|
+
logger?.debug(`Detected framework: ${framework}${frameworkVersion ? ` (version: ${frameworkVersion})` : ''}, package: ${pkg}`);
|
|
2934
|
+
return {
|
|
2935
|
+
framework,
|
|
2936
|
+
frameworkVersion,
|
|
2937
|
+
pkg,
|
|
2938
|
+
hasReact,
|
|
2939
|
+
reactVersion,
|
|
2940
|
+
tailwindVersion
|
|
2941
|
+
};
|
|
2942
|
+
} catch (error) {
|
|
2943
|
+
logger?.debug(`Framework detection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2944
|
+
return {
|
|
2945
|
+
framework: null,
|
|
2946
|
+
frameworkVersion: null,
|
|
2947
|
+
pkg: 'c15t',
|
|
2948
|
+
hasReact: false,
|
|
2949
|
+
reactVersion: null,
|
|
2950
|
+
tailwindVersion: null
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
async function detectProjectRoot(cwd, logger) {
|
|
2955
|
+
let projectRoot = cwd;
|
|
2956
|
+
logger?.debug(`Starting project root detection from: ${cwd}`);
|
|
2957
|
+
try {
|
|
2958
|
+
let prevDir = '';
|
|
2959
|
+
let depth = 0;
|
|
2960
|
+
const maxDepth = 10;
|
|
2961
|
+
while(projectRoot !== prevDir && depth < maxDepth){
|
|
2962
|
+
logger?.debug(`Checking directory (depth ${depth}): ${projectRoot}`);
|
|
2963
|
+
try {
|
|
2964
|
+
const packageJsonPath = external_node_path_["default"].join(projectRoot, 'package.json');
|
|
2965
|
+
logger?.debug(`Looking for package.json at: ${packageJsonPath}`);
|
|
2966
|
+
await promises_["default"].access(packageJsonPath);
|
|
2967
|
+
logger?.debug(`Found package.json at: ${projectRoot}`);
|
|
2968
|
+
break;
|
|
2969
|
+
} catch (error) {
|
|
2970
|
+
logger?.debug(`No package.json found in ${projectRoot}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2971
|
+
prevDir = projectRoot;
|
|
2972
|
+
projectRoot = external_node_path_["default"].dirname(projectRoot);
|
|
2973
|
+
depth++;
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
if (projectRoot === prevDir) {
|
|
2977
|
+
logger?.debug('Reached root directory without finding package.json');
|
|
2978
|
+
logger?.failed('Could not find project root (no package.json found)');
|
|
2979
|
+
}
|
|
2980
|
+
if (depth >= maxDepth) {
|
|
2981
|
+
logger?.debug('Reached maximum directory depth without finding package.json');
|
|
2982
|
+
logger?.failed('Could not find project root (reached maximum directory depth)');
|
|
2983
|
+
}
|
|
2984
|
+
logger?.debug(`Project root detection complete. Found at: ${projectRoot}`);
|
|
2985
|
+
return projectRoot;
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
logger?.debug(`Project root detection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
2988
|
+
logger?.debug(`Falling back to current directory: ${cwd}`);
|
|
2989
|
+
return cwd;
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2762
2992
|
const SUPPORTED_PACKAGE_MANAGERS = [
|
|
2763
2993
|
'npm',
|
|
2764
2994
|
'yarn',
|
|
@@ -2777,7 +3007,7 @@ async function getPackageManagerVersion(pm) {
|
|
|
2777
3007
|
async function detectPackageManager(projectRoot, logger) {
|
|
2778
3008
|
try {
|
|
2779
3009
|
logger?.debug('Detecting package manager');
|
|
2780
|
-
const pm = await detect({
|
|
3010
|
+
const pm = await (0, __rspack_external_package_manager_detector_detect_94d6a9ae.detect)({
|
|
2781
3011
|
cwd: projectRoot
|
|
2782
3012
|
});
|
|
2783
3013
|
if (!pm) throw new Error('No package manager detected');
|
|
@@ -2790,7 +3020,7 @@ async function detectPackageManager(projectRoot, logger) {
|
|
|
2790
3020
|
} catch (error) {
|
|
2791
3021
|
logger?.error(`Error detecting package manager: ${error instanceof Error ? error.message : String(error)}`);
|
|
2792
3022
|
}
|
|
2793
|
-
const selectedPackageManager = await
|
|
3023
|
+
const selectedPackageManager = await prompts_.select({
|
|
2794
3024
|
message: 'Please select your package manager:',
|
|
2795
3025
|
options: [
|
|
2796
3026
|
{
|
|
@@ -2812,7 +3042,7 @@ async function detectPackageManager(projectRoot, logger) {
|
|
|
2812
3042
|
],
|
|
2813
3043
|
initialValue: 'npm'
|
|
2814
3044
|
});
|
|
2815
|
-
if (isCancel(selectedPackageManager)) {
|
|
3045
|
+
if (prompts_.isCancel(selectedPackageManager)) {
|
|
2816
3046
|
logger?.debug('Package manager selection cancelled by user');
|
|
2817
3047
|
logger?.failed('Package manager selection cancelled. Exiting.');
|
|
2818
3048
|
process.exit(0);
|
|
@@ -2907,7 +3137,7 @@ function parseCliArgs(rawArgs, commands) {
|
|
|
2907
3137
|
if (nextArg && !nextArg.startsWith('-')) {
|
|
2908
3138
|
parsedFlags[primaryName] = nextArg;
|
|
2909
3139
|
i++;
|
|
2910
|
-
} else
|
|
3140
|
+
} else prompts_.log.warn((0, utils_logger.$e)('warn', `Flag ${arg} expects a value, but none was found or the next item is a flag.`));
|
|
2911
3141
|
} else parsedFlags[primaryName] = true;
|
|
2912
3142
|
}
|
|
2913
3143
|
break;
|
|
@@ -2928,11 +3158,11 @@ function createUserInteraction(context) {
|
|
|
2928
3158
|
return {
|
|
2929
3159
|
confirm: async (message, initialValue)=>{
|
|
2930
3160
|
logger.debug(`Confirm action: "${message}", Initial: ${initialValue}`);
|
|
2931
|
-
const confirmed = await
|
|
3161
|
+
const confirmed = await prompts_.confirm({
|
|
2932
3162
|
message,
|
|
2933
3163
|
initialValue
|
|
2934
3164
|
});
|
|
2935
|
-
if (isCancel(confirmed)) {
|
|
3165
|
+
if (prompts_.isCancel(confirmed)) {
|
|
2936
3166
|
error.handleCancel();
|
|
2937
3167
|
return false;
|
|
2938
3168
|
}
|
|
@@ -2945,10 +3175,10 @@ async function createCliContext(rawArgs, cwd, commands) {
|
|
|
2945
3175
|
const { commandName, commandArgs, parsedFlags } = parseCliArgs(rawArgs, commands);
|
|
2946
3176
|
let desiredLogLevel = 'info';
|
|
2947
3177
|
const levelArg = parsedFlags.logger;
|
|
2948
|
-
if ('string' == typeof levelArg) if (
|
|
3178
|
+
if ('string' == typeof levelArg) if (utils_logger.U0.includes(levelArg)) desiredLogLevel = levelArg;
|
|
2949
3179
|
else console.warn(`[CLI Setup] Invalid log level '${levelArg}' provided via --logger. Using default 'info'.`);
|
|
2950
3180
|
else if (true === levelArg) console.warn("[CLI Setup] --logger flag found but no level specified. Using default 'info'.");
|
|
2951
|
-
const logger =
|
|
3181
|
+
const logger = (0, utils_logger.xw)(desiredLogLevel);
|
|
2952
3182
|
logger.debug(`Logger initialized with level: ${desiredLogLevel}`);
|
|
2953
3183
|
const baseContext = {
|
|
2954
3184
|
logger,
|
|
@@ -3018,8 +3248,8 @@ const src_commands = [
|
|
|
3018
3248
|
description: 'Open our GitHub repository to give us a star.',
|
|
3019
3249
|
action: async (context)=>{
|
|
3020
3250
|
const { logger } = context;
|
|
3021
|
-
logger.note(`We're building c15t as an ${
|
|
3022
|
-
await
|
|
3251
|
+
logger.note(`We're building c15t as an ${external_picocolors_["default"].bold('open source')} project to make consent management more accessible.\nIf you find this useful, we'd really appreciate a GitHub star - it helps others discover the project!`, 'Star Us on GitHub');
|
|
3252
|
+
await (0, __rspack_external_open["default"])(constants.tl.GITHUB);
|
|
3023
3253
|
logger.success('Thank you for your support!');
|
|
3024
3254
|
}
|
|
3025
3255
|
},
|
|
@@ -3030,7 +3260,7 @@ const src_commands = [
|
|
|
3030
3260
|
description: 'Open the c15t documentation in your browser.',
|
|
3031
3261
|
action: async (context)=>{
|
|
3032
3262
|
const { logger } = context;
|
|
3033
|
-
await
|
|
3263
|
+
await (0, __rspack_external_open["default"])(`${constants.tl.DOCS}?ref=cli`);
|
|
3034
3264
|
logger.success('Documentation opened in your browser.');
|
|
3035
3265
|
}
|
|
3036
3266
|
}
|
|
@@ -3044,8 +3274,8 @@ async function main() {
|
|
|
3044
3274
|
const version = packageInfo.version;
|
|
3045
3275
|
if (!telemetry.isDisabled()) logger.note(`c15t collects anonymous usage data to help improve the CLI.
|
|
3046
3276
|
This data is not personally identifiable and helps us prioritize features.
|
|
3047
|
-
To disable telemetry, use the ${
|
|
3048
|
-
flag or set ${
|
|
3277
|
+
To disable telemetry, use the ${external_picocolors_["default"].cyan('--no-telemetry')}
|
|
3278
|
+
flag or set ${external_picocolors_["default"].cyan('C15T_TELEMETRY_DISABLED=1')} in your environment.`, `${(0, utils_logger.$e)('info', 'Telemetry Notice')}`);
|
|
3049
3279
|
try {
|
|
3050
3280
|
telemetry.trackEvent(TelemetryEventName.CLI_INVOKED, {
|
|
3051
3281
|
version,
|
|
@@ -3118,14 +3348,14 @@ flag or set ${picocolors.cyan('C15T_TELEMETRY_DISABLED=1')} in your environment.
|
|
|
3118
3348
|
label: 'exit',
|
|
3119
3349
|
hint: 'Close the CLI'
|
|
3120
3350
|
});
|
|
3121
|
-
const selectedCommandName = await
|
|
3122
|
-
message:
|
|
3351
|
+
const selectedCommandName = await prompts_.select({
|
|
3352
|
+
message: (0, utils_logger.$e)('info', 'Which command would you like to run?'),
|
|
3123
3353
|
options: promptOptions
|
|
3124
3354
|
});
|
|
3125
|
-
if (isCancel(selectedCommandName) || 'exit' === selectedCommandName) {
|
|
3355
|
+
if (prompts_.isCancel(selectedCommandName) || 'exit' === selectedCommandName) {
|
|
3126
3356
|
logger.debug('Interactive selection cancelled or exit chosen.');
|
|
3127
3357
|
telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_EXITED, {
|
|
3128
|
-
action: isCancel(selectedCommandName) ? 'cancelled' : 'exit'
|
|
3358
|
+
action: prompts_.isCancel(selectedCommandName) ? 'cancelled' : 'exit'
|
|
3129
3359
|
});
|
|
3130
3360
|
context.error.handleCancel('Operation cancelled.', {
|
|
3131
3361
|
command: 'interactive_menu',
|