@skalfa/skalfa-cli 1.0.10 → 1.0.12
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/bin/skalfa.js +16 -5
- package/dist/commands/add-extension.js +69 -0
- package/dist/commands/create-api.js +6 -0
- package/dist/commands/init.js +21 -7
- package/dist/commands/pick.js +131 -3
- package/package.json +1 -1
package/dist/bin/skalfa.js
CHANGED
|
@@ -62,7 +62,7 @@ program
|
|
|
62
62
|
program
|
|
63
63
|
.command("init")
|
|
64
64
|
.description("Initialize a new Skalfa monorepo project containing both API and App.")
|
|
65
|
-
.argument("
|
|
65
|
+
.argument("[name]", "project folder name")
|
|
66
66
|
.action(async (name) => {
|
|
67
67
|
await runCommand(() => (0, init_1.initProject)(name));
|
|
68
68
|
});
|
|
@@ -89,10 +89,21 @@ program
|
|
|
89
89
|
});
|
|
90
90
|
program
|
|
91
91
|
.command("pick")
|
|
92
|
-
.description("Eject/copy a core utility
|
|
93
|
-
.argument("<
|
|
94
|
-
.
|
|
95
|
-
|
|
92
|
+
.description("Eject/copy a core utility or component into your local project for customization.")
|
|
93
|
+
.argument("<name>", "utility or component name")
|
|
94
|
+
.argument("[newName]", "new name for the component (optional, component only)")
|
|
95
|
+
.action(async (name, newName) => {
|
|
96
|
+
await runCommand(() => {
|
|
97
|
+
if (pick_1.UTILITIES.includes(name)) {
|
|
98
|
+
if (newName) {
|
|
99
|
+
throw new Error("Renaming is only supported for components, not utilities.");
|
|
100
|
+
}
|
|
101
|
+
(0, pick_1.pickUtility)(name);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
(0, pick_1.pickComponent)(name, newName);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
96
107
|
});
|
|
97
108
|
program
|
|
98
109
|
.command("update")
|
|
@@ -43,6 +43,75 @@ async function addExtension(extensionName) {
|
|
|
43
43
|
(0, installer_1.installPackage)(projectRoot, isDev ? "file:../skalfa-idb" : "@skalfa/skalfa-idb");
|
|
44
44
|
addTsconfigPath(node_path_1.default.join(projectRoot, "tsconfig.json"), "@skalfa/skalfa-idb");
|
|
45
45
|
addUtilExport(node_path_1.default.join(projectRoot, "utils", "index.ts"), "@skalfa/skalfa-idb");
|
|
46
|
+
// Scaffold IDBProvider
|
|
47
|
+
console.log("Scaffolding IDBProvider...");
|
|
48
|
+
const providerDir = node_path_1.default.join(projectRoot, "components", "base.components", "wrap");
|
|
49
|
+
if (!node_fs_1.default.existsSync(providerDir)) {
|
|
50
|
+
node_fs_1.default.mkdirSync(providerDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
const providerPath = node_path_1.default.join(providerDir, "IDBProvider.tsx");
|
|
53
|
+
const providerContent = `"use client"
|
|
54
|
+
|
|
55
|
+
import { useEffect } from "react"
|
|
56
|
+
import { idb } from "@skalfa/skalfa-idb"
|
|
57
|
+
import { AppSchema } from "@schema"
|
|
58
|
+
import { registry } from "@utils"
|
|
59
|
+
|
|
60
|
+
export function IDBProvider({ children }: { children: React.ReactNode }) {
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
idb.setDefaultSchema(AppSchema);
|
|
63
|
+
registry.register("idb", idb);
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
return <>{children}</>
|
|
67
|
+
}
|
|
68
|
+
`;
|
|
69
|
+
node_fs_1.default.writeFileSync(providerPath, providerContent, "utf8");
|
|
70
|
+
console.log(`Created: ${providerPath}`);
|
|
71
|
+
// Scaffold app.schema.ts
|
|
72
|
+
console.log("Scaffolding AppSchema...");
|
|
73
|
+
const schemaDir = node_path_1.default.join(projectRoot, "schema", "idb");
|
|
74
|
+
if (!node_fs_1.default.existsSync(schemaDir)) {
|
|
75
|
+
node_fs_1.default.mkdirSync(schemaDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
const schemaPath = node_path_1.default.join(schemaDir, "app.schema.ts");
|
|
78
|
+
const schemaContent = `import { DBSchema } from "@skalfa/skalfa-idb"
|
|
79
|
+
|
|
80
|
+
const name = String(process.env.NEXT_PUBLIC_APP_NAME || "").toLowerCase().trim().replace(/[^\\w\\s-]/g, "").replace(/[\\s_-]+/g, "-").replace(/^-+|-+$/g, "") + ".idb-app";
|
|
81
|
+
|
|
82
|
+
export const AppSchema: DBSchema = {
|
|
83
|
+
name: name,
|
|
84
|
+
version: 1,
|
|
85
|
+
stores: {}
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
node_fs_1.default.writeFileSync(schemaPath, schemaContent, "utf8");
|
|
89
|
+
console.log(`Created: ${schemaPath}`);
|
|
90
|
+
// Ensure schema/index.ts exists so the @schema alias works
|
|
91
|
+
const schemaIndexPath = node_path_1.default.join(projectRoot, "schema", "index.ts");
|
|
92
|
+
if (!node_fs_1.default.existsSync(schemaIndexPath)) {
|
|
93
|
+
node_fs_1.default.writeFileSync(schemaIndexPath, `export * from "./idb/app.schema";\n`, "utf8");
|
|
94
|
+
console.log(`Created: ${schemaIndexPath}`);
|
|
95
|
+
}
|
|
96
|
+
// Update app/layout.tsx
|
|
97
|
+
console.log("Updating app/layout.tsx...");
|
|
98
|
+
const layoutPath = node_path_1.default.join(projectRoot, "app", "layout.tsx");
|
|
99
|
+
if (node_fs_1.default.existsSync(layoutPath)) {
|
|
100
|
+
let layoutContent = node_fs_1.default.readFileSync(layoutPath, "utf8");
|
|
101
|
+
// 1. Add IDBProvider to import
|
|
102
|
+
if (layoutContent.includes('import { ShortcutProvider } from "@components";')) {
|
|
103
|
+
layoutContent = layoutContent.replace('import { ShortcutProvider } from "@components";', 'import { IDBProvider, ShortcutProvider } from "@components";');
|
|
104
|
+
}
|
|
105
|
+
else if (layoutContent.includes('import { ShortcutProvider } from "@components"')) {
|
|
106
|
+
layoutContent = layoutContent.replace('import { ShortcutProvider } from "@components"', 'import { IDBProvider, ShortcutProvider } from "@components"');
|
|
107
|
+
}
|
|
108
|
+
// 2. Wrap {children} with <IDBProvider>
|
|
109
|
+
if (layoutContent.includes("{children}") && !layoutContent.includes("<IDBProvider>")) {
|
|
110
|
+
layoutContent = layoutContent.replace(/\{\s*children\s*\}/, `<IDBProvider>\n {children}\n </IDBProvider>`);
|
|
111
|
+
}
|
|
112
|
+
node_fs_1.default.writeFileSync(layoutPath, layoutContent, "utf8");
|
|
113
|
+
console.log(`Updated: ${layoutPath}`);
|
|
114
|
+
}
|
|
46
115
|
}
|
|
47
116
|
else if (extensionName === "socket") {
|
|
48
117
|
console.log("Installing Skalfa Socket.io client extension...");
|
|
@@ -152,6 +152,12 @@ Here is the list of features and their development status:
|
|
|
152
152
|
| :--- | :--- | :---: |
|
|
153
153
|
| **Login** | API Login | \\\`[x] Completed\\\` |
|
|
154
154
|
|
|
155
|
+
## API Documentation
|
|
156
|
+
The API documentation is automatically generated and updated in the \\\`./docs/\\\` folder.
|
|
157
|
+
|
|
158
|
+
To generate or update the documentation, run:
|
|
159
|
+
\\\`bun skalfa generate:docs\\\`
|
|
160
|
+
|
|
155
161
|
## Development Setup
|
|
156
162
|
|
|
157
163
|
### Prerequisites
|
package/dist/commands/init.js
CHANGED
|
@@ -32,10 +32,19 @@ class Questioner {
|
|
|
32
32
|
}
|
|
33
33
|
async function initProject(projectName) {
|
|
34
34
|
const cwd = process.cwd();
|
|
35
|
-
const
|
|
35
|
+
const isCurrentDir = !projectName || projectName === ".";
|
|
36
|
+
const target = isCurrentDir ? cwd : node_path_1.default.resolve(cwd, projectName);
|
|
37
|
+
const resolvedProjectName = isCurrentDir ? (node_path_1.default.basename(cwd) || "skalfa-project") : projectName;
|
|
36
38
|
(0, fs_1.assertInsideDirectory)(cwd, target);
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
+
if (isCurrentDir) {
|
|
40
|
+
if ((0, fs_1.exists)(node_path_1.default.join(target, "api")) || (0, fs_1.exists)(node_path_1.default.join(target, "app")) || (0, fs_1.exists)(node_path_1.default.join(target, "package.json"))) {
|
|
41
|
+
throw new Error(`Current directory already contains conflicting files/folders (api, app, or package.json).`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
if ((0, fs_1.exists)(target)) {
|
|
46
|
+
throw new Error(`Target directory already exists: ${target}`);
|
|
47
|
+
}
|
|
39
48
|
}
|
|
40
49
|
// Ask interactive questions sequentially
|
|
41
50
|
const q = new Questioner();
|
|
@@ -110,7 +119,7 @@ async function initProject(projectName) {
|
|
|
110
119
|
console.log("\nConfiguring root files...");
|
|
111
120
|
// 1. Root package.json
|
|
112
121
|
const rootPackageJson = {
|
|
113
|
-
name:
|
|
122
|
+
name: resolvedProjectName,
|
|
114
123
|
private: true,
|
|
115
124
|
workspaces: [
|
|
116
125
|
"api",
|
|
@@ -124,7 +133,7 @@ async function initProject(projectName) {
|
|
|
124
133
|
`;
|
|
125
134
|
node_fs_1.default.writeFileSync(node_path_1.default.join(target, ".gitignore"), rootGitignore, "utf8");
|
|
126
135
|
// 3. Root README.md
|
|
127
|
-
const rootReadme = `# ${
|
|
136
|
+
const rootReadme = `# ${resolvedProjectName}
|
|
128
137
|
|
|
129
138
|
This is a Skalfa monorepo project containing both the backend (API) and the frontend (App).
|
|
130
139
|
|
|
@@ -170,6 +179,11 @@ This is a combined Skalfa project containing both the backend (\`api\`) and fron
|
|
|
170
179
|
3. Always ensure that API contracts and communication between the frontend and backend are aligned.
|
|
171
180
|
`;
|
|
172
181
|
node_fs_1.default.writeFileSync(node_path_1.default.join(agentsDir, "AGENTS.md"), agentsMd, "utf8");
|
|
173
|
-
console.log(`\nSuccessfully initialized ${
|
|
174
|
-
|
|
182
|
+
console.log(`\nSuccessfully initialized ${resolvedProjectName}!`);
|
|
183
|
+
if (isCurrentDir) {
|
|
184
|
+
console.log(`\nNext steps:\n bun install\n bun run --cwd api dev (or cd api && bun run dev)\n bun run --cwd app dev (or cd app && bun run dev)`);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.log(`\nNext steps:\n cd ${projectName}\n bun install\n bun run --cwd api dev (or cd api && bun run dev)\n bun run --cwd app dev (or cd app && bun run dev)`);
|
|
188
|
+
}
|
|
175
189
|
}
|
package/dist/commands/pick.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.UTILITIES = exports.UTILITY_EXPORTS = void 0;
|
|
7
7
|
exports.pickUtility = pickUtility;
|
|
8
|
+
exports.pickComponent = pickComponent;
|
|
8
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
11
|
const fs_1 = require("../utils/fs");
|
|
@@ -38,7 +39,6 @@ function pickUtility(utilityName) {
|
|
|
38
39
|
if (!(0, fs_1.exists)(utilsDir) || !(0, fs_1.exists)(indexPath)) {
|
|
39
40
|
throw new Error("Folder utils or utils/index.ts not found. Make sure you are at the project root.");
|
|
40
41
|
}
|
|
41
|
-
// 1. Tentukan path source dari node_modules dan target di lokal proyek
|
|
42
42
|
const corePackagePath = node_path_1.default.join(projectRoot, "node_modules", "@skalfa", "skalfa-api-core");
|
|
43
43
|
const sourceDir = node_path_1.default.join(corePackagePath, "src", utilityName);
|
|
44
44
|
const targetDir = node_path_1.default.join(utilsDir, utilityName);
|
|
@@ -48,7 +48,6 @@ function pickUtility(utilityName) {
|
|
|
48
48
|
if ((0, fs_1.exists)(targetDir)) {
|
|
49
49
|
throw new Error(`Utility folder "${utilityName}" is already present in your local utils folder.`);
|
|
50
50
|
}
|
|
51
|
-
// 2. Salin folder dari node_modules ke lokal proyek secara rekursif
|
|
52
51
|
console.log(`Copying ${utilityName} folder from @skalfa/skalfa-api-core to utils/ ...`);
|
|
53
52
|
node_fs_1.default.cpSync(sourceDir, targetDir, { recursive: true });
|
|
54
53
|
const filesToDelete = ["CONTRIBUTING.md", "LICENSE"];
|
|
@@ -59,7 +58,6 @@ function pickUtility(utilityName) {
|
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
console.log(`✓ Copied ${utilityName} folder`);
|
|
62
|
-
// 3. Perbarui utils/index.ts untuk mereferensikan folder lokal
|
|
63
61
|
console.log("Updating utils/index.ts with explicit local export override ...");
|
|
64
62
|
let indexContent = node_fs_1.default.readFileSync(indexPath, "utf8").trim();
|
|
65
63
|
const localExportLine = `export { ${utilitySymbols.join(", ")} } from "./${utilityName}";`;
|
|
@@ -73,3 +71,133 @@ function pickUtility(utilityName) {
|
|
|
73
71
|
}
|
|
74
72
|
console.log(`\nSuccess! You can now customize files under: utils/${utilityName}/`);
|
|
75
73
|
}
|
|
74
|
+
function findComponentFolder(srcDir, componentName) {
|
|
75
|
+
if (!node_fs_1.default.existsSync(srcDir))
|
|
76
|
+
return null;
|
|
77
|
+
const folders = node_fs_1.default.readdirSync(srcDir);
|
|
78
|
+
for (const folder of folders) {
|
|
79
|
+
const folderPath = node_path_1.default.join(srcDir, folder);
|
|
80
|
+
if (!node_fs_1.default.statSync(folderPath).isDirectory())
|
|
81
|
+
continue;
|
|
82
|
+
const files = node_fs_1.default.readdirSync(folderPath);
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
if (!file.endsWith(".tsx") && !file.endsWith(".ts"))
|
|
85
|
+
continue;
|
|
86
|
+
const filePath = node_path_1.default.join(folderPath, file);
|
|
87
|
+
const content = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
88
|
+
// Match the component name or the component name with "Component" suffix (e.g. Button or ButtonComponent)
|
|
89
|
+
const regex = new RegExp(`export\\s+(async\\s+)?(function|const|class|type|interface)\\s+\\b(${componentName}|${componentName}Component)\\b`);
|
|
90
|
+
if (regex.test(content)) {
|
|
91
|
+
return { folderName: folder, fileName: file };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function pickComponent(componentName, newName) {
|
|
98
|
+
const projectRoot = (0, fs_1.findProjectRoot)(process.cwd());
|
|
99
|
+
if (!projectRoot) {
|
|
100
|
+
throw new Error("No package.json found. Run this command inside a Skalfa project.");
|
|
101
|
+
}
|
|
102
|
+
// 1. Locate the component source in @skalfa/skalfa-component
|
|
103
|
+
let componentSrcDir = node_path_1.default.join(projectRoot, "node_modules", "@skalfa", "skalfa-component", "src");
|
|
104
|
+
if (!node_fs_1.default.existsSync(componentSrcDir)) {
|
|
105
|
+
componentSrcDir = node_path_1.default.resolve(projectRoot, "..", "skalfa-component", "src");
|
|
106
|
+
}
|
|
107
|
+
// Clean the componentName if it has "Component" suffix for searching
|
|
108
|
+
const cleanComponentName = componentName.endsWith("Component") ? componentName.replace(/Component$/, "") : componentName;
|
|
109
|
+
const match = findComponentFolder(componentSrcDir, cleanComponentName);
|
|
110
|
+
if (!match) {
|
|
111
|
+
throw new Error(`Component "${componentName}" not found in @skalfa/skalfa-component.`);
|
|
112
|
+
}
|
|
113
|
+
const { folderName, fileName } = match;
|
|
114
|
+
const sourceFolder = node_path_1.default.join(componentSrcDir, folderName);
|
|
115
|
+
const sourceFilePath = node_path_1.default.join(sourceFolder, fileName);
|
|
116
|
+
// Extract base name of the component file (e.g. "Button" from "Button.component.tsx")
|
|
117
|
+
const fileBaseName = fileName.replace(/\.(component)?\.(tsx|ts)$/, "");
|
|
118
|
+
// Find all exported symbols in the file that start with or match the file base name
|
|
119
|
+
const sourceContent = node_fs_1.default.readFileSync(sourceFilePath, "utf8");
|
|
120
|
+
const exportRegex = new RegExp(`export\\s+(async\\s+)?(function|const|class|type|interface)\\s+\\b(${fileBaseName}\\w*)\\b`, "g");
|
|
121
|
+
const exportedTypes = [];
|
|
122
|
+
const exportedValues = [];
|
|
123
|
+
let exportMatch;
|
|
124
|
+
while ((exportMatch = exportRegex.exec(sourceContent)) !== null) {
|
|
125
|
+
const keyword = exportMatch[2];
|
|
126
|
+
const symbol = exportMatch[3];
|
|
127
|
+
if (keyword === "type" || keyword === "interface") {
|
|
128
|
+
exportedTypes.push(symbol);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
exportedValues.push(symbol);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const componentsDir = node_path_1.default.join(projectRoot, "components");
|
|
135
|
+
const indexPath = node_path_1.default.join(componentsDir, "index.ts");
|
|
136
|
+
if (!node_fs_1.default.existsSync(componentsDir)) {
|
|
137
|
+
node_fs_1.default.mkdirSync(componentsDir, { recursive: true });
|
|
138
|
+
}
|
|
139
|
+
// Ensure index.ts exists
|
|
140
|
+
if (!node_fs_1.default.existsSync(indexPath)) {
|
|
141
|
+
node_fs_1.default.writeFileSync(indexPath, `export * from "@skalfa/skalfa-component";\n`, "utf8");
|
|
142
|
+
}
|
|
143
|
+
// Determine target names
|
|
144
|
+
const targetFolderName = newName ? newName.charAt(0).toLowerCase() + newName.slice(1) : folderName;
|
|
145
|
+
const targetFolder = node_path_1.default.join(componentsDir, targetFolderName);
|
|
146
|
+
if (node_fs_1.default.existsSync(targetFolder)) {
|
|
147
|
+
throw new Error(`Component folder "${targetFolderName}" already exists in components/.`);
|
|
148
|
+
}
|
|
149
|
+
console.log(`Copying component "${cleanComponentName}" from ${sourceFolder} to components/${targetFolderName} ...`);
|
|
150
|
+
node_fs_1.default.cpSync(sourceFolder, targetFolder, { recursive: true });
|
|
151
|
+
let finalFileBaseName = fileBaseName;
|
|
152
|
+
let finalExportedTypes = [...exportedTypes];
|
|
153
|
+
let finalExportedValues = [...exportedValues];
|
|
154
|
+
// If renaming is requested, perform renaming of files and contents
|
|
155
|
+
if (newName) {
|
|
156
|
+
const cleanNewName = newName.endsWith("Component") ? newName.replace(/Component$/, "") : newName;
|
|
157
|
+
finalFileBaseName = cleanNewName;
|
|
158
|
+
finalExportedTypes = exportedTypes.map(sym => sym.replace(new RegExp(fileBaseName, "g"), cleanNewName));
|
|
159
|
+
finalExportedValues = exportedValues.map(sym => sym.replace(new RegExp(fileBaseName, "g"), cleanNewName));
|
|
160
|
+
const files = node_fs_1.default.readdirSync(targetFolder);
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
const filePath = node_path_1.default.join(targetFolder, file);
|
|
163
|
+
if (node_fs_1.default.statSync(filePath).isDirectory())
|
|
164
|
+
continue;
|
|
165
|
+
// Read file content and replace component names
|
|
166
|
+
let content = node_fs_1.default.readFileSync(filePath, "utf8");
|
|
167
|
+
// Replace fileBaseName (e.g. Button) with cleanNewName (e.g. MyButton)
|
|
168
|
+
const nameRegex = new RegExp(fileBaseName, "g");
|
|
169
|
+
content = content.replace(nameRegex, cleanNewName);
|
|
170
|
+
node_fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
171
|
+
// Rename the file if it contains the original file base name
|
|
172
|
+
if (file.includes(fileBaseName)) {
|
|
173
|
+
const newFile = file.replace(fileBaseName, cleanNewName);
|
|
174
|
+
node_fs_1.default.renameSync(filePath, node_path_1.default.join(targetFolder, newFile));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Update components/index.ts to export the local component explicitly
|
|
179
|
+
let indexContent = node_fs_1.default.readFileSync(indexPath, "utf8");
|
|
180
|
+
// Find the exact filename of the main component file inside the target folder
|
|
181
|
+
const targetFiles = node_fs_1.default.readdirSync(targetFolder);
|
|
182
|
+
const componentFile = targetFiles.find(f => f.includes(finalFileBaseName) && (f.endsWith(".tsx") || f.endsWith(".ts")));
|
|
183
|
+
if (componentFile) {
|
|
184
|
+
const componentBaseFile = componentFile.replace(/\.(tsx|ts)$/, "");
|
|
185
|
+
const valueExport = finalExportedValues.length > 0
|
|
186
|
+
? `export { ${finalExportedValues.join(", ")} } from "./${targetFolderName}/${componentBaseFile}";`
|
|
187
|
+
: "";
|
|
188
|
+
const typeExport = finalExportedTypes.length > 0
|
|
189
|
+
? `export type { ${finalExportedTypes.join(", ")} } from "./${targetFolderName}/${componentBaseFile}";`
|
|
190
|
+
: "";
|
|
191
|
+
const localExportLine = [valueExport, typeExport].filter(Boolean).join("\n");
|
|
192
|
+
if (!indexContent.includes(`./${targetFolderName}/`)) {
|
|
193
|
+
indexContent = indexContent.trim() + `\n${localExportLine}\n`;
|
|
194
|
+
node_fs_1.default.writeFileSync(indexPath, indexContent, "utf8");
|
|
195
|
+
console.log(`✓ Updated components/index.ts with local export for: ${[...finalExportedValues, ...finalExportedTypes].join(", ")}`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
console.log(`⚠️ Info: Local export for "${targetFolderName}" already exists in components/index.ts`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const finalSymbols = [...finalExportedValues, ...finalExportedTypes];
|
|
202
|
+
console.log(`\nSuccess! Component "${finalSymbols.find((s) => !s.endsWith("Props")) || finalSymbols[0]}" is now available locally at components/${targetFolderName}/`);
|
|
203
|
+
}
|
package/package.json
CHANGED