sh-ui-cli 0.22.2 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/README.md +19 -2
  2. package/bin/sh-ui.mjs +7 -0
  3. package/data/changelog/versions.json +14 -0
  4. package/package.json +13 -2
  5. package/src/create/cli-args.js +63 -0
  6. package/src/create/generator.js +542 -0
  7. package/src/create/index.mjs +68 -0
  8. package/src/create/plugins/index.js +17 -0
  9. package/src/create/plugins/nextIntl.js +197 -0
  10. package/src/create/plugins/sentry.js +689 -0
  11. package/src/create/theme/decode.js +66 -0
  12. package/src/create/theme/inject.js +111 -0
  13. package/src/mcp.mjs +81 -27
  14. package/src/paths.mjs +5 -0
  15. package/templates/flutter-standalone/README.md +34 -0
  16. package/templates/flutter-standalone/analysis_options.yaml +1 -0
  17. package/templates/flutter-standalone/lib/main.dart +103 -0
  18. package/templates/flutter-standalone/lib/sh_ui/foundation/sh_ui_tokens.dart +389 -0
  19. package/templates/flutter-standalone/pubspec.yaml +20 -0
  20. package/templates/flutter-standalone/sh-ui.config.json +15 -0
  21. package/templates/monorepo/.dockerignore +7 -0
  22. package/templates/monorepo/.eslintrc.js +8 -0
  23. package/templates/monorepo/.prettierrc +17 -0
  24. package/templates/monorepo/README.md +103 -0
  25. package/templates/monorepo/package.json +24 -0
  26. package/templates/monorepo/packages/eslint-config/base.js +31 -0
  27. package/templates/monorepo/packages/eslint-config/fsd.js +119 -0
  28. package/templates/monorepo/packages/eslint-config/next.js +65 -0
  29. package/templates/monorepo/packages/eslint-config/package.json +31 -0
  30. package/templates/monorepo/packages/eslint-config/react-internal.js +36 -0
  31. package/templates/monorepo/packages/typescript-config/base.json +20 -0
  32. package/templates/monorepo/packages/typescript-config/nextjs.json +13 -0
  33. package/templates/monorepo/packages/typescript-config/package.json +5 -0
  34. package/templates/monorepo/packages/typescript-config/react-library.json +8 -0
  35. package/templates/monorepo/packages/ui/ui-apps/.gitkeep +0 -0
  36. package/templates/monorepo/packages/ui/ui-core/eslint.config.js +3 -0
  37. package/templates/monorepo/packages/ui/ui-core/package.json +23 -0
  38. package/templates/monorepo/packages/ui/ui-core/src/lib/utils.ts +6 -0
  39. package/templates/monorepo/packages/ui/ui-core/tsconfig.json +11 -0
  40. package/templates/monorepo/pnpm-workspace.yaml +5 -0
  41. package/templates/monorepo/tsconfig.json +3 -0
  42. package/templates/monorepo/turbo.json +26 -0
  43. package/templates/nextjs-app/.env.example +2 -0
  44. package/templates/nextjs-app/Dockerfile +11 -0
  45. package/templates/nextjs-app/README.md +64 -0
  46. package/templates/nextjs-app/app/layout.tsx +22 -0
  47. package/templates/nextjs-app/app/page.tsx +7 -0
  48. package/templates/nextjs-app/eslint.config.js +10 -0
  49. package/templates/nextjs-app/next.config.ts +12 -0
  50. package/templates/nextjs-app/package.json +45 -0
  51. package/templates/nextjs-app/postcss.config.mjs +1 -0
  52. package/templates/nextjs-app/src/app/layouts/.gitkeep +0 -0
  53. package/templates/nextjs-app/src/app/providers/GlobalProvider/index.tsx +23 -0
  54. package/templates/nextjs-app/src/app/providers/index.tsx +1 -0
  55. package/templates/nextjs-app/src/app/providers/tanstack/QueryClientProvider.tsx +27 -0
  56. package/templates/nextjs-app/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  57. package/templates/nextjs-app/src/app/providers/theme/ThemeProviders.tsx +12 -0
  58. package/templates/nextjs-app/src/entities/.gitkeep +0 -0
  59. package/templates/nextjs-app/src/features/.gitkeep +0 -0
  60. package/templates/nextjs-app/src/shared/api/.gitkeep +0 -0
  61. package/templates/nextjs-app/src/shared/config/.gitkeep +0 -0
  62. package/templates/nextjs-app/src/shared/hooks/.gitkeep +0 -0
  63. package/templates/nextjs-app/src/shared/lib/.gitkeep +0 -0
  64. package/templates/nextjs-app/src/shared/model/.gitkeep +0 -0
  65. package/templates/nextjs-app/src/shared/ui/.gitkeep +0 -0
  66. package/templates/nextjs-app/src/views/.gitkeep +0 -0
  67. package/templates/nextjs-app/src/widgets/.gitkeep +0 -0
  68. package/templates/nextjs-app/tsconfig.json +23 -0
  69. package/templates/nextjs-app/vitest.config.ts +15 -0
  70. package/templates/nextjs-app/vitest.setup.ts +1 -0
  71. package/templates/nextjs-standalone/.env.example +2 -0
  72. package/templates/nextjs-standalone/.prettierrc +17 -0
  73. package/templates/nextjs-standalone/README.md +77 -0
  74. package/templates/nextjs-standalone/app/globals.css +33 -0
  75. package/templates/nextjs-standalone/app/layout.tsx +22 -0
  76. package/templates/nextjs-standalone/app/page.tsx +7 -0
  77. package/templates/nextjs-standalone/eslint.config.js +162 -0
  78. package/templates/nextjs-standalone/next.config.ts +10 -0
  79. package/templates/nextjs-standalone/package.json +66 -0
  80. package/templates/nextjs-standalone/postcss.config.mjs +5 -0
  81. package/templates/nextjs-standalone/sh-ui.config.json +19 -0
  82. package/templates/nextjs-standalone/src/app/layouts/.gitkeep +0 -0
  83. package/templates/nextjs-standalone/src/app/providers/GlobalProvider/index.tsx +23 -0
  84. package/templates/nextjs-standalone/src/app/providers/index.tsx +1 -0
  85. package/templates/nextjs-standalone/src/app/providers/tanstack/QueryClientProvider.tsx +27 -0
  86. package/templates/nextjs-standalone/src/app/providers/tanstack/TanstackDevtoolsProvider.tsx +13 -0
  87. package/templates/nextjs-standalone/src/app/providers/theme/ThemeProviders.tsx +12 -0
  88. package/templates/nextjs-standalone/src/entities/.gitkeep +0 -0
  89. package/templates/nextjs-standalone/src/features/.gitkeep +0 -0
  90. package/templates/nextjs-standalone/src/shared/api/.gitkeep +0 -0
  91. package/templates/nextjs-standalone/src/shared/config/.gitkeep +0 -0
  92. package/templates/nextjs-standalone/src/shared/hooks/.gitkeep +0 -0
  93. package/templates/nextjs-standalone/src/shared/lib/utils.ts +6 -0
  94. package/templates/nextjs-standalone/src/shared/model/.gitkeep +0 -0
  95. package/templates/nextjs-standalone/src/shared/styles/tokens.css +95 -0
  96. package/templates/nextjs-standalone/src/shared/ui/.gitkeep +0 -0
  97. package/templates/nextjs-standalone/src/views/.gitkeep +0 -0
  98. package/templates/nextjs-standalone/src/widgets/.gitkeep +0 -0
  99. package/templates/nextjs-standalone/tsconfig.json +39 -0
  100. package/templates/nextjs-standalone/vitest.config.ts +15 -0
  101. package/templates/nextjs-standalone/vitest.setup.ts +1 -0
  102. package/templates/ui-app-template/eslint.config.js +3 -0
  103. package/templates/ui-app-template/package.json +38 -0
  104. package/templates/ui-app-template/postcss.config.mjs +5 -0
  105. package/templates/ui-app-template/sh-ui.config.json +14 -0
  106. package/templates/ui-app-template/src/components/.gitkeep +0 -0
  107. package/templates/ui-app-template/src/hooks/.gitkeep +0 -0
  108. package/templates/ui-app-template/src/lib/.gitkeep +0 -0
  109. package/templates/ui-app-template/src/styles/globals.css +37 -0
  110. package/templates/ui-app-template/src/styles/tokens.css +95 -0
  111. package/templates/ui-app-template/tsconfig.json +11 -0
@@ -0,0 +1,389 @@
1
+ // Generated by @sh-ui/tokens — do not edit directly
2
+ // base=neutral radius=md mode=light-dark
3
+
4
+ import 'package:flutter/material.dart';
5
+
6
+ @immutable
7
+ class ShUiColorTokens {
8
+ final Color background;
9
+ final Color backgroundSubtle;
10
+ final Color backgroundMuted;
11
+ final Color backgroundInverse;
12
+ final Color foreground;
13
+ final Color foregroundMuted;
14
+ final Color foregroundSubtle;
15
+ final Color foregroundInverse;
16
+ final Color border;
17
+ final Color borderStrong;
18
+ final Color primary;
19
+ final Color primaryForeground;
20
+ final Color primaryHover;
21
+ final Color danger;
22
+ final Color dangerForeground;
23
+
24
+ const ShUiColorTokens({
25
+ required this.background,
26
+ required this.backgroundSubtle,
27
+ required this.backgroundMuted,
28
+ required this.backgroundInverse,
29
+ required this.foreground,
30
+ required this.foregroundMuted,
31
+ required this.foregroundSubtle,
32
+ required this.foregroundInverse,
33
+ required this.border,
34
+ required this.borderStrong,
35
+ required this.primary,
36
+ required this.primaryForeground,
37
+ required this.primaryHover,
38
+ required this.danger,
39
+ required this.dangerForeground,
40
+ });
41
+
42
+ // sh-ui:theme-colors-start
43
+ static const light = ShUiColorTokens(
44
+ background: Color(0xFFFFFFFF),
45
+ backgroundSubtle: Color(0xFFFAFAFA),
46
+ backgroundMuted: Color(0xFFF5F5F5),
47
+ backgroundInverse: Color(0xFF0A0A0A),
48
+ foreground: Color(0xFF0A0A0A),
49
+ foregroundMuted: Color(0xFF525252),
50
+ foregroundSubtle: Color(0xFFA3A3A3),
51
+ foregroundInverse: Color(0xFFFFFFFF),
52
+ border: Color(0xFFE5E5E5),
53
+ borderStrong: Color(0xFFD4D4D4),
54
+ primary: Color(0xFF171717),
55
+ primaryForeground: Color(0xFFFAFAFA),
56
+ primaryHover: Color(0xFF262626),
57
+ danger: Color(0xFFDC2626),
58
+ dangerForeground: Color(0xFFFFFFFF),
59
+ );
60
+
61
+ static const dark = ShUiColorTokens(
62
+ background: Color(0xFF0A0A0A),
63
+ backgroundSubtle: Color(0xFF171717),
64
+ backgroundMuted: Color(0xFF262626),
65
+ backgroundInverse: Color(0xFFFFFFFF),
66
+ foreground: Color(0xFFFAFAFA),
67
+ foregroundMuted: Color(0xFFA3A3A3),
68
+ foregroundSubtle: Color(0xFF737373),
69
+ foregroundInverse: Color(0xFF0A0A0A),
70
+ border: Color(0xFF262626),
71
+ borderStrong: Color(0xFF404040),
72
+ primary: Color(0xFFFAFAFA),
73
+ primaryForeground: Color(0xFF171717),
74
+ primaryHover: Color(0xFFE5E5E5),
75
+ danger: Color(0xFFDC2626),
76
+ dangerForeground: Color(0xFFFFFFFF),
77
+ );
78
+ // sh-ui:theme-colors-end
79
+ }
80
+
81
+ @immutable
82
+ class ShUiRadiusTokens {
83
+ final double defaultRadius;
84
+
85
+ const ShUiRadiusTokens({
86
+ required this.defaultRadius,
87
+ });
88
+
89
+ static const tokens = ShUiRadiusTokens(
90
+ // sh-ui:theme-radius-start
91
+ defaultRadius: 8.0,
92
+ // sh-ui:theme-radius-end
93
+ );
94
+ }
95
+
96
+ @immutable
97
+ class ShUiSpacingTokens {
98
+ final double s0;
99
+ final double s1;
100
+ final double s2;
101
+ final double s3;
102
+ final double s4;
103
+ final double s5;
104
+ final double s6;
105
+ final double s8;
106
+ final double s10;
107
+ final double s12;
108
+ final double s16;
109
+
110
+ const ShUiSpacingTokens({
111
+ required this.s0,
112
+ required this.s1,
113
+ required this.s2,
114
+ required this.s3,
115
+ required this.s4,
116
+ required this.s5,
117
+ required this.s6,
118
+ required this.s8,
119
+ required this.s10,
120
+ required this.s12,
121
+ required this.s16,
122
+ });
123
+
124
+ static const tokens = ShUiSpacingTokens(
125
+ s0: 0.0,
126
+ s1: 4.0,
127
+ s2: 8.0,
128
+ s3: 12.0,
129
+ s4: 16.0,
130
+ s5: 20.0,
131
+ s6: 24.0,
132
+ s8: 32.0,
133
+ s10: 40.0,
134
+ s12: 48.0,
135
+ s16: 64.0,
136
+ );
137
+ }
138
+
139
+ @immutable
140
+ class ShUiTextTokens {
141
+ final double xs;
142
+ final double sm;
143
+ final double base;
144
+ final double lg;
145
+ final double xl;
146
+ final double xl2;
147
+ final double xl3;
148
+ final double xl4;
149
+
150
+ const ShUiTextTokens({
151
+ required this.xs,
152
+ required this.sm,
153
+ required this.base,
154
+ required this.lg,
155
+ required this.xl,
156
+ required this.xl2,
157
+ required this.xl3,
158
+ required this.xl4,
159
+ });
160
+
161
+ static const tokens = ShUiTextTokens(
162
+ xs: 12.0,
163
+ sm: 14.0,
164
+ base: 16.0,
165
+ lg: 18.0,
166
+ xl: 20.0,
167
+ xl2: 24.0,
168
+ xl3: 30.0,
169
+ xl4: 36.0,
170
+ );
171
+ }
172
+
173
+ @immutable
174
+ class ShUiWeightTokens {
175
+ final FontWeight regular;
176
+ final FontWeight medium;
177
+ final FontWeight semibold;
178
+ final FontWeight bold;
179
+
180
+ const ShUiWeightTokens({
181
+ required this.regular,
182
+ required this.medium,
183
+ required this.semibold,
184
+ required this.bold,
185
+ });
186
+
187
+ static const tokens = ShUiWeightTokens(
188
+ regular: FontWeight.w400,
189
+ medium: FontWeight.w500,
190
+ semibold: FontWeight.w600,
191
+ bold: FontWeight.w700,
192
+ );
193
+ }
194
+
195
+ @immutable
196
+ class ShUiShadowTokens {
197
+ final List<BoxShadow> sm;
198
+ final List<BoxShadow> md;
199
+ final List<BoxShadow> lg;
200
+ final List<BoxShadow> xl;
201
+
202
+ const ShUiShadowTokens({
203
+ required this.sm,
204
+ required this.md,
205
+ required this.lg,
206
+ required this.xl,
207
+ });
208
+
209
+ static const tokens = ShUiShadowTokens(
210
+ sm: <BoxShadow>[BoxShadow(offset: Offset(0.0, 1.0), blurRadius: 2.0, spreadRadius: 0.0, color: Color(0x14000000))],
211
+ md: <BoxShadow>[BoxShadow(offset: Offset(0.0, 4.0), blurRadius: 12.0, spreadRadius: 0.0, color: Color(0x1F000000))],
212
+ lg: <BoxShadow>[BoxShadow(offset: Offset(0.0, 8.0), blurRadius: 24.0, spreadRadius: 0.0, color: Color(0x26000000))],
213
+ xl: <BoxShadow>[BoxShadow(offset: Offset(0.0, 16.0), blurRadius: 48.0, spreadRadius: 0.0, color: Color(0x2E000000))],
214
+ );
215
+ }
216
+
217
+ @immutable
218
+ class ShUiDurationTokens {
219
+ final Duration fast;
220
+ final Duration base;
221
+ final Duration slow;
222
+
223
+ const ShUiDurationTokens({
224
+ required this.fast,
225
+ required this.base,
226
+ required this.slow,
227
+ });
228
+
229
+ static const tokens = ShUiDurationTokens(
230
+ fast: Duration(milliseconds: 120),
231
+ base: Duration(milliseconds: 160),
232
+ slow: Duration(milliseconds: 200),
233
+ );
234
+ }
235
+
236
+ @immutable
237
+ class ShUiEaseTokens {
238
+ final Curve standard;
239
+ final Curve emphasized;
240
+
241
+ const ShUiEaseTokens({
242
+ required this.standard,
243
+ required this.emphasized,
244
+ });
245
+
246
+ static const tokens = ShUiEaseTokens(
247
+ standard: Cubic(0.4, 0, 0.2, 1),
248
+ emphasized: Cubic(0.2, 0, 0, 1),
249
+ );
250
+ }
251
+
252
+ @immutable
253
+ class ShUiControlTokens {
254
+ final double sm;
255
+ final double md;
256
+ final double lg;
257
+
258
+ const ShUiControlTokens({
259
+ required this.sm,
260
+ required this.md,
261
+ required this.lg,
262
+ });
263
+
264
+ static const tokens = ShUiControlTokens(
265
+ sm: 32.0,
266
+ md: 40.0,
267
+ lg: 48.0,
268
+ );
269
+ }
270
+
271
+ @immutable
272
+ class ShUiBorderWidthTokens {
273
+ final double normal;
274
+ final double strong;
275
+
276
+ const ShUiBorderWidthTokens({
277
+ required this.normal,
278
+ required this.strong,
279
+ });
280
+
281
+ static const tokens = ShUiBorderWidthTokens(
282
+ normal: 1.0,
283
+ strong: 2.0,
284
+ );
285
+ }
286
+
287
+ @immutable
288
+ class ShUiOpacityTokens {
289
+ final double disabled;
290
+
291
+ const ShUiOpacityTokens({
292
+ required this.disabled,
293
+ });
294
+
295
+ static const tokens = ShUiOpacityTokens(
296
+ disabled: 0.5,
297
+ );
298
+ }
299
+
300
+ @immutable
301
+ class ShUiBreakpointTokens {
302
+ final double sm;
303
+ final double md;
304
+ final double lg;
305
+ final double xl;
306
+
307
+ const ShUiBreakpointTokens({
308
+ required this.sm,
309
+ required this.md,
310
+ required this.lg,
311
+ required this.xl,
312
+ });
313
+
314
+ static const tokens = ShUiBreakpointTokens(
315
+ sm: 640.0,
316
+ md: 768.0,
317
+ lg: 1024.0,
318
+ xl: 1280.0,
319
+ );
320
+ }
321
+
322
+ class ShUiTheme extends ThemeExtension<ShUiTheme> {
323
+ final ShUiColorTokens colors;
324
+ final ShUiRadiusTokens radius;
325
+ final ShUiSpacingTokens spacing;
326
+ final ShUiTextTokens text;
327
+ final ShUiWeightTokens weight;
328
+ final ShUiShadowTokens shadow;
329
+ final ShUiDurationTokens duration;
330
+ final ShUiEaseTokens ease;
331
+ final ShUiControlTokens control;
332
+ final ShUiBorderWidthTokens borderWidth;
333
+ final ShUiOpacityTokens opacity;
334
+ final ShUiBreakpointTokens breakpoint;
335
+
336
+ const ShUiTheme({
337
+ required this.colors,
338
+ required this.radius,
339
+ required this.spacing,
340
+ required this.text,
341
+ required this.weight,
342
+ required this.shadow,
343
+ required this.duration,
344
+ required this.ease,
345
+ required this.control,
346
+ required this.borderWidth,
347
+ required this.opacity,
348
+ required this.breakpoint,
349
+ });
350
+
351
+ static const light = ShUiTheme(
352
+ colors: ShUiColorTokens.light,
353
+ radius: ShUiRadiusTokens.tokens,
354
+ spacing: ShUiSpacingTokens.tokens,
355
+ text: ShUiTextTokens.tokens,
356
+ weight: ShUiWeightTokens.tokens,
357
+ shadow: ShUiShadowTokens.tokens,
358
+ duration: ShUiDurationTokens.tokens,
359
+ ease: ShUiEaseTokens.tokens,
360
+ control: ShUiControlTokens.tokens,
361
+ borderWidth: ShUiBorderWidthTokens.tokens,
362
+ opacity: ShUiOpacityTokens.tokens,
363
+ breakpoint: ShUiBreakpointTokens.tokens,
364
+ );
365
+ static const dark = ShUiTheme(
366
+ colors: ShUiColorTokens.dark,
367
+ radius: ShUiRadiusTokens.tokens,
368
+ spacing: ShUiSpacingTokens.tokens,
369
+ text: ShUiTextTokens.tokens,
370
+ weight: ShUiWeightTokens.tokens,
371
+ shadow: ShUiShadowTokens.tokens,
372
+ duration: ShUiDurationTokens.tokens,
373
+ ease: ShUiEaseTokens.tokens,
374
+ control: ShUiControlTokens.tokens,
375
+ borderWidth: ShUiBorderWidthTokens.tokens,
376
+ opacity: ShUiOpacityTokens.tokens,
377
+ breakpoint: ShUiBreakpointTokens.tokens,
378
+ );
379
+
380
+ @override
381
+ ShUiTheme copyWith({ShUiColorTokens? colors, ShUiRadiusTokens? radius, ShUiSpacingTokens? spacing, ShUiTextTokens? text, ShUiWeightTokens? weight, ShUiShadowTokens? shadow, ShUiDurationTokens? duration, ShUiEaseTokens? ease, ShUiControlTokens? control, ShUiBorderWidthTokens? borderWidth, ShUiOpacityTokens? opacity, ShUiBreakpointTokens? breakpoint}) =>
382
+ ShUiTheme(colors: colors ?? this.colors, radius: radius ?? this.radius, spacing: spacing ?? this.spacing, text: text ?? this.text, weight: weight ?? this.weight, shadow: shadow ?? this.shadow, duration: duration ?? this.duration, ease: ease ?? this.ease, control: control ?? this.control, borderWidth: borderWidth ?? this.borderWidth, opacity: opacity ?? this.opacity, breakpoint: breakpoint ?? this.breakpoint);
383
+
384
+ @override
385
+ ShUiTheme lerp(ThemeExtension<ShUiTheme>? other, double t) {
386
+ if (other is! ShUiTheme) return this;
387
+ return t < 0.5 ? this : other;
388
+ }
389
+ }
@@ -0,0 +1,20 @@
1
+ name: {{project_name}}
2
+ description: sh-ui 기반 Flutter 앱
3
+ publish_to: 'none'
4
+ version: 0.0.1
5
+
6
+ environment:
7
+ sdk: ^3.7.0
8
+ flutter: ^3.29.0
9
+
10
+ dependencies:
11
+ flutter:
12
+ sdk: flutter
13
+
14
+ dev_dependencies:
15
+ flutter_test:
16
+ sdk: flutter
17
+ flutter_lints: ^5.0.0
18
+
19
+ flutter:
20
+ uses-material-design: true
@@ -0,0 +1,15 @@
1
+ {
2
+ "platform": "flutter",
3
+ "style": "default",
4
+ "theme": {
5
+ "base": "neutral",
6
+ "radius": "md",
7
+ "mode": "light-dark"
8
+ },
9
+ "paths": {
10
+ "tokens": "lib/sh_ui/foundation/sh_ui_tokens.dart",
11
+ "components": "lib/sh_ui/widgets",
12
+ "foundation": "lib/sh_ui/foundation",
13
+ "widgets": "lib/sh_ui/widgets"
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ node_modules
2
+ .next
3
+ .git
4
+ .gitignore
5
+ *.md
6
+ .env*
7
+ .turbo
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ ignorePatterns: ["apps/**", "packages/**"],
3
+ extends: ["@workspace/eslint-config/library.js"],
4
+ parser: "@typescript-eslint/parser",
5
+ parserOptions: {
6
+ project: true,
7
+ },
8
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "arrowParens": "always",
3
+ "bracketSpacing": true,
4
+ "endOfLine": "auto",
5
+ "htmlWhitespaceSensitivity": "css",
6
+ "bracketSameLine": true,
7
+ "jsxSingleQuote": true,
8
+ "printWidth": 80,
9
+ "proseWrap": "always",
10
+ "quoteProps": "as-needed",
11
+ "semi": true,
12
+ "singleQuote": true,
13
+ "tabWidth": 2,
14
+ "trailingComma": "all",
15
+ "useTabs": false,
16
+ "plugins": ["prettier-plugin-tailwindcss"]
17
+ }
@@ -0,0 +1,103 @@
1
+ # Monorepo Template
2
+
3
+ Turborepo + pnpm workspace 기반 모노레포 템플릿 (sh-ui 기반).
4
+
5
+ ## 기술 스택
6
+
7
+ - **Turborepo** (빌드 오케스트레이션)
8
+ - **pnpm 10** (워크스페이스 패키지 매니저)
9
+ - **TypeScript 5.9**
10
+ - **sh-ui** (앱별 독립 테마 — 각 `ui-{app}/` 패키지가 자체 `sh-ui.config.json` 보유)
11
+ - **ESLint 9** (flat config)
12
+ - **Prettier** (tailwind 플러그인)
13
+
14
+ ## 프로젝트 구조
15
+
16
+ ```
17
+ ├── apps/ # 애플리케이션
18
+ │ └── (nextjs-app 템플릿으로 추가)
19
+
20
+ ├── packages/
21
+ │ ├── ui/
22
+ │ │ ├── ui-core/ # 기능/로직 공유 (스타일 없음)
23
+ │ │ │ └── src/lib/utils.ts # cn() 유틸
24
+ │ │ │
25
+ │ │ └── ui-apps/
26
+ │ │ └── ui-{app}/ # 앱별 sh-ui 패키지 (독립 테마)
27
+ │ │ ├── sh-ui.config.json # 앱별 theme/paths
28
+ │ │ ├── src/
29
+ │ │ │ ├── components/ # sh-ui 컴포넌트
30
+ │ │ │ ├── hooks/
31
+ │ │ │ └── styles/
32
+ │ │ │ ├── globals.css
33
+ │ │ │ └── tokens.css
34
+ │ │ └── postcss.config.mjs
35
+ │ │
36
+ │ ├── eslint-config/ # 공유 ESLint 설정
37
+ │ │ ├── base.js # 기본 (TS + Turbo + Prettier)
38
+ │ │ ├── next.js # Next.js 앱용
39
+ │ │ ├── react-internal.js # React 라이브러리용
40
+ │ │ └── fsd.js # FSD 레이어 규칙 (boundaries, 파일 네이밍)
41
+ │ │
42
+ │ └── typescript-config/ # 공유 TypeScript 설정
43
+ │ ├── base.json # 기본 (strict, ES2022)
44
+ │ ├── nextjs.json # Next.js 앱용 (Bundler, JSX preserve)
45
+ │ └── react-library.json # React 라이브러리용 (react-jsx)
46
+
47
+ ├── turbo.json # Turbo 태스크 파이프라인
48
+ ├── pnpm-workspace.yaml # apps/* + packages/*
49
+ ├── .prettierrc
50
+ ├── .eslintrc.js
51
+ ├── .gitignore
52
+ └── .dockerignore
53
+ ```
54
+
55
+ ## 앱 간 의존 관계
56
+
57
+ ```
58
+ apps/{name}
59
+ ├── @workspace/ui-{name} (앱 전용 UI: sh-ui 컴포넌트, 스타일)
60
+ ├── @workspace/ui-core (공통 유틸: cn 등)
61
+ ├── @workspace/eslint-config (ESLint 규칙)
62
+ └── @workspace/typescript-config (tsconfig)
63
+
64
+ packages/ui/ui-apps/ui-{name}
65
+ ├── @workspace/ui-core
66
+ ├── @workspace/eslint-config
67
+ └── @workspace/typescript-config
68
+ ```
69
+
70
+ ## 시작하기
71
+
72
+ ```bash
73
+ pnpm install
74
+ pnpm dev # 모든 앱 동시 실행
75
+ ```
76
+
77
+ ## 앱 추가
78
+
79
+ ```bash
80
+ npx sh-ui-create add-app
81
+ ```
82
+
83
+ `apps/{name}/` 과 `packages/ui/ui-apps/ui-{name}/` 을 함께 생성합니다.
84
+
85
+ ## 개별 앱 실행
86
+
87
+ ```bash
88
+ pnpm --filter web dev
89
+ pnpm --filter admin build
90
+ ```
91
+
92
+ ## sh-ui 컴포넌트 추가
93
+
94
+ ```bash
95
+ # 모든 ui 패키지에 추가 (대화형)
96
+ npx sh-ui-create add-component button
97
+
98
+ # 특정 앱에만 추가
99
+ npx sh-ui-create add-component button --app web
100
+ ```
101
+
102
+ 내부적으로 `packages/ui/ui-apps/ui-{app}/` 디렉토리에서 `npx sh-ui add button` 이 실행되며,
103
+ 각 패키지의 `sh-ui.config.json` 에 선언된 경로로 컴포넌트가 복사됩니다.
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "my-project",
3
+ "version": "0.0.1",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "turbo build",
7
+ "dev": "turbo dev",
8
+ "lint": "turbo lint",
9
+ "typecheck": "turbo typecheck",
10
+ "format": "prettier --write \"**/*.{ts,tsx,md}\""
11
+ },
12
+ "devDependencies": {
13
+ "@workspace/eslint-config": "workspace:*",
14
+ "@workspace/typescript-config": "workspace:*",
15
+ "prettier": "^3.8.1",
16
+ "prettier-plugin-tailwindcss": "^0.6.13",
17
+ "turbo": "^2.8.1",
18
+ "typescript": "5.9.3"
19
+ },
20
+ "packageManager": "pnpm@10.4.1",
21
+ "engines": {
22
+ "node": ">=20"
23
+ }
24
+ }
@@ -0,0 +1,31 @@
1
+ import js from "@eslint/js"
2
+ import eslintConfigPrettier from "eslint-config-prettier"
3
+ import onlyWarn from "eslint-plugin-only-warn"
4
+ import turboPlugin from "eslint-plugin-turbo"
5
+ import tseslint from "typescript-eslint"
6
+
7
+ export const config = [
8
+ js.configs.recommended,
9
+ eslintConfigPrettier,
10
+ ...tseslint.configs.recommended,
11
+ {
12
+ plugins: {
13
+ turbo: turboPlugin,
14
+ },
15
+ rules: {
16
+ "turbo/no-undeclared-env-vars": "warn",
17
+ "@typescript-eslint/no-unused-vars": [
18
+ "warn",
19
+ { argsIgnorePattern: "^_", varsIgnorePattern: "^_" },
20
+ ],
21
+ },
22
+ },
23
+ {
24
+ plugins: {
25
+ onlyWarn,
26
+ },
27
+ },
28
+ {
29
+ ignores: ["dist/**"],
30
+ },
31
+ ]