@aex.is/zero 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aex.is/zero",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Aexis Zero scaffolding CLI",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -17,7 +17,14 @@ export const frameworks: FrameworkDefinition[] = [
17
17
  id: 'nextjs',
18
18
  label: 'Next.js',
19
19
  description: 'React framework with App Router and Tailwind.',
20
- packages: [],
20
+ packages: [
21
+ 'class-variance-authority',
22
+ 'clsx',
23
+ 'lucide-react',
24
+ 'tailwind-merge',
25
+ 'tailwindcss-animate',
26
+ '@radix-ui/react-slot'
27
+ ],
21
28
  scaffold: {
22
29
  command: 'bunx',
23
30
  packageName: 'create-next-app@latest',
@@ -71,7 +78,15 @@ export const frameworks: FrameworkDefinition[] = [
71
78
  id: 'expo',
72
79
  label: 'Expo (React Native)',
73
80
  description: 'Expo app with Router and EAS configuration.',
74
- packages: ['expo-router'],
81
+ packages: [
82
+ 'expo-router',
83
+ 'tamagui',
84
+ '@tamagui/config',
85
+ '@tamagui/animations-react-native',
86
+ '@tamagui/metro-plugin',
87
+ '@tamagui/babel-plugin',
88
+ 'react-native-svg'
89
+ ],
75
90
  scaffold: {
76
91
  command: 'bunx',
77
92
  packageName: 'create-expo-app',
@@ -8,6 +8,10 @@ import { writeEnvExample } from './env.js';
8
8
  import {
9
9
  nextLayoutTemplate,
10
10
  nextPageTemplate,
11
+ shadcnUtilsTemplate,
12
+ componentsJsonTemplate,
13
+ tamaguiConfigTemplate,
14
+ metroConfigTemplate,
11
15
  expoLayoutTemplate,
12
16
  expoIndexTemplate
13
17
  } from './templates.js';
@@ -93,10 +97,13 @@ async function runScaffoldCommand(
93
97
  async function applyNextTemplates(targetDir: string): Promise<void> {
94
98
  const rootAppDir = path.join(targetDir, 'app');
95
99
  const srcAppDir = path.join(targetDir, 'src', 'app');
96
- const appDir = (await pathExists(srcAppDir)) ? srcAppDir : rootAppDir;
100
+ const usesSrcDir = await pathExists(srcAppDir);
101
+ const appDir = usesSrcDir ? srcAppDir : rootAppDir;
102
+ const projectSrcBase = usesSrcDir ? path.join(targetDir, 'src') : targetDir;
97
103
  await fs.mkdir(appDir, { recursive: true });
98
104
  await fs.writeFile(path.join(appDir, 'layout.tsx'), nextLayoutTemplate, 'utf8');
99
105
  await fs.writeFile(path.join(appDir, 'page.tsx'), nextPageTemplate, 'utf8');
106
+ await ensureShadcnSetup(targetDir, projectSrcBase, usesSrcDir);
100
107
  await ensureNextTurbo(targetDir);
101
108
  }
102
109
 
@@ -106,6 +113,7 @@ async function applyExpoTemplates(targetDir: string): Promise<void> {
106
113
  await fs.writeFile(path.join(appDir, '_layout.tsx'), expoLayoutTemplate, 'utf8');
107
114
  await fs.writeFile(path.join(appDir, 'index.tsx'), expoIndexTemplate, 'utf8');
108
115
  await ensureExpoConfig(targetDir);
116
+ await ensureExpoTamagui(targetDir);
109
117
  }
110
118
 
111
119
  async function ensureExpoConfig(targetDir: string): Promise<void> {
@@ -216,3 +224,73 @@ async function ensureNextTurbo(targetDir: string): Promise<void> {
216
224
  }
217
225
  await writeJson(packageJsonPath, packageJson);
218
226
  }
227
+
228
+ async function ensureShadcnSetup(targetDir: string, projectSrcBase: string, usesSrcDir: boolean): Promise<void> {
229
+ const libDir = path.join(projectSrcBase, 'lib');
230
+ await fs.mkdir(libDir, { recursive: true });
231
+ await fs.writeFile(path.join(libDir, 'utils.ts'), shadcnUtilsTemplate, 'utf8');
232
+
233
+ const globalsPath = usesSrcDir ? 'src/app/globals.css' : 'app/globals.css';
234
+ const tailwindConfigPath = await detectTailwindConfig(targetDir);
235
+ const componentsJson = componentsJsonTemplate(globalsPath, tailwindConfigPath ?? 'tailwind.config.ts');
236
+ await fs.writeFile(path.join(targetDir, 'components.json'), componentsJson, 'utf8');
237
+ }
238
+
239
+ async function detectTailwindConfig(targetDir: string): Promise<string | null> {
240
+ const candidates = [
241
+ 'tailwind.config.ts',
242
+ 'tailwind.config.js',
243
+ 'tailwind.config.cjs',
244
+ 'tailwind.config.mjs'
245
+ ];
246
+ for (const filename of candidates) {
247
+ const fullPath = path.join(targetDir, filename);
248
+ if (await pathExists(fullPath)) {
249
+ return filename;
250
+ }
251
+ }
252
+ return null;
253
+ }
254
+
255
+ async function ensureExpoTamagui(targetDir: string): Promise<void> {
256
+ const configPath = path.join(targetDir, 'tamagui.config.ts');
257
+ await fs.writeFile(configPath, tamaguiConfigTemplate, 'utf8');
258
+
259
+ const metroPath = path.join(targetDir, 'metro.config.js');
260
+ await fs.writeFile(metroPath, metroConfigTemplate, 'utf8');
261
+
262
+ await ensureBabelTamagui(targetDir);
263
+ }
264
+
265
+ async function ensureBabelTamagui(targetDir: string): Promise<void> {
266
+ const babelPath = path.join(targetDir, 'babel.config.js');
267
+ let content = '';
268
+ if (await pathExists(babelPath)) {
269
+ content = await fs.readFile(babelPath, 'utf8');
270
+ }
271
+
272
+ if (!content) {
273
+ const defaultConfig = `module.exports = function (api) {\n api.cache(true);\n return {\n presets: ['babel-preset-expo'],\n plugins: [\n 'expo-router/babel',\n [\n '@tamagui/babel-plugin',\n {\n config: './tamagui.config.ts',\n components: ['tamagui']\n }\n ]\n ]\n };\n};\n`;
274
+ await fs.writeFile(babelPath, defaultConfig, 'utf8');
275
+ return;
276
+ }
277
+
278
+ if (content.includes('@tamagui/babel-plugin')) {
279
+ return;
280
+ }
281
+
282
+ const pluginSnippet = `[\n '@tamagui/babel-plugin',\n {\n config: './tamagui.config.ts',\n components: ['tamagui']\n }\n ]`;
283
+
284
+ const pluginsRegex = /plugins:\\s*\\[(.*)\\]/s;
285
+ if (pluginsRegex.test(content)) {
286
+ content = content.replace(pluginsRegex, (match, inner) => {
287
+ const trimmed = inner.trim();
288
+ const updatedInner = trimmed.length > 0 ? `${trimmed},\n ${pluginSnippet}` : pluginSnippet;
289
+ return `plugins: [${updatedInner}]`;
290
+ });
291
+ } else {
292
+ content = content.replace(/return\\s*\\{/, (match) => `${match}\n plugins: [${pluginSnippet}],`);
293
+ }
294
+
295
+ await fs.writeFile(babelPath, content, 'utf8');
296
+ }
@@ -23,20 +23,70 @@ export const nextPageTemplate = `export default function Home() {
23
23
  }
24
24
  `;
25
25
 
26
+ export const shadcnUtilsTemplate = `import { clsx, type ClassValue } from 'clsx';
27
+ import { twMerge } from 'tailwind-merge';
28
+
29
+ export function cn(...inputs: ClassValue[]) {
30
+ return twMerge(clsx(inputs));
31
+ }
32
+ `;
33
+
34
+ export const componentsJsonTemplate = (globalsPath: string, tailwindConfigPath: string) => `{
35
+ "$schema": "https://ui.shadcn.com/schema.json",
36
+ "style": "default",
37
+ "rsc": true,
38
+ "tsx": true,
39
+ "tailwind": {
40
+ "config": "${tailwindConfigPath}",
41
+ "css": "${globalsPath}",
42
+ "baseColor": "slate",
43
+ "cssVariables": true
44
+ },
45
+ "aliases": {
46
+ "components": "@/components",
47
+ "utils": "@/lib/utils"
48
+ }
49
+ }
50
+ `;
51
+
52
+ export const tamaguiConfigTemplate = `import { config } from '@tamagui/config/v3';
53
+
54
+ export default config;
55
+ `;
56
+
57
+ export const metroConfigTemplate = `const { getDefaultConfig } = require('expo/metro-config');
58
+ const { withTamagui } = require('@tamagui/metro-plugin');
59
+
60
+ const config = getDefaultConfig(__dirname);
61
+
62
+ module.exports = withTamagui(config, {
63
+ components: ['tamagui'],
64
+ config: './tamagui.config.ts',
65
+ outputCSS: './tamagui-web.css'
66
+ });
67
+ `;
68
+
26
69
  export const expoLayoutTemplate = `import { Stack } from 'expo-router';
70
+ import { TamaguiProvider } from 'tamagui';
71
+ import config from '../tamagui.config';
27
72
 
28
73
  export default function RootLayout() {
29
- return <Stack screenOptions={{ headerShown: false }} />;
74
+ return (
75
+ <TamaguiProvider config={config}>
76
+ <Stack screenOptions={{ headerShown: false }} />
77
+ </TamaguiProvider>
78
+ );
30
79
  }
31
80
  `;
32
81
 
33
- export const expoIndexTemplate = `import { Text, View } from 'react-native';
82
+ export const expoIndexTemplate = `import { Button, H1, YStack } from 'tamagui';
34
83
 
35
84
  export default function Home() {
36
85
  return (
37
- <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
38
- <Text>Aexis Zero</Text>
39
- </View>
86
+ <YStack flex={1} alignItems=\"center\" justifyContent=\"center\" gap=\"$4\">
87
+ <H1>Aexis Zero</H1>
88
+ <Button>Start building</Button>
89
+ </YStack>
40
90
  );
41
91
  }
42
92
  `;