@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 +1 -1
- package/src/config/frameworks.ts +17 -2
- package/src/engine/scaffold.ts +79 -1
- package/src/engine/templates.ts +55 -5
package/package.json
CHANGED
package/src/config/frameworks.ts
CHANGED
|
@@ -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: [
|
|
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',
|
package/src/engine/scaffold.ts
CHANGED
|
@@ -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
|
|
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
|
+
}
|
package/src/engine/templates.ts
CHANGED
|
@@ -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
|
|
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 {
|
|
82
|
+
export const expoIndexTemplate = `import { Button, H1, YStack } from 'tamagui';
|
|
34
83
|
|
|
35
84
|
export default function Home() {
|
|
36
85
|
return (
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
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
|
`;
|