create-aziz-app 3.1.2 → 3.1.3

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 (33) hide show
  1. package/README.md +658 -207
  2. package/bin/create-aziz-app.js +302 -302
  3. package/package.json +1 -1
  4. package/template/README.md +402 -402
  5. package/template/_framework/api.js +219 -219
  6. package/template/_framework/boot.js +33 -33
  7. package/template/_framework/hooks.js +367 -291
  8. package/template/_framework/next-response.js +128 -128
  9. package/template/_framework/route-match.js +145 -145
  10. package/template/_framework/router.js +465 -259
  11. package/template/_framework/runtime.js +264 -264
  12. package/template/app/layout.js +18 -18
  13. package/template/app/page.js +33 -33
  14. package/template/docs/01-getting-started.md +151 -151
  15. package/template/docs/02-project-structure.md +109 -109
  16. package/template/docs/03-pages-and-layouts.md +140 -140
  17. package/template/docs/04-routing.md +120 -120
  18. package/template/docs/05-hooks.md +169 -169
  19. package/template/docs/06-api-routes.md +210 -210
  20. package/template/docs/07-api-client.md +151 -151
  21. package/template/docs/08-styling.md +94 -94
  22. package/template/docs/09-imports-and-modules.md +97 -97
  23. package/template/docs/10-database-and-models.md +154 -154
  24. package/template/docs/11-authentication.md +171 -171
  25. package/template/docs/12-production-and-deployment.md +121 -121
  26. package/template/docs/13-internals.md +179 -179
  27. package/template/docs/README.md +67 -67
  28. package/template/index.html +20 -20
  29. package/template/package.json +16 -16
  30. package/template/scripts/dev-server.js +493 -381
  31. package/template/scripts/lib/api-router.js +525 -525
  32. package/template/scripts/lib/js-transform.js +213 -195
  33. package/template/scripts/lib/module-resolver.js +132 -132
@@ -1,195 +1,213 @@
1
- // ============================================================
2
- // Aziz.js — JS/JSX transform pipeline (server-side)
3
- // - JSX → h() calls via @babel/standalone
4
- // - Bare / @/ import rewriting to browser-servable URLs
5
- // - mtime-based transform cache (big dev-server speed win)
6
- // ============================================================
7
-
8
- const path = require('path');
9
- const fs = require('fs');
10
-
11
- // ─── Babel loading (with fallback resolution for monorepos/tests) ───
12
- let Babel = null;
13
- try {
14
- Babel = require('@babel/standalone');
15
- } catch (e) {
16
- const fallbacks = [
17
- path.join(__dirname, '..', '..', 'node_modules', '@babel', 'standalone'),
18
- path.join(process.cwd(), 'node_modules', '@babel', 'standalone'),
19
- ];
20
- for (const fb of fallbacks) {
21
- try { Babel = require(fb); break; } catch { /* keep looking */ }
22
- }
23
- }
24
-
25
- function hasBabel() {
26
- return !!Babel;
27
- }
28
-
29
- // ─── JSX transform ───────────────────────────────────────────
30
-
31
- function isFrameworkPath(filePath) {
32
- return filePath.includes('/_framework/') || filePath.includes('\\_framework\\');
33
- }
34
-
35
- function transformJSX(code, filename) {
36
- if (!Babel) return code;
37
- const result = Babel.transform(code, {
38
- filename,
39
- presets: [
40
- ['react', { pragma: 'h', pragmaFrag: 'Fragment' }]
41
- ],
42
- plugins: [],
43
- sourceType: 'module',
44
- });
45
- return result.code;
46
- }
47
-
48
- // ─── Import rewriting ─────────────────────────────────────────
49
- // import X from 'mongoose' → '/_modules/mongoose'
50
- // import X from '@/lib/db' → '/lib/db'
51
- // import X from '/absolute.js' → unchanged
52
- // import('pkg') → import('/_modules/pkg')
53
- //
54
- // Runs on the whole module (not line-by-line) so multi-line
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
- }
105
-
106
- function isRelativeOrAbsolute(spec) {
107
- return spec.startsWith('/') ||
108
- spec.startsWith('./') ||
109
- spec.startsWith('../') ||
110
- spec.startsWith('#') ||
111
- spec.startsWith('node:') ||
112
- spec.startsWith('data:') ||
113
- spec.startsWith('file:') ||
114
- /^https?:\/\//.test(spec);
115
- }
116
-
117
- function mapSpecifier(spec) {
118
- if (isRelativeOrAbsolute(spec)) return spec;
119
- if (spec.startsWith('@/')) return '/' + spec.slice(2);
120
- return '/_modules/' + spec;
121
- }
122
-
123
- function rewriteImports(code) {
124
- const isCode = codePositions(code);
125
-
126
- // from 'specifier' (covers `import ... from`, `export ... from`)
127
- code = code.replace(/(\bfrom\s*)(['"])([^'"\n]+)\2/g, (m, from, q, spec, offset) => {
128
- if (!isCode[offset]) return m;
129
- const mapped = mapSpecifier(spec);
130
- return mapped === spec ? m : `${from}${q}${mapped}${q}`;
131
- });
132
-
133
- // side-effect imports: import 'specifier';
134
- // (requires the quote right after `import`, so `import(` is not matched)
135
- code = code.replace(/(\bimport\s+)(['"])([^'"\n]+)\2/g, (m, kw, q, spec, offset) => {
136
- if (!isCode[offset]) return m;
137
- const mapped = mapSpecifier(spec);
138
- return mapped === spec ? m : `${kw}${q}${mapped}${q}`;
139
- });
140
-
141
- // dynamic imports: import('specifier')
142
- code = code.replace(/(\bimport\s*\(\s*)(['"])([^'"\n]+)\2(\s*\))/g, (m, pre, q, spec, post, offset) => {
143
- if (!isCode[offset]) return m;
144
- const mapped = mapSpecifier(spec);
145
- return mapped === spec ? m : `${pre}${q}${mapped}${q}${post}`;
146
- });
147
-
148
- return code;
149
- }
150
-
151
- // ─── mtime-keyed transform cache ─────────────────────────────
152
-
153
- const cache = new Map(); // filePath → { mtimeMs, code }
154
-
155
- function transformModule(code, filePath, mtimeMs) {
156
- // Framework internals contain no JSX and only use absolute imports.
157
- if (isFrameworkPath(filePath)) return code;
158
-
159
- const hit = cache.get(filePath);
160
- if (hit && hit.mtimeMs === mtimeMs) return hit.code;
161
-
162
- let out;
163
- try {
164
- out = transformJSX(code, path.basename(filePath));
165
- } catch (err) {
166
- console.error(`❌ JSX transform error in ${filePath}:`, err.message);
167
- out = `throw new Error(${JSON.stringify('JSX transform error in ' + filePath + ': ' + err.message)});`;
168
- }
169
- out = rewriteImports(out);
170
- cache.set(filePath, { mtimeMs, code: out });
171
- return out;
172
- }
173
-
174
- function invalidate(filePath) {
175
- cache.delete(filePath);
176
- }
177
-
178
- function clearCache() {
179
- cache.clear();
180
- }
181
-
182
- function cacheSize() {
183
- return cache.size;
184
- }
185
-
186
- module.exports = {
187
- hasBabel,
188
- transformJSX,
189
- rewriteImports,
190
- transformModule,
191
- invalidate,
192
- clearCache,
193
- cacheSize,
194
- isFrameworkPath,
195
- };
1
+ // ============================================================
2
+ // Aziz.js — JS/JSX transform pipeline (server-side)
3
+ // - JSX → h() calls via @babel/standalone
4
+ // - Bare / @/ import rewriting to browser-servable URLs
5
+ // - mtime-based transform cache (big dev-server speed win)
6
+ // ============================================================
7
+
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ // ─── Babel loading (with fallback resolution for monorepos/tests) ───
12
+ let Babel = null;
13
+ try {
14
+ Babel = require('@babel/standalone');
15
+ } catch (e) {
16
+ const fallbacks = [
17
+ path.join(__dirname, '..', '..', 'node_modules', '@babel', 'standalone'),
18
+ path.join(process.cwd(), 'node_modules', '@babel', 'standalone'),
19
+ ];
20
+ for (const fb of fallbacks) {
21
+ try { Babel = require(fb); break; } catch { /* keep looking */ }
22
+ }
23
+ }
24
+
25
+ function hasBabel() {
26
+ return !!Babel;
27
+ }
28
+
29
+ // Thrown whenever a module needs compiling but Babel is unavailable.
30
+ // Never fall back to the untransformed source: the browser cannot execute
31
+ // JSX, the module import rejects, and the router silently renders its
32
+ // built-in 404 — a missing dependency disguised as a routing bug.
33
+ const BABEL_MISSING_MESSAGE =
34
+ '@babel/standalone is not installed, so JSX cannot be compiled. ' +
35
+ 'Run "npm install" in the project folder, then restart the dev server.';
36
+
37
+ // ─── JSX transform ───────────────────────────────────────────
38
+
39
+ function isFrameworkPath(filePath) {
40
+ return filePath.includes('/_framework/') || filePath.includes('\\_framework\\');
41
+ }
42
+
43
+ function transformJSX(code, filename) {
44
+ if (!Babel) throw new Error(BABEL_MISSING_MESSAGE);
45
+ const result = Babel.transform(code, {
46
+ filename,
47
+ presets: [
48
+ // runtime: 'classic' is required. Babel 8 defaults to the automatic
49
+ // runtime, which rejects an explicit pragma ("pragma and pragmaFrag
50
+ // cannot be set when runtime is automatic"). We compile to the
51
+ // global `h`/`Fragment` that boot.js installs, not to jsx-runtime.
52
+ ['react', { runtime: 'classic', pragma: 'h', pragmaFrag: 'Fragment' }]
53
+ ],
54
+ plugins: [],
55
+ sourceType: 'module',
56
+ });
57
+ return result.code;
58
+ }
59
+
60
+ // ─── Import rewriting ─────────────────────────────────────────
61
+ // import X from 'mongoose' → '/_modules/mongoose'
62
+ // import X from '@/lib/db' → '/lib/db'
63
+ // import X from '/absolute.js' → unchanged
64
+ // import('pkg') → import('/_modules/pkg')
65
+ //
66
+ // Runs on the whole module (not line-by-line) so multi-line
67
+ // import statements are handled correctly.
68
+ //
69
+ // Only keywords sitting in CODE position are rewritten. A `from`
70
+ // or `import` inside a string literal / template literal / comment
71
+ // is prose (e.g. documentation text in JSX) and must be left alone
72
+ // — rewriting it corrupts the module and the page fails to load.
73
+
74
+ // Uint8Array marking every character that is in code position
75
+ // (outside string literals, template literals and comments).
76
+ function codePositions(code) {
77
+ const n = code.length;
78
+ const isCode = new Uint8Array(n);
79
+ const stack = []; // '{' for blocks, 'tpl' for ${ … } in templates
80
+ let mode = 'code'; // code | ' | " | ` | // | /*
81
+ let i = 0;
82
+
83
+ while (i < n) {
84
+ const c = code[i];
85
+
86
+ if (mode === 'code') {
87
+ isCode[i] = 1;
88
+ if (c === '\\') { i += 2; continue; }
89
+ if (c === "'" || c === '"' || c === '`') mode = c;
90
+ else if (c === '/' && code[i + 1] === '/') mode = '//';
91
+ else if (c === '/' && code[i + 1] === '*') mode = '/*';
92
+ else if (c === '{') stack.push('{');
93
+ else if (c === '}') { if (stack.pop() === 'tpl') mode = '`'; }
94
+ i++;
95
+ } else if (mode === "'" || mode === '"' || mode === '`') {
96
+ if (c === '\\') { i += 2; continue; }
97
+ if (c === mode) mode = 'code';
98
+ else if (mode === '`' && c === '$' && code[i + 1] === '{') {
99
+ stack.push('tpl');
100
+ mode = 'code';
101
+ isCode[i] = 1;
102
+ isCode[i + 1] = 1;
103
+ i += 2;
104
+ continue;
105
+ }
106
+ i++;
107
+ } else if (mode === '//') {
108
+ if (c === '\n') mode = 'code';
109
+ i++;
110
+ } else { // block comment
111
+ if (c === '*' && code[i + 1] === '/') { mode = 'code'; i += 2; continue; }
112
+ i++;
113
+ }
114
+ }
115
+ return isCode;
116
+ }
117
+
118
+ function isRelativeOrAbsolute(spec) {
119
+ return spec.startsWith('/') ||
120
+ spec.startsWith('./') ||
121
+ spec.startsWith('../') ||
122
+ spec.startsWith('#') ||
123
+ spec.startsWith('node:') ||
124
+ spec.startsWith('data:') ||
125
+ spec.startsWith('file:') ||
126
+ /^https?:\/\//.test(spec);
127
+ }
128
+
129
+ function mapSpecifier(spec) {
130
+ if (isRelativeOrAbsolute(spec)) return spec;
131
+ if (spec.startsWith('@/')) return '/' + spec.slice(2);
132
+ return '/_modules/' + spec;
133
+ }
134
+
135
+ function rewriteImports(code) {
136
+ const isCode = codePositions(code);
137
+
138
+ // from 'specifier' (covers `import ... from`, `export ... from`)
139
+ code = code.replace(/(\bfrom\s*)(['"])([^'"\n]+)\2/g, (m, from, q, spec, offset) => {
140
+ if (!isCode[offset]) return m;
141
+ const mapped = mapSpecifier(spec);
142
+ return mapped === spec ? m : `${from}${q}${mapped}${q}`;
143
+ });
144
+
145
+ // side-effect imports: import 'specifier';
146
+ // (requires the quote right after `import`, so `import(` is not matched)
147
+ code = code.replace(/(\bimport\s+)(['"])([^'"\n]+)\2/g, (m, kw, q, spec, offset) => {
148
+ if (!isCode[offset]) return m;
149
+ const mapped = mapSpecifier(spec);
150
+ return mapped === spec ? m : `${kw}${q}${mapped}${q}`;
151
+ });
152
+
153
+ // dynamic imports: import('specifier')
154
+ code = code.replace(/(\bimport\s*\(\s*)(['"])([^'"\n]+)\2(\s*\))/g, (m, pre, q, spec, post, offset) => {
155
+ if (!isCode[offset]) return m;
156
+ const mapped = mapSpecifier(spec);
157
+ return mapped === spec ? m : `${pre}${q}${mapped}${q}${post}`;
158
+ });
159
+
160
+ return code;
161
+ }
162
+
163
+ // ─── mtime-keyed transform cache ─────────────────────────────
164
+
165
+ const cache = new Map(); // filePath → { mtimeMs, code }
166
+
167
+ function transformModule(code, filePath, mtimeMs) {
168
+ // Framework internals contain no JSX and only use absolute imports.
169
+ if (isFrameworkPath(filePath)) return code;
170
+
171
+ const hit = cache.get(filePath);
172
+ if (hit && hit.mtimeMs === mtimeMs) return hit.code;
173
+
174
+ let out;
175
+ try {
176
+ out = transformJSX(code, path.basename(filePath));
177
+ } catch (err) {
178
+ if (err.message === BABEL_MISSING_MESSAGE) {
179
+ // Startup prints an actionable banner — don't repeat it for every file.
180
+ out = `throw new Error(${JSON.stringify(BABEL_MISSING_MESSAGE)});`;
181
+ } else {
182
+ console.error(`❌ JSX transform error in ${filePath}:`, err.message);
183
+ out = `throw new Error(${JSON.stringify('JSX transform error in ' + filePath + ': ' + err.message)});`;
184
+ }
185
+ }
186
+ out = rewriteImports(out);
187
+ cache.set(filePath, { mtimeMs, code: out });
188
+ return out;
189
+ }
190
+
191
+ function invalidate(filePath) {
192
+ cache.delete(filePath);
193
+ }
194
+
195
+ function clearCache() {
196
+ cache.clear();
197
+ }
198
+
199
+ function cacheSize() {
200
+ return cache.size;
201
+ }
202
+
203
+ module.exports = {
204
+ hasBabel,
205
+ BABEL_MISSING_MESSAGE,
206
+ transformJSX,
207
+ rewriteImports,
208
+ transformModule,
209
+ invalidate,
210
+ clearCache,
211
+ cacheSize,
212
+ isFrameworkPath,
213
+ };