mirror of
https://github.com/lune-org/docs.git
synced 2025-04-03 01:50:57 +01:00
Refactor the entire running scripts & writing scripts sections
This commit is contained in:
parent
a09bbfd363
commit
c611bb3274
15 changed files with 771 additions and 323 deletions
|
@ -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.
|
||||
|
|
24
pages/getting-started/2-introduction/1-hello-lune.md
Normal file
24
pages/getting-started/2-introduction/1-hello-lune.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
<!-- markdownlint-disable MD026 -->
|
||||
|
||||
# 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)
|
|
@ -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! 🚀
|
27
pages/getting-started/2-introduction/2-built-in-libraries.md
Normal file
27
pages/getting-started/2-introduction/2-built-in-libraries.md
Normal file
|
@ -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.
|
92
pages/getting-started/2-introduction/3-standard-io.mdx
Normal file
92
pages/getting-started/2-introduction/3-standard-io.mdx
Normal file
|
@ -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!
|
||||
|
||||
<details>
|
||||
<summary>Bonus</summary>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
</details>
|
40
pages/getting-started/2-introduction/4-script-arguments.md
Normal file
40
pages/getting-started/2-introduction/4-script-arguments.md
Normal file
|
@ -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
|
||||
```
|
127
pages/getting-started/2-introduction/5-network-requests.mdx
Normal file
127
pages/getting-started/2-introduction/5-network-requests.mdx
Normal file
|
@ -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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<details>
|
||||
<summary>Bonus</summary>
|
||||
|
||||
## 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.
|
||||
|
||||
</details>
|
129
pages/getting-started/2-introduction/6-files-and-directories.mdx
Normal file
129
pages/getting-started/2-introduction/6-files-and-directories.mdx
Normal file
|
@ -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:
|
||||
|
||||
<FileTree>
|
||||
<FileTree.Folder name="-" defaultOpen>
|
||||
<FileTree.File name="files.luau" />
|
||||
<FileTree.File name="dirs.luau" />
|
||||
<FileTree.File name="hello-world.json" />
|
||||
<FileTree.Folder name="files" defaultOpen>
|
||||
<FileTree.File name="coolstuff.toml" />
|
||||
<FileTree.File name="super.secret.txt" />
|
||||
</FileTree.Folder>
|
||||
</FileTree.Folder>
|
||||
</FileTree>
|
||||
|
||||
<details>
|
||||
<summary>Show file contents</summary>
|
||||
|
||||
<Tabs items={['hello-world.json', 'files/coolstuff.toml', 'files/super.secret.txt']}>
|
||||
<Tab>
|
||||
```json copy filename="hello-world.json"
|
||||
{
|
||||
"Hello": "World"
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab>
|
||||
```toml copy filename="coolstuff.toml"
|
||||
[you]
|
||||
cool = true
|
||||
awesome = "yep"
|
||||
```
|
||||
</Tab>
|
||||
<Tab>
|
||||
```txt copy filename="super.secret.txt"
|
||||
Hey you're not supposed to be in here!
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
## 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:
|
||||
|
||||
<FileTree>
|
||||
<FileTree.Folder name="-" defaultOpen>
|
||||
<FileTree.File name="files.luau" />
|
||||
<FileTree.File name="dirs.luau" />
|
||||
<FileTree.File name="hello-world.json" />
|
||||
<FileTree.Folder name="myCoolDir" />
|
||||
</FileTree.Folder>
|
||||
</FileTree>
|
|
@ -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.
|
83
pages/getting-started/2-introduction/8-modules.mdx
Normal file
83
pages/getting-started/2-introduction/8-modules.mdx
Normal file
|
@ -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:
|
||||
|
||||
<FileTree>
|
||||
<FileTree.Folder name="-" defaultOpen>
|
||||
<FileTree.File name="main.luau" />
|
||||
<FileTree.File name="sibling.luau" />
|
||||
<FileTree.Folder name="modules" defaultOpen>
|
||||
<FileTree.File name="init.luau" />
|
||||
<FileTree.File name="module.luau" />
|
||||
</FileTree.Folder>
|
||||
</FileTree.Folder>
|
||||
</FileTree>
|
||||
|
||||
<Tabs items={['main', 'sibling', 'modules/init', 'modules/module']}>
|
||||
<Tab>
|
||||
```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
|
||||
```
|
||||
</Tab>
|
||||
<Tab>
|
||||
```lua copy filename="sibling.luau"
|
||||
return {
|
||||
Hello = "World",
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab>
|
||||
```lua copy filename="modules/init.luau"
|
||||
return {
|
||||
Module = require("module"),
|
||||
Sibling = require("../sibling"),
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab>
|
||||
```lua copy filename="modules/module.luau"
|
||||
return {
|
||||
Foo = "Bar",
|
||||
Fizz = "Buzz",
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 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
|
||||
(`@`).
|
||||
|
||||
<Callout type="info" emoji="❔">
|
||||
**Q:** Wait, hold on... The `main` script requires the _**directory**_ called `modules`? <br />
|
||||
**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.
|
||||
</Callout>
|
139
pages/getting-started/2-introduction/9-task-scheduler.mdx
Normal file
139
pages/getting-started/2-introduction/9-task-scheduler.mdx
Normal file
|
@ -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
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Bonus</summary>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
</details>
|
12
pages/getting-started/2-introduction/_meta.json
Normal file
12
pages/getting-started/2-introduction/_meta.json
Normal file
|
@ -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"
|
||||
}
|
|
@ -1,297 +0,0 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
<!-- markdownlint-disable MD026 -->
|
||||
|
||||
# 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.
|
|
@ -1,8 +1,8 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
# 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`**_<sup>[1]</sup>_** 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. <br /> 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 -
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue