diff --git a/docs/stdlib/http-server.md b/docs/stdlib/http-server.md index 405c91c6..3c35cf76 100644 --- a/docs/stdlib/http-server.md +++ b/docs/stdlib/http-server.md @@ -4,10 +4,10 @@ Built-in HTTP server with websocket support compiled to native code via libuv TC ## Router (recommended) -For most servers, use the `Router` class from `src/router.ts`. It provides an Express/Hono-style API with URL parameter extraction, method matching, and chainable response helpers. +For most servers, use the `Router` class from `chadscript/router`. It provides an Express/Hono-style API with URL parameter extraction, method matching, and chainable response helpers. ```typescript -import { Router, Context } from "./src/router"; +import { Router, Context } from "chadscript/router"; const app = new Router(); @@ -86,10 +86,10 @@ app.get("/example", (c: Context) => { ### HTTP utility functions -`src/http-utils.ts` provides helpers for parsing common request data: +`chadscript/http-utils` provides helpers for parsing common request data: ```typescript -import { getHeader, parseQueryString, parseCookies } from "./src/http-utils"; +import { getHeader, parseQueryString, parseCookies } from "chadscript/http-utils"; // Parse a single request header by name (case-insensitive) const auth = getHeader(req.headers, "Authorization"); // "Bearer abc" diff --git a/examples/cli-parser-demo.ts b/examples/cli-parser-demo.ts index 97a6fb68..36454d7b 100644 --- a/examples/cli-parser-demo.ts +++ b/examples/cli-parser-demo.ts @@ -1,5 +1,5 @@ // CLI Parser Demo - shows how to use ArgumentParser for professional CLI tools -import { ArgumentParser } from "../src/argparse.js"; +import { ArgumentParser } from "chadscript/argparse"; const parser = new ArgumentParser("cli-parser-demo", "Example CLI tool with argument parsing"); parser.addFlag("verbose", "v", "Enable verbose output"); diff --git a/examples/hackernews/app.ts b/examples/hackernews/app.ts index e6f2aad6..f0f6c2b5 100644 --- a/examples/hackernews/app.ts +++ b/examples/hackernews/app.ts @@ -1,5 +1,5 @@ // Hacker News Clone - full-stack app with SQLite, embedded files, and a JSON API -import { ArgumentParser } from "../../src/argparse.js"; +import { ArgumentParser } from "chadscript/argparse"; const parser = new ArgumentParser( "hackernews", diff --git a/examples/http-server.ts b/examples/http-server.ts index 6da68995..a488c82e 100644 --- a/examples/http-server.ts +++ b/examples/http-server.ts @@ -1,6 +1,6 @@ -import { ArgumentParser } from "../src/argparse.js"; -import { Router, Context } from "../src/router.js"; -import { getHeader, parseQueryString } from "../src/http-utils.js"; +import { ArgumentParser } from "chadscript/argparse"; +import { Router, Context } from "chadscript/router"; +import { getHeader, parseQueryString } from "chadscript/http-utils"; const parser = new ArgumentParser("http-server", "HTTP server with Router API"); parser.addOption("port", "p", "Port to listen on", "3000"); diff --git a/examples/string-search.ts b/examples/string-search.ts index b43f2ed3..851360bf 100644 --- a/examples/string-search.ts +++ b/examples/string-search.ts @@ -1,5 +1,5 @@ // String Search - grep-like file search tool with colorized output -import { ArgumentParser } from "../src/argparse.js"; +import { ArgumentParser } from "chadscript/argparse"; const parser = new ArgumentParser("string-search", "Search for a string pattern in files"); parser.addFlag("ignore-case", "i", "Case-insensitive search"); diff --git a/examples/websocket/app.ts b/examples/websocket/app.ts index d6b0338f..5854715a 100644 --- a/examples/websocket/app.ts +++ b/examples/websocket/app.ts @@ -1,5 +1,5 @@ // WebSocket Chat - real-time chat with embedded HTML/CSS served statically -import { ArgumentParser } from "../../src/argparse.js"; +import { ArgumentParser } from "chadscript/argparse"; const parser = new ArgumentParser("websocket-chat", "Real-time WebSocket chat server"); parser.addOption("port", "p", "Port to listen on (0 = auto)", "0"); diff --git a/examples/word-count.ts b/examples/word-count.ts index 2b5de35f..c29e7759 100644 --- a/examples/word-count.ts +++ b/examples/word-count.ts @@ -1,5 +1,5 @@ // Word Count - count lines, words, and characters in files (like wc) -import { ArgumentParser } from "../src/argparse.js"; +import { ArgumentParser } from "chadscript/argparse"; const parser = new ArgumentParser("word-count", "Count lines, words, and characters in a file"); parser.addFlag("lines", "l", "Only show line count"); diff --git a/lib/argparse.ts b/lib/argparse.ts deleted file mode 100644 index cbed0251..00000000 --- a/lib/argparse.ts +++ /dev/null @@ -1,781 +0,0 @@ -// DEPRECATED: canonical version moved to src/argparse.ts. -// This copy is kept for any external references but should not be edited directly. -// -// ChadScript-native ArgumentParser -// Simplified argparse-style CLI argument parsing for native binaries - -interface ArgDef { - name: string; - shortFlag: string; - longFlag: string; - help: string; - isFlag: boolean; - defaultValue: string; - isPositional: boolean; - subcommands: string; -} - -interface SubcommandDef { - name: string; - description: string; -} - -interface ParsedFlag { - name: string; - value: boolean; -} - -interface ParsedOption { - name: string; - value: string; -} - -export class ArgumentParser { - programName: string; - description: string; - - args: ArgDef[]; - subcommands: SubcommandDef[]; - parsedSubcommand: string; - - // Rest args (after --) - restArgs: string[]; - - parsedPositionals: string[]; - parsedFlags: ParsedFlag[]; - parsedOptions: ParsedOption[]; - - constructor(name: string, desc: string) { - this.programName = name; - this.description = desc; - this.args = []; - this.subcommands = []; - this.parsedSubcommand = ""; - this.restArgs = []; - this.parsedPositionals = []; - this.parsedFlags = []; - this.parsedOptions = []; - } - - addSubcommand(name: string, desc: string): void { - this.subcommands.push({ name: name, description: desc }); - } - - // Add a boolean flag (e.g., -v, --verbose) - addFlag(name: string, shortFlag: string, help: string): void { - this.args.push({ - name: name, - shortFlag: shortFlag, - longFlag: name, - help: help, - isFlag: true, - defaultValue: "", - isPositional: false, - subcommands: "", - }); - } - - addScopedFlag(name: string, shortFlag: string, help: string, subcommands: string): void { - this.args.push({ - name: name, - shortFlag: shortFlag, - longFlag: name, - help: help, - isFlag: true, - defaultValue: "", - isPositional: false, - subcommands: subcommands, - }); - } - - // Add an option that takes a value (e.g., -o file.txt, --output file.txt) - addOption(name: string, shortFlag: string, help: string, defaultVal: string): void { - this.args.push({ - name: name, - shortFlag: shortFlag, - longFlag: name, - help: help, - isFlag: false, - defaultValue: defaultVal, - isPositional: false, - subcommands: "", - }); - } - - addScopedOption( - name: string, - shortFlag: string, - help: string, - defaultVal: string, - subcommands: string, - ): void { - this.args.push({ - name: name, - shortFlag: shortFlag, - longFlag: name, - help: help, - isFlag: false, - defaultValue: defaultVal, - isPositional: false, - subcommands: subcommands, - }); - } - - // Add a positional argument (e.g., filename) - addPositional(name: string, help: string): void { - this.args.push({ - name: name, - shortFlag: "", - longFlag: "", - help: help, - isFlag: false, - defaultValue: "", - isPositional: true, - subcommands: "", - }); - } - - isArgInScope(argIndex: number, subcommand: string): boolean { - if (this.args[argIndex].subcommands.length === 0) { - return true; - } - if (subcommand.length === 0) { - return false; - } - const scopes = this.args[argIndex].subcommands; - let start = 0; - let pos = 0; - while (pos <= scopes.length) { - if (pos === scopes.length || scopes.charAt(pos) === ",") { - const part = scopes.substring(start, pos); - if (part.length > 0 && part === subcommand) { - return true; - } - start = pos + 1; - } - pos = pos + 1; - } - return false; - } - - findSubcommand(name: string): number { - let i = 0; - while (i < this.subcommands.length) { - if (this.subcommands[i].name.length > 0 && this.subcommands[i].name === name) { - return i; - } - i = i + 1; - } - return -1; - } - - splitEqualsFlag(arg: string): string { - let pos = 0; - while (pos < arg.length) { - if (arg.charAt(pos) === "=") { - return arg.substring(0, pos); - } - pos = pos + 1; - } - return arg; - } - - splitEqualsValue(arg: string): string { - let pos = 0; - while (pos < arg.length) { - if (arg.charAt(pos) === "=") { - return arg.substring(pos + 1, arg.length); - } - pos = pos + 1; - } - return ""; - } - - hasEquals(arg: string): boolean { - let pos = 0; - while (pos < arg.length) { - if (arg.charAt(pos) === "=") { - return true; - } - pos = pos + 1; - } - return false; - } - - setFlagValue(argIndex: number): void { - let flagIdx = 0; - while (flagIdx < this.parsedFlags.length) { - if ( - this.parsedFlags[flagIdx].name.length > 0 && - this.args[argIndex].name.length > 0 && - this.parsedFlags[flagIdx].name === this.args[argIndex].name - ) { - this.parsedFlags[flagIdx].value = true; - } - flagIdx = flagIdx + 1; - } - } - - setOptionValue(argIndex: number, value: string): void { - let optIdx = 0; - while (optIdx < this.parsedOptions.length) { - if ( - this.parsedOptions[optIdx].name.length > 0 && - this.args[argIndex].name.length > 0 && - this.parsedOptions[optIdx].name === this.args[argIndex].name - ) { - this.parsedOptions[optIdx].value = value; - } - optIdx = optIdx + 1; - } - } - - initDefaults(): void { - this.parsedPositionals = []; - this.parsedFlags = []; - this.parsedOptions = []; - this.parsedSubcommand = ""; - this.restArgs = []; - - let i = 0; - while (i < this.args.length) { - if (this.args[i].name.length > 0) { - if (this.args[i].isFlag) { - this.parsedFlags.push({ name: this.args[i].name, value: false }); - } else if (!this.args[i].isPositional) { - if (this.args[i].defaultValue.length > 0) { - this.parsedOptions.push({ name: this.args[i].name, value: this.args[i].defaultValue }); - } else { - this.parsedOptions.push({ name: this.args[i].name, value: "" }); - } - } - } - i = i + 1; - } - } - - parseFlag(argv: string[], argIdx: number): number { - const raw = argv[argIdx]; - let flagPart = raw; - let valuePart = ""; - let gotEquals = false; - - if (this.hasEquals(raw)) { - flagPart = this.splitEqualsFlag(raw); - valuePart = this.splitEqualsValue(raw); - gotEquals = true; - } - - const argIndex = this.findArgument(flagPart); - - if (argIndex === -1) { - console.error("Unknown option: " + raw); - console.error("Try '" + this.programName + " --help' for more information"); - process.exit(1); - } - - if (this.args[argIndex].isFlag) { - this.setFlagValue(argIndex); - return argIdx + 1; - } else { - if (gotEquals) { - this.setOptionValue(argIndex, valuePart); - return argIdx + 1; - } else { - const nextIdx = argIdx + 1; - if (nextIdx >= argv.length) { - console.error("Error: Option --" + this.args[argIndex].name + " requires a value"); - process.exit(1); - } - this.setOptionValue(argIndex, argv[nextIdx]); - return nextIdx + 1; - } - } - } - - parse(argv: string[]): number { - this.initDefaults(); - - if (this.subcommands.length === 0) { - return this.parseSimple(argv); - } - return this.parseWithSubcommands(argv); - } - - parseSimple(argv: string[]): number { - let argIdx = 0; - while (argIdx < argv.length) { - if (argv[argIdx].length > 0 && (argv[argIdx] === "-h" || argv[argIdx] === "--help")) { - this.printHelp(); - process.exit(0); - } - - if (argv[argIdx].length > 0 && argv[argIdx] === "--") { - argIdx = argIdx + 1; - while (argIdx < argv.length) { - this.restArgs.push(argv[argIdx]); - argIdx = argIdx + 1; - } - return 0; - } - - if (argv[argIdx].length > 0 && argv[argIdx].charAt(0) === "-") { - argIdx = this.parseFlag(argv, argIdx); - } else { - if (argv[argIdx].length > 0) { - this.parsedPositionals.push(argv[argIdx]); - } - argIdx = argIdx + 1; - } - } - - return 0; - } - - parseWithSubcommands(argv: string[]): number { - let argIdx = 0; - - while (argIdx < argv.length) { - const cur = argv[argIdx]; - - if (cur.length > 0 && (cur === "-h" || cur === "--help")) { - this.printHelp(); - process.exit(0); - } - - if (cur.length > 0 && cur.charAt(0) === "-") { - const flagPart = this.hasEquals(cur) ? this.splitEqualsFlag(cur) : cur; - const argIndex = this.findArgument(flagPart); - if (argIndex !== -1 && this.isArgInScope(argIndex, "")) { - argIdx = this.parseFlag(argv, argIdx); - } else if (argIndex !== -1) { - argIdx = this.parseFlag(argv, argIdx); - } else { - console.error("Unknown option: " + cur); - console.error("Try '" + this.programName + " --help' for more information"); - process.exit(1); - } - } else { - if (cur.length > 0) { - const subcmdIdx = this.findSubcommand(cur); - if (subcmdIdx !== -1) { - this.parsedSubcommand = cur; - argIdx = argIdx + 1; - return this.parseAfterSubcommand(argv, argIdx); - } else { - const endsTs = cur.length >= 3 && cur.substr(cur.length - 3) === ".ts"; - const endsJs = cur.length >= 3 && cur.substr(cur.length - 3) === ".js"; - if (endsTs || endsJs) { - console.error( - this.programName + - ": error: missing command. did you mean " + - this.programName + - " build " + - cur + - "?", - ); - } else { - console.error(this.programName + ": error: unknown command '" + cur + "'"); - } - console.error("Run " + this.programName + " --help for usage"); - process.exit(1); - } - } - argIdx = argIdx + 1; - } - } - - return 0; - } - - parseAfterSubcommand(argv: string[], startIdx: number): number { - let argIdx = startIdx; - - while (argIdx < argv.length) { - const cur = argv[argIdx]; - - if (cur.length > 0 && (cur === "-h" || cur === "--help")) { - this.printSubcommandHelp(this.parsedSubcommand); - process.exit(0); - } - - if (cur.length > 0 && cur === "--") { - argIdx = argIdx + 1; - while (argIdx < argv.length) { - this.restArgs.push(argv[argIdx]); - argIdx = argIdx + 1; - } - return 0; - } - - if (cur.length > 0 && cur.charAt(0) === "-") { - argIdx = this.parseFlag(argv, argIdx); - } else { - if (cur.length > 0) { - this.parsedPositionals.push(cur); - } - argIdx = argIdx + 1; - } - } - - return 0; - } - - findArgument(flag: string): number { - // Remove leading dashes - let cleanFlag = flag; - if (flag.charAt(0) === "-") { - if (flag.charAt(1) === "-") { - cleanFlag = flag.substring(2, flag.length); - } else { - cleanFlag = flag.substring(1, flag.length); - } - } - - let i = 0; - while (i < this.args.length) { - // Check length > 0 to avoid strcmp on NULL/empty strings - if ( - (this.args[i].shortFlag.length > 0 && this.args[i].shortFlag === cleanFlag) || - (this.args[i].longFlag.length > 0 && this.args[i].longFlag === cleanFlag) - ) { - return i; - } - i = i + 1; - } - return -1; - } - - printHelp(): void { - if (this.subcommands.length > 0) { - this.printTopLevelHelp(); - } else { - this.printSimpleHelp(); - } - } - - printSimpleHelp(): void { - console.log(this.programName); - if (this.description.length > 0) { - console.log(this.description); - } - console.log(""); - - let usage = "Usage: " + this.programName; - - let i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional) { - if (this.args[i].shortFlag.length > 0) { - if (!this.args[i].isFlag) { - usage = usage + " [-" + this.args[i].shortFlag + " <" + this.args[i].name + ">]"; - } else { - usage = usage + " [-" + this.args[i].shortFlag + "]"; - } - } else { - if (!this.args[i].isFlag) { - usage = usage + " [--" + this.args[i].longFlag + " <" + this.args[i].name + ">]"; - } else { - usage = usage + " [--" + this.args[i].longFlag + "]"; - } - } - } - i = i + 1; - } - - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - usage = usage + " <" + this.args[i].name + ">"; - } - i = i + 1; - } - - console.log(usage); - console.log(""); - - // Print options - console.log("Options:"); - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional) { - if (this.args[i].shortFlag.length > 0) { - if (!this.args[i].isFlag && this.args[i].defaultValue.length > 0) { - console.log( - " -" + - this.args[i].shortFlag + - ", --" + - this.args[i].longFlag + - " (default: " + - this.args[i].defaultValue + - ")", - ); - } else { - console.log(" -" + this.args[i].shortFlag + ", --" + this.args[i].longFlag); - } - } else { - if (!this.args[i].isFlag && this.args[i].defaultValue.length > 0) { - console.log( - " --" + this.args[i].longFlag + " (default: " + this.args[i].defaultValue + ")", - ); - } else { - console.log(" --" + this.args[i].longFlag); - } - } - console.log(" " + this.args[i].help); - } - i = i + 1; - } - - // Print positional arguments - let hasPositionals = false; - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - hasPositionals = true; - } - i = i + 1; - } - - if (hasPositionals) { - console.log(""); - console.log("Arguments:"); - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - console.log(" " + this.args[i].name); - console.log(" " + this.args[i].help); - } - i = i + 1; - } - } - - console.log(""); - console.log(" -h, --help"); - console.log(" Show this help message and exit"); - } - - printTopLevelHelp(): void { - console.log(this.programName); - if (this.description.length > 0) { - console.log(this.description); - } - console.log(""); - console.log("Usage: " + this.programName + " [options]"); - console.log(""); - console.log("Commands:"); - let i = 0; - while (i < this.subcommands.length) { - let line = " " + this.subcommands[i].name; - let padLen = 16 - this.subcommands[i].name.length; - if (padLen < 2) { - padLen = 2; - } - let pad = 0; - while (pad < padLen) { - line = line + " "; - pad = pad + 1; - } - line = line + this.subcommands[i].description; - console.log(line); - i = i + 1; - } - - let hasGlobalArgs = false; - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional && this.args[i].subcommands.length === 0) { - hasGlobalArgs = true; - } - i = i + 1; - } - - if (hasGlobalArgs) { - console.log(""); - console.log("Global options:"); - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional && this.args[i].subcommands.length === 0) { - this.printArgLine(i); - } - i = i + 1; - } - } - - console.log(""); - console.log(" -h, --help"); - console.log(" Show this help message and exit"); - console.log(""); - console.log( - "Run '" + this.programName + " --help' for more information on a command.", - ); - } - - printSubcommandHelp(subcmd: string): void { - let subcmdDesc = ""; - let i = 0; - while (i < this.subcommands.length) { - if (this.subcommands[i].name === subcmd) { - subcmdDesc = this.subcommands[i].description; - } - i = i + 1; - } - - console.log(this.programName + " " + subcmd); - if (subcmdDesc.length > 0) { - console.log(subcmdDesc); - } - console.log(""); - - let usage = "Usage: " + this.programName + " " + subcmd; - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional && this.isArgInScope(i, subcmd)) { - if (this.args[i].shortFlag.length > 0) { - if (!this.args[i].isFlag) { - usage = usage + " [-" + this.args[i].shortFlag + " <" + this.args[i].name + ">]"; - } else { - usage = usage + " [-" + this.args[i].shortFlag + "]"; - } - } else { - if (!this.args[i].isFlag) { - usage = usage + " [--" + this.args[i].longFlag + " <" + this.args[i].name + ">]"; - } else { - usage = usage + " [--" + this.args[i].longFlag + "]"; - } - } - } - i = i + 1; - } - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - usage = usage + " <" + this.args[i].name + ">"; - } - i = i + 1; - } - console.log(usage); - console.log(""); - - let hasOptions = false; - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional && this.isArgInScope(i, subcmd)) { - hasOptions = true; - } - i = i + 1; - } - - if (hasOptions) { - console.log("Options:"); - i = 0; - while (i < this.args.length) { - if (!this.args[i].isPositional && this.isArgInScope(i, subcmd)) { - this.printArgLine(i); - } - i = i + 1; - } - } - - let hasPositionals = false; - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - hasPositionals = true; - } - i = i + 1; - } - - if (hasPositionals) { - console.log(""); - console.log("Arguments:"); - i = 0; - while (i < this.args.length) { - if (this.args[i].isPositional) { - console.log(" " + this.args[i].name); - console.log(" " + this.args[i].help); - } - i = i + 1; - } - } - - console.log(""); - console.log(" -h, --help"); - console.log(" Show this help message and exit"); - } - - printArgLine(i: number): void { - if (this.args[i].shortFlag.length > 0) { - if (!this.args[i].isFlag && this.args[i].defaultValue.length > 0) { - console.log( - " -" + - this.args[i].shortFlag + - ", --" + - this.args[i].longFlag + - " (default: " + - this.args[i].defaultValue + - ")", - ); - } else { - console.log(" -" + this.args[i].shortFlag + ", --" + this.args[i].longFlag); - } - } else { - if (!this.args[i].isFlag && this.args[i].defaultValue.length > 0) { - console.log( - " --" + this.args[i].longFlag + " (default: " + this.args[i].defaultValue + ")", - ); - } else { - console.log(" --" + this.args[i].longFlag); - } - } - console.log(" " + this.args[i].help); - } - - // Public API - getSubcommand(): string { - return this.parsedSubcommand; - } - - getRestArgs(): string[] { - return this.restArgs; - } - - getFlag(name: string): boolean { - let i = 0; - while (i < this.parsedFlags.length) { - // Check length > 0 to avoid strcmp on NULL/empty strings - if (this.parsedFlags[i].name.length > 0 && this.parsedFlags[i].name === name) { - return this.parsedFlags[i].value; - } - i = i + 1; - } - return false; - } - - getOption(name: string): string { - let i = 0; - while (i < this.parsedOptions.length) { - // Check length > 0 to avoid strcmp on NULL/empty strings and only compare if valid - if (this.parsedOptions[i].name.length > 0 && this.parsedOptions[i].name === name) { - // Return the value, checking if it's empty first - if (this.parsedOptions[i].value.length === 0) { - return ""; - } - return this.parsedOptions[i].value; - } - i = i + 1; - } - return ""; - } - - getPositional(index: number): string { - if (index < this.parsedPositionals.length) { - // Return empty string if value is NULL/empty to avoid crashes - if (this.parsedPositionals[index].length === 0) { - return ""; - } - return this.parsedPositionals[index]; - } - return ""; - } -} diff --git a/lib/argparse.ts b/lib/argparse.ts new file mode 120000 index 00000000..165ea281 --- /dev/null +++ b/lib/argparse.ts @@ -0,0 +1 @@ +../src/argparse.ts \ No newline at end of file diff --git a/src/http-utils.ts b/lib/http-utils.ts similarity index 100% rename from src/http-utils.ts rename to lib/http-utils.ts diff --git a/src/router.ts b/lib/router.ts similarity index 99% rename from src/router.ts rename to lib/router.ts index 9696ae4b..cdbe6355 100644 --- a/src/router.ts +++ b/lib/router.ts @@ -1,4 +1,4 @@ -import { getHeader } from "./http-utils"; +import { getHeader } from "chadscript/http-utils"; interface HttpRequest { method: string; diff --git a/src/chad-native.ts b/src/chad-native.ts index 95fe7644..293d563e 100644 --- a/src/chad-native.ts +++ b/src/chad-native.ts @@ -11,10 +11,14 @@ import { addLinkLib, addLinkPath, setDiagnosticColor, + registerStdlib, } from "./native-compiler-lib.js"; // d.ts content is embedded at compile time via ChadScript.embedFile const dtsContent = ChadScript.embedFile("../chadscript.d.ts"); -import { ArgumentParser } from "./argparse.js"; +registerStdlib("router.ts", ChadScript.embedFile("../lib/router.ts")); +registerStdlib("argparse.ts", ChadScript.embedFile("../lib/argparse.ts")); +registerStdlib("http-utils.ts", ChadScript.embedFile("../lib/http-utils.ts")); +import { ArgumentParser } from "chadscript/argparse"; declare const fs: { existsSync(filename: string): boolean; diff --git a/src/compiler.ts b/src/compiler.ts index 719a9f03..1309ce81 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,5 +1,7 @@ import * as fs from "fs"; import * as path from "path"; +import { fileURLToPath } from "url"; +const _libDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "../lib"); import { execSync } from "child_process"; import { parseWithTSAPI } from "./parser-ts/index.js"; import { LLVMGenerator, LLVMGeneratorOptions, SemaSymbolData } from "./codegen/llvm-generator.js"; @@ -621,6 +623,44 @@ function compileMultiFile( continue; } + if (imp.source.startsWith("chadscript/")) { + const stdlibName = imp.source.substring("chadscript/".length); + const libPath = path.join(_libDir, stdlibName + ".ts"); + const importedAST = compileMultiFile( + libPath, + compiledFiles, + fileContentKeys, + fileContentValues, + ); + mergedAST.imports = mergedAST.imports.concat(importedAST.imports); + mergedAST.functions = mergedAST.functions.concat(importedAST.functions); + mergedAST.classes = mergedAST.classes.concat(importedAST.classes); + mergedAST.interfaces = mergedAST.interfaces.concat(importedAST.interfaces); + mergedAST.typeAliases = mergedAST.typeAliases.concat(importedAST.typeAliases || []); + mergedAST.enums = mergedAST.enums.concat(importedAST.enums || []); + mergedAST.topLevelStatements = importedAST.topLevelStatements.concat( + mergedAST.topLevelStatements, + ); + if (importedAST.topLevelItems) { + mergedAST.topLevelItems = importedAST.topLevelItems.concat(mergedAST.topLevelItems || []); + } + if (importedAST.topLevelItemTypes) { + mergedAST.topLevelItemTypes = importedAST.topLevelItemTypes.concat( + mergedAST.topLevelItemTypes || [], + ); + } + if (importedAST.importAliasNames && importedAST.importAliasNames.length > 0) { + mergedAST.importAliasNames = mergedAST.importAliasNames!.concat( + importedAST.importAliasNames, + ); + mergedAST.importAliasOriginals = mergedAST.importAliasOriginals!.concat( + importedAST.importAliasOriginals!, + ); + } + i = i + 1; + continue; + } + const npmPath = resolveNodeModule(absPath, imp.source); if (npmPath) { const importedAST = compileMultiFile( diff --git a/src/native-compiler-lib.ts b/src/native-compiler-lib.ts index 28262ad5..1560bb66 100644 --- a/src/native-compiler-lib.ts +++ b/src/native-compiler-lib.ts @@ -8,6 +8,14 @@ import { SemanticAnalyzer } from "./analysis/semantic-analyzer.js"; import { AST, ImportDeclaration, FunctionNode, ClassNode, ClassMethod } from "./ast/types.js"; import { TargetInfo } from "./target-types.js"; +const stdlibKeys: string[] = []; +const stdlibValues: string[] = []; + +export function registerStdlib(key: string, content: string): void { + stdlibKeys.push(key); + stdlibValues.push(content); +} + declare const child_process: { execSync(command: string): number; }; @@ -618,7 +626,25 @@ export function compileMultiFile(entryFile: string, compiledFiles: string[]): AS if (verbose) { console.log("Parsing: " + absPath); } - const code = fs.readFileSync(absPath); + const STDLIB_PREFIX = "/CHADSCRIPT_STDLIB/"; + let code = ""; + if (absPath.substr(0, STDLIB_PREFIX.length) === STDLIB_PREFIX) { + const key = absPath.substr(STDLIB_PREFIX.length); + let found = false; + for (let si = 0; si < stdlibKeys.length; si++) { + if (stdlibKeys[si] === key) { + code = stdlibValues[si]; + found = true; + break; + } + } + if (!found) { + console.log("stdlib module not found: " + key); + process.exit(1); + } + } else { + code = fs.readFileSync(absPath); + } setCurrentFile(absPath); const tree = parseSource(code); const ast = transformTree(tree); @@ -660,6 +686,29 @@ export function compileMultiFile(entryFile: string, compiledFiles: string[]): AS i = i + 1; continue; } + if (src.substr(0, 11) === "chadscript/") { + const stdlibName = src.substr(11); + const virtualPath = "/CHADSCRIPT_STDLIB/" + stdlibName + ".ts"; + const importedAST = compileMultiFile(virtualPath, compiledFiles); + mergedAST.functions = mergedAST.functions.concat(importedAST.functions); + mergedAST.classes = mergedAST.classes.concat(importedAST.classes); + mergedAST.interfaces = mergedAST.interfaces.concat(importedAST.interfaces); + mergedAST.typeAliases = mergedAST.typeAliases.concat(importedAST.typeAliases); + mergedAST.enums = mergedAST.enums.concat(importedAST.enums); + mergedAST.topLevelStatements = importedAST.topLevelStatements.concat( + mergedAST.topLevelStatements, + ); + if (importedAST.topLevelItems) { + mergedAST.topLevelItems = importedAST.topLevelItems.concat(mergedAST.topLevelItems || []); + } + if (importedAST.topLevelItemTypes) { + mergedAST.topLevelItemTypes = importedAST.topLevelItemTypes.concat( + mergedAST.topLevelItemTypes || [], + ); + } + i = i + 1; + continue; + } console.log("Cannot compile npm package: " + src); process.exit(1); } diff --git a/tests/fixtures/network/router-params.ts b/tests/fixtures/network/router-params.ts index ec2fa298..5703c3c0 100644 --- a/tests/fixtures/network/router-params.ts +++ b/tests/fixtures/network/router-params.ts @@ -1,6 +1,6 @@ // @test-description: Router param extraction and basic routing -import { Router, Context } from "../../../src/router"; +import { Router, Context } from "chadscript/router"; function testRouter(): void { const app = new Router(); diff --git a/tsconfig.json b/tsconfig.json index 1228bffc..d8662da6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,12 +16,5 @@ "moduleResolution": "node" }, "include": ["src/**/*"], - "exclude": [ - "node_modules", - "dist", - "tests", - "src/chad-native.ts", - "src/router.ts", - "src/http-utils.ts" - ] + "exclude": ["node_modules", "dist", "tests", "src/chad-native.ts"] }