create-zoijs 0.1.3 → 0.2.0
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/CHANGELOG.md +127 -74
- package/LICENSE +21 -21
- package/README.md +98 -97
- package/bin/create-zoijs.js +197 -169
- package/core-cdn.json +36 -0
- package/package.json +44 -43
- package/templates/app/README.md +68 -48
- package/templates/app/_gitignore +1 -1
- package/templates/app/_vscode/extensions.json +7 -0
- package/templates/app/dev-server.mjs +124 -74
- package/templates/app/index.html +24 -22
- package/templates/app/jsconfig.json +16 -0
- package/templates/app/package.json +12 -12
- package/templates/app/src/app.js +98 -98
- package/templates/app/src/components/Header.js +16 -16
- package/templates/app/src/components/StatCard.js +13 -13
- package/templates/app/src/components/TaskItem.js +18 -18
- package/templates/app/src/style.css +241 -241
- package/templates/basic/README.md +50 -30
- package/templates/basic/_gitignore +1 -1
- package/templates/basic/_vscode/extensions.json +7 -0
- package/templates/basic/dev-server.mjs +124 -74
- package/templates/basic/index.html +24 -22
- package/templates/basic/jsconfig.json +16 -0
- package/templates/basic/package.json +12 -12
- package/templates/basic/src/app.js +18 -18
- package/templates/basic/src/style.css +33 -33
- package/templates/library/_vscode/extensions.json +7 -0
- package/templates/library/package.json +2 -2
- package/templates/minimal/README.md +29 -8
- package/templates/minimal/dev-server.mjs +124 -0
- package/templates/minimal/index.html +5 -3
- package/templates/minimal/package.json +9 -0
- package/templates/typescript/README.md +20 -0
- package/templates/typescript/_gitignore +1 -1
- package/templates/typescript/_vscode/extensions.json +7 -0
- package/templates/typescript/dev-server.mjs +124 -74
- package/templates/typescript/index.html +24 -22
- package/templates/typescript/package.json +1 -1
- package/templates/typescript/src/style.css +33 -33
package/bin/create-zoijs.js
CHANGED
|
@@ -1,169 +1,197 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// create-zoijs — a tiny scaffolder for new Zoijs apps. No build step, no
|
|
3
|
-
// dependencies. It copies a template, fills in the app name, and prints the
|
|
4
|
-
// three commands to get going. That's the whole tool — read it top to bottom.
|
|
5
|
-
//
|
|
6
|
-
// Usage:
|
|
7
|
-
// npm create zoijs@latest my-app (npm maps this to create-zoijs)
|
|
8
|
-
// npx create-zoijs my-app --template basic
|
|
9
|
-
//
|
|
10
|
-
// The pure functions below are exported so they can be unit-tested; main() only
|
|
11
|
-
// runs when this file is executed directly (not when imported).
|
|
12
|
-
|
|
13
|
-
import fs from "node:fs";
|
|
14
|
-
import path from "node:path";
|
|
15
|
-
import readline from "node:readline";
|
|
16
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
17
|
-
|
|
18
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
|
|
20
|
-
export const TEMPLATES = ["basic", "app", "typescript", "minimal", "library"];
|
|
21
|
-
export const DEFAULT_TEMPLATE = "app";
|
|
22
|
-
const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// create-zoijs — a tiny scaffolder for new Zoijs apps. No build step, no
|
|
3
|
+
// dependencies. It copies a template, fills in the app name, and prints the
|
|
4
|
+
// three commands to get going. That's the whole tool — read it top to bottom.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// npm create zoijs@latest my-app (npm maps this to create-zoijs)
|
|
8
|
+
// npx create-zoijs my-app --template basic
|
|
9
|
+
//
|
|
10
|
+
// The pure functions below are exported so they can be unit-tested; main() only
|
|
11
|
+
// runs when this file is executed directly (not when imported).
|
|
12
|
+
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import readline from "node:readline";
|
|
16
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
17
|
+
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
|
|
20
|
+
export const TEMPLATES = ["basic", "app", "typescript", "minimal", "library"];
|
|
21
|
+
export const DEFAULT_TEMPLATE = "app";
|
|
22
|
+
const TEMPLATES_DIR = path.join(__dirname, "..", "templates");
|
|
23
|
+
|
|
24
|
+
// The one exact @zoijs/core version generated apps target, plus an integrity-protected
|
|
25
|
+
// jsDelivr import map for it (generated by scripts/cdn-importmap.mjs, verified at release).
|
|
26
|
+
// Templates never hard-code a Zoijs version: they use the tokens below.
|
|
27
|
+
const CORE_CDN = path.join(__dirname, "..", "core-cdn.json");
|
|
28
|
+
export function loadCore(file = CORE_CDN) {
|
|
29
|
+
const cdn = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
30
|
+
return { version: cdn.packages["@zoijs/core"].version, imports: cdn.imports, integrity: cdn.integrity };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---- argument parsing --------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/** Parse argv into { name, template }. The first non-flag arg is the app name. */
|
|
36
|
+
export function parseArgs(argv) {
|
|
37
|
+
let name;
|
|
38
|
+
let template = DEFAULT_TEMPLATE;
|
|
39
|
+
for (let i = 0; i < argv.length; i++) {
|
|
40
|
+
const a = argv[i];
|
|
41
|
+
if (a === "--template" || a === "-t") template = argv[++i];
|
|
42
|
+
else if (a.startsWith("--template=")) template = a.slice("--template=".length);
|
|
43
|
+
else if (!a.startsWith("-") && name === undefined) name = a;
|
|
44
|
+
}
|
|
45
|
+
return { name, template };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---- naming ------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
/** A readable Title Case form of a project name: "task-manager" → "Task Manager". */
|
|
51
|
+
export function toTitle(name) {
|
|
52
|
+
return String(name)
|
|
53
|
+
.replace(/[-_]+/g, " ")
|
|
54
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
55
|
+
.trim()
|
|
56
|
+
.split(/\s+/)
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
59
|
+
.join(" ");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Validate a project name. Returns { valid, problems[] }. Keeps it npm-safe. */
|
|
63
|
+
export function validateName(name) {
|
|
64
|
+
const problems = [];
|
|
65
|
+
if (!name || !name.trim()) {
|
|
66
|
+
problems.push("Project name cannot be empty.");
|
|
67
|
+
return { valid: false, problems };
|
|
68
|
+
}
|
|
69
|
+
if (name.trim() !== name) problems.push("Project name cannot have leading or trailing spaces.");
|
|
70
|
+
if (name.length > 214) problems.push("Project name must be 214 characters or fewer.");
|
|
71
|
+
if (/^[._]/.test(name)) problems.push("Project name cannot start with a dot or underscore.");
|
|
72
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(name)) {
|
|
73
|
+
problems.push("Project name may only contain letters, numbers, dashes, dots, and underscores.");
|
|
74
|
+
}
|
|
75
|
+
return { valid: problems.length === 0, problems };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---- filesystem --------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/** Throw if the target directory exists and already has files in it. */
|
|
81
|
+
export function ensureTargetAvailable(targetDir) {
|
|
82
|
+
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
83
|
+
throw new Error(`Directory "${path.basename(targetDir)}" already exists and is not empty.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const TEXT_EXT = new Set([".js", ".ts", ".mjs", ".html", ".css", ".json", ".md", ".txt", ".svg"]);
|
|
88
|
+
const isText = (file) => file === "_gitignore" || TEXT_EXT.has(path.extname(file));
|
|
89
|
+
|
|
90
|
+
// npm strips/renames files and dirs that start with a dot from a published package,
|
|
91
|
+
// so templates ship them with a "_" prefix and we restore the dot name on copy.
|
|
92
|
+
const RENAME = { _gitignore: ".gitignore", _vscode: ".vscode" };
|
|
93
|
+
|
|
94
|
+
function applyTokens(content, tokens) {
|
|
95
|
+
return content.replace(/\{\{(APP_NAME|APP_TITLE|ZOIJS_CORE_VERSION|ZOIJS_CORE_CDN|ZOIJS_CORE_IMPORTMAP)\}\}/g, (_, key) => tokens[key]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// The import map for an HTML template, indented to sit inside <script type="importmap">.
|
|
99
|
+
function importMapJson(core) {
|
|
100
|
+
return JSON.stringify({ imports: core.imports, integrity: core.integrity }, null, 2).replace(/\n/g, "\n ");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function copyDir(srcDir, destDir, tokens, written) {
|
|
104
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
105
|
+
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
106
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
107
|
+
const destName = RENAME[entry.name] ?? entry.name; // restore dot-prefixed names
|
|
108
|
+
const destPath = path.join(destDir, destName);
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
copyDir(srcPath, destPath, tokens, written);
|
|
111
|
+
} else if (isText(entry.name)) {
|
|
112
|
+
fs.writeFileSync(destPath, applyTokens(fs.readFileSync(srcPath, "utf8"), tokens));
|
|
113
|
+
written.push(destPath);
|
|
114
|
+
} else {
|
|
115
|
+
fs.copyFileSync(srcPath, destPath);
|
|
116
|
+
written.push(destPath);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Copy a template into targetDir, substituting the app name. Pure FS work — the
|
|
123
|
+
* caller decides the name/template/dirs, which makes this easy to test.
|
|
124
|
+
*/
|
|
125
|
+
export function scaffold({ name, template = DEFAULT_TEMPLATE, targetDir, templatesDir = TEMPLATES_DIR, core = loadCore() }) {
|
|
126
|
+
const tplDir = path.join(templatesDir, template);
|
|
127
|
+
if (!TEMPLATES.includes(template) || !fs.existsSync(tplDir)) {
|
|
128
|
+
throw new Error(`Unknown template "${template}". Available templates: ${TEMPLATES.join(", ")}.`);
|
|
129
|
+
}
|
|
130
|
+
const tokens = {
|
|
131
|
+
APP_NAME: name.toLowerCase(),
|
|
132
|
+
APP_TITLE: toTitle(name),
|
|
133
|
+
ZOIJS_CORE_VERSION: core.version,
|
|
134
|
+
ZOIJS_CORE_CDN: `https://cdn.jsdelivr.net/npm/@zoijs/core@${core.version}/src/`,
|
|
135
|
+
ZOIJS_CORE_IMPORTMAP: importMapJson(core),
|
|
136
|
+
};
|
|
137
|
+
const written = [];
|
|
138
|
+
copyDir(tplDir, targetDir, tokens, written);
|
|
139
|
+
return { written, tokens };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** The commands to print after scaffolding (minimal has nothing to install; library has no app to serve). */
|
|
143
|
+
export function nextSteps(name, template = DEFAULT_TEMPLATE) {
|
|
144
|
+
if (template === "minimal") return [`cd ${name}`, "npm run dev"];
|
|
145
|
+
if (template === "library") return [`cd ${name}`, "npm install", "npm test"];
|
|
146
|
+
return [`cd ${name}`, "npm install", "npm run dev"];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---- the CLI -----------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
function ask(question) {
|
|
152
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
153
|
+
return new Promise((resolve) => rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); }));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const fail = (message) => { console.error(`\n✖ ${message}\n`); process.exit(1); };
|
|
157
|
+
|
|
158
|
+
async function main(argv) {
|
|
159
|
+
const { name: argName, template } = parseArgs(argv);
|
|
160
|
+
|
|
161
|
+
let name = argName;
|
|
162
|
+
if (!name) name = await ask("Project name: ");
|
|
163
|
+
|
|
164
|
+
const { valid, problems } = validateName(name);
|
|
165
|
+
if (!valid) fail(problems.join("\n "));
|
|
166
|
+
|
|
167
|
+
const targetDir = path.resolve(process.cwd(), name);
|
|
168
|
+
try {
|
|
169
|
+
ensureTargetAvailable(targetDir);
|
|
170
|
+
} catch (err) {
|
|
171
|
+
fail(err.message);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let result;
|
|
175
|
+
try {
|
|
176
|
+
result = scaffold({ name, template, targetDir });
|
|
177
|
+
} catch (err) {
|
|
178
|
+
fail(err.message);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(`\n✔ Created ${name} (${template} template) — ${result.written.length} files\n`);
|
|
182
|
+
console.log("Next steps:");
|
|
183
|
+
for (const step of nextSteps(name, template)) console.log(` ${step}`);
|
|
184
|
+
console.log("");
|
|
185
|
+
console.log("Zoijs works without this tool, too — it's only a convenience. See https://zoijs.dev/start\n");
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Run only when executed directly (resolve symlinks so the npm bin shim works).
|
|
189
|
+
function invokedDirectly() {
|
|
190
|
+
try {
|
|
191
|
+
return process.argv[1] && fs.realpathSync(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
192
|
+
} catch {
|
|
193
|
+
return process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (invokedDirectly()) main(process.argv.slice(2));
|
package/core-cdn.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prod": false,
|
|
3
|
+
"imports": {
|
|
4
|
+
"@zoijs/core": "https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/index.js"
|
|
5
|
+
},
|
|
6
|
+
"integrity": {
|
|
7
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/boundary.js": "sha384-sgP+7Uk+gh1Gk39OqMxZEo9Mox6uM6upgCbk9n4HKa8L3LywliSiXjQPQrjj/OG/",
|
|
8
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/brand.js": "sha384-SQKAavcGif93XT58P5unWneM/VMR1rPsGM/wF63kNN4YaR2RdK2mu2IwULo9VYix",
|
|
9
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/each.js": "sha384-ple5ogdJi5exkzQbgaQ8jnLLOymkmnG8dG/B8L36QkDbmIOiuRL+IdNVNNLYh+3N",
|
|
10
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/html.js": "sha384-5sS5ABTdRpfXmhwZuIyhqcSO7fCY1VFVlYcolWRfLsRVzytc9JnUkjfX2Adfy8WI",
|
|
11
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/mount.js": "sha384-eAm8c9K1SY4j66NnZ4pc4exzlek7iG67di9hU5U9S3PnaZnElkqRGpzOy2z4Uz2h",
|
|
12
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/core/renderer.js": "sha384-H2K6FI3+kv2iPxyLb8V9e9Ux4X95PcPQGYu/h7Gq9rk4brE1nLHKFvI4iG5vt7Uz",
|
|
13
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/index.js": "sha384-dDKK+RVA9Pj7X7aJvv0zgepWNZQi9W20zZ1JgACZ4EbIQ9LjvLsss8gsQGiyvcRb",
|
|
14
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/prod.js": "sha384-2Wg5h5XW9gULwwZPet1azoni63tKm9iTs9FrAoLV0vGUa77h5WPgqsG/F3TFPuBy",
|
|
15
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/computed.js": "sha384-nlAK86Jy1IR3yFSePE38l6cjZqcu5SdnYRVQxJh/+2YQL3WhTHdZ18yCKeKL4ffm",
|
|
16
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/core.js": "sha384-dBKT0ohkuylmZHrv0HAjRtTxrMNyo6FZTk8Fqf6LH+aAi3moXikSLGdlPaeZi5IV",
|
|
17
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/devtools.js": "sha384-KyEdZp4AcE9RdgocIKg0Q3NKTIyCUPnMi8LLPGrDDhRCJ55DA/JcqSZRFjoHHR5X",
|
|
18
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/effect.js": "sha384-EMCI2Kpw6k0EUn5QPC6tJLoON+nUgQ7L25AXgIrWLmhMCLKJq/jP+C6Bk/P6gGDY",
|
|
19
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/env.js": "sha384-41791FgWw6cq/pCRB6up+hZExngmUr1tAVxQG58SdvonImEcU6jK7g14jzZAPBFL",
|
|
20
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/owner.js": "sha384-r8/NbofI3PXPfKvjxBuhWcRZGMJQn+Ldbne0ghFkjELu6SjvpaWIHnxF8FuScUAp",
|
|
21
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/runtime.js": "sha384-5CQnBBrMFUfcq+ahlgE4DX/nG0ua+Bo0l70VtRkXVPWQyvGtXoSScup7FHsQqbA2",
|
|
22
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/reactivity/state.js": "sha384-w2HVkiWmSdb8h7qqr4OViqHJrDqlD/oInr+5Lal+lSvXWu3SPuZv35WWDPsEOgUq",
|
|
23
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/utils/dom.js": "sha384-XsdCmmxzHfpBmOdz1648tCRrAMMHbWKIzvm4Aj2VIEpWJDIIaKmihqEARvxN6d+2",
|
|
24
|
+
"https://cdn.jsdelivr.net/npm/@zoijs/core@1.9.0/src/utils/security.js": "sha384-7UafFG7FtgE/ZSVl+gEOKHc7qewaF0V6hKLEqgI9yj9mGTxz62GTzkrUPfbOXOtT"
|
|
25
|
+
},
|
|
26
|
+
"packages": {
|
|
27
|
+
"@zoijs/core": {
|
|
28
|
+
"version": "1.9.0",
|
|
29
|
+
"entries": [
|
|
30
|
+
"src/index.js",
|
|
31
|
+
"src/prod.js"
|
|
32
|
+
],
|
|
33
|
+
"files": 18
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
package/package.json
CHANGED
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-zoijs",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Create a new Zoijs app with one command. Plain HTML, CSS, and JavaScript — no build step.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-zoijs": "bin/create-zoijs.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"bin",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
"create
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"no-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-zoijs",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Create a new Zoijs app with one command. Plain HTML, CSS, and JavaScript — no build step.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-zoijs": "bin/create-zoijs.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"core-cdn.json",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"author": "Zoijs contributors (https://zoijs.dev)",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"homepage": "https://zoijs.dev",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Zoijs/zoijs.git",
|
|
26
|
+
"directory": "create"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Zoijs/zoijs/issues"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"zoijs",
|
|
33
|
+
"create",
|
|
34
|
+
"create-zoijs",
|
|
35
|
+
"scaffold",
|
|
36
|
+
"starter",
|
|
37
|
+
"cli",
|
|
38
|
+
"no-build",
|
|
39
|
+
"no-jsx"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test tests/create.test.js tests/dev-server.test.js tests/versions.test.js"
|
|
43
|
+
}
|
|
44
|
+
}
|
package/templates/app/README.md
CHANGED
|
@@ -1,48 +1,68 @@
|
|
|
1
|
-
# {{APP_TITLE}}
|
|
2
|
-
|
|
3
|
-
A small **project dashboard** built with [Zoijs](https://zoijs.dev) — plain HTML,
|
|
4
|
-
CSS, and JavaScript. **No build step.**
|
|
5
|
-
|
|
6
|
-
## Develop
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
npm install
|
|
10
|
-
npm run dev
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
```text
|
|
14
|
-
Zoijs dev server
|
|
15
|
-
|
|
16
|
-
- Local: http://localhost:7310
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Open the printed URL (it falls back to 7311–7313 if the port is busy).
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
1
|
+
# {{APP_TITLE}}
|
|
2
|
+
|
|
3
|
+
A small **project dashboard** built with [Zoijs](https://zoijs.dev) — plain HTML,
|
|
4
|
+
CSS, and JavaScript. **No build step.**
|
|
5
|
+
|
|
6
|
+
## Develop
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install
|
|
10
|
+
npm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
Zoijs dev server
|
|
15
|
+
|
|
16
|
+
- Local: http://localhost:7310
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Open the printed URL (it falls back to 7311–7313 if the port is busy).
|
|
20
|
+
|
|
21
|
+
The dev server is for **development only**: it listens on `127.0.0.1` (this machine),
|
|
22
|
+
never serves dotfiles (`.env`, `.git/`, …) or anything outside the project folder, and is
|
|
23
|
+
not meant for hosting. To try the app from a phone on your network, opt in explicitly with
|
|
24
|
+
`ZOIJS_HOST=0.0.0.0 npm run dev` — while it runs, anyone on that network can read the
|
|
25
|
+
project's files.
|
|
26
|
+
|
|
27
|
+
## Deploy
|
|
28
|
+
|
|
29
|
+
This page loads the **development** entry of `@zoijs/core` (helpful warnings, devtools).
|
|
30
|
+
When you deploy, change the import map in `index.html` to the **production** entry —
|
|
31
|
+
same API, warnings and the devtools hook off:
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
"@zoijs/core": "./node_modules/@zoijs/core/src/prod.js"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then deploy the files to a static host (not the dev server) and go through the
|
|
38
|
+
[production security checklist](https://zoijs.dev/production-security) — security headers,
|
|
39
|
+
a Content-Security-Policy with your import map's hash, CSRF, and more.
|
|
40
|
+
|
|
41
|
+
## Project layout
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
{{APP_NAME}}/
|
|
45
|
+
index.html import map + #app mount point
|
|
46
|
+
dev-server.mjs tiny zero-dependency static server (npm run dev)
|
|
47
|
+
src/
|
|
48
|
+
app.js owns all state; composes the dashboard
|
|
49
|
+
style.css plain CSS (light/dark, responsive)
|
|
50
|
+
components/
|
|
51
|
+
Header.js parent → child: hero + summary stats
|
|
52
|
+
StatCard.js parent → child: a reusable stat card (used ×4)
|
|
53
|
+
TaskItem.js child → parent: reports toggle/delete via callbacks
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## What it shows
|
|
57
|
+
|
|
58
|
+
- **`createState`** — the task list, the active filter, and the input draft.
|
|
59
|
+
- **`computed`** — total / completed / active counts and the filtered list.
|
|
60
|
+
- **`each`** — keyed rendering of the visible tasks.
|
|
61
|
+
- **Conditional rendering** — the empty state and the active-filter highlight.
|
|
62
|
+
- **Component composition** — `Header`, `StatCard` (×4), and `TaskItem`.
|
|
63
|
+
- **Parent → child** — `App` passes data down to its components as arguments.
|
|
64
|
+
- **Child → parent** — `TaskItem` reports events up through callbacks; `App`
|
|
65
|
+
owns the state and performs the changes.
|
|
66
|
+
|
|
67
|
+
No bundler, no build step. Open the files and read them. Learn more at
|
|
68
|
+
[zoijs.dev](https://zoijs.dev).
|
package/templates/app/_gitignore
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
node_modules
|
|
1
|
+
node_modules
|