atomsystem 0.1.39 → 0.1.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env node
2
+
3
+ // node_modules/tsup/assets/esm_shims.js
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+ var getFilename = () => fileURLToPath(import.meta.url);
7
+ var getDirname = () => path.dirname(getFilename());
8
+ var __dirname = /* @__PURE__ */ getDirname();
9
+
10
+ // src/index.ts
11
+ import { Command as Command3 } from "commander";
12
+
13
+ // src/commands/add.ts
14
+ import { Command } from "commander";
15
+ import fs from "fs";
16
+ import path2 from "path";
17
+ import prompts from "prompts";
18
+ import { z } from "zod";
19
+
20
+ // src/utils/logger.ts
21
+ import chalk from "chalk";
22
+ var logger = {
23
+ error(...args) {
24
+ console.error(chalk.red(...args));
25
+ },
26
+ warn(...args) {
27
+ console.warn(chalk.yellow(...args));
28
+ },
29
+ info(...args) {
30
+ console.info(chalk.cyan(...args));
31
+ },
32
+ success(...args) {
33
+ console.log(chalk.green(...args));
34
+ },
35
+ log(...args) {
36
+ console.log(...args);
37
+ },
38
+ break() {
39
+ console.log("");
40
+ }
41
+ };
42
+
43
+ // src/commands/add.ts
44
+ var templateDir = path2.join(process.cwd(), "app", "templates");
45
+ var components = [
46
+ "Button",
47
+ "Badge",
48
+ "Card",
49
+ "Collapse",
50
+ "CodeBlock",
51
+ "Navigation",
52
+ "Sidebar",
53
+ "Input",
54
+ "Theme",
55
+ "Tab",
56
+ "Toast",
57
+ "Pagination",
58
+ "Table",
59
+ "Modal",
60
+ "TOC",
61
+ "Dropdown"
62
+ ];
63
+ var dependenciesMap = {
64
+ Theme: ["Button", "Form", "Modal"]
65
+ };
66
+ var addOptionsSchema = z.object({
67
+ components: z.array(z.string()).optional(),
68
+ all: z.boolean(),
69
+ cwd: z.string()
70
+ });
71
+ var add = new Command().name("add").description("\uD504\uB85C\uC81D\uD2B8\uC5D0 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD569\uB2C8\uB2E4.").argument("[components...]", "\uCD94\uAC00\uD560 \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984 \uBAA9\uB85D").option("-a, --all", "\uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBAA8\uB4E0 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uCD94\uAC00\uD569\uB2C8\uB2E4.", false).option("-c, --cwd <cwd>", "\uC791\uC5C5 \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C", process.cwd()).action(async (componentsArg, opts) => {
72
+ try {
73
+ const options = addOptionsSchema.parse({
74
+ components: componentsArg,
75
+ ...opts,
76
+ cwd: path2.resolve(opts.cwd)
77
+ });
78
+ let selectedComponents = options.components || [];
79
+ if (options.all) {
80
+ selectedComponents = components;
81
+ }
82
+ if (selectedComponents.length === 0) {
83
+ const { components: promptedComponents } = await prompts({
84
+ type: "multiselect",
85
+ name: "components",
86
+ message: "\uCD94\uAC00\uD560 \uCEF4\uD3EC\uB10C\uD2B8\uB97C \uC120\uD0DD\uD558\uC138\uC694:",
87
+ choices: components.map((comp) => ({ title: comp, value: comp }))
88
+ });
89
+ selectedComponents = promptedComponents;
90
+ }
91
+ if (!selectedComponents || selectedComponents.length === 0) {
92
+ logger.warn("\uC120\uD0DD\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
93
+ return;
94
+ }
95
+ const addedComponents = /* @__PURE__ */ new Set();
96
+ for (const comp of selectedComponents) {
97
+ addComponent(comp, options.cwd, addedComponents);
98
+ }
99
+ } catch (error) {
100
+ logger.error("\uCEF4\uD3EC\uB10C\uD2B8 \uCD94\uAC00 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.");
101
+ logger.error(error);
102
+ }
103
+ });
104
+ function addComponent(comp, cwd, addedComponents) {
105
+ if (addedComponents.has(comp)) return;
106
+ addedComponents.add(comp);
107
+ const componentSourceDir = path2.join(templateDir, comp);
108
+ const componentPath = path2.join(componentSourceDir, `${comp}.tsx`);
109
+ const targetDir = path2.join(cwd, "app", "components", comp);
110
+ const targetPath = path2.join(targetDir, `${comp}.tsx`);
111
+ const cssFileName = `${comp}.css`.toLowerCase();
112
+ const sourceCssPath = path2.join(componentSourceDir, cssFileName);
113
+ const targetCssPath = path2.join(targetDir, cssFileName);
114
+ if (fs.existsSync(componentPath)) {
115
+ if (!fs.existsSync(targetDir)) {
116
+ fs.mkdirSync(targetDir, { recursive: true });
117
+ logger.info(`\uB514\uB809\uD1A0\uB9AC \uC0DD\uC131: ${targetDir}`);
118
+ }
119
+ fs.copyFileSync(componentPath, targetPath);
120
+ logger.success(`\u2705 ${comp} \uCEF4\uD3EC\uB10C\uD2B8(TSX)\uAC00 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${targetPath}`);
121
+ if (fs.existsSync(sourceCssPath)) {
122
+ fs.copyFileSync(sourceCssPath, targetCssPath);
123
+ logger.success(`\u2705 ${comp} \uCEF4\uD3EC\uB10C\uD2B8(CSS)\uAC00 \uCD94\uAC00\uB418\uC5C8\uC2B5\uB2C8\uB2E4: ${targetCssPath}`);
124
+ updateGlobalsCss(comp, cssFileName, cwd);
125
+ }
126
+ if (dependenciesMap[comp]) {
127
+ logger.info(`\u23F3 ${comp}\uC758 \uC758\uC874\uC131 \uCEF4\uD3EC\uB10C\uD2B8 \uD655\uC778 \uC911...`);
128
+ for (const dep of dependenciesMap[comp]) {
129
+ addComponent(dep, cwd, addedComponents);
130
+ }
131
+ }
132
+ } else {
133
+ logger.error(`\u274C ${comp} \uCEF4\uD3EC\uB10C\uD2B8\uAC00 atomsystem\uC5D0 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`);
134
+ }
135
+ }
136
+ function updateGlobalsCss(comp, cssFileName, cwd) {
137
+ const globalsPath = path2.join(cwd, "app", "globals.css");
138
+ const importLine = `@import "./components/${comp}/${cssFileName}";`;
139
+ const componentSectionMarker = "/* Component Styles */";
140
+ if (fs.existsSync(globalsPath)) {
141
+ let content = fs.readFileSync(globalsPath, "utf8");
142
+ if (!content.includes(importLine)) {
143
+ if (content.includes(componentSectionMarker)) {
144
+ const markerIndex = content.indexOf(componentSectionMarker);
145
+ const afterMarker = content.slice(markerIndex);
146
+ const lastImportIndex = afterMarker.lastIndexOf("@import");
147
+ if (lastImportIndex !== -1) {
148
+ const endOfLastImport = markerIndex + lastImportIndex + afterMarker.slice(lastImportIndex).indexOf("\n") + 1;
149
+ content = content.slice(0, endOfLastImport) + importLine + "\n" + content.slice(endOfLastImport);
150
+ } else {
151
+ content = content.slice(0, markerIndex + componentSectionMarker.length) + "\n" + importLine + "\n" + content.slice(markerIndex + componentSectionMarker.length);
152
+ }
153
+ } else {
154
+ content = content.trimEnd() + "\n\n" + componentSectionMarker + "\n" + importLine + "\n";
155
+ }
156
+ fs.writeFileSync(globalsPath, content, "utf8");
157
+ logger.success(`\u2705 globals.css\uC5D0 ${comp} @import\uB97C \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`);
158
+ }
159
+ } else {
160
+ logger.warn(`\u26A0\uFE0F globals.css\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC218\uB3D9\uC73C\uB85C \uCD94\uAC00\uD574 \uC8FC\uC138\uC694:
161
+ ${importLine}`);
162
+ }
163
+ }
164
+
165
+ // src/commands/init.ts
166
+ import { Command as Command2 } from "commander";
167
+ import fs2 from "fs";
168
+ import path3 from "path";
169
+ import { exec } from "child_process";
170
+ import { promisify } from "util";
171
+ import ora from "ora";
172
+ import prompts2 from "prompts";
173
+ import { z as z2 } from "zod";
174
+ var execPromise = promisify(exec);
175
+ var initOptionsSchema = z2.object({
176
+ cwd: z2.string(),
177
+ yes: z2.boolean()
178
+ });
179
+ var init = new Command2().name("init").description("\uD504\uB85C\uC81D\uD2B8\uB97C \uCD08\uAE30\uD654\uD558\uACE0 atomsystem\uC744 \uC124\uC815\uD569\uB2C8\uB2E4.").option("-y, --yes", "\uBAA8\uB4E0 \uC9C8\uBB38\uC5D0 'yes'\uB85C \uC751\uB2F5\uD569\uB2C8\uB2E4.", false).option("-c, --cwd <cwd>", "\uC791\uC5C5 \uB514\uB809\uD1A0\uB9AC \uACBD\uB85C", process.cwd()).action(async (opts) => {
180
+ try {
181
+ const options = initOptionsSchema.parse({
182
+ ...opts,
183
+ cwd: path3.resolve(opts.cwd)
184
+ });
185
+ const projectRoot = options.cwd;
186
+ const packageJsonPath = path3.join(projectRoot, "package.json");
187
+ if (!fs2.existsSync(packageJsonPath)) {
188
+ logger.error("\u274C \uC624\uB958: \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0\uC11C package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
189
+ logger.info("'npm init'\uC744 \uBA3C\uC800 \uC2E4\uD589\uD55C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.");
190
+ process.exit(1);
191
+ }
192
+ const answers = options.yes ? { installPackages: true } : await prompts2({
193
+ type: "confirm",
194
+ name: "installPackages",
195
+ message: "Next.js\uC640 React\uB97C \uC124\uCE58\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
196
+ initial: true
197
+ });
198
+ if (answers.installPackages) {
199
+ const pkgSpinner = ora("Next.js\uC640 React \uC124\uCE58 \uC911...").start();
200
+ try {
201
+ await execPromise(
202
+ "npm install next@latest react@latest react-dom@latest tailwindcss @tailwindcss/postcss postcss clsx react-modal typescript @types/react @types/node @types/react-dom @types/react-modal",
203
+ { cwd: projectRoot }
204
+ );
205
+ pkgSpinner.succeed("Next.js\uC640 React \uC124\uCE58 \uC644\uB8CC!");
206
+ } catch (err) {
207
+ pkgSpinner.fail("\uC124\uCE58 \uC911 \uC624\uB958 \uBC1C\uC0DD");
208
+ logger.error(err);
209
+ return;
210
+ }
211
+ const sdSpinner = ora("style-dictionary \uC124\uCE58 \uC911...").start();
212
+ try {
213
+ await execPromise("npm install style-dictionary -D", { cwd: projectRoot });
214
+ sdSpinner.succeed("style-dictionary \uC124\uCE58 \uC644\uB8CC!");
215
+ } catch (err) {
216
+ sdSpinner.fail("style-dictionary \uC124\uCE58 \uC911 \uC624\uB958 \uBC1C\uC0DD");
217
+ logger.error(err);
218
+ return;
219
+ }
220
+ try {
221
+ const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf8"));
222
+ packageJson.type = "module";
223
+ packageJson.scripts = {
224
+ ...packageJson.scripts,
225
+ "dev": "node build/build-tokens.js && next dev",
226
+ "build": "next build",
227
+ "start": "next start",
228
+ "lint": "next lint",
229
+ "token-build": "node build/build-tokens.js"
230
+ };
231
+ fs2.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
232
+ logger.success("\u2705 package.json \uC2A4\uD06C\uB9BD\uD2B8 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC.");
233
+ } catch (err) {
234
+ logger.error("\u274C package.json \uC5C5\uB370\uC774\uD2B8 \uC624\uB958:", err);
235
+ return;
236
+ }
237
+ const appDir = path3.join(projectRoot, "app");
238
+ if (!fs2.existsSync(appDir)) fs2.mkdirSync(appDir, { recursive: true });
239
+ const files = [
240
+ {
241
+ path: path3.join(appDir, "layout.tsx"),
242
+ content: `import { ThemeProvider } from "./components/Theme/Theme";
243
+ import "./globals.css";
244
+
245
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
246
+ return (
247
+ <html lang="en" suppressHydrationWarning>
248
+ <body>
249
+ <ThemeProvider />
250
+ {children}
251
+ <div id="modal-root"></div>
252
+ </body>
253
+ </html>
254
+ )
255
+ }`
256
+ },
257
+ {
258
+ path: path3.join(appDir, "globals.css"),
259
+ content: `@import "tailwindcss";
260
+ @import "../build/css/_variables.css";
261
+ @import "../css/text.css";`
262
+ },
263
+ {
264
+ path: path3.join(appDir, "page.tsx"),
265
+ content: `import { ThemeSelector } from "./components/Theme/Theme";
266
+ import { Button } from "./components/Button/Button";
267
+ import { Card, CardHeader, CardContent, CardFooter } from "./components/Card/Card";
268
+
269
+ export default function Page() {
270
+ return (
271
+ <main style={{ padding: "40px", minHeight: "100vh", background: "var(--color-bg-wrapper)", transition: "background 0.3s ease" }}>
272
+ <div style={{ maxWidth: "800px", margin: "0 auto", display: "flex", flexDirection: "column", gap: "24px" }}>
273
+ <h1 style={{ fontSize: "var(--font-sizes-h2)", color: "var(--color-text-standard)", margin: 0 }}>Hello, atomsystem!</h1>
274
+ <p style={{ fontSize: "var(--font-sizes-body)", margin: 0 }}>
275
+ \uC774 \uD654\uBA74\uC740 \uB514\uC790\uC778 \uD1A0\uD070\uC774 \uC801\uC6A9\uB41C \uC0D8\uD50C \uD654\uBA74\uC785\uB2C8\uB2E4. \uC544\uB798 \uD14C\uB9C8\uB97C \uBCC0\uACBD\uD574\uBCF4\uC138\uC694.
276
+ </p>
277
+
278
+ <ThemeSelector />
279
+
280
+ <Card>
281
+ <CardHeader>
282
+ <h2 style={{ fontSize: "var(--font-sizes-h3)", margin: 0 }}>Sample Card Components</h2>
283
+ </CardHeader>
284
+ <CardContent>
285
+ <p style={{ margin: 0, color: "var(--color-text-disabled)" }}>
286
+ \uC774 \uCE74\uB4DC\uB294 \uD14C\uB9C8 \uBCC0\uACBD \uC2DC \uBC30\uACBD\uC0C9, \uD14D\uC2A4\uD2B8 \uC0C9\uC0C1, \uD14C\uB450\uB9AC \uC0C9\uC0C1\uC774 \uC790\uB3D9\uC73C\uB85C \uBCC0\uACBD\uB429\uB2C8\uB2E4.
287
+ \uC5EC\uAE30 \uC788\uB294 \uBC84\uD2BC\uB4E4\uC740 \uC81C\uACF5\uB41C Button \uCEF4\uD3EC\uB10C\uD2B8\uB85C \uAD6C\uC131\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
288
+ </p>
289
+ </CardContent>
290
+ <CardFooter>
291
+ <div style={{ display: "flex", gap: "12px" }}>
292
+ <Button type="primary">Primary Button</Button>
293
+ <Button type="secondary">Secondary Button</Button>
294
+ <Button type="gray">Gray Button</Button>
295
+ </div>
296
+ </CardFooter>
297
+ </Card>
298
+ </div>
299
+ </main>
300
+ )
301
+ }`
302
+ },
303
+ {
304
+ path: path3.join(projectRoot, "tsconfig.json"),
305
+ content: JSON.stringify({
306
+ compilerOptions: {
307
+ target: "ES2017",
308
+ lib: ["dom", "dom.iterable", "esnext"],
309
+ allowJs: true,
310
+ skipLibCheck: true,
311
+ strict: true,
312
+ noEmit: true,
313
+ esModuleInterop: true,
314
+ module: "esnext",
315
+ moduleResolution: "bundler",
316
+ resolveJsonModule: true,
317
+ isolatedModules: true,
318
+ jsx: "preserve",
319
+ incremental: true,
320
+ plugins: [{ name: "next" }],
321
+ paths: {
322
+ "@/*": ["./*"]
323
+ }
324
+ },
325
+ include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
326
+ exclude: ["node_modules"]
327
+ }, null, 2)
328
+ },
329
+ {
330
+ path: path3.join(projectRoot, "next.config.ts"),
331
+ content: `import type { NextConfig } from "next";
332
+
333
+ const nextConfig: NextConfig = { reactStrictMode: false };
334
+ export default nextConfig;`
335
+ },
336
+ {
337
+ path: path3.join(projectRoot, "postcss.config.mjs"),
338
+ content: `/** @type {import('postcss-load-config').Config} */
339
+ const config = { plugins: { "@tailwindcss/postcss": {} } };
340
+ export default config;`
341
+ }
342
+ ];
343
+ for (const file of files) {
344
+ fs2.writeFileSync(file.path, file.content);
345
+ logger.info(`\u2705 \uD30C\uC77C \uC0DD\uC131: ${file.path}`);
346
+ }
347
+ const buildTokensPath = path3.join(projectRoot, "build", "build-tokens.js");
348
+ if (fs2.existsSync(buildTokensPath)) {
349
+ const spin = ora("\uB514\uC790\uC778 \uD1A0\uD070 \uBE4C\uB4DC \uC911...").start();
350
+ try {
351
+ await execPromise(`node "${buildTokensPath}"`, { cwd: projectRoot });
352
+ spin.succeed("\uB514\uC790\uC778 \uD1A0\uD070 \uBE4C\uB4DC \uC644\uB8CC.");
353
+ } catch (err) {
354
+ spin.fail("\uD1A0\uD070 \uBE4C\uB4DC \uC2E4\uD328");
355
+ logger.error(err);
356
+ }
357
+ }
358
+ }
359
+ const tokenFile = path3.join(__dirname, "..", "..", "tokens", "tokens.json");
360
+ const buildFile = path3.join(__dirname, "..", "..", "build", "build-tokens.js");
361
+ const textCssFile = path3.join(__dirname, "..", "..", "css", "text.css");
362
+ copyFile(tokenFile, path3.join(projectRoot, "tokens", "tokens.json"));
363
+ copyFile(buildFile, path3.join(projectRoot, "build", "build-tokens.js"));
364
+ copyFile(textCssFile, path3.join(projectRoot, "css", "text.css"));
365
+ logger.success("\u{1F680} Atomsystem \uCD08\uAE30 \uC124\uC815 \uC644\uB8CC!");
366
+ } catch (error) {
367
+ logger.error("\uCD08\uAE30\uD654 \uC911 \uC624\uB958 \uBC1C\uC0DD:");
368
+ logger.error(error);
369
+ }
370
+ });
371
+ function copyFile(src, dest) {
372
+ const dir = path3.dirname(dest);
373
+ if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
374
+ if (fs2.existsSync(src)) {
375
+ fs2.copyFileSync(src, dest);
376
+ logger.info(`\u2705 \uBCF5\uC0AC \uC644\uB8CC: ${src} -> ${dest}`);
377
+ } else {
378
+ logger.warn(`\u26A0\uFE0F \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uBCF5\uC0AC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${src}`);
379
+ }
380
+ }
381
+
382
+ // src/index.ts
383
+ import { readFileSync } from "fs";
384
+ import { join } from "path";
385
+ async function main() {
386
+ const packageJsonPath = join(__dirname, "../package.json");
387
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
388
+ const program = new Command3().name("atomsystem").description("Atom Ground Design System CLI").version(packageJson.version);
389
+ program.addCommand(add);
390
+ program.addCommand(init);
391
+ program.parse();
392
+ }
393
+ main();
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "atomsystem",
3
- "version": "0.1.39",
3
+ "version": "0.1.41",
4
4
  "type": "module",
5
5
  "homepage": "https://system.atomground.com",
6
6
  "bin": {
7
- "atomsystem-init": "cli/init.js",
8
- "atomsystem-add": "cli/add.js"
7
+ "atomsystem": "dist/index.js"
9
8
  },
10
9
  "files": [
11
10
  "tokens",
@@ -19,7 +18,9 @@
19
18
  "build": "next build",
20
19
  "start": "next start",
21
20
  "lint": "next lint",
22
- "token-build": "node build/build-tokens.js"
21
+ "token-build": "node build/build-tokens.js",
22
+ "cli:build": "tsup src/index.ts --format esm --clean --shims --target node20",
23
+ "cli:dev": "tsx src/index.ts"
23
24
  },
24
25
  "peerDependencies": {
25
26
  "react": "^18.0.0",
@@ -28,21 +29,28 @@
28
29
  "style-dictionary": "^4.2.0"
29
30
  },
30
31
  "dependencies": {
32
+ "@emotion/react": "^11.14.0",
33
+ "@emotion/styled": "^11.14.1",
31
34
  "@gsap/react": "^2.1.2",
32
35
  "@mdx-js/loader": "^3.1.0",
33
36
  "@mdx-js/react": "^3.1.0",
37
+ "@mui/icons-material": "^7.3.9",
38
+ "@mui/material": "^7.3.9",
34
39
  "@next/mdx": "^15.4.6",
35
40
  "@next/third-parties": "^15.0.3",
36
41
  "@tailwindcss/postcss": "^4.0.17",
37
42
  "@tokens-studio/sd-transforms": "^1.3.0",
38
43
  "@types/mdx": "^2.0.13",
44
+ "chalk": "^5.6.2",
39
45
  "clsx": "^2.1.1",
46
+ "commander": "^14.0.3",
40
47
  "gsap": "^3.12.7",
41
48
  "highlight.js": "^11.11.1",
42
49
  "inquirer": "^12.4.2",
43
50
  "lucide-react": "^1.7.0",
44
51
  "next": "15.2.8",
45
52
  "ora": "^9.3.0",
53
+ "prompts": "^2.4.2",
46
54
  "react": "^18.0.0",
47
55
  "react-dom": "^18.0.0",
48
56
  "react-modal": "^3.16.3",
@@ -50,10 +58,12 @@
50
58
  "remark-gfm": "^4.0.1",
51
59
  "sd-tailwindcss-transformer": "^2.0.0",
52
60
  "style-dictionary": "^4.2.0",
53
- "tailwind-merge": "^3.5.0"
61
+ "tailwind-merge": "^3.5.0",
62
+ "zod": "^4.3.6"
54
63
  },
55
64
  "devDependencies": {
56
65
  "@types/node": "^20",
66
+ "@types/prompts": "^2.4.9",
57
67
  "@types/react": "^18",
58
68
  "@types/react-dom": "^18",
59
69
  "@types/react-modal": "^3.16.3",
@@ -64,6 +74,8 @@
64
74
  "playwright": "^1.51.1",
65
75
  "postcss": "^8.5.3",
66
76
  "tailwindcss": "^4.0.17",
77
+ "tsup": "^8.5.1",
78
+ "tsx": "^4.21.0",
67
79
  "typescript": "^5",
68
80
  "vitest": "^3.1.1"
69
81
  }
@@ -1,55 +0,0 @@
1
- "use client"
2
-
3
- import Link from "next/link";
4
- import { usePathname } from "next/navigation";
5
-
6
- // Navigation을 베이스 interface로 정의
7
- export interface Sidemenu {
8
- children: React.ReactNode;
9
- minWidth?: number
10
- }
11
-
12
- export interface NavLink {
13
- children: React.ReactNode;
14
- href: string;
15
- }
16
-
17
-
18
- const Sidemenu = ({ children, minWidth }: Sidemenu) => {
19
- return (
20
- <aside className={`nav-side flex flex-col overflow-auto h-full`} style={{minWidth: `${minWidth}px`}}>
21
- {children}
22
- </aside>
23
- )
24
- }
25
-
26
-
27
- const NavBrand = ({children, href}: NavLink) => {
28
- return (
29
- <Link href={href} className="flex items-center">{children}</Link>
30
- )
31
- }
32
-
33
- const NavList = ({children}: Sidemenu) => {
34
- return (
35
- <ul className="nav-list flex flex-col">{children}</ul>
36
- )
37
- }
38
-
39
-
40
- const NavItem = ({children, href}: NavLink) => {
41
- const pathname = usePathname();
42
- const active = pathname === href;
43
-
44
- return (
45
- <Link href={href} className={`nav-item text-nowrap ${active ? "text-primary!" : ""}`}>{children}</Link>
46
- )
47
- }
48
-
49
-
50
- export {
51
- Sidemenu,
52
- NavBrand,
53
- NavList,
54
- NavItem
55
- }
package/cli/add.js DELETED
@@ -1,143 +0,0 @@
1
- #!/usr/bin/env node
2
- import inquirer from "inquirer";
3
- import fs from "fs";
4
- import path from "path";
5
- import { copyFileSync } from "fs";
6
- import { fileURLToPath } from "url";
7
-
8
- // 현재 실행중인 CLI 파일의 디렉토리 찾기
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
-
12
- // 컴포넌트 템플릿이 위치한 디렉토리
13
- const templateDir = path.join(__dirname, "..", "app", "templates");
14
-
15
- // 사용할 수 있는 컴포넌트 목록
16
- const components = [
17
- "Button",
18
- "Badge",
19
- "Card",
20
- "Collapse",
21
- "CodeBlock",
22
- "Navigation",
23
- "Sidemenu",
24
- "Input",
25
- "Theme",
26
- "Tab",
27
- "Toast",
28
- "Pagination",
29
- "Table",
30
- "Modal",
31
- "TOC",
32
- "Dropdown",
33
- ]; // 여기 추가할 컴포넌트 목록
34
-
35
-
36
- // 명령어에서 컴포넌트 이름 가져오기
37
- const args = process.argv.slice(2); // 'npx atomsystem-add Button -> [Button]'
38
- const component = args[0] || "all"; // '사용자가 입력하지 않으면 기본적으로 'all''
39
-
40
- // 일부 컴포넌트의 의존성 구조 (Theme 설치시 내부적으로 Button, Form, Modal 등이 필요함)
41
- const dependenciesMap = {
42
- Theme: ["Button", "Form", "Modal"], // Form 안에는 Radio가 들어있음
43
- };
44
-
45
- // 재귀 방지를 위한 Set
46
- const addedComponents = new Set();
47
-
48
- function addComponent(comp) {
49
- if (addedComponents.has(comp)) return;
50
- addedComponents.add(comp);
51
- const componentPath = path.join(templateDir, comp, `${comp}.tsx`);
52
- const targetDir = path.join(process.cwd(), "app", "components", comp);
53
- const targetPath = path.join(targetDir, `${comp}.tsx`);
54
-
55
- // 컴포넌트의 CSS 파일 복제 지원
56
- const cssFileName = `${comp}.css`.toLowerCase();
57
- const sourceCssPath = path.join(templateDir, comp, cssFileName);
58
- const targetCssPath = path.join(targetDir, cssFileName);
59
-
60
- if (fs.existsSync(componentPath)) {
61
- // 대상 디렉토리가 없으면 생성
62
- if (!fs.existsSync(targetDir)) {
63
- fs.mkdirSync(targetDir, { recursive: true });
64
- console.log(`Created directory: ${targetDir}`);
65
- }
66
-
67
- // 1) TSX 복사
68
- copyFileSync(componentPath, targetPath);
69
- console.log(`✅ ${comp} component typescript has been added to ${targetPath}`);
70
-
71
- // 2) CSS 복사 + globals.css에 @import 자동 추가
72
- if (fs.existsSync(sourceCssPath)) {
73
- copyFileSync(sourceCssPath, targetCssPath);
74
- console.log(`✅ ${comp} component stylesheet has been added to ${targetCssPath}`);
75
-
76
- // globals.css에 @import 추가
77
- const globalsPath = path.join(process.cwd(), "app", "globals.css");
78
- const importLine = `@import "./components/${comp}/${cssFileName}";`;
79
-
80
- if (fs.existsSync(globalsPath)) {
81
- const globalsContent = fs.readFileSync(globalsPath, "utf8");
82
-
83
- if (!globalsContent.includes(importLine)) {
84
- // "/* Component Styles */" 섹션 이후에 추가, 없으면 파일 끝에 추가
85
- let updatedContent;
86
- const componentSectionMarker = "/* Component Styles */";
87
-
88
- if (globalsContent.includes(componentSectionMarker)) {
89
- // 마커 이후의 마지막 @import 뒤에 추가
90
- const markerIndex = globalsContent.indexOf(componentSectionMarker);
91
- const afterMarker = globalsContent.slice(markerIndex);
92
- const lastImportInSection = afterMarker.lastIndexOf("@import");
93
-
94
- if (lastImportInSection !== -1) {
95
- const endOfLastImport = markerIndex + lastImportInSection + afterMarker.slice(lastImportInSection).indexOf("\n") + 1;
96
- updatedContent =
97
- globalsContent.slice(0, endOfLastImport) +
98
- importLine + "\n" +
99
- globalsContent.slice(endOfLastImport);
100
- } else {
101
- updatedContent =
102
- globalsContent.slice(0, markerIndex + componentSectionMarker.length) +
103
- "\n" + importLine + "\n" +
104
- globalsContent.slice(markerIndex + componentSectionMarker.length);
105
- }
106
- } else {
107
- // 마커가 없으면 "/* Component Styles */" 섹션과 함께 파일 끝에 추가
108
- updatedContent = globalsContent.trimEnd() + "\n\n/* Component Styles */\n" + importLine + "\n";
109
- }
110
-
111
- fs.writeFileSync(globalsPath, updatedContent, "utf8");
112
- console.log(`✅ Added @import for ${comp} to globals.css`);
113
- } else {
114
- console.log(`ℹ️ ${comp} is already imported in globals.css`);
115
- }
116
- } else {
117
- console.log(`⚠️ globals.css not found at ${globalsPath}. Please add manually:\n ${importLine}`);
118
- }
119
- }
120
- // 3) 의존성 컴포넌트 자동 복사 (Recursive Copy)
121
- if (dependenciesMap[comp]) {
122
- console.log(`\n⏳ Checking dependencies for ${comp}...`);
123
- dependenciesMap[comp].forEach((dep) => {
124
- addComponent(dep);
125
- });
126
- console.log(`✅ Finished adding dependencies for ${comp}.\n`);
127
- }
128
-
129
- } else {
130
- console.log(`❌ Component ${comp} does not exist in the atomsystem.`);
131
- }
132
- }
133
-
134
- if (component == "all") {
135
- console.log("Adding all components...");
136
-
137
- components.forEach((comp) => {
138
- addComponent(comp);
139
- });
140
- } else {
141
- // 특정 컴포넌트 추가
142
- addComponent(component);
143
- }