torch-glare 1.0.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.
- package/LICENSE +21 -0
- package/README.md +207 -0
- package/cli/bin/addComponent.js +278 -0
- package/cli/bin/addHooks.js +75 -0
- package/cli/bin/addLayout.js +71 -0
- package/cli/bin/addProvider.js +71 -0
- package/cli/bin/addUtils.js +74 -0
- package/cli/bin/cli.js +73 -0
- package/cli/bin/init/init.js +15 -0
- package/cli/bin/init/tailwindInit.js +174 -0
- package/cli/bin/update.js +147 -0
- package/lib/components/ActionButton.tsx +63 -0
- package/lib/components/ActionsGroup.tsx +34 -0
- package/lib/components/AlertDialog.tsx +211 -0
- package/lib/components/Badge.tsx +116 -0
- package/lib/components/BadgeField.tsx +192 -0
- package/lib/components/Button.tsx +277 -0
- package/lib/components/Card.tsx +63 -0
- package/lib/components/Checkbox.tsx +104 -0
- package/lib/components/CountBadge.tsx +54 -0
- package/lib/components/DatePicker.tsx +464 -0
- package/lib/components/Drawer.tsx +118 -0
- package/lib/components/DropdownMenu.tsx +399 -0
- package/lib/components/FieldHint.tsx +76 -0
- package/lib/components/ImageAttachment.tsx +171 -0
- package/lib/components/InnerLabelField.tsx +155 -0
- package/lib/components/Input.tsx +179 -0
- package/lib/components/InputField.tsx +147 -0
- package/lib/components/Label.tsx +107 -0
- package/lib/components/LabelField.tsx +75 -0
- package/lib/components/LabeledCheckBox.tsx +65 -0
- package/lib/components/LabeledRadio.tsx +45 -0
- package/lib/components/LinkButton.tsx +90 -0
- package/lib/components/LoginButton.tsx +56 -0
- package/lib/components/PasswordLevel.tsx +58 -0
- package/lib/components/Popover.tsx +274 -0
- package/lib/components/ProfileMenu.tsx +90 -0
- package/lib/components/Radio.tsx +69 -0
- package/lib/components/RadioCard.tsx +70 -0
- package/lib/components/RingLoading.tsx +190 -0
- package/lib/components/SearchField.tsx +49 -0
- package/lib/components/Select.tsx +417 -0
- package/lib/components/SlideDatePicker.tsx +120 -0
- package/lib/components/SpinLoading.tsx +190 -0
- package/lib/components/Switcher.tsx +56 -0
- package/lib/components/TabFormItem.tsx +158 -0
- package/lib/components/Table.tsx +395 -0
- package/lib/components/Textarea.tsx +108 -0
- package/lib/components/Tooltip.tsx +111 -0
- package/lib/components/TransparentLabel.tsx +72 -0
- package/lib/components/TreeDropDown.tsx +69 -0
- package/lib/hooks/MobileSlidePicker/components/Picker.tsx +218 -0
- package/lib/hooks/MobileSlidePicker/components/PickerColumn.tsx +238 -0
- package/lib/hooks/MobileSlidePicker/components/PickerItem.tsx +64 -0
- package/lib/hooks/MobileSlidePicker/index.ts +10 -0
- package/lib/hooks/useActiveTreeItem.tsx +61 -0
- package/lib/hooks/useClickOutside.tsx +20 -0
- package/lib/hooks/useResize.tsx +78 -0
- package/lib/layouts/CLayout.tsx +326 -0
- package/lib/layouts/FieldSection.tsx +64 -0
- package/lib/layouts/TreeSubLayout.tsx +187 -0
- package/lib/providers/ThemeProvider.tsx +99 -0
- package/lib/utils/cn.ts +6 -0
- package/lib/utils/convertImageFileToDataUrl.ts +17 -0
- package/lib/utils/resize.ts +35 -0
- package/lib/utils/types.ts +12 -0
- package/package.json +28 -0
- package/torch-glare.js +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 TORCH corp.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# TORCH Glare Components Library
|
|
2
|
+
|
|
3
|
+
Welcome to the **TORCH Glare Components Library**! This library provides a collection of reusable React components to help you build user interfaces efficiently. Additionally, a CLI tool (**torch-glare CLI**) is available to streamline component management.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Installation](#installation)
|
|
8
|
+
2. [Usage](#usage)
|
|
9
|
+
3. [CLI Commands](#cli-commands)
|
|
10
|
+
4. [Theming](#theming)
|
|
11
|
+
5. [Contributing](#contributing)
|
|
12
|
+
6. [License](#license)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 1. Initialize Your Project
|
|
18
|
+
|
|
19
|
+
To install the TORCH Glare Components Library, run the following command:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npx torch-glare@latest init
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This command will:
|
|
26
|
+
- Create or modify the `tailwind.config.js` file to support Tailwind CSS for Tailwind versions less then 4.
|
|
27
|
+
- Generate a `torch.json` file where you can customize the installation path for components.
|
|
28
|
+
|
|
29
|
+
### Tailwind CSS Requirement
|
|
30
|
+
Ensure that Tailwind CSS is installed in your project before running the initialization command.
|
|
31
|
+
|
|
32
|
+
## 2. Add Essential Plugins for Tailwind CSS
|
|
33
|
+
|
|
34
|
+
If you're using Tailwind CSS version 4 or above, add the following plugins to your global CSS file:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
@import "tailwindcss";
|
|
38
|
+
/* Essential plugins */
|
|
39
|
+
@plugin 'glare-typography';
|
|
40
|
+
@plugin 'glare-themes';
|
|
41
|
+
@plugin 'glare-torch-mode';
|
|
42
|
+
@plugin 'tailwind-scrollbar-hide';
|
|
43
|
+
@plugin 'tailwindcss-animate';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### 2. Add Remix Icon Library
|
|
48
|
+
Include the following in `index.html` or nextjs `layout.tsx` or meta data for icon support:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<html>
|
|
52
|
+
<head>
|
|
53
|
+
<link
|
|
54
|
+
href="https://cdn.jsdelivr.net/npm/remixicon@4.5.0/fonts/remixicon.css"
|
|
55
|
+
rel="stylesheet"
|
|
56
|
+
/>
|
|
57
|
+
</head>
|
|
58
|
+
<body></body>
|
|
59
|
+
</html>
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 3. Configure Installation Path
|
|
64
|
+
|
|
65
|
+
Adjust the `glare.json` file to specify where you want to install components:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"path": "./src" // The directory where components will be installed
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Add Components
|
|
74
|
+
To add a specific component, run:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npx torch-glare@latest add [component-name]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or, to add components interactively:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npx torch-glare@latest add
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
Once installed, import and use the components as needed:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import React from "react";
|
|
92
|
+
import { Button } from "./components";
|
|
93
|
+
|
|
94
|
+
const App = () => {
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
<Button >Hello.</Button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default App;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## CLI Commands
|
|
106
|
+
|
|
107
|
+
### Initialize Configuration
|
|
108
|
+
```sh
|
|
109
|
+
npx torch-glare@latest init
|
|
110
|
+
```
|
|
111
|
+
- Creates a `torch.json` configuration file.
|
|
112
|
+
- Create or modify `tailwind.config.ts` file for tailwind support.
|
|
113
|
+
|
|
114
|
+
### Add Components
|
|
115
|
+
```sh
|
|
116
|
+
npx torch-glare@latest add [component]
|
|
117
|
+
```
|
|
118
|
+
Adds a specific component or runs an interactive prompt if no name is provided.
|
|
119
|
+
|
|
120
|
+
### Add Hooks
|
|
121
|
+
```sh
|
|
122
|
+
npx torch-glare@latest hook [hook]
|
|
123
|
+
```
|
|
124
|
+
Adds a specific hook or runs an interactive prompt if no name is provided.
|
|
125
|
+
|
|
126
|
+
### Add Utilities
|
|
127
|
+
```sh
|
|
128
|
+
npx torch-glare@latest util [util]
|
|
129
|
+
```
|
|
130
|
+
Adds a specific utility or runs an interactive prompt if no name is provided.
|
|
131
|
+
|
|
132
|
+
### Providers
|
|
133
|
+
```sh
|
|
134
|
+
npx torch-glare@latest provider [provider]
|
|
135
|
+
```
|
|
136
|
+
Adds a specific provider or runs an interactive prompt if no name is provided.
|
|
137
|
+
|
|
138
|
+
### Update Installed Resources
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npx torch-glare@latest update
|
|
142
|
+
```
|
|
143
|
+
Updates all installed components, hooks, utilities, and providers.
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
## Theming
|
|
147
|
+
|
|
148
|
+
The TORCH Glare Components Library supports both light and dark themes. You can set a fixed theme for your components using the `theme` attribute.
|
|
149
|
+
|
|
150
|
+
### Setting a Fixed Theme
|
|
151
|
+
|
|
152
|
+
To apply a fixed theme (dark or light) to a component, add the `theme `attribute with the desired theme value:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import React from "react";
|
|
156
|
+
import { Button } from "./components";
|
|
157
|
+
|
|
158
|
+
const App = () => {
|
|
159
|
+
return (
|
|
160
|
+
<div>
|
|
161
|
+
<Button theme="dark">Dark Theme Button</Button>
|
|
162
|
+
<Button theme="light">Light Theme Button</Button>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default App;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Global Theme
|
|
171
|
+
|
|
172
|
+
To apply a theme globally, wrap your application with the `ThemeProvider` and set the optional `defaultTheme` props:
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { ThemeProvider } from "./components";
|
|
177
|
+
|
|
178
|
+
const App = () => {
|
|
179
|
+
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|
180
|
+
<ThemeProvider defaultTheme="light" defaultThemeMode="TORCH">
|
|
181
|
+
<App />
|
|
182
|
+
</ThemeProvider>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default App;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Contributing
|
|
190
|
+
|
|
191
|
+
We welcome contributions! Follow these steps:
|
|
192
|
+
|
|
193
|
+
1. Fork the repository.
|
|
194
|
+
2. Create a new branch.
|
|
195
|
+
3. Implement your changes.
|
|
196
|
+
4. Commit with a clear message.
|
|
197
|
+
5. Push your changes and open a pull request.
|
|
198
|
+
|
|
199
|
+
### Contribution Guidelines
|
|
200
|
+
- Follow existing code style.
|
|
201
|
+
- Update documentation if necessary.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
This project is licensed under the **MIT License**.
|
|
206
|
+
|
|
207
|
+
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import { getConfig } from "./cli.js";
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { addUtil } from "./addUtils.js";
|
|
8
|
+
import { addHook } from "./addHooks.js";
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
|
|
12
|
+
// Get the current file and directory paths
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
|
|
15
|
+
// Define the path to the templates directory
|
|
16
|
+
const templatesDir = path.resolve(__dirname, "../../lib/components");
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Main function to add a component and its dependencies.
|
|
21
|
+
* @param {string} component - The name of the component to add.
|
|
22
|
+
*/
|
|
23
|
+
export async function addComponent(component) {
|
|
24
|
+
const config = getConfig();
|
|
25
|
+
const availableComponents = getAvailableComponents(templatesDir);
|
|
26
|
+
|
|
27
|
+
// If no component is provided, prompt the user to select one
|
|
28
|
+
if (!component) {
|
|
29
|
+
component = await promptComponentSelection(availableComponents);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validate if the component exists in the templates directory
|
|
33
|
+
if (!availableComponents.includes(component)) {
|
|
34
|
+
console.error(`❌ Component "${component}" not found.`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { source, targetDir } = getComponentPaths(component, config, templatesDir, "components");
|
|
39
|
+
const target = path.join(targetDir, component);
|
|
40
|
+
|
|
41
|
+
// replace the component
|
|
42
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
43
|
+
console.log(`🔄 Replacing "${component}"...`);
|
|
44
|
+
|
|
45
|
+
// Ensure the target directory exists
|
|
46
|
+
ensureDirectoryExists(targetDir);
|
|
47
|
+
|
|
48
|
+
// Copy the component (directory or file) and install dependencies
|
|
49
|
+
copyComponent(source, target, addComponent);
|
|
50
|
+
|
|
51
|
+
console.log(`✅ ${component} has been added to ${config.path}!`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get a list of available components from the templates directory.
|
|
56
|
+
* @param {string} templatesDir - Path to the templates directory.
|
|
57
|
+
* @returns {string[]} - Array of component names.
|
|
58
|
+
*/
|
|
59
|
+
function getAvailableComponents(templatesDir) {
|
|
60
|
+
return fs.readdirSync(templatesDir).map((file) => path.basename(file));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Prompt the user to select a component from a list.
|
|
65
|
+
* @param {string[]} availableComponents - Array of available components.
|
|
66
|
+
* @returns {string} - The selected component.
|
|
67
|
+
*/
|
|
68
|
+
async function promptComponentSelection(availableComponents) {
|
|
69
|
+
const { selectedComponent } = await inquirer.prompt([
|
|
70
|
+
{
|
|
71
|
+
type: "list",
|
|
72
|
+
name: "selectedComponent",
|
|
73
|
+
message: "Which component would you like to add?",
|
|
74
|
+
choices: availableComponents,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
return selectedComponent;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get the source and target paths for the component.
|
|
82
|
+
* @param {string} component - The name of the component.
|
|
83
|
+
* @param {object} config - Configuration object.
|
|
84
|
+
* @returns {object} - Object containing source and target directory paths.
|
|
85
|
+
*/
|
|
86
|
+
export function getComponentPaths(component, config, templatesDir, saveFolderName) {
|
|
87
|
+
const source = path.join(templatesDir, `${component}`);
|
|
88
|
+
const normalizedPath = config.path.replace("@/", "");
|
|
89
|
+
const targetDir = path.join(process.cwd(), normalizedPath, saveFolderName);
|
|
90
|
+
return { source, targetDir };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Ensure the target directory exists.
|
|
95
|
+
* @param {string} targetDir - The target directory path.
|
|
96
|
+
*/
|
|
97
|
+
export function ensureDirectoryExists(targetDir) {
|
|
98
|
+
if (!fs.existsSync(targetDir)) {
|
|
99
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Copy a component (directory or file) and install its dependencies.
|
|
105
|
+
* @param {string} source - The source path of the component.
|
|
106
|
+
* @param {string} target - The target path of the component.
|
|
107
|
+
*/
|
|
108
|
+
export function copyComponent(source, target, addFunction) {
|
|
109
|
+
if (fs.lstatSync(source).isDirectory()) {
|
|
110
|
+
copyDirectorySync(source, target, addFunction);
|
|
111
|
+
} else {
|
|
112
|
+
fs.copyFileSync(source, target);
|
|
113
|
+
installDependencies(source, addFunction); // Pass addFunction here
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Detect the package manager used in the project.
|
|
119
|
+
* @returns {string} - The detected package manager (pnpm, yarn, or npm).
|
|
120
|
+
*/
|
|
121
|
+
export function detectPackageManager() {
|
|
122
|
+
if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml"))) return "pnpm";
|
|
123
|
+
if (fs.existsSync(path.join(process.cwd(), "yarn.lock"))) return "yarn";
|
|
124
|
+
if (fs.existsSync(path.join(process.cwd(), "package-lock.json"))) return "npm";
|
|
125
|
+
return "npm"; // Default to npm if no lock file is found
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get the installed dependencies from the project's package.json.
|
|
130
|
+
* @returns {Set<string>} - Set of installed dependencies.
|
|
131
|
+
*/
|
|
132
|
+
export function getCurrentInstalledDependencies() {
|
|
133
|
+
const packageJsonPath = path.join(process.cwd(), "package.json");
|
|
134
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
135
|
+
console.error("❌ No package.json found. Run `npm init` or `yarn init` first.");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
140
|
+
|
|
141
|
+
const depsNames = new Set([
|
|
142
|
+
...Object.keys(packageJson.dependencies || {}),
|
|
143
|
+
...Object.keys(packageJson.devDependencies || {}),
|
|
144
|
+
]);
|
|
145
|
+
|
|
146
|
+
const depsNamesAndVersions = {
|
|
147
|
+
...(packageJson.dependencies || {}),
|
|
148
|
+
...(packageJson.devDependencies || {}),
|
|
149
|
+
};
|
|
150
|
+
return { depsNames, depsNamesAndVersions }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extract dependencies from a component file.
|
|
155
|
+
* @param {string} componentPath - Path to the component file.
|
|
156
|
+
* @param {Set<string>} installedDependencies - Set of installed dependencies.
|
|
157
|
+
* @param {function} addFunction - Function to make add operation
|
|
158
|
+
* @returns {Set<string>} - Set of dependencies to install.
|
|
159
|
+
*/
|
|
160
|
+
function getDependenciesToInstall(componentPath, installedDependencies, addFunction) {
|
|
161
|
+
const componentContent = fs.readFileSync(componentPath, "utf-8");
|
|
162
|
+
const importRegex = /import\s+.*?\s+from\s+['"]([^'"]+)['"]/g;
|
|
163
|
+
const dependenciesToInstall = new Set();
|
|
164
|
+
|
|
165
|
+
let match;
|
|
166
|
+
while ((match = importRegex.exec(componentContent)) !== null) {
|
|
167
|
+
const moduleName = match[1];
|
|
168
|
+
|
|
169
|
+
if (!moduleName.startsWith(".") && !installedDependencies.has(moduleName)) {
|
|
170
|
+
dependenciesToInstall.add(moduleName);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// install required utils.
|
|
174
|
+
else if (
|
|
175
|
+
moduleName.startsWith("../utils") &&
|
|
176
|
+
!installedDependencies.has(moduleName)
|
|
177
|
+
) {
|
|
178
|
+
addUtil(moduleName.slice(9) + ".ts"); // Use addFunction here
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// install required hooks.
|
|
182
|
+
else if (
|
|
183
|
+
moduleName.startsWith("../hooks") &&
|
|
184
|
+
!installedDependencies.has(moduleName)
|
|
185
|
+
) {
|
|
186
|
+
addHook(moduleName.slice(9)); // Use addFunction here
|
|
187
|
+
}
|
|
188
|
+
// install required components
|
|
189
|
+
else if (
|
|
190
|
+
moduleName.startsWith("./") ||
|
|
191
|
+
!moduleName.startsWith("../components") &&
|
|
192
|
+
!installedDependencies.has(moduleName)
|
|
193
|
+
) {
|
|
194
|
+
addComponent(moduleName.slice(2) + ".tsx"); // Use addFunction here
|
|
195
|
+
}
|
|
196
|
+
// install required for layouts components
|
|
197
|
+
else if (
|
|
198
|
+
moduleName.startsWith("../components") &&
|
|
199
|
+
!installedDependencies.has(moduleName)
|
|
200
|
+
) {
|
|
201
|
+
addComponent(moduleName.slice(14) + ".tsx"); // Use addFunction here
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return dependenciesToInstall;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Install dependencies for a component.
|
|
209
|
+
* @param {string} componentPath - Path to the component file.
|
|
210
|
+
*/
|
|
211
|
+
export function installDependencies(componentPath, addFunction) {
|
|
212
|
+
const { depsNames } = getCurrentInstalledDependencies();
|
|
213
|
+
const dependenciesToInstall = getDependenciesToInstall(
|
|
214
|
+
componentPath,
|
|
215
|
+
depsNames,
|
|
216
|
+
addFunction // Pass addFunction here
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (dependenciesToInstall.size > 0) {
|
|
220
|
+
const packageManager = detectPackageManager();
|
|
221
|
+
const installCommand = getInstallCommand(packageManager, dependenciesToInstall);
|
|
222
|
+
|
|
223
|
+
console.log(
|
|
224
|
+
`📦 Installing missing dependencies using ${packageManager}:`,
|
|
225
|
+
[...dependenciesToInstall].join(", ")
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
execSync(installCommand, { stdio: "inherit" });
|
|
230
|
+
console.log("✅ Dependencies installed successfully.");
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error("❌ Error installing dependencies:", error.message);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
console.log("✅ All dependencies are already installed.");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Generate the installation command based on the package manager.
|
|
240
|
+
* @param {string} packageManager - The package manager (pnpm, yarn, or npm).
|
|
241
|
+
* @param {Set<string>} dependencies - Set of dependencies to install.
|
|
242
|
+
* @returns {string} - The installation command.
|
|
243
|
+
*/
|
|
244
|
+
function getInstallCommand(packageManager, dependencies) {
|
|
245
|
+
const deps = [...dependencies].join(" ");
|
|
246
|
+
switch (packageManager) {
|
|
247
|
+
case "pnpm":
|
|
248
|
+
return `pnpm add ${deps}`;
|
|
249
|
+
case "yarn":
|
|
250
|
+
return `yarn add ${deps}`;
|
|
251
|
+
default:
|
|
252
|
+
return `npm install ${deps}`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Copy a directory and its contents recursively.
|
|
258
|
+
* @param {string} source - Source directory path.
|
|
259
|
+
* @param {string} target - Target directory path.
|
|
260
|
+
*/
|
|
261
|
+
export function copyDirectorySync(source, target, addFunction) {
|
|
262
|
+
if (!fs.existsSync(target)) {
|
|
263
|
+
fs.mkdirSync(target, { recursive: true });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const items = fs.readdirSync(source, { withFileTypes: true });
|
|
267
|
+
for (const item of items) {
|
|
268
|
+
const sourcePath = path.join(source, item.name);
|
|
269
|
+
const targetPath = path.join(target, item.name);
|
|
270
|
+
|
|
271
|
+
if (item.isDirectory()) {
|
|
272
|
+
copyDirectorySync(sourcePath, targetPath, addFunction);
|
|
273
|
+
} else {
|
|
274
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
275
|
+
installDependencies(sourcePath, addFunction); // Pass addFunction here
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getConfig } from "./cli.js";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { ensureDirectoryExists, getComponentPaths, copyComponent } from "./addComponent.js";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Define the path to the hooks templates directory
|
|
12
|
+
const hooksTemplatesDir = path.resolve(__dirname, "../../lib/hooks");
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Main function to add a hook and its dependencies.
|
|
17
|
+
* @param {string} hook - The name of the hook to add.
|
|
18
|
+
*/
|
|
19
|
+
export async function addHook(hook) {
|
|
20
|
+
const config = getConfig();
|
|
21
|
+
const availableHooks = getAvailableHooks(hooksTemplatesDir);
|
|
22
|
+
|
|
23
|
+
// If no hook is provided, prompt the user to select one
|
|
24
|
+
if (!hook) {
|
|
25
|
+
hook = await promptHookSelection(availableHooks);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Validate if the hook exists in the hooks templates directory
|
|
29
|
+
if (!availableHooks.includes(hook)) {
|
|
30
|
+
// console.error(`❌ Hook "${hook}" not found.`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// get the path and create the create the target directory
|
|
35
|
+
const { source, targetDir } = getComponentPaths(hook, config, hooksTemplatesDir, "hooks");
|
|
36
|
+
const target = path.join(targetDir, hook);
|
|
37
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
38
|
+
|
|
39
|
+
// Ensure the target directory exists
|
|
40
|
+
ensureDirectoryExists(targetDir);
|
|
41
|
+
|
|
42
|
+
// Copy the hook (file) and install dependencies
|
|
43
|
+
copyComponent(source, target, addHook);
|
|
44
|
+
|
|
45
|
+
console.log(`✅ ${hook} has been added to ${config.path}!`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get a list of available hooks from the hooks templates directory.
|
|
50
|
+
* @param {string} hooksTemplatesDir - Path to the hooks templates directory.
|
|
51
|
+
* @returns {string[]} - Array of hook names.
|
|
52
|
+
*/
|
|
53
|
+
function getAvailableHooks(hooksTemplatesDir) {
|
|
54
|
+
return fs.readdirSync(hooksTemplatesDir).map((file) => path.basename(file));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Prompt the user to select a hook from a list.
|
|
60
|
+
* @param {string[]} availableHooks - Array of available hooks.
|
|
61
|
+
* @returns {string} - The selected hook.
|
|
62
|
+
*/
|
|
63
|
+
async function promptHookSelection(availableHooks) {
|
|
64
|
+
const { selectedHook } = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: "list",
|
|
67
|
+
name: "selectedHook",
|
|
68
|
+
message: "Which hook would you like to add?",
|
|
69
|
+
choices: availableHooks,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
return selectedHook;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getConfig } from "./cli.js";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { ensureDirectoryExists, getComponentPaths, copyComponent } from "./addComponent.js";
|
|
6
|
+
import inquirer from "inquirer";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
// Define the path to the layouts templates directory
|
|
12
|
+
const layoutsTemplatesDir = path.resolve(__dirname, "../../lib/layouts");
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Main function to add a layout and its dependencies.
|
|
16
|
+
* @param {string} layout - The name of the layout to add.
|
|
17
|
+
*/
|
|
18
|
+
export async function addLayout(layout) {
|
|
19
|
+
const config = getConfig();
|
|
20
|
+
const availableLayouts = getAvailableLayouts(layoutsTemplatesDir);
|
|
21
|
+
|
|
22
|
+
// If no layout is provided, prompt the user to select one
|
|
23
|
+
if (!layout) {
|
|
24
|
+
layout = await promptLayoutSelection(availableLayouts);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate if the layout exists in the layouts templates directory
|
|
28
|
+
if (!availableLayouts.includes(layout)) {
|
|
29
|
+
// console.error(`❌ Layout "${layout}" not found.`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// get the path and create the create the target directory
|
|
34
|
+
const { source, targetDir } = getComponentPaths(layout, config, layoutsTemplatesDir, "layouts");
|
|
35
|
+
const target = path.join(targetDir, layout);
|
|
36
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
37
|
+
|
|
38
|
+
// Ensure the target directory exists
|
|
39
|
+
ensureDirectoryExists(targetDir);
|
|
40
|
+
|
|
41
|
+
// Copy the layout (file) and install dependencies
|
|
42
|
+
copyComponent(source, target, addLayout);
|
|
43
|
+
|
|
44
|
+
console.log(`✅ ${layout} has been added to ${config.path}!`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a list of available layouts from the layouts templates directory.
|
|
49
|
+
* @param {string} layoutsTemplatesDir - Path to the layouts templates directory.
|
|
50
|
+
* @returns {string[]} - Array of layout names.
|
|
51
|
+
*/
|
|
52
|
+
function getAvailableLayouts(layoutsTemplatesDir) {
|
|
53
|
+
return fs.readdirSync(layoutsTemplatesDir).map((file) => path.basename(file));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Prompt the user to select a layout from a list.
|
|
58
|
+
* @param {string[]} availableLayouts - Array of available layouts.
|
|
59
|
+
* @returns {string} - The selected layout.
|
|
60
|
+
*/
|
|
61
|
+
async function promptLayoutSelection(availableLayouts) {
|
|
62
|
+
const { selectedLayout } = await inquirer.prompt([
|
|
63
|
+
{
|
|
64
|
+
type: "list",
|
|
65
|
+
name: "selectedLayout",
|
|
66
|
+
message: "Which layout would you like to add?",
|
|
67
|
+
choices: availableLayouts,
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
return selectedLayout;
|
|
71
|
+
}
|