create-aziz-app 3.1.0 → 3.1.2

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.
@@ -1,20 +1,18 @@
1
- import Navbar from '/components/Navbar.js';
2
- import Footer from '/components/Footer.js';
3
-
4
- // Root layout — wraps every page (like Next.js app/layout.js)
5
- export default function Layout({ children }) {
6
- return (
7
- <div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
8
- <Navbar />
9
- <main style={{ flex: 1 }}>
10
- {children}
11
- </main>
12
- <Footer />
13
- </div>
14
- );
15
- }
16
-
17
- export const metadata = {
18
- title: 'My App',
19
- description: 'Built with Aziz.js'
20
- };
1
+ // Root layout — wraps every page (like Next.js app/layout.js)
2
+ export default function Layout({ children }) {
3
+ return (
4
+ <div style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
5
+ <main style={{ flex: 1 }}>
6
+ {children}
7
+ </main>
8
+ <footer>
9
+ Built with Aziz.js
10
+ </footer>
11
+ </div>
12
+ );
13
+ }
14
+
15
+ export const metadata = {
16
+ title: 'My App',
17
+ description: 'Built with Aziz.js'
18
+ };
@@ -1,56 +1,33 @@
1
- // Home page — route: /
2
- export default function Page() {
3
- const [count, setCount] = useState(0);
4
-
5
- return (
6
- <div style={{ padding: '40px 32px', maxWidth: '900px', margin: '0 auto' }}>
7
- <h1>Welcome Home 👋</h1>
8
-
9
- <div class="card">
10
- <h2>Counter</h2>
11
- <p>useState works exactly like React!</p>
12
- <div class="count-display">{count}</div>
13
- <div class="counter-actions">
14
- <button class="btn btn-danger" onClick={() => setCount(c => c - 1)}>−</button>
15
- <button class="btn btn-primary" onClick={() => setCount(c => c + 1)}>+</button>
16
- <button class="btn btn-grey" onClick={() => setCount(0)}>Reset</button>
17
- </div>
18
- </div>
19
-
20
- <div class="card">
21
- <h2>File-based Routing</h2>
22
- <p>
23
- Each <code>page.js</code> inside <code>app/</code> is a route — just like Next.js.
24
- Create <code>app/about/page.js</code> and it becomes <code>/about</code>.
25
- </p>
26
- <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap' }}>
27
- <button class="btn btn-primary" onClick={() => navigate('/about')}>
28
- Go to About →
29
- </button>
30
- <button class="btn btn-primary" onClick={() => navigate('/blog/hello-world')}>
31
- Blog Post →
32
- </button>
33
- <button class="btn btn-primary" onClick={() => navigate('/contact')}>
34
- Contact →
35
- </button>
36
- <button class="btn btn-primary" onClick={() => navigate('/test')}>
37
- Go to Test →
38
- </button>
39
- </div>
40
- </div>
41
-
42
- <div class="card">
43
- <h2>Import Anything</h2>
44
- <p>
45
- Use <code>import</code> statements just like Next.js.
46
- The dev server resolves bare imports from <code>node_modules</code> automatically.
47
- </p>
48
- <pre style={{ background: '#282c34', color: '#61dafb', padding: '16px', borderRadius: '8px', overflow: 'auto', fontSize: '13px' }}>
49
- {`import mongoose from 'mongoose';
50
- import connectDB from '@/lib/db';
51
- import User from '@/models/User';`}
52
- </pre>
53
- </div>
54
- </div>
55
- );
56
- }
1
+ // Home page — route: /
2
+ export default function Page() {
3
+ const [count, setCount] = useState(0);
4
+
5
+ return (
6
+ <div style={{ padding: '48px 32px', maxWidth: '720px', margin: '0 auto' }}>
7
+ <h1>Welcome to Aziz.js</h1>
8
+ <p>
9
+ Your app is running. Edit <code>app/page.js</code> and save —
10
+ the browser refreshes automatically.
11
+ </p>
12
+
13
+ <div class="card">
14
+ <h2>Interactive state</h2>
15
+ <p>A tiny slice of state, exactly like React's <code>useState</code>.</p>
16
+ <div class="count-display">{count}</div>
17
+ <div class="counter-actions">
18
+ <button class="btn btn-danger" onClick={() => setCount(c => c - 1)}>−</button>
19
+ <button class="btn btn-primary" onClick={() => setCount(c => c + 1)}>+</button>
20
+ <button class="btn btn-grey" onClick={() => setCount(0)}>Reset</button>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="card">
25
+ <h2>Add a page</h2>
26
+ <p>
27
+ Each <code>page.js</code> inside <code>app/</code> is a route.
28
+ Create <code>app/about/page.js</code> and it becomes <code>/about</code>.
29
+ </p>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -53,6 +53,55 @@ function transformJSX(code, filename) {
53
53
  //
54
54
  // Runs on the whole module (not line-by-line) so multi-line
55
55
  // import statements are handled correctly.
56
+ //
57
+ // Only keywords sitting in CODE position are rewritten. A `from`
58
+ // or `import` inside a string literal / template literal / comment
59
+ // is prose (e.g. documentation text in JSX) and must be left alone
60
+ // — rewriting it corrupts the module and the page fails to load.
61
+
62
+ // Uint8Array marking every character that is in code position
63
+ // (outside string literals, template literals and comments).
64
+ function codePositions(code) {
65
+ const n = code.length;
66
+ const isCode = new Uint8Array(n);
67
+ const stack = []; // '{' for blocks, 'tpl' for ${ … } in templates
68
+ let mode = 'code'; // code | ' | " | ` | // | /*
69
+ let i = 0;
70
+
71
+ while (i < n) {
72
+ const c = code[i];
73
+
74
+ if (mode === 'code') {
75
+ isCode[i] = 1;
76
+ if (c === '\\') { i += 2; continue; }
77
+ if (c === "'" || c === '"' || c === '`') mode = c;
78
+ else if (c === '/' && code[i + 1] === '/') mode = '//';
79
+ else if (c === '/' && code[i + 1] === '*') mode = '/*';
80
+ else if (c === '{') stack.push('{');
81
+ else if (c === '}') { if (stack.pop() === 'tpl') mode = '`'; }
82
+ i++;
83
+ } else if (mode === "'" || mode === '"' || mode === '`') {
84
+ if (c === '\\') { i += 2; continue; }
85
+ if (c === mode) mode = 'code';
86
+ else if (mode === '`' && c === '$' && code[i + 1] === '{') {
87
+ stack.push('tpl');
88
+ mode = 'code';
89
+ isCode[i] = 1;
90
+ isCode[i + 1] = 1;
91
+ i += 2;
92
+ continue;
93
+ }
94
+ i++;
95
+ } else if (mode === '//') {
96
+ if (c === '\n') mode = 'code';
97
+ i++;
98
+ } else { // block comment
99
+ if (c === '*' && code[i + 1] === '/') { mode = 'code'; i += 2; continue; }
100
+ i++;
101
+ }
102
+ }
103
+ return isCode;
104
+ }
56
105
 
57
106
  function isRelativeOrAbsolute(spec) {
58
107
  return spec.startsWith('/') ||
@@ -72,21 +121,26 @@ function mapSpecifier(spec) {
72
121
  }
73
122
 
74
123
  function rewriteImports(code) {
124
+ const isCode = codePositions(code);
125
+
75
126
  // from 'specifier' (covers `import ... from`, `export ... from`)
76
- code = code.replace(/(\bfrom\s*)(['"])([^'"\n]+)\2/g, (m, from, q, spec) => {
127
+ code = code.replace(/(\bfrom\s*)(['"])([^'"\n]+)\2/g, (m, from, q, spec, offset) => {
128
+ if (!isCode[offset]) return m;
77
129
  const mapped = mapSpecifier(spec);
78
130
  return mapped === spec ? m : `${from}${q}${mapped}${q}`;
79
131
  });
80
132
 
81
133
  // side-effect imports: import 'specifier';
82
134
  // (requires the quote right after `import`, so `import(` is not matched)
83
- code = code.replace(/(\bimport\s+)(['"])([^'"\n]+)\2/g, (m, kw, q, spec) => {
135
+ code = code.replace(/(\bimport\s+)(['"])([^'"\n]+)\2/g, (m, kw, q, spec, offset) => {
136
+ if (!isCode[offset]) return m;
84
137
  const mapped = mapSpecifier(spec);
85
138
  return mapped === spec ? m : `${kw}${q}${mapped}${q}`;
86
139
  });
87
140
 
88
141
  // dynamic imports: import('specifier')
89
- code = code.replace(/(\bimport\s*\(\s*)(['"])([^'"\n]+)\2(\s*\))/g, (m, pre, q, spec, post) => {
142
+ code = code.replace(/(\bimport\s*\(\s*)(['"])([^'"\n]+)\2(\s*\))/g, (m, pre, q, spec, post, offset) => {
143
+ if (!isCode[offset]) return m;
90
144
  const mapped = mapSpecifier(spec);
91
145
  return mapped === spec ? m : `${pre}${q}${mapped}${q}${post}`;
92
146
  });
@@ -1,39 +0,0 @@
1
- // API Route: POST /api/contact (classic style — api/contact.js)
2
- //
3
- // Demonstrates validation, cookies, and NextResponse helpers.
4
-
5
- const { NextResponse } = require('../_framework/next-response');
6
-
7
- // In-memory store (replace with a real database in production)
8
- const messages = [];
9
-
10
- async function GET(request) {
11
- return NextResponse.json({ count: messages.length, messages });
12
- }
13
-
14
- async function POST(request) {
15
- let body;
16
- try {
17
- body = await request.json();
18
- } catch (err) {
19
- return NextResponse.json({ error: 'Invalid JSON body' }, { status: err.status || 400 });
20
- }
21
-
22
- const { name, email, message } = body;
23
-
24
- if (!name || !email || !message) {
25
- return NextResponse.json(
26
- { error: 'name, email and message are required' },
27
- { status: 400 }
28
- );
29
- }
30
-
31
- const entry = { name, email, message, at: new Date().toISOString() };
32
- messages.push(entry);
33
-
34
- const res = NextResponse.json({ message: `Thanks, ${name}! We received your message.`, entry }, { status: 201 });
35
- res.cookies.set('last_contact', name, { path: '/', maxAge: 60 * 60 });
36
- return res;
37
- }
38
-
39
- module.exports = { GET, POST };
@@ -1,69 +0,0 @@
1
- // API Route: /api/createUser
2
- // Next.js App Router style
3
-
4
- const { NextResponse } = require('../_framework/next-response');
5
-
6
- // Mock database
7
- let users = [
8
- { _id: '1', name: 'Aziz Ahmed', email: 'aziz@example.com', role: 'admin', phone: '123-456-7890' }
9
- ];
10
-
11
- // POST handler
12
- async function POST(request) {
13
- try {
14
- // Parse request body
15
- const body = await request.json();
16
- const { name, email, password, role, phone } = body;
17
-
18
- // Validation
19
- if (!name || !email || !password) {
20
- return NextResponse.json(
21
- {
22
- error: 'Validation failed',
23
- message: 'Name, email, and password are required'
24
- },
25
- { status: 400 }
26
- );
27
- }
28
-
29
- // Check if email already exists
30
- if (users.find(u => u.email === email)) {
31
- return NextResponse.json(
32
- { error: 'User with this email already exists' },
33
- { status: 409 }
34
- );
35
- }
36
-
37
- // Create new user
38
- const newUser = {
39
- _id: String(users.length + 1),
40
- name,
41
- email,
42
- password, // In production, hash this with bcrypt!
43
- role: role || 'user',
44
- phone: phone || '',
45
- createdAt: new Date().toISOString()
46
- };
47
-
48
- users.push(newUser);
49
-
50
- // Return user data (without password)
51
- const { password: _, ...userData } = newUser;
52
-
53
- return NextResponse.json(
54
- {
55
- message: 'User created successfully',
56
- user: userData
57
- },
58
- { status: 201 }
59
- );
60
- } catch (error) {
61
- console.error('Create user error:', error);
62
- return NextResponse.json(
63
- { error: 'Failed to create user' },
64
- { status: 500 }
65
- );
66
- }
67
- }
68
-
69
- module.exports = { POST };
@@ -1,17 +0,0 @@
1
- // API Route: /api/hello
2
- // Next.js App Router style
3
-
4
- const { NextResponse } = require('../_framework/next-response');
5
-
6
- async function GET(request) {
7
- const { searchParams } = new URL(request.url);
8
- const name = searchParams.get('name') || 'World';
9
-
10
- return NextResponse.json({
11
- message: `Hello, ${name}!`,
12
- timestamp: new Date().toISOString(),
13
- method: request.method
14
- });
15
- }
16
-
17
- module.exports = { GET };
@@ -1,61 +0,0 @@
1
- // API Route: /api/loginUser
2
- // Next.js App Router style
3
-
4
- const { NextResponse } = require('../_framework/next-response');
5
-
6
- // Mock database
7
- const users = [
8
- { _id: '1', name: 'Aziz Ahmed', email: 'aziz@example.com', password: 'admin123', role: 'admin' },
9
- { _id: '2', name: 'John Doe', email: 'john@example.com', password: 'user123', role: 'user' }
10
- ];
11
-
12
- // POST handler
13
- async function POST(request) {
14
- try {
15
- // Parse request body
16
- const body = await request.json();
17
- const { email, password } = body;
18
-
19
- // Validation
20
- if (!email || !password) {
21
- return NextResponse.json(
22
- { error: 'Email and password are required' },
23
- { status: 400 }
24
- );
25
- }
26
-
27
- // Find user
28
- const user = users.find(u => u.email === email);
29
-
30
- if (!user) {
31
- return NextResponse.json(
32
- { error: 'Invalid credentials' },
33
- { status: 401 }
34
- );
35
- }
36
-
37
- // Check password (in production, use bcrypt!)
38
- if (user.password !== password) {
39
- return NextResponse.json(
40
- { error: 'Invalid credentials' },
41
- { status: 401 }
42
- );
43
- }
44
-
45
- // Return user data (without password)
46
- const { password: _, ...userData } = user;
47
-
48
- return NextResponse.json({
49
- ...userData,
50
- message: 'Login successful'
51
- });
52
- } catch (error) {
53
- console.error('Login error:', error);
54
- return NextResponse.json(
55
- { error: 'Failed to login' },
56
- { status: 500 }
57
- );
58
- }
59
- }
60
-
61
- module.exports = { POST };
@@ -1,59 +0,0 @@
1
- // API Route: /api/users
2
- // Next.js App Router style
3
-
4
- const { NextResponse } = require('../_framework/next-response');
5
-
6
- let users = [
7
- { id: 1, name: 'Aziz Ahmed', email: 'aziz@example.com', createdAt: new Date().toISOString() },
8
- { id: 2, name: 'John Doe', email: 'john@example.com', createdAt: new Date().toISOString() },
9
- { id: 3, name: 'Jane Smith', email: 'jane@example.com', createdAt: new Date().toISOString() }
10
- ];
11
-
12
- async function GET(request) {
13
- const { searchParams } = new URL(request.url);
14
- const id = searchParams.get('id');
15
-
16
- if (id) {
17
- const user = users.find(u => u.id === parseInt(id));
18
- if (!user) return NextResponse.json({ error: 'User not found' }, { status: 404 });
19
- return NextResponse.json(user);
20
- }
21
-
22
- return NextResponse.json({ users, total: users.length });
23
- }
24
-
25
- async function POST(request) {
26
- const { name, email } = await request.json();
27
-
28
- if (!name || !email) {
29
- return NextResponse.json({ error: 'Name and email are required' }, { status: 400 });
30
- }
31
-
32
- if (users.find(u => u.email === email)) {
33
- return NextResponse.json({ error: 'Email already exists' }, { status: 409 });
34
- }
35
-
36
- const newUser = {
37
- id: Math.max(...users.map(u => u.id)) + 1,
38
- name, email,
39
- createdAt: new Date().toISOString()
40
- };
41
- users.push(newUser);
42
-
43
- return NextResponse.json({ message: 'User created', user: newUser }, { status: 201 });
44
- }
45
-
46
- async function DELETE(request) {
47
- const { searchParams } = new URL(request.url);
48
- const id = searchParams.get('id');
49
-
50
- if (!id) return NextResponse.json({ error: 'ID required' }, { status: 400 });
51
-
52
- const idx = users.findIndex(u => u.id === parseInt(id));
53
- if (idx === -1) return NextResponse.json({ error: 'User not found' }, { status: 404 });
54
-
55
- const deleted = users.splice(idx, 1)[0];
56
- return NextResponse.json({ message: 'User deleted', user: deleted });
57
- }
58
-
59
- module.exports = { GET, POST, DELETE };
@@ -1,41 +0,0 @@
1
- // About page — route: /about
2
- export default function Page() {
3
- return (
4
- <div style={{ padding: '40px 32px', maxWidth: '900px', margin: '0 auto' }}>
5
- <h1>About 📖</h1>
6
-
7
- <div class="card">
8
- <h2>What is Aziz.js?</h2>
9
- <p>
10
- A lightweight Next.js clone — file-based routing, React hooks,
11
- server-side API routes, and ES module imports. Zero dependencies.
12
- </p>
13
- </div>
14
-
15
- <div class="card">
16
- <h2>File Structure</h2>
17
- <pre style={{ background: '#282c34', color: '#61dafb', padding: '16px', borderRadius: '8px', fontSize: '13px' }}>
18
- {`app/
19
- layout.js ← root layout
20
- page.js ← /
21
- about/
22
- page.js ← /about
23
- blog/
24
- [slug]/
25
- page.js ← /blog/:slug
26
-
27
- api/
28
- users.js ← /api/users
29
-
30
- components/
31
- Navbar.js
32
- Footer.js`}
33
- </pre>
34
- </div>
35
-
36
- <button class="btn btn-primary" onClick={() => navigate('/')}>
37
- ← Back Home
38
- </button>
39
- </div>
40
- );
41
- }
@@ -1,31 +0,0 @@
1
- // Blog post page — route: /blog/:slug (dynamic segment)
2
- export default function BlogPost() {
3
- const { slug } = useParams();
4
- const search = useSearchParams();
5
- const from = search.get('from');
6
-
7
- return (
8
- <div style={{ padding: '40px 32px', maxWidth: '900px', margin: '0 auto' }}>
9
- <p style={{ color: '#888', marginBottom: '8px' }}>
10
- <a href="/blog/hello-world" onClick={(e) => { e.preventDefault(); navigate('/blog/hello-world'); }}>hello-world</a>
11
- {' · '}
12
- <a href="/blog/aziz-js" onClick={(e) => { e.preventDefault(); navigate('/blog/aziz-js'); }}>aziz-js</a>
13
- </p>
14
-
15
- <h1>Blog: {slug}</h1>
16
-
17
- <div class="card">
18
- <h2>Dynamic routing works!</h2>
19
- <p>
20
- This page is <code>app/blog/[slug]/page.js</code> and it matches any
21
- <code>/blog/&lt;anything&gt;</code> URL. The matched value is available
22
- through the <code>useParams()</code> hook.
23
- </p>
24
- <p><strong>useParams().slug</strong> = <code>{slug}</code></p>
25
- {from && <p><strong>?from=</strong> = <code>{from}</code></p>}
26
- </div>
27
-
28
- <button class="btn btn-primary" onClick={() => navigate('/')}>← Back Home</button>
29
- </div>
30
- );
31
- }
@@ -1,68 +0,0 @@
1
- // Contact page — route: /contact
2
- // Demonstrates the global `api` client calling a server route handler.
3
- export default function ContactPage() {
4
- const [name, setName] = useState('');
5
- const [email, setEmail] = useState('');
6
- const [message, setMessage] = useState('');
7
- const [status, setStatus] = useState(null); // { type, text }
8
- const [sending, setSending] = useState(false);
9
-
10
- const handleSubmit = async (e) => {
11
- e.preventDefault();
12
- setSending(true);
13
- setStatus(null);
14
- try {
15
- const res = await api.post('/api/contact', { name, email, message });
16
- setStatus({ type: 'ok', text: res.message || 'Message sent!' });
17
- setName(''); setEmail(''); setMessage('');
18
- } catch (err) {
19
- setStatus({ type: 'error', text: err.message || 'Something went wrong' });
20
- } finally {
21
- setSending(false);
22
- }
23
- };
24
-
25
- return (
26
- <div style={{ padding: '40px 32px', maxWidth: '640px', margin: '0 auto' }}>
27
- <h1>Contact 📬</h1>
28
-
29
- <div class="card">
30
- <h2>Send us a message</h2>
31
- <p>This form posts to <code>/api/contact</code> using the global <code>api</code> client.</p>
32
-
33
- <form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '12px', marginTop: '16px' }}>
34
- <input
35
- type="text"
36
- placeholder="Your name"
37
- value={name}
38
- onInput={(e) => setName(e.target.value)}
39
- required
40
- />
41
- <input
42
- type="email"
43
- placeholder="Your email"
44
- value={email}
45
- onInput={(e) => setEmail(e.target.value)}
46
- required
47
- />
48
- <textarea
49
- placeholder="Your message"
50
- value={message}
51
- onInput={(e) => setMessage(e.target.value)}
52
- rows={4}
53
- required
54
- />
55
- <button type="submit" class="btn btn-primary" disabled={sending}>
56
- {sending ? 'Sending…' : 'Send message'}
57
- </button>
58
- </form>
59
-
60
- {status && (
61
- <p style={{ marginTop: '12px', color: status.type === 'ok' ? '#1a7f37' : '#c0392b' }}>
62
- {status.text}
63
- </p>
64
- )}
65
- </div>
66
- </div>
67
- );
68
- }
@@ -1,15 +0,0 @@
1
- // Custom 404 page — rendered when no route matches (like Next.js app/not-found.js)
2
- export default function NotFound() {
3
- return (
4
- <div style={{ padding: '80px 24px', textAlign: 'center' }}>
5
- <h1 style={{ fontSize: '96px', margin: '0 0 8px', color: '#61dafb' }}>404</h1>
6
- <h2 style={{ margin: '0 0 16px' }}>Page not found</h2>
7
- <p style={{ color: '#888', marginBottom: '32px' }}>
8
- We couldn't find the page you were looking for.
9
- </p>
10
- <button class="btn btn-primary" onClick={() => navigate('/')}>
11
- ← Back to Home
12
- </button>
13
- </div>
14
- );
15
- }