Lua: Reimplement `template` and `config` in Space Lua, distributed with Library/Std
parent
d72983bde5
commit
517cfb209a
|
@ -16,7 +16,6 @@ import { tableApi } from "$common/space_lua/stdlib/table.ts";
|
|||
import { osApi } from "$common/space_lua/stdlib/os.ts";
|
||||
import { jsApi } from "$common/space_lua/stdlib/js.ts";
|
||||
import { spaceLuaApi } from "$common/space_lua/stdlib/space_lua.ts";
|
||||
import { templateApi } from "$common/space_lua/stdlib/template.ts";
|
||||
import { mathApi } from "$common/space_lua/stdlib/math.ts";
|
||||
import { parse } from "$common/space_lua/parse.ts";
|
||||
import { evalStatement } from "$common/space_lua/eval.ts";
|
||||
|
@ -192,6 +191,6 @@ export function luaBuildStandardEnv() {
|
|||
// Non-standard
|
||||
env.set("each", eachFunction);
|
||||
env.set("space_lua", spaceLuaApi);
|
||||
env.set("template", templateApi);
|
||||
// env.set("template", templateApi);
|
||||
return env;
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import {
|
||||
type ILuaFunction,
|
||||
jsToLuaValue,
|
||||
LuaBuiltinFunction,
|
||||
LuaTable,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import { interpolateLuaString } from "$common/space_lua/stdlib/space_lua.ts";
|
||||
|
||||
export const templateApi = new LuaTable({
|
||||
each: new LuaBuiltinFunction(
|
||||
async (sf, tbl: LuaTable | any[], fn: ILuaFunction): Promise<string> => {
|
||||
const result = [];
|
||||
if (tbl instanceof LuaTable) {
|
||||
tbl = tbl.toJSArray();
|
||||
}
|
||||
for (const item of tbl) {
|
||||
result.push(await fn.call(sf, item));
|
||||
}
|
||||
return result.join("");
|
||||
},
|
||||
),
|
||||
new: new LuaBuiltinFunction(
|
||||
(_sf, template: string): ILuaFunction => {
|
||||
const lines = template.split("\n").map((line) =>
|
||||
line.replace(/^\s{4}/, "")
|
||||
);
|
||||
const processed = lines.join("\n");
|
||||
return new LuaBuiltinFunction(
|
||||
async (sf, env: LuaTable | any) => {
|
||||
if (!(env instanceof LuaTable)) {
|
||||
env = jsToLuaValue(env);
|
||||
}
|
||||
return await interpolateLuaString(sf, processed, env);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
import type { FileMeta } from "@silverbulletmd/silverbullet/types";
|
||||
import type { SysCallMapping, System } from "../system.ts";
|
||||
|
||||
export default function assetSyscalls(system: System<any>): SysCallMapping {
|
||||
|
@ -7,5 +8,28 @@ export default function assetSyscalls(system: System<any>): SysCallMapping {
|
|||
name,
|
||||
);
|
||||
},
|
||||
"asset.listFiles": (_ctx, plugName: string): FileMeta[] => {
|
||||
const assets = system.loadedPlugs.get(plugName)!.assets!;
|
||||
const fileNames = assets.listFiles();
|
||||
return fileNames.map((name) => ({
|
||||
name,
|
||||
contentType: assets.getMimeType(name),
|
||||
created: assets.getMtime(name),
|
||||
lastModified: assets.getMtime(name),
|
||||
size: -1,
|
||||
perm: "ro",
|
||||
}));
|
||||
},
|
||||
"asset.getFileMeta": (_ctx, plugName: string, name: string): FileMeta => {
|
||||
const assets = system.loadedPlugs.get(plugName)!.assets!;
|
||||
return {
|
||||
name,
|
||||
contentType: assets.getMimeType(name),
|
||||
created: assets.getMtime(name),
|
||||
lastModified: assets.getMtime(name),
|
||||
size: -1,
|
||||
perm: "ro",
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { base64DecodeDataUrl } from "../../lib/crypto.ts";
|
||||
import { syscall } from "../syscall.ts";
|
||||
import type { FileMeta } from "@silverbulletmd/silverbullet/types";
|
||||
|
||||
/**
|
||||
* Reads an asset embedded in a plug (via the `assets` field in the plug manifest).
|
||||
|
@ -21,3 +22,14 @@ export async function readAsset(
|
|||
return dataUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export async function listFiles(plugName: string): Promise<FileMeta[]> {
|
||||
return await syscall("asset.listFiles", plugName);
|
||||
}
|
||||
|
||||
export async function getFileMeta(
|
||||
plugName: string,
|
||||
name: string,
|
||||
): Promise<FileMeta> {
|
||||
return await syscall("asset.getFileMeta", plugName, name);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
#meta
|
||||
|
||||
Config library for defining and getting config values
|
||||
|
||||
```space-lua
|
||||
-- priority: 100
|
||||
config = {}
|
||||
|
||||
local config_values = {}
|
||||
local config_schema = {}
|
||||
|
||||
function config.define(key, schema)
|
||||
config_schema[key] = schema or true
|
||||
end
|
||||
|
||||
function config.set(key, value)
|
||||
local schema = config_schema[key]
|
||||
if schema == nil then
|
||||
error("Config key not defined: " .. key)
|
||||
end
|
||||
if schema != true then
|
||||
result = jsonschema.validate_object(schema, value)
|
||||
if result != nil then
|
||||
error("Validation error (" .. key .. "): " .. result)
|
||||
end
|
||||
end
|
||||
config_values[key] = value
|
||||
end
|
||||
|
||||
function config.get(key)
|
||||
return config_values[key]
|
||||
end
|
||||
```
|
|
@ -0,0 +1,38 @@
|
|||
#meta
|
||||
|
||||
Implements useful template functions
|
||||
|
||||
```space-lua
|
||||
-- priority: 100
|
||||
-- Template library for working with templates and iterables
|
||||
template = {}
|
||||
|
||||
-- Iterates over a table/array and applies a function to each element,
|
||||
-- concatenating the results
|
||||
function template.each(tbl, fn)
|
||||
local result = {}
|
||||
for _, item in ipairs(tbl) do
|
||||
table.insert(result, fn(item))
|
||||
end
|
||||
return table.concat(result)
|
||||
end
|
||||
|
||||
-- Creates a new template function from a string template
|
||||
function template.new(template_str)
|
||||
-- Preprocess: strip indentation
|
||||
local lines = {}
|
||||
local split_lines = string.split(template_str, "\n")
|
||||
for _, line in ipairs(split_lines) do
|
||||
line = string.gsub(line, "^ ", "")
|
||||
table.insert(lines, line)
|
||||
end
|
||||
template_str = table.concat(lines, "\n")
|
||||
return function(obj)
|
||||
return space_lua.interpolate(template_str, obj)
|
||||
end
|
||||
end
|
||||
|
||||
print("template loaded!!")
|
||||
|
||||
|
||||
```
|
|
@ -1,4 +1,37 @@
|
|||
name: core
|
||||
assets:
|
||||
- "Library/Std/*"
|
||||
functions:
|
||||
init:
|
||||
path: ./std.ts:init
|
||||
env: server
|
||||
events:
|
||||
- system:ready
|
||||
readFile:
|
||||
path: ./std.ts:readFile
|
||||
pageNamespace:
|
||||
pattern: "^Library/Std/.+"
|
||||
operation: readFile
|
||||
writeFile:
|
||||
path: ./std.ts:writeFile
|
||||
pageNamespace:
|
||||
pattern: "^Library/Std/.+"
|
||||
operation: writeFile
|
||||
deleteFile:
|
||||
path: ./std.ts:deleteFile
|
||||
pageNamespace:
|
||||
pattern: "^Library/Std/.+"
|
||||
operation: deleteFile
|
||||
getFileMeta:
|
||||
path: ./std.ts:getFileMeta
|
||||
pageNamespace:
|
||||
pattern: "^Library/Std/.+"
|
||||
operation: getFileMeta
|
||||
listFiles:
|
||||
path: ./std.ts:listFiles
|
||||
pageNamespace:
|
||||
pattern: "^Library/Std/.+"
|
||||
operation: listFiles
|
||||
config:
|
||||
# Built-in schemas
|
||||
schema.tag:
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
asset,
|
||||
datastore,
|
||||
mq,
|
||||
system,
|
||||
} from "@silverbulletmd/silverbullet/syscalls";
|
||||
import type { FileMeta } from "@silverbulletmd/silverbullet/types";
|
||||
|
||||
export async function listFiles(): Promise<FileMeta[]> {
|
||||
return await asset.listFiles("core");
|
||||
}
|
||||
|
||||
export async function readFile(
|
||||
name: string,
|
||||
): Promise<{ data: Uint8Array; meta: FileMeta }> {
|
||||
const text = await asset.readAsset("core", name, "utf8");
|
||||
return {
|
||||
data: new TextEncoder().encode(text),
|
||||
meta: await asset.getFileMeta("core", name),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeFile(): Promise<FileMeta> {
|
||||
throw new Error("Writing std files not supported");
|
||||
}
|
||||
|
||||
export function deleteFile(): Promise<void> {
|
||||
throw new Error("Deleting std files not supported");
|
||||
}
|
||||
|
||||
export function getFileMeta(name: string): Promise<FileMeta> {
|
||||
return asset.getFileMeta("core", name);
|
||||
}
|
||||
|
||||
const stdLibCacheKey = ["stdLibCache"];
|
||||
|
||||
type StdLibCache = Record<string, number>; // page name -> last modified time
|
||||
|
||||
export async function init() {
|
||||
let stdLibCache: StdLibCache | undefined = await datastore.get(
|
||||
stdLibCacheKey,
|
||||
);
|
||||
if (!stdLibCache) {
|
||||
stdLibCache = {};
|
||||
}
|
||||
// Iterate over the current file listing, check if any new files have been added, removed or modified
|
||||
const newListing = await listFiles();
|
||||
// First check for files that were removed
|
||||
for (const cachedFile of Object.keys(stdLibCache)) {
|
||||
if (!newListing.find((f) => f.name === cachedFile)) {
|
||||
console.log(`Clearing index for removed file ${cachedFile}`);
|
||||
await system.invokeFunction("index.clearDSIndex", cachedFile);
|
||||
delete stdLibCache[cachedFile];
|
||||
}
|
||||
}
|
||||
|
||||
// Then check for new/modified files
|
||||
for (const file of newListing) {
|
||||
const lastModified = file.lastModified;
|
||||
|
||||
// Check if file is new or modified compared to cache
|
||||
if (!stdLibCache[file.name] || stdLibCache[file.name] !== lastModified) {
|
||||
// console.log(`Queuing for indexing ${file.name}`);
|
||||
await system.invokeFunction("index.clearDSIndex", file.name);
|
||||
await mq.send("indexQueue", file.name);
|
||||
stdLibCache[file.name] = lastModified;
|
||||
}
|
||||
}
|
||||
|
||||
// Save updated cache
|
||||
await datastore.set(stdLibCacheKey, stdLibCache);
|
||||
}
|
|
@ -80,11 +80,14 @@ export default function reducer(
|
|||
currPageMeta = pageMeta;
|
||||
}
|
||||
}
|
||||
return {
|
||||
const newState = {
|
||||
...state,
|
||||
allPages: action.allPages,
|
||||
currentPageMeta: currPageMeta,
|
||||
};
|
||||
if (currPageMeta) {
|
||||
newState.currentPageMeta = currPageMeta;
|
||||
}
|
||||
return newState;
|
||||
}
|
||||
case "start-navigate": {
|
||||
return {
|
||||
|
|
Loading…
Reference in New Issue