@aex.is/zero 0.1.16 → 0.1.17
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/bin/zero-darwin-amd64 +0 -0
- package/bin/zero-darwin-arm64 +0 -0
- package/bin/zero-linux-amd64 +0 -0
- package/bin/zero-linux-arm64 +0 -0
- package/bin/zero-windows-amd64.exe +0 -0
- package/dist/config/base-env.js +2 -21
- package/dist/config/frameworks.js +0 -1
- package/dist/config/modules.js +51 -2
- package/dist/engine/env.js +1 -1
- package/dist/engine/scaffold.js +8 -4
- package/dist/engine/templates.js +59 -43
- package/package.json +1 -1
package/bin/zero-darwin-amd64
CHANGED
|
Binary file
|
package/bin/zero-darwin-arm64
CHANGED
|
Binary file
|
package/bin/zero-linux-amd64
CHANGED
|
Binary file
|
package/bin/zero-linux-arm64
CHANGED
|
Binary file
|
|
Binary file
|
package/dist/config/base-env.js
CHANGED
|
@@ -1,24 +1,5 @@
|
|
|
1
|
-
const nextBaseEnv = [
|
|
2
|
-
|
|
3
|
-
key: 'RESEND_API_KEY',
|
|
4
|
-
description: 'Resend API key',
|
|
5
|
-
url: 'https://resend.com'
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
key: 'CONTACT_FROM_EMAIL',
|
|
9
|
-
description: 'Verified sender email address'
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
key: 'CONTACT_TO_EMAIL',
|
|
13
|
-
description: 'Destination email address'
|
|
14
|
-
}
|
|
15
|
-
];
|
|
16
|
-
const expoBaseEnv = [
|
|
17
|
-
{
|
|
18
|
-
key: 'EXPO_PUBLIC_CONTACT_ENDPOINT',
|
|
19
|
-
description: 'Contact API endpoint (e.g. https://yourdomain.com/api/contact)'
|
|
20
|
-
}
|
|
21
|
-
];
|
|
1
|
+
const nextBaseEnv = [];
|
|
2
|
+
const expoBaseEnv = [];
|
|
22
3
|
export function getBaseEnvHelp(framework) {
|
|
23
4
|
return framework === 'nextjs' ? nextBaseEnv : expoBaseEnv;
|
|
24
5
|
}
|
package/dist/config/modules.js
CHANGED
|
@@ -93,6 +93,43 @@ export const modules = [
|
|
|
93
93
|
nextjs: ['stripe'],
|
|
94
94
|
expo: ['stripe']
|
|
95
95
|
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: 'email',
|
|
99
|
+
label: 'Email',
|
|
100
|
+
description: 'Contact form email via Resend.',
|
|
101
|
+
connect: {
|
|
102
|
+
label: 'Connect to Resend',
|
|
103
|
+
url: 'https://resend.com/api-keys'
|
|
104
|
+
},
|
|
105
|
+
envVars: [
|
|
106
|
+
{
|
|
107
|
+
key: 'RESEND_API_KEY',
|
|
108
|
+
description: 'Resend API key',
|
|
109
|
+
url: 'https://resend.com/api-keys',
|
|
110
|
+
frameworks: ['nextjs']
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
key: 'CONTACT_FROM_EMAIL',
|
|
114
|
+
description: 'Verified sender email address',
|
|
115
|
+
url: 'https://resend.com/domains',
|
|
116
|
+
frameworks: ['nextjs']
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
key: 'CONTACT_TO_EMAIL',
|
|
120
|
+
description: 'Destination email address',
|
|
121
|
+
frameworks: ['nextjs']
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
key: 'EXPO_PUBLIC_CONTACT_ENDPOINT',
|
|
125
|
+
description: 'Contact API endpoint (e.g. https://yourdomain.com/api/contact)',
|
|
126
|
+
frameworks: ['expo']
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
packages: {
|
|
130
|
+
nextjs: ['resend'],
|
|
131
|
+
expo: []
|
|
132
|
+
}
|
|
96
133
|
}
|
|
97
134
|
];
|
|
98
135
|
export function getModuleDefinition(id) {
|
|
@@ -112,21 +149,27 @@ export function getModulePackages(moduleIds, framework) {
|
|
|
112
149
|
}
|
|
113
150
|
return Array.from(packages).sort();
|
|
114
151
|
}
|
|
115
|
-
export function getModuleEnvVars(moduleIds) {
|
|
152
|
+
export function getModuleEnvVars(moduleIds, framework) {
|
|
116
153
|
const envVars = new Set();
|
|
117
154
|
for (const id of moduleIds) {
|
|
118
155
|
const module = getModuleDefinition(id);
|
|
119
156
|
for (const envVar of module.envVars) {
|
|
157
|
+
if (!supportsFramework(envVar, framework)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
120
160
|
envVars.add(envVar.key);
|
|
121
161
|
}
|
|
122
162
|
}
|
|
123
163
|
return Array.from(envVars).sort();
|
|
124
164
|
}
|
|
125
|
-
export function getModuleEnvHelp(moduleIds) {
|
|
165
|
+
export function getModuleEnvHelp(moduleIds, framework) {
|
|
126
166
|
const map = new Map();
|
|
127
167
|
for (const id of moduleIds) {
|
|
128
168
|
const module = getModuleDefinition(id);
|
|
129
169
|
for (const envVar of module.envVars) {
|
|
170
|
+
if (!supportsFramework(envVar, framework)) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
130
173
|
if (!map.has(envVar.key)) {
|
|
131
174
|
map.set(envVar.key, envVar);
|
|
132
175
|
}
|
|
@@ -152,3 +195,9 @@ export function getModuleConnections(moduleIds) {
|
|
|
152
195
|
}
|
|
153
196
|
return connections;
|
|
154
197
|
}
|
|
198
|
+
function supportsFramework(envVar, framework) {
|
|
199
|
+
if (!envVar.frameworks || envVar.frameworks.length === 0) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return envVar.frameworks.includes(framework);
|
|
203
|
+
}
|
package/dist/engine/env.js
CHANGED
|
@@ -3,7 +3,7 @@ import { promises as fs } from 'fs';
|
|
|
3
3
|
import { getBaseEnvVars } from '../config/base-env.js';
|
|
4
4
|
import { getModuleEnvVars } from '../config/modules.js';
|
|
5
5
|
export async function writeEnvExample(moduleIds, framework, targetDir) {
|
|
6
|
-
const envVars = [...getBaseEnvVars(framework), ...getModuleEnvVars(moduleIds)];
|
|
6
|
+
const envVars = [...getBaseEnvVars(framework), ...getModuleEnvVars(moduleIds, framework)];
|
|
7
7
|
const unique = Array.from(new Set(envVars));
|
|
8
8
|
const lines = unique.map((key) => `${key}=`);
|
|
9
9
|
const content = lines.length > 0 ? `${lines.join('\n')}\n` : '';
|
package/dist/engine/scaffold.js
CHANGED
|
@@ -151,14 +151,16 @@ async function applyNextTemplates(config, targetDir) {
|
|
|
151
151
|
const srcAppDir = path.join(targetDir, 'src', 'app');
|
|
152
152
|
const usesSrcDir = await pathExists(srcAppDir);
|
|
153
153
|
const basePath = usesSrcDir ? 'src' : '';
|
|
154
|
-
const envHelp = mergeEnvHelp(getBaseEnvHelp(config.framework), getModuleEnvHelp(config.modules));
|
|
154
|
+
const envHelp = mergeEnvHelp(getBaseEnvHelp(config.framework), getModuleEnvHelp(config.modules, config.framework));
|
|
155
155
|
const connections = getModuleConnections(config.modules);
|
|
156
|
+
const includeContact = config.modules.includes('email');
|
|
156
157
|
const files = buildNextTemplateFiles({
|
|
157
158
|
appName: config.appName,
|
|
158
159
|
domain: config.domain,
|
|
159
160
|
envVars: envHelp,
|
|
160
161
|
connections,
|
|
161
|
-
basePath
|
|
162
|
+
basePath,
|
|
163
|
+
includeContact
|
|
162
164
|
});
|
|
163
165
|
await writeTemplateFiles(targetDir, files);
|
|
164
166
|
const globalsPath = usesSrcDir ? 'src/app/globals.css' : 'app/globals.css';
|
|
@@ -169,14 +171,16 @@ async function applyNextTemplates(config, targetDir) {
|
|
|
169
171
|
await ensurePackageName(targetDir, config.appName);
|
|
170
172
|
}
|
|
171
173
|
async function applyExpoTemplates(config, targetDir) {
|
|
172
|
-
const envHelp = mergeEnvHelp(getBaseEnvHelp(config.framework), getModuleEnvHelp(config.modules));
|
|
174
|
+
const envHelp = mergeEnvHelp(getBaseEnvHelp(config.framework), getModuleEnvHelp(config.modules, config.framework));
|
|
173
175
|
const connections = getModuleConnections(config.modules);
|
|
176
|
+
const includeContact = config.modules.includes('email');
|
|
174
177
|
const files = buildExpoTemplateFiles({
|
|
175
178
|
appName: config.appName,
|
|
176
179
|
domain: config.domain,
|
|
177
180
|
envVars: envHelp,
|
|
178
181
|
connections,
|
|
179
|
-
basePath: ''
|
|
182
|
+
basePath: '',
|
|
183
|
+
includeContact
|
|
180
184
|
});
|
|
181
185
|
await writeTemplateFiles(targetDir, files);
|
|
182
186
|
await ensureExpoConfig(targetDir, config.appName);
|
package/dist/engine/templates.js
CHANGED
|
@@ -21,14 +21,15 @@ export function buildNextTemplateFiles(data) {
|
|
|
21
21
|
const base = data.basePath ? `${data.basePath}/` : '';
|
|
22
22
|
const envList = renderNextEnvList(data.envVars);
|
|
23
23
|
const connectionSection = renderNextConnectionSection(data.connections);
|
|
24
|
-
|
|
24
|
+
const includeContact = data.includeContact;
|
|
25
|
+
const files = [
|
|
25
26
|
{
|
|
26
27
|
path: `${base}app/layout.tsx`,
|
|
27
28
|
content: nextLayoutTemplate(data.appName, data.domain)
|
|
28
29
|
},
|
|
29
30
|
{
|
|
30
31
|
path: `${base}app/page.tsx`,
|
|
31
|
-
content: nextHomeTemplate(data.appName, data.domain, envList, connectionSection)
|
|
32
|
+
content: nextHomeTemplate(data.appName, data.domain, envList, connectionSection, includeContact)
|
|
32
33
|
},
|
|
33
34
|
{
|
|
34
35
|
path: `${base}app/about/page.tsx`,
|
|
@@ -38,30 +39,18 @@ export function buildNextTemplateFiles(data) {
|
|
|
38
39
|
path: `${base}app/guide/page.tsx`,
|
|
39
40
|
content: nextRouteTemplate('Guide', 'Three routes are ready. Customize and ship.')
|
|
40
41
|
},
|
|
41
|
-
{
|
|
42
|
-
path: `${base}app/contact/page.tsx`,
|
|
43
|
-
content: nextContactPageTemplate()
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
path: `${base}app/api/contact/route.ts`,
|
|
47
|
-
content: nextContactRouteTemplate(data.appName)
|
|
48
|
-
},
|
|
49
42
|
{
|
|
50
43
|
path: `${base}app/globals.css`,
|
|
51
44
|
content: nextGlobalsCss()
|
|
52
45
|
},
|
|
53
46
|
{
|
|
54
47
|
path: `${base}components/site-header.tsx`,
|
|
55
|
-
content: nextHeaderTemplate(data.appName)
|
|
48
|
+
content: nextHeaderTemplate(data.appName, includeContact)
|
|
56
49
|
},
|
|
57
50
|
{
|
|
58
51
|
path: `${base}components/site-footer.tsx`,
|
|
59
52
|
content: nextFooterTemplate(data.domain)
|
|
60
53
|
},
|
|
61
|
-
{
|
|
62
|
-
path: `${base}components/contact-form.tsx`,
|
|
63
|
-
content: nextContactFormTemplate()
|
|
64
|
-
},
|
|
65
54
|
{
|
|
66
55
|
path: `${base}components/connection-guide.tsx`,
|
|
67
56
|
content: nextConnectionGuideTemplate(data.connections)
|
|
@@ -75,22 +64,34 @@ export function buildNextTemplateFiles(data) {
|
|
|
75
64
|
content: nextUtilsTemplate()
|
|
76
65
|
}
|
|
77
66
|
];
|
|
67
|
+
if (includeContact) {
|
|
68
|
+
files.splice(4, 0, {
|
|
69
|
+
path: `${base}app/contact/page.tsx`,
|
|
70
|
+
content: nextContactPageTemplate()
|
|
71
|
+
});
|
|
72
|
+
files.splice(5, 0, {
|
|
73
|
+
path: `${base}app/api/contact/route.ts`,
|
|
74
|
+
content: nextContactRouteTemplate(data.appName)
|
|
75
|
+
});
|
|
76
|
+
files.push({
|
|
77
|
+
path: `${base}components/contact-form.tsx`,
|
|
78
|
+
content: nextContactFormTemplate()
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
return files;
|
|
78
82
|
}
|
|
79
83
|
export function buildExpoTemplateFiles(data) {
|
|
80
84
|
const envItems = renderExpoEnvItems(data.envVars);
|
|
81
85
|
const connectionItems = renderConnectionItems(data.connections);
|
|
82
|
-
|
|
86
|
+
const includeContact = data.includeContact;
|
|
87
|
+
const files = [
|
|
83
88
|
{
|
|
84
89
|
path: 'app/_layout.tsx',
|
|
85
90
|
content: expoLayoutTemplate()
|
|
86
91
|
},
|
|
87
92
|
{
|
|
88
93
|
path: 'app/index.tsx',
|
|
89
|
-
content: expoHomeTemplate(data.appName, data.domain, envItems)
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
path: 'app/contact.tsx',
|
|
93
|
-
content: expoContactTemplate()
|
|
94
|
+
content: expoHomeTemplate(data.appName, data.domain, envItems, includeContact)
|
|
94
95
|
},
|
|
95
96
|
{
|
|
96
97
|
path: 'app/about.tsx',
|
|
@@ -106,7 +107,7 @@ export function buildExpoTemplateFiles(data) {
|
|
|
106
107
|
},
|
|
107
108
|
{
|
|
108
109
|
path: 'components/site-header.tsx',
|
|
109
|
-
content: expoHeaderTemplate(data.appName)
|
|
110
|
+
content: expoHeaderTemplate(data.appName, includeContact)
|
|
110
111
|
},
|
|
111
112
|
{
|
|
112
113
|
path: 'components/site-footer.tsx',
|
|
@@ -120,10 +121,6 @@ export function buildExpoTemplateFiles(data) {
|
|
|
120
121
|
path: 'components/connection-guide.tsx',
|
|
121
122
|
content: expoConnectionGuideTemplate(connectionItems)
|
|
122
123
|
},
|
|
123
|
-
{
|
|
124
|
-
path: 'components/contact-form.tsx',
|
|
125
|
-
content: expoContactFormTemplate()
|
|
126
|
-
},
|
|
127
124
|
{
|
|
128
125
|
path: 'components/page-shell.tsx',
|
|
129
126
|
content: expoPageShellTemplate()
|
|
@@ -141,6 +138,17 @@ export function buildExpoTemplateFiles(data) {
|
|
|
141
138
|
content: babelConfigTemplate()
|
|
142
139
|
}
|
|
143
140
|
];
|
|
141
|
+
if (includeContact) {
|
|
142
|
+
files.splice(2, 0, {
|
|
143
|
+
path: 'app/contact.tsx',
|
|
144
|
+
content: expoContactTemplate()
|
|
145
|
+
});
|
|
146
|
+
files.push({
|
|
147
|
+
path: 'components/contact-form.tsx',
|
|
148
|
+
content: expoContactFormTemplate()
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return files;
|
|
144
152
|
}
|
|
145
153
|
function nextLayoutTemplate(appName, domain) {
|
|
146
154
|
const description = `${escapeTemplate(appName)} starter crafted by Aexis Zero.`;
|
|
@@ -189,10 +197,17 @@ export default function RootLayout({
|
|
|
189
197
|
}
|
|
190
198
|
`;
|
|
191
199
|
}
|
|
192
|
-
function nextHomeTemplate(appName, domain, envList, connectionSection) {
|
|
200
|
+
function nextHomeTemplate(appName, domain, envList, connectionSection, includeContact) {
|
|
201
|
+
const contactImport = includeContact ? "import { ContactForm } from '@/components/contact-form';\n" : '';
|
|
202
|
+
const contactSection = includeContact ? '\n <ContactForm />' : '';
|
|
203
|
+
const routeList = includeContact
|
|
204
|
+
? `Explore <code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/about</code>,{' '}
|
|
205
|
+
<code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/guide</code>, and{' '}
|
|
206
|
+
<code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/contact</code>.`
|
|
207
|
+
: `Explore <code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/about</code> and{' '}
|
|
208
|
+
<code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/guide</code>.`;
|
|
193
209
|
return `import { EnvList } from '@/components/env-list';
|
|
194
|
-
import {
|
|
195
|
-
import { ConnectionGuide } from '@/components/connection-guide';
|
|
210
|
+
${contactImport}import { ConnectionGuide } from '@/components/connection-guide';
|
|
196
211
|
|
|
197
212
|
export const metadata = {
|
|
198
213
|
title: 'Home',
|
|
@@ -220,12 +235,10 @@ export default function Home() {
|
|
|
220
235
|
<div className="flex flex-col gap-2">
|
|
221
236
|
<h2 className="text-base font-bold">Routes</h2>
|
|
222
237
|
<p className="text-base">
|
|
223
|
-
|
|
224
|
-
<code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/guide</code>, and{' '}
|
|
225
|
-
<code className="bg-[var(--fg)] px-2 py-1 text-[var(--bg)]">/contact</code>.
|
|
238
|
+
${routeList}
|
|
226
239
|
</p>
|
|
227
240
|
</div>
|
|
228
|
-
|
|
241
|
+
${contactSection}
|
|
229
242
|
</section>
|
|
230
243
|
);
|
|
231
244
|
}
|
|
@@ -247,14 +260,14 @@ export default function Page() {
|
|
|
247
260
|
}
|
|
248
261
|
`;
|
|
249
262
|
}
|
|
250
|
-
function nextHeaderTemplate(appName) {
|
|
263
|
+
function nextHeaderTemplate(appName, includeContact) {
|
|
264
|
+
const contactLink = includeContact ? "\n { href: '/contact', label: 'Contact' }" : '';
|
|
251
265
|
return `import Link from 'next/link';
|
|
252
266
|
|
|
253
267
|
const links = [
|
|
254
268
|
{ href: '/', label: 'Home' },
|
|
255
269
|
{ href: '/about', label: 'About' },
|
|
256
|
-
{ href: '/guide', label: 'Guide' }
|
|
257
|
-
{ href: '/contact', label: 'Contact' }
|
|
270
|
+
{ href: '/guide', label: 'Guide' },${contactLink}
|
|
258
271
|
];
|
|
259
272
|
|
|
260
273
|
export function SiteHeader({ appName }: { appName: string }) {
|
|
@@ -654,13 +667,16 @@ export default function RootLayout() {
|
|
|
654
667
|
}
|
|
655
668
|
`;
|
|
656
669
|
}
|
|
657
|
-
function expoHomeTemplate(appName, domain, envItems) {
|
|
670
|
+
function expoHomeTemplate(appName, domain, envItems, includeContact) {
|
|
671
|
+
const contactImport = includeContact ? "import { ContactForm } from '../components/contact-form';\n" : '';
|
|
672
|
+
const contactSection = includeContact ? '\n <ContactForm />' : '';
|
|
673
|
+
const routeLine = includeContact ? 'Visit /about, /guide, and /contact.' : 'Visit /about and /guide.';
|
|
658
674
|
return `import { Head } from 'expo-router/head';
|
|
659
675
|
import { Text, YStack } from 'tamagui';
|
|
660
676
|
import { PageShell } from '../components/page-shell';
|
|
661
677
|
import { EnvList } from '../components/env-list';
|
|
662
678
|
import { ConnectionGuide } from '../components/connection-guide';
|
|
663
|
-
|
|
679
|
+
${contactImport}
|
|
664
680
|
import { FONT_BOLD, FONT_REGULAR, FONT_SIZE, useThemeColors } from '../components/theme';
|
|
665
681
|
|
|
666
682
|
export default function Home() {
|
|
@@ -691,10 +707,10 @@ export default function Home() {
|
|
|
691
707
|
Routes
|
|
692
708
|
</Text>
|
|
693
709
|
<Text fontFamily={FONT_REGULAR} fontSize={FONT_SIZE} color={fg}>
|
|
694
|
-
|
|
710
|
+
${routeLine}
|
|
695
711
|
</Text>
|
|
696
712
|
</YStack>
|
|
697
|
-
|
|
713
|
+
${contactSection}
|
|
698
714
|
</PageShell>
|
|
699
715
|
);
|
|
700
716
|
}
|
|
@@ -770,7 +786,8 @@ export function useThemeColors() {
|
|
|
770
786
|
}
|
|
771
787
|
`;
|
|
772
788
|
}
|
|
773
|
-
function expoHeaderTemplate(appName) {
|
|
789
|
+
function expoHeaderTemplate(appName, includeContact) {
|
|
790
|
+
const contactLink = includeContact ? "\n { href: '/contact', label: 'Contact' }" : '';
|
|
774
791
|
return `import { useState } from 'react';
|
|
775
792
|
import { Link } from 'expo-router';
|
|
776
793
|
import { Button, Text, XStack, YStack } from 'tamagui';
|
|
@@ -779,8 +796,7 @@ import { FONT_BOLD, FONT_REGULAR, FONT_SIZE, useThemeColors } from './theme';
|
|
|
779
796
|
const links = [
|
|
780
797
|
{ href: '/', label: 'Home' },
|
|
781
798
|
{ href: '/about', label: 'About' },
|
|
782
|
-
{ href: '/guide', label: 'Guide' }
|
|
783
|
-
{ href: '/contact', label: 'Contact' }
|
|
799
|
+
{ href: '/guide', label: 'Guide' },${contactLink}
|
|
784
800
|
];
|
|
785
801
|
|
|
786
802
|
export function SiteHeader() {
|