chocola 2.0.0-next.10 → 2.0.0-next.11

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/LICENSE CHANGED
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
package/README.md CHANGED
@@ -15,11 +15,10 @@ Import components. Instantiate them. Mount, update, remove. Client-side or serve
15
15
  <script>
16
16
  import CoolButton from './CoolButton.html';
17
17
 
18
- let self;
19
- let input;
20
-
21
18
  export let title = "Hello";
22
19
 
20
+ let input;
21
+
23
22
  function $runtime() {
24
23
  input.focus();
25
24
  }
@@ -40,4 +39,17 @@ Import components. Instantiate them. Mount, update, remove. Client-side or serve
40
39
 
41
40
  ## Documentation
42
41
 
43
- https://github.com/chocolajs/chocola/tree/main/documentation
42
+ - [Getting started](documentation/01-introduction/02-getting-started.md)
43
+ - [Project structure](documentation/01-introduction/03-project-structure.md)
44
+ - [CLI reference](documentation/07-reference/04-cli.md)
45
+
46
+ ## Quick start
47
+
48
+ ```sh
49
+ npm install chocola
50
+ npx chocola dev # dev server
51
+ npx chocola build # production build
52
+ npx chocola serve # SSR production server
53
+ ```
54
+
55
+ No init scripts required: `chocola.config.json` is optional. See [Getting started](documentation/01-introduction/02-getting-started.md) for more.
package/bin/chocola.js ADDED
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import { fileURLToPath, pathToFileURL } from "url";
4
+ import { createRequire } from "module";
5
+ import { readFile } from "fs/promises";
6
+ import { getConfig, isMissingConfigFile } from "../utils.js";
7
+ import { loadConfig } from "../compiler/config.js";
8
+
9
+ import chalk from "../compiler/chalk.js";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+ const isPlain = process.env.NO_COLOR === "1" || process.argv.includes("--plain");
14
+ const c = isPlain ? {
15
+ hex: () => (s) => s, bold: (s) => s, green: (s) => s, yellow: (s) => s, red: (s) => s, underline: (s) => s,
16
+ } : chalk;
17
+
18
+ export default __dirname;
19
+
20
+ export async function resolveConfig(rootDir, cliOverrides, configPathOpt) {
21
+ const customPath = configPathOpt ? path.resolve(rootDir, configPathOpt) : null;
22
+ const fullConfig = customPath
23
+ ? await readFile(customPath, "utf-8").then(r => JSON.parse(r))
24
+ : await getConfig(rootDir, { silent: true });
25
+ const isMissing = customPath ? false : isMissingConfigFile(fullConfig);
26
+ const base = await loadConfig(rootDir, { silent: true, customPath });
27
+ if (cliOverrides.outDir) base.outDir = path.resolve(rootDir, cliOverrides.outDir);
28
+ if (cliOverrides.srcDir) base.srcDir = path.resolve(rootDir, cliOverrides.srcDir);
29
+ if (cliOverrides.libDir) base.libDir = path.resolve(rootDir, cliOverrides.libDir);
30
+ if (cliOverrides.emptyOutDir != null) base.emptyOutDir = cliOverrides.emptyOutDir;
31
+ const effectiveDev = { hostname: "localhost", port: 3000, ...(!isMissing && fullConfig.dev || {}), ...cliOverrides.dev };
32
+ const effectiveServer = { hostname: "localhost", port: 8080, middleware: null, ...(!isMissing && fullConfig.server || {}), ...cliOverrides.server };
33
+ if (!cliOverrides.server?.port && process.env.PORT) {
34
+ const envPort = parseInt(process.env.PORT, 10);
35
+ if (Number.isFinite(envPort)) effectiveServer.port = envPort;
36
+ }
37
+ if (!cliOverrides.server?.hostname && !fullConfig.server?.hostname && !fullConfig.server?.host && process.env.PORT) {
38
+ effectiveServer.hostname = "0.0.0.0";
39
+ }
40
+ return { fullConfig, base, effectiveDev, effectiveServer, isMissing };
41
+ }
42
+
43
+ function parsePort(val) {
44
+ const n = parseInt(val, 10);
45
+ if (Number.isNaN(n) || n < 1 || n > 65535) {
46
+ throw new Error(`Invalid port: ${val}. Must be between 1 and 65535.`);
47
+ }
48
+ return n;
49
+ }
50
+
51
+ function kebabToCamel(str) {
52
+ return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
53
+ }
54
+
55
+ export function parseArgs(argv) {
56
+ const args = argv.slice(2).filter(a => a !== "--plain");
57
+ const cmd = args[0];
58
+ if (!cmd || cmd === "--help" || cmd === "-h") return { cmd: "help", rest: [] };
59
+ if (cmd === "--version" || cmd === "-v") return { cmd: "version", rest: [] };
60
+ const positional = [];
61
+ const flags = {};
62
+ let configPath = null;
63
+ let i = 1;
64
+ while (i < args.length) {
65
+ const arg = args[i];
66
+ if (arg === "--config" || arg === "-c") {
67
+ configPath = args[++i];
68
+ } else if (arg.startsWith("--")) {
69
+ const key = kebabToCamel(arg.slice(2));
70
+ if (i + 1 < args.length && !args[i + 1].startsWith("--")) {
71
+ flags[key] = args[++i];
72
+ } else {
73
+ flags[key] = true;
74
+ }
75
+ } else {
76
+ positional.push(arg);
77
+ }
78
+ i++;
79
+ }
80
+ return { cmd, root: positional[0] || undefined, flags, configPath, rest: args };
81
+ }
82
+
83
+ async function getLocalModules(rootDir) {
84
+ try {
85
+ const req = createRequire(pathToFileURL(path.join(rootDir, "package.json")).href);
86
+ const localPkgPath = req.resolve("chocola/package.json");
87
+ const localRoot = path.dirname(localPkgPath);
88
+ const cliDir = path.dirname(path.dirname(__filename));
89
+ if (localRoot === cliDir) return null;
90
+ const mod = await import(pathToFileURL(path.join(localRoot, "compiler/index.js")).href);
91
+ return { rootDir: localRoot, mod };
92
+ } catch { return null; }
93
+ }
94
+
95
+ async function banner() {
96
+ if (!isPlain) {
97
+ const { logBanner } = await import("../compiler/index.js");
98
+ logBanner();
99
+ }
100
+ }
101
+
102
+ export async function main() {
103
+ const { cmd, root, flags, configPath } = parseArgs(process.argv);
104
+
105
+ if (cmd === "help") {
106
+ await banner();
107
+ console.log(c.bold("Usage: ") + "chocola <command> [root] [options]");
108
+ console.log("");
109
+ console.log("Commands:");
110
+ console.log(" build Build for production");
111
+ console.log(" dev Start dev server with hot-reload");
112
+ console.log(" serve Start SSR production server");
113
+ console.log("");
114
+ console.log("Options:");
115
+ console.log(" -c, --config <path> Path to config file");
116
+ console.log(" --help, -h Show help");
117
+ console.log(" --version, -v Show version");
118
+ console.log(" --plain, NO_COLOR=1 Disable color output");
119
+ console.log("");
120
+ console.log("build options:");
121
+ console.log(" --srcDir <dir> Source directory (default: src)");
122
+ console.log(" --outDir <dir> Output directory (default: dist)");
123
+ console.log(" --libDir <dir> Components subdir (default: lib)");
124
+ console.log(" --no-emptyOutDir Do not clean outDir before build");
125
+ console.log("");
126
+ console.log("dev options:");
127
+ console.log(" --host <hostname> Hostname (default: localhost)");
128
+ console.log(" --port <number> Port (default: 3000)");
129
+ console.log(" --open Open browser after start");
130
+ console.log("");
131
+ console.log("serve options:");
132
+ console.log(" --host <hostname> Hostname (default: localhost)");
133
+ console.log(" --port <number> Port (default: 8080)");
134
+ console.log(" --middleware <path> Path to middleware file");
135
+ return;
136
+ }
137
+ if (cmd === "version") {
138
+ const pkg = await readFile(path.join(__dirname, "../package.json"), "utf-8");
139
+ console.log(JSON.parse(pkg).version);
140
+ return;
141
+ }
142
+
143
+ if (!["build", "dev", "serve"].includes(cmd)) {
144
+ console.error(c.red(`Unknown command: ${cmd}. Use "chocola --help" for usage.`));
145
+ process.exit(2);
146
+ }
147
+
148
+ const rootDir = path.resolve(root || process.cwd());
149
+
150
+ try {
151
+ if (configPath) {
152
+ await readFile(path.resolve(rootDir, configPath), "utf-8");
153
+ }
154
+ const rawPort = flags.port ? parsePort(flags.port) : undefined;
155
+ const effectiveHost = flags.host || flags.hostname;
156
+
157
+ const { base, effectiveDev, effectiveServer } = await resolveConfig(rootDir, {
158
+ outDir: flags.outDir,
159
+ srcDir: flags.srcDir,
160
+ libDir: flags.libDir,
161
+ emptyOutDir: flags.emptyOutDir === false ? false : (flags.noEmptyOutDir === true ? false : undefined),
162
+ dev: { port: rawPort, hostname: effectiveHost },
163
+ server: { port: rawPort, hostname: effectiveHost },
164
+ }, configPath);
165
+
166
+ let serverInstance = null;
167
+ let devServer = null;
168
+ const cleanup = () => {
169
+ if (devServer) { try { devServer.close(); } catch {} }
170
+ if (serverInstance) { try { serverInstance.close(); } catch {} }
171
+ process.exit(0);
172
+ };
173
+ process.on("SIGINT", cleanup);
174
+ process.on("SIGTERM", cleanup);
175
+
176
+ switch (cmd) {
177
+ case "build": {
178
+ const local = await getLocalModules(rootDir);
179
+ const compile = local ? local.mod.default : (await import("../compiler/index.js")).default;
180
+ await compile(rootDir, { overrides: base });
181
+ process.exit(0);
182
+ }
183
+ case "dev": {
184
+ const local = await getLocalModules(rootDir);
185
+ const devMod = local
186
+ ? await import(pathToFileURL(path.join(local.rootDir, "dev/index.js")).href)
187
+ : await import("../dev/index.js");
188
+ const serve = devMod.serve || devMod.default;
189
+ serverInstance = await serve(rootDir, {
190
+ port: rawPort || effectiveDev.port,
191
+ hostname: effectiveHost || effectiveDev.hostname,
192
+ open: flags.open || false,
193
+ silent: true,
194
+ });
195
+ devServer = serverInstance;
196
+ break;
197
+ }
198
+ case "serve": {
199
+ const local = await getLocalModules(rootDir);
200
+ const serverMod = local
201
+ ? await import(pathToFileURL(path.join(local.rootDir, "server/index.js")).href)
202
+ : await import("../server/index.js");
203
+ const serve = serverMod.serve || serverMod.default?.serve;
204
+ serverInstance = await serve(rootDir, {
205
+ port: rawPort || effectiveServer.port,
206
+ hostname: effectiveHost || effectiveServer.hostname,
207
+ middleware: flags.middleware || null,
208
+ silent: true,
209
+ });
210
+ }
211
+ }
212
+ } catch (err) {
213
+ if (err.code === "ENOENT" && configPath) {
214
+ console.error(c.red(`Config file not found: ${path.resolve(rootDir, configPath)}`));
215
+ process.exit(1);
216
+ }
217
+ console.error(c.red(err.message || err));
218
+ process.exit(1);
219
+ }
220
+ }
221
+
222
+ if (process.argv[1] === __filename) {
223
+ main().catch(err => {
224
+ console.error(err.message || err);
225
+ process.exit(1);
226
+ });
227
+ }