diff --git a/pages/getting-started/1-installation.md b/pages/getting-started/1-installation.md index 005fb12..eacdb2d 100644 --- a/pages/getting-started/1-installation.md +++ b/pages/getting-started/1-installation.md @@ -38,6 +38,6 @@ when installing here. Congratulations! You've installed Lune and are now ready to write your first script. - If you want to write standalone scripts, head over to the - [Writing Scripts](./1-writing-scripts.md) page. + [Introduction](./2-introduction/1-hello-lune.md) section. - If you want to write Lune scripts specifically for Roblox, check out the [Roblox](../roblox/1-introduction.md) section. diff --git a/pages/getting-started/2-introduction/1-hello-lune.md b/pages/getting-started/2-introduction/1-hello-lune.md new file mode 100644 index 0000000..8c8cb5c --- /dev/null +++ b/pages/getting-started/2-introduction/1-hello-lune.md @@ -0,0 +1,24 @@ + + + +# Hello, Lune! + +Congratulations! Lune is now set up and you are ready to start writing scripts 🎉 + +If you've already written some kind of Lua (or Luau) script before, the examples provided in the +overview below should make you feel right at home. They are organized in order of least complex to +most complex, and you don't really have to read them all to understand how Lune works, though it may +help you out. Good luck! + +## Overview + +- `1` [Hello, Lune!](./1-hello-lune.md) (you are here) +- `2` [Built-in Libraries](./2-built-in-libraries.md) +- `3` [Standard I/O](./3-standard-io.mdx) +- `4` [Script Arguments](./4-script-arguments.md) +- `5` [Network Requests](./5-network-requests.mdx) +- `6` [Files & Directories](./6-files-and-directories.mdx) +- `7` [Environment Variables](./7-environment-variables.md) +- `8` [Modules](./8-modules.mdx) +- `9` [Task Scheduler](./9-task-scheduler.mdx) +- `10` [Spawning Processes](./10-spawning-processes.md) diff --git a/pages/getting-started/2-introduction/10-spawning-processes.md b/pages/getting-started/2-introduction/10-spawning-processes.md new file mode 100644 index 0000000..2cf371e --- /dev/null +++ b/pages/getting-started/2-introduction/10-spawning-processes.md @@ -0,0 +1,48 @@ +# Spawning Processes + +Whenever Lune does not have the API you need as part of its built-in libraries, or when you want to +use a program that already exists but interact with it from within Lune, you can use +[`process.spawn`](../../api-reference/process.md#spawn). + +## Example + +This example calls out to the native "ping" program found in many operating systems, and parses its +output into something more usable to us. + +This may look scary with lots of weird symbols, but, it's just some Lua-style pattern matching to +parse the lines of "min/avg/max/stddev = W/X/Y/Z ms" that the ping program gives back to us. + +```lua copy +print("Sending 4 pings to google 🌏") + +local result = process.spawn("ping", { + "google.com", + "-c 4", +}) + +if result.ok then + assert(#result.stdout > 0, "Result output was empty") + local min, avg, max, stddev = string.match( + result.stdout, + "min/avg/max/stddev = ([%d%.]+)/([%d%.]+)/([%d%.]+)/([%d%.]+) ms" + ) + print(string.format("Minimum ping time: %.3fms", tonumber(min))) + print(string.format("Maximum ping time: %.3fms", tonumber(max))) + print(string.format("Average ping time: %.3fms", tonumber(avg))) + print(string.format("Standard deviation: %.3fms", tonumber(stddev))) +else + print("Failed to send ping to google!") + print(result.stderr) + process.exit(result.code) +end +``` + +Note that if the result of the subprocess was non-zero, meaning it errored and `ok` was set to +`false`, we will also propagate the exit code of that subprocess using +[`process.exit`](../../api-reference/process.md#exit). This will ensure that if our subprocess +fails, our script will do the same, and let the user know with a proper exit code. + +## Conclusion + +This is the last page of the introduction book, for more specific usage and a full overview of all +of the APIs that Lune provides, please check out the API Reference section in the sidebar. Enjoy! 🚀 diff --git a/pages/getting-started/2-introduction/2-built-in-libraries.md b/pages/getting-started/2-introduction/2-built-in-libraries.md new file mode 100644 index 0000000..927b412 --- /dev/null +++ b/pages/getting-started/2-introduction/2-built-in-libraries.md @@ -0,0 +1,27 @@ +# Built-in Libraries + +Lune contains a large set of built-in libraries, much like Luau itself. These libraries include, but +are not limited to, these libraries and their common use cases: + +- The [`fs`](../../api-reference/fs.md) library for manipulating files +- The [`net`](../../api-reference/net.md) library for making HTTP requests +- The [`process`](../../api-reference/process.md) library for executing external programs and + processes + +This is just a small subset of what is available in Lune, but for now, what is important is that +these libraries must be imported using a special kind of `require` statement: + +```lua copy +local fs = require("@lune/fs") +local net = require("@lune/net") +local process = require("@lune/process") +``` + +As you can see above, unlike Luau's standard libraries such as +[`math`](https://luau-lang.org/library#math-library), +[`table`](https://luau-lang.org/library#table-library), +[`string`](https://luau-lang.org/library#string-library), and others, Lune's built-in libraries are +not available as global variables and importing them before using them is required (pun intended). + +The next few sections will contain examples of how to run scripts, more specific usage of Lune's +built-in libraries, and what they are most commonly used for. diff --git a/pages/getting-started/2-introduction/3-standard-io.mdx b/pages/getting-started/2-introduction/3-standard-io.mdx new file mode 100644 index 0000000..261e8cb --- /dev/null +++ b/pages/getting-started/2-introduction/3-standard-io.mdx @@ -0,0 +1,92 @@ +# Standard I/O + +One of Lune's most useful libraries for writing scripts is the standard I/O library, also known as +`stdio`, which will be the first one we introduce here. The pages following this one will introduce +several others. + +## Prompting for User Input + +The easiest way to get started and being productive using Lune is to prompt the person running your +script for some text input, which you can do using the [`stdio`](../../api-reference/stdio.md) +library. Let's make a script called `hello.luau`: + +```lua copy filename="hello.luau" +local stdio = require("@lune/stdio") + +local name = stdio.prompt("text", "Hello there! What's your name?") + +print("Hello, " .. name .. "!") +``` + +Now you can place this script in your current directory, and run it using Lune: + +```sh copy filename="Bash" +lune hello +``` + +You can also prompt for more than just text. Let's extend the above script and ask the person +running the script if that was really their name: + +```lua copy filename="hello.luau" +local confirmed = stdio.prompt("confirm", "Is that really your name?") +if confirmed then + print("Nice to meet you, " .. name .. "!") + print("Have a great day!") +else + print("You lied to me! Goodbye 😡") +end +``` + +There are more options for prompting for user input than what we covered in this example, but these +two basic prompting methods should cover most of your use cases and get you started with making +interactive scripts. + +Next, head on over to the following section on [Script Arguments](./4-script-arguments.md) or check +out the bonus game below! + +
+Bonus + +## Guessing Game + +Here's a tiny game you can play versus the computer, using nothing but Lune's +[`stdio`](../../api-reference/stdio.md) library and Luau's `math` library. This is a bit longer of a +script, but don't worry, it is still only using the same functions as the script above, albeit this +time together with a `while ... do` loop and a couple `if ... then` statements: + +```lua copy filename="guessing-game.luau" +local stdio = require("@lune/stdio") + +print("") +print("Let's play a game! Whoever guesses the correct number between 1 and 10 first will win.") +print("") + +local answer = tostring(math.random(1, 10)) + +local guess = stdio.prompt("text", "Input a number between 1 and 10") +while guess ~= answer do + print("Incorrect! Computer's turn...") + + local computer = tostring(math.random(1, 10)) + print("The computer guessed", computer) + + if computer == answer then + break + end + + print("Incorrect! Your turn...") + guess = stdio.prompt("text", "Input a number between 1 and 10") +end + +print("") +print(if guess == answer then "You won the game! 🎉" else "The computer won the game! 😭") +print("") +``` + +Just like before, you can place this script in your current directory, and run it using Lune: + +```sh copy filename="Bash" +lune guessing-game +``` + +
diff --git a/pages/getting-started/2-introduction/4-script-arguments.md b/pages/getting-started/2-introduction/4-script-arguments.md new file mode 100644 index 0000000..3010490 --- /dev/null +++ b/pages/getting-started/2-introduction/4-script-arguments.md @@ -0,0 +1,40 @@ +# Script Arguments + +Arguments can be passed to Lune scripts directly from the command line when running them: + +```sh copy filename="Bash" +lune script-name arg1 arg2 "argument three" +``` + +These arguments will then be available in your script using the +[process](../../api-reference/process.md) built-in library, more specifically in +[`process.args`](../../api-reference/process.md#args): + +```lua copy +local process = require("@lune/process") + +print(process.args) +--> { "arg1", "arg2", "argument three" } +``` + +--- + +Arguments in [`process.args`](../../api-reference/process.md#args) will always be a table that is a +contiguous array, and are guaranteed to not change during runtime. A useful pattern here could be to +check for arguments given, and if there are none, prompt the user for input: + +```lua copy +local process = require("@lune/process") +local stdio = require("@lune/stdio") + +if #process.args > 3 then + error("Too many arguments!") +elseif #process.args > 0 then + print("Got arguments:") + print(process.args) +else + print("Got no arguments ☚ī¸") + local prompted = stdio.prompt("Please enter some text:") + print("Got prompted text:", prompted) +end +``` diff --git a/pages/getting-started/2-introduction/5-network-requests.mdx b/pages/getting-started/2-introduction/5-network-requests.mdx new file mode 100644 index 0000000..f7f10d2 --- /dev/null +++ b/pages/getting-started/2-introduction/5-network-requests.mdx @@ -0,0 +1,127 @@ +# Network Requests + +One of Lune's most useful libraries is the networking library, also known as `net`. This library +lets you access the internet and make HTTP requests to different websites, servers, and external +APIs. + +## Sending HTTP Requests + +Sending HTTP requests is the most basic function of the [`net`](../../api-reference/net.md) library. +Let's make a script called `googler.luau`: + +```lua copy filename="googler.luau" +local net = require("@lune/net") + +local response = net.request("https://google.com") + +if response.ok then + print( + "Google responded with status code", + response.statusCode, + "and response message", + response.statusMessage, + "!" + ) +else + print("Google is down!! What?!?") +end +``` + +Now you can place this script in your current directory, and run it using Lune: + +```sh copy filename="Bash" +lune googler +``` + +## Sending JSON Requests + +[JSON](https://www.json.org/json-en.html), or JavaScript Object Notation, is the most common format +for sending data over the network. Lune includes APIs for serializing & deserializing JSON, also +known as stringifying & parsing, in the `net` library. Let's use the free testing web API at +[`https://jsonplaceholder.typicode.com/`](https://jsonplaceholder.typicode.com/) and send + receive +some JSON data: + +```lua copy filename="json-api.luau" +print("Sending PATCH request to web API") + +local apiResponse = net.request({ + url = "https://jsonplaceholder.typicode.com/posts/1", + method = "PATCH", + headers = { + ["Content-Type"] = "application/json", + }, + body = net.jsonEncode({ + title = "foo", + body = "bar", + }), +}) + +if not apiResponse.ok then + error( + string.format( + "%s\n%d (%s)\n%s", + "Failed to send network request!", + apiResponse.statusCode, + apiResponse.statusMessage, + apiResponse.body + ) + ) +end + +type ResponseType = { + id: number, + title: string, + body: string, + userId: number, +} + +local responseTable: ResponseType = net.jsonDecode(apiResponse.body) +assert(responseTable.title == "foo", "Invalid json response") +assert(responseTable.body == "bar", "Invalid json response") + +print("Got valid JSON response with post title set to 'foo' and post body set to 'bar'") +print("Full response:", responseTable) +``` + +Running the above script Lune should now send a request to the placeholder API, verify that the +response was correct, and print it out: + +```sh copy filename="Bash" +lune json-api +``` + +--- + +
+Bonus + +## Network Server + +Lune can not only perform network requests, it can also open a server on a given port and serve +requests on it. Here's a small example: + +```lua copy filename="network-server.luau" +local net = require("@lune/net") + +local counter = 0 +net.serve(8080, function() + counter += 1 + return { + status = 200, + body = "Hello! This is response #" .. tostring(counter), + } +end) + +print("Listening on port 8080 🚀") +``` + +Just like before, you can place this script in your current directory, and run it using Lune: + +```sh copy filename="Bash" +lune network-server +``` + +Now, when you visit [`http://localhost:8080/`](http://localhost:8080/) you should see the response +text above and the counter increasing each time you visit the page. + +
diff --git a/pages/getting-started/2-introduction/6-files-and-directories.mdx b/pages/getting-started/2-introduction/6-files-and-directories.mdx new file mode 100644 index 0000000..eb77b71 --- /dev/null +++ b/pages/getting-started/2-introduction/6-files-and-directories.mdx @@ -0,0 +1,129 @@ +import { FileTree, Tabs, Tab } from 'nextra/components' + +# Files & Directories + +Lune has a built-in library for interacting with the filesystem, [`fs`](../../api-reference/fs.md). +This library will let you read, write, move, copy files & directories, and more. + +## Example File Tree + +Let's use this directory & file tree structure for our examples: + + + + + + + + + + + + + +
+Show file contents + + + + ```json copy filename="hello-world.json" + { + "Hello": "World" + } + ``` + + + ```toml copy filename="coolstuff.toml" + [you] + cool = true + awesome = "yep" + ``` + + + ```txt copy filename="super.secret.txt" + Hey you're not supposed to be in here! + ``` + + + +
+ +## Files + +Reading and writing files using the `fs` library is very simple and *only* involves strings: + +```lua copy filename="files.luau" +local fs = require("@lune/fs") + +--> Print out the contents of all of the files +print(fs.readFile("hello-world.json")) +print(fs.readFile("files/coolstuff.toml")) +print(fs.readFile("files/super.secret.txt")) + +--> Create a new file in our "files" directory +fs.writeFile("files/My Favorite Numbers.txt", "2 4 6 8 0 😃") + +--> Write to one of our files, overwriting any previous contents +fs.writeFile("files/super.secret.txt", "Super secret message") + +--> Remove the new file we created in our "files" directory +fs.removeFile("files/My Favorite Numbers.txt") +``` + +Note that the filesystem library deals with *raw* strings for file contents, and does not +differentiate between if the contents of the file are using binary, utf-8, or some other encoding. +It is up to you to know how your files are structured and handle them appropriately. + +## Directories + +Reading and creating directories has a very similar API, but slightly different parameters and return values: + +```lua copy filename="dirs.luau" +local fs = require("@lune/fs") + +--[[ + Print out the entries found in our directory. + The "." here means the current directory. + + This will output: + * 📄 files.luau + * 📄 dirs.luau + * 📄 hello-world.json + * 📁 files +]] +for _, entry in fs.readDir(".") do + if fs.isDir(entry) then + print("📁 " .. entry) + elseif fs.isFile(entry) then + print("📄 " .. entry) + end +end + +--> Create a new directory next to the above entries +fs.writeDir("myCoolDir") + +--> Create a new directory in our "files" directory +fs.writeDir("files/myCoolSecondDir") + +--> Remove the entire files directory +fs.removeDir("files") +``` + +In the above example: + +- `fs.readDir` returns a table (array) of strings, with file and directory names +- `fs.writeDir` takes only the directory name (path) to create a directory at +- `fs.removeDir` removes the directory ***and everything inside it***, use with caution + +## Resulting File Tree + +This is what our directory & file tree structure would look like after running the above examples: + + + + + + + + + diff --git a/pages/getting-started/2-introduction/7-environment-variables.md b/pages/getting-started/2-introduction/7-environment-variables.md new file mode 100644 index 0000000..2135f73 --- /dev/null +++ b/pages/getting-started/2-introduction/7-environment-variables.md @@ -0,0 +1,35 @@ +# Environment Variables + +Environment variables, just like script arguments, are available using the +[process](../../api-reference/process.md) built-in library, more specifically in +[`process.env`](../../api-reference/process.md#env): + +```lua copy +local process = require("@lune/process") + +assert(process.env.PATH ~= nil, "Missing PATH") +assert(process.env.PWD ~= nil, "Missing PWD") + +process.env.MY_VAR = "Hello, env!" + +print(process.env.MY_VAR) +--> Hello, env! +``` + +Unlike [`process.args`](../../api-reference/process.md#args), environment variables can be read from +and written to freely, and can be done at any point during runtime. + +You can also iterate over all of the known environment variables using Luau's generalized iteration. +Here is an example snippet that prints a checkmark if an environment variable has some contents and +is not empty, and a red cross otherwise: + +```lua copy +local process = require("@lune/process") + +for key, value in process.env do + local box = if value ~= "" then "✅" else "❌" + print(string.format("[%s] %s", box, key)) +end +``` + +Note that using `pairs` or `ipairs` will _not_ work here, only generalized iteration. diff --git a/pages/getting-started/2-introduction/8-modules.mdx b/pages/getting-started/2-introduction/8-modules.mdx new file mode 100644 index 0000000..28941b2 --- /dev/null +++ b/pages/getting-started/2-introduction/8-modules.mdx @@ -0,0 +1,83 @@ +import { FileTree, Tabs, Tab, Callout } from 'nextra/components' + +# Modules + +At this point you know how the most important built-in libraries in Lune work and how to use them, +and your code is probably getting longer and more difficult to read. Splitting your code into +multiple files can help you stay organized. + +Modularizing your code and splitting it across several files in Lune is different from other +versions of Lua and Luau, and more similar to how things work in other languages such as JavaScript. + +## Example File Tree + +Let's use this directory & file tree structure for our examples: + + + + + + + + + + + + + + + ```lua copy filename="main.luau" + local sibling = require("sibling") + local modules = require("modules") + + print(sibling.Hello) --> World + + print(modules.Module.Foo) --> Bar + print(modules.Module.Fizz) --> Buzz + + print(modules.Sibling.Hello) --> World + ``` + + + ```lua copy filename="sibling.luau" + return { + Hello = "World", + } + ``` + + + ```lua copy filename="modules/init.luau" + return { + Module = require("module"), + Sibling = require("../sibling"), + } + ``` + + + ```lua copy filename="modules/module.luau" + return { + Foo = "Bar", + Fizz = "Buzz", + } + ``` + + + +## File Require Statements + +Let's decipher these files and what they are doing: + +- The `main` script requires `sibling` and `modules` next to it +- The `modules/init` script requires `module` next to it, and `sibling` going up one directory + using `../` + +In the above `require` statements, we can see that are relative to the file that they are in, and in +Lune this is always the case, except for built-in libraries, which always start with an at sign +(`@`). + + + **Q:** Wait, hold on... The `main` script requires the _**directory**_ called `modules`?
+ **A:** Yep, that's right. The file name `init` is special, and putting a file named `init.luau` in a + directory will let you use `require` directly on the directory. Similar to `index.js` in JavaScript + or `mod.rs` in Rust. +
diff --git a/pages/getting-started/2-introduction/9-task-scheduler.mdx b/pages/getting-started/2-introduction/9-task-scheduler.mdx new file mode 100644 index 0000000..e54c8eb --- /dev/null +++ b/pages/getting-started/2-introduction/9-task-scheduler.mdx @@ -0,0 +1,139 @@ +# The Task Scheduler + +Lune has a built-in task scheduler, which can let you run things at fixed intervals, ensure some +work happens after everything else is already done, and more. + +## Spawning Tasks & Waiting + +This example script will run several tasks concurrently, in lightweight Lua threads, also known as +coroutines: + +```lua copy +local task = require("@lune/task") + +print("Hello, scheduler!") + +task.spawn(function() + print("Spawned a task that will run instantly but not block") + task.wait(2) + print("The instant task resumed again after 2 seconds") +end) + +print("Spawning a delayed task that will run after 5 seconds") + +task.delay(5, function() + print("Waking up from my deep slumber...") + task.wait(1) + print("Hello again!") + task.wait(1) + print("Goodbye again! 🌙") +end) +``` + +## Deferring Work + +This example script runs a bit of work after all other threads have finished their work or are +yielding waiting for some other result: + +```lua copy +local task = require("@lune/task") + +task.defer(function() + print("All the scheduled work has finished, let's do some more!") + local a = 0 + for _ = 1, 100000 do + local b = a + 1 + end + print("Phew, that was tough.") +end) + +print("Working...") +local s = "" +for _ = 1, 5000 do + s ..= "" +end +print("Done!") +``` + +## Advanced Usage & Async + +Spawning tasks like this can be very useful together with asynchronous APIs from other built-in +libraries, such as [`net.request`](../../api-reference/net.md#request): + +```lua copy +local net = require("@lune/net") +local task = require("@lune/task") + +local completed = false +task.spawn(function() + while not completed do + print("Waiting for response...") + task.wait() -- Wait the minimum amount possible + end + print("No longer waiting!") +end) + +print("Sending request") +net.request("https://google.com") +print("Got response") + +completed = true +``` + +
+Bonus + +## Barebones Signal Implementation + +Using the task library, it becomes trivial to implement signal objects that take callbacks to run +when a signal is fired, and that can handle both synchronous and yielding (async) callbacks without +additional complexity: + +```lua copy +local task = require("@lune/task") + +local function newSignal() + local callbacks = {} + + local function connect(callback: (...any) -> ()) + table.insert(callbacks, callback) + end + + local function fire(...: any) + for _, callback in callbacks do + task.spawn(callback, ...) + end + end + + return connect, fire +end + +local connectToThing, fireThing = newSignal() + +connectToThing(function(value) + print("Callback #1 got value:", value) + task.wait(1) + print("Callback #1 still has value:", value) +end) + +connectToThing(function(value) + print("Callback #2 got value:", value) + task.wait(0.5) + print("Callback #2 still has value:", value) +end) + +print("Before firing") +fireThing(123) +print("After firing") + +--> Before firing +--> Callback #1 got value: 123 +--> Callback #2 got value: 123 +--> After firing +--> ... +--> Callback #2 still has value: 123 +--> ... +--> Callback #1 still has value: 123 +``` + +
diff --git a/pages/getting-started/2-introduction/_meta.json b/pages/getting-started/2-introduction/_meta.json new file mode 100644 index 0000000..0aa48b1 --- /dev/null +++ b/pages/getting-started/2-introduction/_meta.json @@ -0,0 +1,12 @@ +{ + "1-hello-lune": "1 â€ĸ Hello, Lune!", + "2-built-in-libraries": "2 â€ĸ Built-in Libraries", + "3-standard-io": "3 â€ĸ Standard I/O", + "4-script-arguments": "4 â€ĸ Script Arguments", + "5-network-requests": "5 â€ĸ Network Requests", + "6-files-and-directories": "6 â€ĸ Files & Directories", + "7-environment-variables": "7 â€ĸ Environment Variables", + "8-modules": "8 â€ĸ Modules", + "9-task-scheduler": "9 â€ĸ Task Scheduler", + "10-spawning-processes": "10 â€ĸ Spawning Processes" +} diff --git a/pages/getting-started/2-writing-scripts.md b/pages/getting-started/2-writing-scripts.md deleted file mode 100644 index b2069ec..0000000 --- a/pages/getting-started/2-writing-scripts.md +++ /dev/null @@ -1,297 +0,0 @@ - - - -# Writing Lune Scripts - -If you've already written some version of Lua (or Luau) scripts before, this walkthrough will make -you feel right at home. - -Once you have a script you want to run, head over to the [Running Scripts](./2-running-scripts.md) -page. - -## Hello, Lune! - -```lua copy ---[[ - EXAMPLE #1 - - Using arguments given to the program -]] - -if #process.args > 0 then - print("Got arguments:") - print(process.args) - if #process.args > 3 then - error("Too many arguments!") - end -else - print("Got no arguments ☚ī¸") -end - - - ---[[ - EXAMPLE #2 - - Using the stdio library to prompt for terminal input -]] - -local text = stdio.prompt("text", "Please write some text") - -print("You wrote '" .. text .. "'!") - -local confirmed = stdio.prompt("confirm", "Please confirm that you wrote some text") -if confirmed == false then - error("You didn't confirm!") -else - print("Confirmed!") -end - - - ---[[ - EXAMPLE #3 - - Get & set environment variables - - Checks if environment variables are empty or not, - prints out ❌ if empty and ✅ if they have a value -]] - -print("Reading current environment 🔎") - --- Environment variables can be read directly -assert(process.env.PATH ~= nil, "Missing PATH") -assert(process.env.PWD ~= nil, "Missing PWD") - --- And they can also be accessed using Luau's generalized iteration (but not pairs()) -for key, value in process.env do - local box = if value and value ~= "" then "✅" else "❌" - print(string.format("[%s] %s", box, key)) -end - - - ---[[ - EXAMPLE #4 - - Writing a module - - Modularizing and splitting up your code is Lune is very straight-forward, - in contrast to other scripting languages and shells such as bash -]] - -local module = {} - -function module.sayHello() - print("Hello, Lune! 🌙") -end - -return module - - - ---[[ - EXAMPLE #5 - - Using a function from another module / script - - Lune has path-relative imports, similar to other popular languages such as JavaScript -]] - -local module = require("../modules/module") -module.sayHello() - - - ---[[ - EXAMPLE #6 - - Spawning concurrent tasks - - These tasks will run at the same time as other Lua code which lets you do primitive multitasking -]] - -task.spawn(function() - print("Spawned a task that will run instantly but not block") - task.wait(5) -end) - -print("Spawning a delayed task that will run in 5 seconds") -task.delay(5, function() - print("...") - task.wait(1) - print("Hello again!") - task.wait(1) - print("Goodbye again! 🌙") -end) - - - ---[[ - EXAMPLE #7 - - Read files in the current directory - - This prints out directory & file names with some fancy icons -]] - -print("Reading current dir 🗂ī¸") -local entries = fs.readDir(".") - --- NOTE: We have to do this outside of the sort function --- to avoid yielding across the metamethod boundary, all --- of the filesystem APIs are asynchronous and yielding -local entryIsDir = {} -for _, entry in entries do - entryIsDir[entry] = fs.isDir(entry) -end - --- Sort prioritizing directories first, then alphabetically -table.sort(entries, function(entry0, entry1) - if entryIsDir[entry0] ~= entryIsDir[entry1] then - return entryIsDir[entry0] - end - return entry0 < entry1 -end) - --- Make sure we got some known files that should always exist -assert(table.find(entries, "Cargo.toml") ~= nil, "Missing Cargo.toml") -assert(table.find(entries, "Cargo.lock") ~= nil, "Missing Cargo.lock") - --- Print the pretty stuff -for _, entry in entries do - if fs.isDir(entry) then - print("📁 " .. entry) - else - print("📄 " .. entry) - end -end - - - ---[[ - EXAMPLE #8 - - Call out to another program / executable - - You can also get creative and combine this with example #6 to spawn several programs at the same time! -]] - -print("Sending 4 pings to google 🌏") -local result = process.spawn("ping", { - "google.com", - "-c 4", -}) - - - ---[[ - EXAMPLE #9 - - Using the result of a spawned process, exiting the process - - This looks scary with lots of weird symbols, but, it's just some Lua-style pattern matching - to parse the lines of "min/avg/max/stddev = W/X/Y/Z ms" that the ping program outputs to us -]] - -if result.ok then - assert(#result.stdout > 0, "Result output was empty") - local min, avg, max, stddev = string.match( - result.stdout, - "min/avg/max/stddev = ([%d%.]+)/([%d%.]+)/([%d%.]+)/([%d%.]+) ms" - ) - print(string.format("Minimum ping time: %.3fms", assert(tonumber(min)))) - print(string.format("Maximum ping time: %.3fms", assert(tonumber(max)))) - print(string.format("Average ping time: %.3fms", assert(tonumber(avg)))) - print(string.format("Standard deviation: %.3fms", assert(tonumber(stddev)))) -else - print("Failed to send ping to google!") - print(result.stderr) - process.exit(result.code) -end - - - ---[[ - EXAMPLE #10 - - Using the built-in networking library, encoding & decoding json -]] - -print("Sending PATCH request to web API 📤") -local apiResult = net.request({ - url = "https://jsonplaceholder.typicode.com/posts/1", - method = "PATCH", - headers = { - ["Content-Type"] = "application/json", - }, - body = net.jsonEncode({ - title = "foo", - body = "bar", - }), -}) - -if not apiResult.ok then - print("Failed to send network request!") - print(string.format("%d (%s)", apiResult.statusCode, apiResult.statusMessage)) - print(apiResult.body) - process.exit(1) -end - -type ApiResponse = { - id: number, - title: string, - body: string, - userId: number, -} - -local apiResponse: ApiResponse = net.jsonDecode(apiResult.body) -assert(apiResponse.title == "foo", "Invalid json response") -assert(apiResponse.body == "bar", "Invalid json response") -print("Got valid JSON response with changes applied") - - - ---[[ - EXAMPLE #11 - - Using the stdio library to print pretty -]] - -print("Printing with pretty colors and auto-formatting 🎨") - -print(stdio.color("blue") .. string.rep("—", 22) .. stdio.color("reset")) - -print("API response:", apiResponse) -warn({ - Oh = { - No = { - TooMuch = { - Nesting = { - "Will not print", - }, - }, - }, - }, -}) - -print(stdio.color("blue") .. string.rep("—", 22) .. stdio.color("reset")) - - - ---[[ - EXAMPLE #12 - - Saying goodbye 😔 -]] - -print("Goodbye, lune! 🌙") - -``` - -More real-world examples of how to write Lune scripts can be found in the -[examples](https://github.com/filiptibell/lune/blob/main/.lune/examples/) folder. - -Documentation for individual APIs and types can be found in the "API Reference" section in the -sidebar. diff --git a/pages/getting-started/3-running-scripts.md b/pages/getting-started/3-command-line-usage.md similarity index 62% rename from pages/getting-started/3-running-scripts.md rename to pages/getting-started/3-command-line-usage.md index 36892a9..b536226 100644 --- a/pages/getting-started/3-running-scripts.md +++ b/pages/getting-started/3-command-line-usage.md @@ -1,8 +1,8 @@ - +# Command-Line Usage -# Running Lune Scripts +## Running Scripts -After you've written a script file, for example `script-name.luau`, you can run it: +When you've written a script file, for example `script-name.luau`, you can run it as such: ```sh copy lune script-name @@ -16,24 +16,7 @@ This will look for the file `script-name.luau`**_[1]_** in a few loca - The folder `lune` in the _home_ directory, if it exists - The folder `.lune` in the _home_ directory, if it exists -## Passing Command-Line Arguments - -Arguments can be passed to a Lune script directory from the command line when running it: - -```sh copy -lune script-name arg1 arg2 "argument three" -``` - -These arguments will then be available in your script using `process.args`: - -```lua copy -local process = require("@lune/process") - -print(process.args) ---> { "arg1", "arg2", "argument three" } -``` - -## Additional Commands +## Listing Scripts ```sh copy lune --list @@ -43,12 +26,18 @@ Lists all scripts found in `lune` or `.lune` directories, including any top-leve comments.
Lune description comments are always written at the top of a file and start with a lua-style comment arrow (`-->`). +## Advanced Usage + ```sh copy lune - ``` -Runs a script passed to Lune using stdin. Occasionally useful for running scripts piped to Lune from -external sources. +Runs a script passed to Lune using stdin. Useful for running scripts piped to Lune from external +sources. Example: + +```sh copy +echo "print 'Hello, terminal!'" | lune - +``` --- diff --git a/pages/getting-started/_meta.json b/pages/getting-started/_meta.json index 4e3181d..50005b4 100644 --- a/pages/getting-started/_meta.json +++ b/pages/getting-started/_meta.json @@ -1,6 +1,6 @@ { "1-installation": "Installation", - "2-writing-scripts": "Writing Scripts", - "3-running-scripts": "Running Scripts", + "2-introduction": "Introduction", + "3-command-line-usage": "Command-Line Usage", "4-editor-setup": "Editor Setup" }