untitledui 0.1.1

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.
Files changed (121) hide show
  1. package/dist/commands/add.js +339 -0
  2. package/dist/commands/init.js +436 -0
  3. package/dist/helper/download-tar-api.js +129 -0
  4. package/dist/helper/download-tar.js +81 -0
  5. package/dist/helper/find-css-file.js +19 -0
  6. package/dist/helper/formatText.js +37 -0
  7. package/dist/helper/get-components-api.js +47 -0
  8. package/dist/helper/get-components-list.js +62 -0
  9. package/dist/helper/get-components.js +19 -0
  10. package/dist/helper/get-config.js +163 -0
  11. package/dist/helper/get-package-info.js +99 -0
  12. package/dist/helper/get-pkg-manager.js +16 -0
  13. package/dist/helper/get-project.js +176 -0
  14. package/dist/helper/install-template.js +29 -0
  15. package/dist/helper/match-color-css.js +82 -0
  16. package/dist/helper/update-color-css.js +134 -0
  17. package/dist/index.js +25 -0
  18. package/dist/package.json +50 -0
  19. package/dist/res/components.json +520 -0
  20. package/dist/res/config.json +3 -0
  21. package/package.json +61 -0
  22. package/templates/default/.prettierrc +10 -0
  23. package/templates/default/README.md +36 -0
  24. package/templates/default/eslint.config.mjs +58 -0
  25. package/templates/default/next.config.ts +6 -0
  26. package/templates/default/package.json +57 -0
  27. package/templates/default/postcss.config.js +5 -0
  28. package/templates/default/public/favicon.ico +0 -0
  29. package/templates/default/public/marketing/smiling-girl.png +0 -0
  30. package/templates/default/public/marketing/spirals.webp +0 -0
  31. package/templates/default/src/app/home-screen.tsx +109 -0
  32. package/templates/default/src/app/layout.tsx +42 -0
  33. package/templates/default/src/app/not-found.tsx +40 -0
  34. package/templates/default/src/app/page.tsx +3 -0
  35. package/templates/default/src/components/foundations/dot-icon.tsx +27 -0
  36. package/templates/default/src/components/foundations/featured-icon/featured-icons.tsx +153 -0
  37. package/templates/default/src/components/foundations/logo/UntitledLogo.tsx +63 -0
  38. package/templates/default/src/components/foundations/logo/UntitledLogoMinimal.tsx +164 -0
  39. package/templates/default/src/components/foundations/payment-icons/amex-icon.tsx +19 -0
  40. package/templates/default/src/components/foundations/payment-icons/apple-pay-icon.tsx +27 -0
  41. package/templates/default/src/components/foundations/payment-icons/discover-icon.tsx +34 -0
  42. package/templates/default/src/components/foundations/payment-icons/index.tsx +10 -0
  43. package/templates/default/src/components/foundations/payment-icons/mastercard-icon.tsx +39 -0
  44. package/templates/default/src/components/foundations/payment-icons/paypal-icon.tsx +45 -0
  45. package/templates/default/src/components/foundations/payment-icons/stripe-icon.tsx +27 -0
  46. package/templates/default/src/components/foundations/payment-icons/union-pay-icon.tsx +37 -0
  47. package/templates/default/src/components/foundations/payment-icons/visa-icon.tsx +27 -0
  48. package/templates/default/src/components/marketing/header-navigation/base-components/nav-menu-item.tsx +41 -0
  49. package/templates/default/src/components/marketing/header-navigation/components/header.tsx +245 -0
  50. package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +53 -0
  51. package/templates/default/src/components/shared/avatar/avatar-label-group.tsx +32 -0
  52. package/templates/default/src/components/shared/avatar/avatar-profile-photo.tsx +84 -0
  53. package/templates/default/src/components/shared/avatar/avatar.tsx +131 -0
  54. package/templates/default/src/components/shared/avatar/base-components/avatar-add-button.tsx +33 -0
  55. package/templates/default/src/components/shared/avatar/base-components/avatar-company-icon.tsx +26 -0
  56. package/templates/default/src/components/shared/avatar/base-components/avatar-online-indicator.tsx +31 -0
  57. package/templates/default/src/components/shared/avatar/base-components/index.ts +4 -0
  58. package/templates/default/src/components/shared/avatar/base-components/verified-tick.tsx +34 -0
  59. package/templates/default/src/components/shared/avatar/utils.ts +12 -0
  60. package/templates/default/src/components/shared/badges/badge-groups.tsx +176 -0
  61. package/templates/default/src/components/shared/badges/badge-types.ts +264 -0
  62. package/templates/default/src/components/shared/badges/badges.tsx +479 -0
  63. package/templates/default/src/components/shared/button-group/button-group.tsx +97 -0
  64. package/templates/default/src/components/shared/buttons/app-store-buttons-outline.tsx +454 -0
  65. package/templates/default/src/components/shared/buttons/app-store-buttons.tsx +806 -0
  66. package/templates/default/src/components/shared/buttons/button-utility.tsx +87 -0
  67. package/templates/default/src/components/shared/buttons/button.tsx +284 -0
  68. package/templates/default/src/components/shared/buttons/close-button.tsx +39 -0
  69. package/templates/default/src/components/shared/buttons/social-button.tsx +135 -0
  70. package/templates/default/src/components/shared/buttons/social-logos.tsx +115 -0
  71. package/templates/default/src/components/shared/checkbox/checkbox.tsx +120 -0
  72. package/templates/default/src/components/shared/dropdown/dropdown.tsx +138 -0
  73. package/templates/default/src/components/shared/input-dropdown/combobox.tsx +161 -0
  74. package/templates/default/src/components/shared/input-dropdown/dropdown-item.tsx +98 -0
  75. package/templates/default/src/components/shared/input-dropdown/input-dropdown.tsx +172 -0
  76. package/templates/default/src/components/shared/input-dropdown/multi-select.tsx +373 -0
  77. package/templates/default/src/components/shared/input-dropdown/popover.tsx +36 -0
  78. package/templates/default/src/components/shared/input-dropdown/select.tsx +63 -0
  79. package/templates/default/src/components/shared/inputs/file-upload-trigger.tsx +74 -0
  80. package/templates/default/src/components/shared/inputs/form/form.tsx +10 -0
  81. package/templates/default/src/components/shared/inputs/hint-text.tsx +34 -0
  82. package/templates/default/src/components/shared/inputs/input/index.tsx +189 -0
  83. package/templates/default/src/components/shared/inputs/input/input-payment.tsx +134 -0
  84. package/templates/default/src/components/shared/inputs/input/input-with-button.tsx +69 -0
  85. package/templates/default/src/components/shared/inputs/input/input-with-dropdown.tsx +178 -0
  86. package/templates/default/src/components/shared/inputs/input/input-with-prefix.tsx +74 -0
  87. package/templates/default/src/components/shared/inputs/label.tsx +46 -0
  88. package/templates/default/src/components/shared/inputs/textarea/textarea.tsx +82 -0
  89. package/templates/default/src/components/shared/progress-indicators/progress-circles.tsx +176 -0
  90. package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +86 -0
  91. package/templates/default/src/components/shared/progress-indicators/simple-circle.tsx +29 -0
  92. package/templates/default/src/components/shared/radio-buttons/radio-buttons.tsx +125 -0
  93. package/templates/default/src/components/shared/radio-groups/radio-group-avatar.tsx +62 -0
  94. package/templates/default/src/components/shared/radio-groups/radio-group-checkbox.tsx +72 -0
  95. package/templates/default/src/components/shared/radio-groups/radio-group-icon-card.tsx +95 -0
  96. package/templates/default/src/components/shared/radio-groups/radio-group-icon-simple.tsx +70 -0
  97. package/templates/default/src/components/shared/radio-groups/radio-group-payment-icon.tsx +71 -0
  98. package/templates/default/src/components/shared/radio-groups/radio-group-radio-button.tsx +76 -0
  99. package/templates/default/src/components/shared/radio-groups/radio-groups.tsx +8 -0
  100. package/templates/default/src/components/shared/slider/slider.tsx +76 -0
  101. package/templates/default/src/components/shared/tags/base-components/tag-checkbox.tsx +47 -0
  102. package/templates/default/src/components/shared/tags/base-components/tag-close-x.tsx +34 -0
  103. package/templates/default/src/components/shared/tags/tags.tsx +162 -0
  104. package/templates/default/src/components/shared/toggle/toggle.tsx +140 -0
  105. package/templates/default/src/components/shared/tooltips/tooltips.tsx +140 -0
  106. package/templates/default/src/components/utils/index.ts +48 -0
  107. package/templates/default/src/components/utils/isDeepEqual.ts +31 -0
  108. package/templates/default/src/components/utils/isReactComponent.ts +22 -0
  109. package/templates/default/src/components/utils/mergeRefs.ts +19 -0
  110. package/templates/default/src/components/utils/useBreakpoint.ts +36 -0
  111. package/templates/default/src/components/utils/uuid.ts +9 -0
  112. package/templates/default/src/fonts/GeistMonoVF.woff +0 -0
  113. package/templates/default/src/fonts/GeistVF.woff +0 -0
  114. package/templates/default/src/hooks/use-resize-observer.tsx +55 -0
  115. package/templates/default/src/providers/theme.tsx +11 -0
  116. package/templates/default/src/styles/colors.css +805 -0
  117. package/templates/default/src/styles/globals.css +86 -0
  118. package/templates/default/src/styles/text-styles.css +177 -0
  119. package/templates/default/src/styles/theme.css +1310 -0
  120. package/templates/default/src/styles/typography.css +428 -0
  121. package/templates/default/tsconfig.json +27 -0
@@ -0,0 +1,436 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __importDefault = (this && this.__importDefault) || function (mod) {
46
+ return (mod && mod.__esModule) ? mod : { "default": mod };
47
+ };
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.program = void 0;
50
+ const async_retry_1 = __importDefault(require("async-retry"));
51
+ const chalk_1 = __importDefault(require("chalk"));
52
+ const commander_1 = require("commander");
53
+ const execa_1 = require("execa");
54
+ const fast_glob_1 = __importDefault(require("fast-glob"));
55
+ const fs_1 = __importDefault(require("fs"));
56
+ const ora_1 = __importDefault(require("ora"));
57
+ const os_1 = __importDefault(require("os"));
58
+ const path = __importStar(require("path"));
59
+ const prompts_1 = __importDefault(require("prompts"));
60
+ const ts_morph_1 = require("ts-morph");
61
+ const download_tar_api_1 = require("../helper/download-tar-api");
62
+ const find_css_file_1 = require("../helper/find-css-file");
63
+ const get_config_1 = require("../helper/get-config");
64
+ const get_package_info_1 = require("../helper/get-package-info");
65
+ const get_pkg_manager_1 = require("../helper/get-pkg-manager");
66
+ const get_project_1 = require("../helper/get-project");
67
+ const install_template_1 = require("../helper/install-template");
68
+ const update_color_css_1 = require("../helper/update-color-css");
69
+ const CONFIG_DIR = path.join(os_1.default.homedir(), ".untitledui");
70
+ const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
71
+ const DEFAULT_TEMPLATE = "default";
72
+ const PRIVATE_TEMPLATES = ["with-stripe", "otp", "magic-link"];
73
+ // Directory where the project will be initialized
74
+ let projectPath = "";
75
+ let promptOptions = {
76
+ template: "",
77
+ color: "",
78
+ };
79
+ if (fs_1.default.existsSync(CONFIG_PATH)) {
80
+ const config = JSON.parse(fs_1.default.readFileSync(CONFIG_PATH, "utf-8"));
81
+ promptOptions.license = config.license;
82
+ }
83
+ const onPromptState = (state) => {
84
+ if (state.aborted) {
85
+ // If we don't re-enable the terminal cursor before exiting
86
+ // the program, the cursor will remain hidden
87
+ process.stdout.write("\x1B[?25h");
88
+ process.stdout.write("\n");
89
+ process.exit(1);
90
+ }
91
+ };
92
+ exports.program = new commander_1.Command()
93
+ .name("init")
94
+ .description("initialize a new project")
95
+ .argument("[directory]")
96
+ .usage("[directory] [options]")
97
+ .helpOption("-h, --help", "Display this help message.")
98
+ .option("-t, --template <starter-kit>", "Specify a template for the project.")
99
+ .option("-c, --color <color-name>", "Specify a color for the project.")
100
+ .option("-o, --overwrite", "Overwrite existing files.", false)
101
+ .option("-l, --license <license-key>", "Add a license key to download the repository.")
102
+ .action((name, options) => __awaiter(void 0, void 0, void 0, function* () {
103
+ if (name) {
104
+ projectPath = name;
105
+ }
106
+ if (options) {
107
+ promptOptions.color = options.color;
108
+ promptOptions.template = options.template;
109
+ promptOptions.overwrite = options.overwrite;
110
+ promptOptions.license = options.license || promptOptions.license;
111
+ }
112
+ try {
113
+ yield run(options);
114
+ }
115
+ catch (e) {
116
+ console.error(chalk_1.default.red(e));
117
+ }
118
+ }));
119
+ function run(opts) {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ var _a, _b;
122
+ const cwd = process.cwd();
123
+ const isExistingProject = fs_1.default.existsSync(path.resolve(cwd, "package.json"));
124
+ // Src dir in main repo where styles and plugins folder are located
125
+ const stylesFolder = path.join(path.resolve(__dirname, "../../../../"));
126
+ // Get `theme.css` file from the src/styles directory
127
+ const cssFilePath = (0, find_css_file_1.findCssFile)(stylesFolder);
128
+ // Extract color variables from the theme.css file
129
+ const colors = (0, update_color_css_1.extractColorVariables)(cssFilePath !== null && cssFilePath !== void 0 ? cssFilePath : "");
130
+ // Get unique prefixes from the color variables
131
+ const prefixes = Array.from(new Set(Object.keys(colors).map((varName) => varName.split("--color-")[1].replace(/-\d{1,3}/, ""))));
132
+ const spinner = (0, ora_1.default)().start();
133
+ // Get the project configuration and check if it is a Next.js project
134
+ const info = yield (0, get_project_1.getNextjsProjectConfig)(cwd);
135
+ if (promptOptions.license) {
136
+ const isValidKey = yield (0, download_tar_api_1.checkLicenseKey)(promptOptions.license);
137
+ if (!isValidKey) {
138
+ spinner.fail("Invalid license key");
139
+ process.exit(0);
140
+ }
141
+ // Save the license key to the config file
142
+ if (!fs_1.default.existsSync(CONFIG_PATH)) {
143
+ const dirs = path.dirname(CONFIG_PATH);
144
+ fs_1.default.mkdirSync(dirs, { recursive: true });
145
+ fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify({ license: promptOptions.license }, null, 2));
146
+ }
147
+ const config = JSON.parse(fs_1.default.readFileSync(CONFIG_PATH, "utf-8"));
148
+ if (config.license !== promptOptions.license) {
149
+ fs_1.default.writeFileSync(CONFIG_PATH, JSON.stringify({ license: promptOptions.license }, null, 2));
150
+ }
151
+ }
152
+ if (!isExistingProject) {
153
+ spinner.stop();
154
+ // If user didn't provide a project name, ask for it
155
+ if (!projectPath) {
156
+ const res = yield (0, prompts_1.default)({
157
+ onState: onPromptState,
158
+ type: "text",
159
+ name: "path",
160
+ message: "What is your project named?",
161
+ initial: "untitled-ui",
162
+ });
163
+ if (typeof res.path === "string") {
164
+ projectPath = res.path.trim();
165
+ }
166
+ }
167
+ // If the directory with the project name already exists,
168
+ // show an error message
169
+ if (fs_1.default.existsSync(path.resolve(projectPath))) {
170
+ spinner.fail(chalk_1.default.red("Directory already exists!"));
171
+ process.exit(1);
172
+ }
173
+ // If user didn't provide a template kit to start with, ask for it
174
+ if (!promptOptions.template) {
175
+ const res = yield (0, prompts_1.default)({
176
+ type: "select",
177
+ name: "template",
178
+ onState: onPromptState,
179
+ message: `Which ${chalk_1.default.cyan("starter kit")} would you like to use?`,
180
+ choices: [
181
+ {
182
+ title: "default-template",
183
+ value: DEFAULT_TEMPLATE,
184
+ },
185
+ {
186
+ title: "basic-form",
187
+ value: "basic-form",
188
+ },
189
+ {
190
+ title: "magic-link",
191
+ value: "magic-link",
192
+ },
193
+ {
194
+ title: "next-intl",
195
+ value: "next-intl",
196
+ },
197
+ {
198
+ title: "otp",
199
+ value: "otp",
200
+ },
201
+ {
202
+ title: "react-hook-form",
203
+ value: "react-hook-form",
204
+ },
205
+ {
206
+ title: "with-stripe",
207
+ value: "with-stripe",
208
+ },
209
+ ],
210
+ });
211
+ promptOptions.template = res.template;
212
+ if (PRIVATE_TEMPLATES.includes(promptOptions.template) && !promptOptions.license) {
213
+ const res = yield (0, prompts_1.default)({
214
+ type: "text",
215
+ name: "license",
216
+ onState: onPromptState,
217
+ message: `Enter the license key to download the ${chalk_1.default.cyan(promptOptions.template)} template:`,
218
+ });
219
+ promptOptions.license = res.license;
220
+ const isValidKey = yield (0, download_tar_api_1.checkLicenseKey)(res.license);
221
+ if (!isValidKey) {
222
+ spinner.fail("Invalid license key");
223
+ process.exit(0);
224
+ }
225
+ }
226
+ }
227
+ spinner.succeed("Template is selected: " + chalk_1.default.green(promptOptions.template));
228
+ // If project exists, but it's not a Next.js project, show an error message
229
+ }
230
+ else if ((info === null || info === void 0 ? void 0 : info.framework) === "other") {
231
+ spinner.fail("Unsupported project framework");
232
+ console.log(`Please refer to the documentation ${chalk_1.default.cyan("https://untitled.xyz/docs")} for supported frameworks or proceed with manual installation.`);
233
+ process.exit(1);
234
+ }
235
+ else {
236
+ spinner.succeed(chalk_1.default.yellow("Detected Next.js project, proceeding with the setup..."));
237
+ }
238
+ // If user didn't provide a color, ask for it
239
+ if (!promptOptions.color) {
240
+ const res = yield (0, prompts_1.default)({
241
+ type: "select",
242
+ name: "color",
243
+ onState: onPromptState,
244
+ initial: (_a = opts.color) !== null && _a !== void 0 ? _a : "",
245
+ message: `Which ${chalk_1.default.cyan("color")} would you like to use as the ${chalk_1.default.cyanBright("brand")} color?`,
246
+ choices: prefixes.map((prefix) => ({
247
+ title: prefix,
248
+ value: prefix,
249
+ })),
250
+ });
251
+ promptOptions.color = res.color;
252
+ }
253
+ // If the project is not an existing project, download the starter kit
254
+ if (projectPath && !isExistingProject) {
255
+ const appPath = path.resolve(projectPath);
256
+ console.log(`\nCreating a new project in ${chalk_1.default.blue(projectPath)}`);
257
+ const spinner = (0, ora_1.default)("Downloading and extracting the repository...").start();
258
+ try {
259
+ // Create the project directory
260
+ fs_1.default.mkdirSync(appPath, { recursive: true });
261
+ if (promptOptions.template === DEFAULT_TEMPLATE) {
262
+ // Template folder path
263
+ const templatePath = path.resolve(path.join(__dirname, "../../templates/", promptOptions.template));
264
+ // Copy the template files to the project directory
265
+ (0, install_template_1.copyTemplateFiles)(templatePath, appPath);
266
+ }
267
+ else {
268
+ // Download and extract the repository, retry 2 times before failing
269
+ // Always use the API-based download for both public and private repositories
270
+ yield (0, async_retry_1.default)(() => (0, download_tar_api_1.downloadAndExtractRepoFromApi)(appPath, {
271
+ username: "a-peak-works",
272
+ repo: "starter-kits",
273
+ branch: "master",
274
+ template: promptOptions.template,
275
+ key: promptOptions.license,
276
+ }), {
277
+ retries: 2,
278
+ });
279
+ }
280
+ spinner.succeed("Files are downloaded and extracted successfully!");
281
+ const dependencies = (0, ora_1.default)({ text: "Installing dependencies..." }).start();
282
+ // Update the theme.css file with the selected color
283
+ // Starter kits always have the theme.css file in the
284
+ // src/styles directory
285
+ const newCssFilePath = fast_glob_1.default.sync(["**/styles/theme.css"], { cwd: appPath, absolute: true, onlyFiles: true })[0];
286
+ (0, update_color_css_1.updateCssFile)("brand", promptOptions.color, newCssFilePath);
287
+ // Install dependencies and initialize git, retry once if there is a peer dependency conflict
288
+ yield (0, async_retry_1.default)(() => (0, execa_1.execa)("sh", ["-c", `cd ${projectPath} && ${(0, get_pkg_manager_1.getPkgManager)()} install && git init`]).catch((e) => __awaiter(this, void 0, void 0, function* () {
289
+ if (e.message.includes("peer")) {
290
+ dependencies.warn("Dependency conflict detected. Retrying with --legacy-peer-deps...");
291
+ dependencies.start("Installing dependencies with --legacy-peer-deps flag");
292
+ // Retry with the --legacy-peer-deps flag
293
+ yield (0, execa_1.execa)("sh", [
294
+ "-c",
295
+ `cd ${projectPath} && ${(0, get_pkg_manager_1.getPkgManager)() === "npm" ? "npm --legacy-peer-deps" : (0, get_pkg_manager_1.getPkgManager)()} install && git init`,
296
+ ]);
297
+ }
298
+ })), {
299
+ retries: 1,
300
+ });
301
+ dependencies.succeed("Dependencies installed");
302
+ console.log(`\nYour project is ready, to get staretd run the following commands:\n\n cd ${chalk_1.default.cyan(projectPath)}\n ${chalk_1.default.cyan((0, get_pkg_manager_1.getPkgManager)())} run dev`);
303
+ process.exit(1);
304
+ }
305
+ catch (error) {
306
+ spinner.fail(chalk_1.default.red("\nFailed to download and extract the repository"));
307
+ // If there is an error, show the error message and exit the process
308
+ if (error instanceof Error) {
309
+ console.error(error.message);
310
+ }
311
+ else {
312
+ console.error("\n");
313
+ }
314
+ // Remove the project directory if the download and extraction fails
315
+ fs_1.default.rmdirSync(appPath, { recursive: true });
316
+ process.exit(0);
317
+ }
318
+ }
319
+ else {
320
+ const existingFiles = [];
321
+ const configFiles = (0, get_config_1.resolveConfigPaths)(cwd);
322
+ // Src dir in main repo where styles and plugins folder are located
323
+ const srcDir = path.join(path.resolve(__dirname, "../../../../"));
324
+ // Get all the files in the styles, plugins and public/fonts directories
325
+ const styleFiles = fast_glob_1.default.sync(["**/{styles,plugins}/**", "postcss.config.*", "**/public/fonts/**"], {
326
+ cwd: srcDir,
327
+ onlyFiles: true,
328
+ ignore: get_project_1.PROJECT_SHARED_IGNORE.filter((ignore) => !ignore.includes("public")),
329
+ });
330
+ // Get dependencies from the package.json file in the main repo
331
+ // and with the specified versions
332
+ const dependencies = (0, get_package_info_1.getDependenciesFromPackageJson)({
333
+ cwd: srcDir,
334
+ dependencies: [
335
+ "tailwindcss",
336
+ "tailwindcss-animate",
337
+ "@tailwindcss/typography",
338
+ "tailwindcss-react-aria-components",
339
+ "@designbycode/tailwindcss-mask-image",
340
+ ],
341
+ devDependencies: ["@tailwindcss/postcss", "postcss"],
342
+ });
343
+ styleFiles.forEach((file) => {
344
+ // Filter out postcss and font files as they are one level above
345
+ // the src directory `root`/ and public/
346
+ const isPostCSSOrFont = file.includes("postcss.config") || file.includes("fonts");
347
+ const newFile = path.resolve(process.cwd(), isPostCSSOrFont ? file : `${(info === null || info === void 0 ? void 0 : info.isSrcDir) ? "src" : ""}/${file}`);
348
+ if (fs_1.default.existsSync(newFile)) {
349
+ // Overwrite public/fonts files or if the user has specified to overwrite
350
+ if (newFile.includes("public/fonts") || (promptOptions === null || promptOptions === void 0 ? void 0 : promptOptions.overwrite)) {
351
+ fs_1.default.copyFileSync(path.resolve(srcDir, file), newFile);
352
+ }
353
+ else {
354
+ existingFiles.push(path.relative(process.cwd(), newFile));
355
+ }
356
+ return;
357
+ }
358
+ // Create the directory if it doesn't exist
359
+ fs_1.default.mkdirSync(path.dirname(newFile), { recursive: true });
360
+ // Create an empty file
361
+ fs_1.default.writeFileSync(newFile, "");
362
+ // Copy the file from the main repo to the project directory
363
+ fs_1.default.copyFileSync(path.resolve(srcDir, file), newFile);
364
+ });
365
+ // If the files already exist in the project directory, show the user
366
+ // which files exist
367
+ if (existingFiles.length && !(promptOptions === null || promptOptions === void 0 ? void 0 : promptOptions.overwrite)) {
368
+ console.log("\n");
369
+ spinner.fail(`Following files already exist in the directory. Use ${chalk_1.default.cyan("--overwrite")} or ${chalk_1.default.cyan("-o")} to overwrite existing files, or refer to the documentation ${chalk_1.default.cyan("https://untitled.xyz/docs")} for manual installation.`);
370
+ existingFiles.forEach((file) => {
371
+ console.log(`- ${chalk_1.default.green(file)}`);
372
+ });
373
+ process.exit(0);
374
+ }
375
+ // If tailwind config file exists, show the user how to add it to the globals.css file
376
+ if (configFiles === null || configFiles === void 0 ? void 0 : configFiles.tailwindFile) {
377
+ console.log("\nTailwind config file exists in the project directory. You can add it to your globals.css as follows:");
378
+ console.log(`\n${chalk_1.default.cyan(`@config "../${(info === null || info === void 0 ? void 0 : info.isSrcDir) && "../"}${path.relative(process.cwd(), configFiles.tailwindFile)}";`)}\n`);
379
+ }
380
+ // Main file in the project directory, it's either layout.tsx or _app.tsx
381
+ const mainFile = (configFiles === null || configFiles === void 0 ? void 0 : configFiles.layoutFile) || (configFiles === null || configFiles === void 0 ? void 0 : configFiles.appFile) || "";
382
+ // Create a new project instance with the tsconfig file
383
+ const project = new ts_morph_1.Project({
384
+ tsConfigFilePath: path.resolve((configFiles === null || configFiles === void 0 ? void 0 : configFiles.tsConfig) || ""),
385
+ });
386
+ const sourceFile = project.addSourceFileAtPath(path.resolve(mainFile));
387
+ // Remove the existing import declarations for the style files
388
+ // to avoid conflicts when updating the alias pathes
389
+ const stylesToRemove = ["colors.css", "globals.css", "inter.css", "text-styles.css", "theme.css", "typography.css"];
390
+ // If style files already exist, remove them to avoid conflicts
391
+ // when updating alias pathes
392
+ sourceFile
393
+ .getImportDeclarations()
394
+ .filter((importDeclaration) => stylesToRemove.includes(importDeclaration.getModuleSpecifierValue()))
395
+ .forEach((importDeclaration) => importDeclaration.remove());
396
+ //If not src alis path is used, we will update it to the correct path
397
+ // using dots
398
+ const pathsDeep = path.relative(path.resolve(process.cwd(), `${(info === null || info === void 0 ? void 0 : info.isSrcDir) && "src"}`), mainFile).split("/").length;
399
+ // Add the import declarations for the style files with the
400
+ // updated alias pathes
401
+ sourceFile.addImportDeclarations(styleFiles
402
+ .filter((file) => file.includes("styles"))
403
+ .map((file) => {
404
+ var _a, _b, _c;
405
+ return ({
406
+ moduleSpecifier: `${((_a = info === null || info === void 0 ? void 0 : info.aliasPrefix) === null || _a === void 0 ? void 0 : _a.stylesPrefix) || ((_b = info === null || info === void 0 ? void 0 : info.aliasPrefix) === null || _b === void 0 ? void 0 : _b.srcPrefix) || "../".repeat(pathsDeep - 1)}${((_c = info === null || info === void 0 ? void 0 : info.aliasPrefix) === null || _c === void 0 ? void 0 : _c.stylesPrefix) ? file === null || file === void 0 ? void 0 : file.split("styles/")[1] : file}`,
407
+ });
408
+ }));
409
+ // Save the source file
410
+ sourceFile.saveSync();
411
+ const dependencySpinner = (0, ora_1.default)();
412
+ // Get the theme.css file from the project directory
413
+ const themeCssFile = fast_glob_1.default.sync(["**/styles/theme.css"], { cwd, absolute: true, onlyFiles: true, ignore: get_project_1.PROJECT_SHARED_IGNORE });
414
+ // If the theme.css file doesn't exist, show an error message
415
+ if (!(themeCssFile === null || themeCssFile === void 0 ? void 0 : themeCssFile.length))
416
+ return spinner.fail("Failed to copy theme.css file");
417
+ // Update the theme.css file with the selected color
418
+ (0, update_color_css_1.updateCssFile)("brand", promptOptions.color, (_b = themeCssFile[0]) !== null && _b !== void 0 ? _b : "");
419
+ // Install dependencies from the package.json file in the main repo
420
+ if (!dependencies) {
421
+ dependencySpinner.fail("Failed to get dependencies from package.json");
422
+ process.exit(1);
423
+ }
424
+ dependencySpinner.start("Installing dependencies");
425
+ // Run the package manager install command to install the dependencies
426
+ // and devDependencies
427
+ yield (0, execa_1.execa)((0, get_pkg_manager_1.getPkgManager)(), [(0, get_pkg_manager_1.getPkgManager)() === "npm" ? "install" : "add", ...dependencies.dependencies], {});
428
+ yield (0, execa_1.execa)((0, get_pkg_manager_1.getPkgManager)(), [(0, get_pkg_manager_1.getPkgManager)() === "npm" ? "install" : "add", "-D", ...dependencies.devDependencies], {});
429
+ dependencySpinner.succeed("Dependencies installed");
430
+ spinner.succeed(chalk_1.default.green("Files are extracted successfully!"));
431
+ console.log("\nYour project is ready, you can now start adding components.");
432
+ // Exit the process with a success status code
433
+ process.exit(1);
434
+ }
435
+ });
436
+ }
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.downloadAndExtractRepoFromApi = downloadAndExtractRepoFromApi;
16
+ exports.downloadTarStream = downloadTarStream;
17
+ exports.checkLicenseKey = checkLicenseKey;
18
+ const node_path_1 = require("node:path");
19
+ const node_stream_1 = require("node:stream");
20
+ const promises_1 = require("node:stream/promises");
21
+ const tar_1 = require("tar");
22
+ const config_json_1 = __importDefault(require("../res/config.json"));
23
+ /**
24
+ * Downloads and extracts the tarball from the returned tarball stream
25
+ * @param root - The main root directory where the tarball should be extracted
26
+ * @param info - The repository information required for downloading the tarball
27
+ */
28
+ function downloadAndExtractRepoFromApi(root, info) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ try {
31
+ // Convert the web ReadableStream to a Node.js ReadableStream
32
+ const stream = yield downloadTarStream(info);
33
+ yield (0, promises_1.pipeline)(stream, (0, tar_1.x)({
34
+ cwd: root, // directory to extract files into
35
+ strip: 2, // how many path segments to remove (depends on the tar structure)
36
+ filter: (p) => {
37
+ // Normalize paths to POSIX format for consistency
38
+ const posixPath = p.split(node_path_1.sep).join(node_path_1.posix.sep);
39
+ // Extract only files that include the specified template name
40
+ return posixPath.includes(info.template || "basic-form");
41
+ },
42
+ }));
43
+ }
44
+ catch (error) {
45
+ if (error instanceof Error) {
46
+ // Provide more context about the error
47
+ throw new Error(`Failed to download or extract repository from API: ${error.message}`);
48
+ }
49
+ throw error;
50
+ }
51
+ });
52
+ }
53
+ /**
54
+ * Fetches the tarball from api endpoint and returns a readable stream of the tarball contents.
55
+ * Works for both public and private repositories, using a license key when provided.
56
+ *
57
+ * @param info - The repository information required for downloading the tarball.
58
+ * @returns A readable stream of the tarball contents.
59
+ * @throws Will throw an error if the fetch request fails or if there is no response body.
60
+ */
61
+ function downloadTarStream(info) {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ // Construct the endpoint with the license
64
+ const apiUrl = `${config_json_1.default.api_url}/download-repo`;
65
+ try {
66
+ // Create a copy of the info to send to the API
67
+ const requestInfo = Object.assign({}, info);
68
+ // Fetch the tar.gz from the server's /download route
69
+ const response = yield fetch(apiUrl, {
70
+ method: "POST",
71
+ headers: {
72
+ "Content-Type": "application/json",
73
+ Accept: "application/octet-stream",
74
+ },
75
+ body: JSON.stringify(requestInfo),
76
+ });
77
+ // Throw an error if authentication failed (only relevant when a license key is provided)
78
+ if (response.status === 403 || response.status === 404) {
79
+ throw new Error(info.key ? "License key is invalid or expired" : "Repository not found");
80
+ }
81
+ // Throw an error if the response isn't OK
82
+ if (!response.ok) {
83
+ throw new Error(`Failed to download from API. Status: ${response.status} ${response.statusText}`);
84
+ }
85
+ // Throw an error if there's no body
86
+ if (!response.body) {
87
+ throw new Error("Response body is empty");
88
+ }
89
+ // Verify content type if available
90
+ const contentType = response.headers.get("content-type");
91
+ if (contentType &&
92
+ !contentType.includes("application/x-gzip") &&
93
+ !contentType.includes("application/octet-stream") &&
94
+ !contentType.includes("application/tar+gzip")) {
95
+ console.warn(`Warning: Unexpected content type: ${contentType}. Expected tar/gzip related content.`);
96
+ }
97
+ // Convert the web ReadableStream to a Node.js ReadableStream
98
+ return node_stream_1.Readable.fromWeb(response.body);
99
+ }
100
+ catch (error) {
101
+ if (error instanceof Error) {
102
+ throw error;
103
+ }
104
+ throw new Error("Unknown error occurred while downloading the tarball");
105
+ }
106
+ });
107
+ }
108
+ /**
109
+ * Checks if the license key is valid by sending a request to the server.
110
+ *
111
+ * @param key - The license key to be validated by the server
112
+ * @returns A boolean indicating whether the license key is valid
113
+ */
114
+ function checkLicenseKey(key) {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const apiUrl = `${config_json_1.default.api_url}/validate-key?key=${key}`;
117
+ try {
118
+ // Fetch the tar.gz from the server's /download route
119
+ const response = yield fetch(apiUrl);
120
+ if (response.status !== 200) {
121
+ return false;
122
+ }
123
+ return true;
124
+ }
125
+ catch (e) {
126
+ return false;
127
+ }
128
+ });
129
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.downloadAndExtractRepo = downloadAndExtractRepo;
13
+ exports.downloadTarStream = downloadTarStream;
14
+ const node_path_1 = require("node:path");
15
+ const node_stream_1 = require("node:stream");
16
+ const promises_1 = require("node:stream/promises");
17
+ const tar_1 = require("tar");
18
+ /**
19
+ * Downloads and extracts a GitHub repository tarball to the specified directory.
20
+ *
21
+ * @param root - The local directory where the repository should be extracted.
22
+ * @param repoInfo - An object containing repository details such as username, name, and branch.
23
+ * @throws Will throw an error if the download fails.
24
+ */
25
+ function downloadAndExtractRepo(root_1, _a) {
26
+ return __awaiter(this, arguments, void 0, function* (root, { username, repo, branch, template = "basic-form" }) {
27
+ try {
28
+ const url = `https://codeload.github.com/${username}/${repo}/tar.gz/${branch}`;
29
+ const stream = yield downloadTarStream(url);
30
+ yield (0, promises_1.pipeline)(
31
+ // Fetch and stream the tar.gz archive from GitHub
32
+ stream,
33
+ // Extract only the necessary files matching the template name
34
+ (0, tar_1.x)({
35
+ cwd: root, // Extract into the specified root directory
36
+ strip: 2, // Remove first two path segments to keep only relevant files
37
+ filter: (p) => {
38
+ // Normalize paths to POSIX format for consistency
39
+ const posixPath = p.split(node_path_1.sep).join(node_path_1.posix.sep);
40
+ // Extract only files that include the specified template name
41
+ return posixPath.includes(template);
42
+ },
43
+ }));
44
+ }
45
+ catch (error) {
46
+ if (error instanceof Error) {
47
+ // Provide more context about the error
48
+ throw new Error(`Failed to download or extract repository: ${error.message}`);
49
+ }
50
+ throw error;
51
+ }
52
+ });
53
+ }
54
+ /**
55
+ * Downloads a repository tarball as a readable stream from a given URL.
56
+ *
57
+ * @param url - The URL to the tarball file.
58
+ * @returns A readable stream of the tarball contents.
59
+ * @throws Will throw an error if the fetch request fails or if there is no response body.
60
+ */
61
+ function downloadTarStream(url) {
62
+ return __awaiter(this, void 0, void 0, function* () {
63
+ // Fetch the tarball file from GitHub
64
+ const res = yield fetch(url);
65
+ // Check if the response is successful
66
+ if (!res.ok) {
67
+ throw new Error(`Failed to download: ${url} - Status: ${res.status} ${res.statusText}`);
68
+ }
69
+ // Ensure the response body exists before proceeding
70
+ if (!res.body) {
71
+ throw new Error(`Failed to download: ${url} - No response body`);
72
+ }
73
+ // Verify content type if available
74
+ const contentType = res.headers.get("content-type");
75
+ if (contentType && !contentType.includes("application/x-gzip") && !contentType.includes("application/octet-stream")) {
76
+ console.warn(`Warning: Unexpected content type: ${contentType}. Expected application/x-gzip or application/octet-stream.`);
77
+ }
78
+ // Convert the web ReadableStream to a Node.js ReadableStream
79
+ return node_stream_1.Readable.fromWeb(res.body);
80
+ });
81
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.findCssFile = findCssFile;
7
+ const fast_glob_1 = __importDefault(require("fast-glob"));
8
+ const get_project_1 = require("./get-project");
9
+ /**
10
+ * Searches for a CSS file named `theme.css` in the project directory and
11
+ * returns it.
12
+ *
13
+ * @param cwd - The current working directory where the search should begin.
14
+ * @returns The absolute path of the `theme.css` file if found, otherwise `null`.
15
+ */
16
+ function findCssFile(cwd) {
17
+ const files = fast_glob_1.default.sync("**/**/theme.css", { onlyFiles: true, absolute: true, cwd, ignore: get_project_1.PROJECT_SHARED_IGNORE });
18
+ return files.length ? files[0] : null;
19
+ }