This commit is contained in:
fenris 2025-04-25 10:47:04 +02:00
parent 1a59e3763c
commit 27336159db
9 changed files with 7367 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/build/
/temp/

1661
libs/plankton/plankton.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

5067
libs/plankton/plankton.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -2,3 +2,11 @@
Führt auf Grundlage eines Datenblatts bestimmte Befehle aus, die häufig für die Entwicklung von Programmen verwendet werden
## Nutzung
- `ivaldi fetch`
- `ivaldi clear`
- `ivaldi build`
- `ivaldi deploy`

498
source/main.ts Normal file
View file

@ -0,0 +1,498 @@
/*
This file is part of »munin«.
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
»munin« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»munin« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _munin
{
/**
* @todo outsource
*/
function execute(
command : string,
{
"log_only": log_only = false,
} : {
log_only ?: boolean;
} = {
}
) : Promise<
{
code : int;
stdout : string;
stderr : string;
}
>
{
const nm_child_process = require("child_process");
return (
new Promise(
(resolve, reject) => {
lib_plankton.log._info(
"execute",
{
"details": {
"command": command,
}
}
);
if (log_only) {
resolve(
{
"code": 0,
"stdout": "",
"stderr": "",
}
);
}
else {
nm_child_process.exec(
command,
{
},
(error, stdout, stderr) => {
if (error) {
reject(error);
}
else {
resolve(
{
/**
* @todo assign correct value
*/
"code": 0,
"stdout": stdout,
"stderr": stderr,
}
);
}
}
);
}
}
)
);
}
/**
*/
export async function main(
args_raw : Array<string>
) : Promise<void>
{
// args
const arg_handler : lib_plankton.args.class_handler = new lib_plankton.args.class_handler(
{
"action": lib_plankton.args.class_argument.positional({
"index": 0,
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "help",
"info": "what to do : fetch | clear | build | deploy",
"name": "action",
}),
"data_path": lib_plankton.args.class_argument.volatile({
"indicators_long": ["data-path"],
"indicators_short": ["d"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "ivaldi.json",
"info": "path to data file",
"name": "data-path",
}),
"verbosity": lib_plankton.args.class_argument.volatile({
"indicators_long": ["verbosity"],
"indicators_short": ["v"],
"type": lib_plankton.args.enum_type.string,
"mode": lib_plankton.args.enum_mode.replace,
"default": "notice",
"info": "error | warning | notice | info | debug",
"name": "verbosity",
}),
"help": lib_plankton.args.class_argument.volatile({
"indicators_long": ["help"],
"indicators_short": ["h"],
"type": lib_plankton.args.enum_type.boolean,
"mode": lib_plankton.args.enum_mode.replace,
"default": false,
"info": "alias for action 'help'",
"name": "help",
}),
}
);
const args : Record<string, any> = arg_handler.read(
lib_plankton.args.enum_environment.cli,
args_raw.join(" ")
);
// init
lib_plankton.log.set_main_logger(
[
{
"kind": "filtered",
"data": {
"core": {
"kind": "std",
"data": {
"target": "stdout",
"format": {
"kind": "human_readable",
"data": {
}
}
}
},
"predicate": [
[
{
"item": {
"kind": "level",
"data": {
"threshold": args.verbosity,
}
},
}
]
],
}
},
]
);
// exec
if (args.help) {
process.stdout.write(
arg_handler.generate_help(
{
"programname": "lixer-event-reminder",
"description": "a telegram bot, which sends reminders about upcoming events",
"executable": "node build/event-reminder",
}
)
);
}
else {
const content : string = await lib_plankton.file.read(args.data_path);
const data_raw : any = lib_plankton.json.decode(content);
const data : _munin.type_data = {
"version": data_raw["version"],
"name": data_raw["name"],
"libs": (data_raw["libs"] ?? {}),
"sources": data_raw["sources"],
};
/**
* @todo outsource
*/
const conf = {
"directories": {
"source": "source",
"libs": "libs",
"temp": "temp",
"build": "build",
},
"command_tsc": "tsc",
"makefile_path": "/tmp/ivaldi-makefile",
};
switch (args.action) {
default: {
return Promise.reject<void>((new Error("unhandled action: " + args.action)));
break;
}
case "fetch": {
for (const lib of data.libs) {
switch (lib.kind) {
case "plankton": {
/**
* @todo outsource
*/
const conf = {
"directory": "libs/plankton",
};
const commands : Array<string> = [
lib_plankton.string.coin(
"mkdir -p {{directory}}",
{
"directory": conf.directory,
}
),
lib_plankton.string.coin(
"cd {{directory}}",
{
"directory": conf.directory,
}
),
lib_plankton.string.coin(
"ptk bundle node {{modules}}",
{
"modules": lib.data.modules.join(" "),
}
),
];
for (const command of commands) {
await execute(command);
}
break;
}
case "node": {
/**
* @todo
*/
throw (new Error("unhanled lib kind: " + "node"));
break;
}
default: {
throw (new Error("unhanled lib kind: " + lib["kind"]));
break;
}
}
}
return Promise.resolve<void>(undefined);
break;
}
case "clear": {
const commands : Array<string> = [
lib_plankton.string.coin(
"rm -r -f {{directory}}",
{
"directory": conf.directories.temp,
}
),
lib_plankton.string.coin(
"rm -r -f {{directory}}",
{
"directory": conf.directories.build,
}
),
];
for (const command of commands) {
await execute(command);
}
return Promise.resolve<void>(undefined);
break;
}
case "build": {
const has_plankton : boolean = data.libs.some(lib => (lib.kind === "plankton"));
const path_plankton_declaration : string = lib_plankton.string.coin(
"{{directory_libs}}/plankton/plankton.d.ts",
{
"directory_libs": conf.directories.libs,
}
);
const path_plankton_implementation : string = lib_plankton.string.coin(
"{{directory_libs}}/plankton/plankton.js",
{
"directory_libs": conf.directories.libs,
}
);
const paths_sources : Array<string> = (
data.sources
.map(
source => lib_plankton.string.coin(
"{{directory}}/{{name}}",
{
"directory": conf.directories.source,
"name": source,
}
)
)
);
const path_unlinked : string = lib_plankton.string.coin(
"{{directory_temp}}/{{name}}-unlinked.js",
{
"directory_temp": conf.directories.temp,
"name": data.name,
}
);
const path_head : string = lib_plankton.string.coin(
"{{directory_temp}}/head.js",
{
"directory_temp": conf.directories.temp,
}
);
const path_result : string = lib_plankton.string.coin(
"{{directory_build}}/{{name}}",
{
"directory_build": conf.directories.build,
"name": data.name,
}
);
const dependencies_compilation : Array<string> = (
[]
.concat(has_plankton ? [path_plankton_declaration] : [])
.concat(paths_sources)
);
const gnu_make_sheet : lib_plankton.gnu_make.type_sheet = {
"rules": [
// root
{
"kind": "meta",
"data": {
"phony": true,
"name": "_root",
"dependencies": [
path_result,
],
}
},
// compile
{
"kind": "concrete",
"data": {
"targets": [
path_unlinked,
],
"dependencies": dependencies_compilation,
"actions": [
// log
lib_plankton.string.coin(
"echo '-- {{message}}'",
{
"message": "compiling …",
}
),
// mkdir
lib_plankton.string.coin(
"mkdir -p $(dir $@)",
{
}
),
// command
lib_plankton.string.coin(
"{{command}} $^ --lib es2020,dom --target es6 --outFile $@",
{
"command": conf.command_tsc,
}
),
],
}
},
// head
{
"kind": "concrete",
"data": {
"targets": [
path_head,
],
"dependencies": [
],
"actions": [
// log
lib_plankton.string.coin(
"echo '-- {{message}}'",
{
"message": "creating head …",
}
),
// mkdir
lib_plankton.string.coin(
"mkdir -p $(dir $@)",
{
}
),
// create
lib_plankton.string.coin(
"echo '#!/usr/bin/env node' > {{path}}",
{
"path": path_head,
}
),
]
}
},
// link
{
"kind": "concrete",
"data": {
"targets": [
path_result,
],
"dependencies": (
[]
.concat([path_head])
.concat(has_plankton ? [path_plankton_implementation] : [])
.concat([path_unlinked])
),
"actions": [
// log
lib_plankton.string.coin(
"echo '-- {{message}}'",
{
"message": "linking …",
}
),
// mkdir
lib_plankton.string.coin(
"mkdir -p $(dir $@)",
{
}
),
// command
lib_plankton.string.coin(
"cat $^ > $@",
{
}
),
// make executable
lib_plankton.string.coin(
"chmod +x {{path}}",
{
"path": path_result,
}
),
],
},
},
],
};
const makefile : string = lib_plankton.gnu_make.render_sheet(
gnu_make_sheet,
{
"break_dependencies": true,
"silent_actions": true,
}
);
await lib_plankton.file.write(
conf.makefile_path,
makefile
);
const command : string = lib_plankton.string.coin(
"make -f {{path}}",
{
"path": conf.makefile_path,
}
);
await execute(command);
return Promise.resolve<void>(undefined);
break;
}
case "deploy": {
return Promise.reject<void>(new Error("not yet implemented"));
break;
}
}
}
}
}
_munin.main(process.argv.slice(2))
.then(() => {})
.catch((reason) => {process.stderr.write(String(reason) + "\n");})
;

52
source/types.ts Normal file
View file

@ -0,0 +1,52 @@
/*
This file is part of »munin«.
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
»munin« is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
»munin« is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with »munin«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _munin
{
/**
*/
type type_lib = (
{
kind : "node";
data : {
modules : Array<string>;
};
}
|
{
kind : "plankton";
data : {
modules : Array<string>;
};
}
);
/**
*/
export type type_data = {
version : string;
name : string;
libs : Array<type_lib>;
sources : Array<string>;
};
}

17
tools/build Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env python3
import os as _os
import argparse as _argparse
def main():
argument_parser = _argparse.ArgumentParser(
)
args = argument_parser.parse_args()
if True:
_os.system("make -f tools/makefile")
main()

37
tools/makefile Normal file
View file

@ -0,0 +1,37 @@
## commands
cmd_mkdir := mkdir -p
cmd_cp := cp -r -u
cmd_tsc := tsc
cmd_log := echo "--"
cmd_cat := cat
cmd_echo := echo
cmd_chmod := chmod
## rules
.PHONY: _default
_default: _root
temp/ivaldi-unlinked.js: \
libs/plankton/plankton.d.ts \
source/types.ts \
source/main.ts
@ ${cmd_log} "compiling …"
@ ${cmd_mkdir} temp
@ ${cmd_tsc} $^ --lib es2020,dom --target es6 --outFile $@
build/ivaldi: libs/plankton/plankton.js temp/ivaldi-unlinked.js
@ ${cmd_log} "linking …"
@ ${cmd_echo} "#!/usr/bin/env node" > temp/head.js
@ ${cmd_mkdir} build
@ ${cmd_cat} temp/head.js $^ > $@
@ ${cmd_chmod} +x $@
.PHONY: sources
sources: build/ivaldi
.PHONY: _root
_root: sources

25
tools/update-plankton Executable file
View file

@ -0,0 +1,25 @@
#!/usr/bin/env sh
## consts
dir="libs/plankton"
## vars
modules=""
modules="${modules} base"
modules="${modules} string"
modules="${modules} log"
modules="${modules} json"
modules="${modules} gnu_make"
modules="${modules} file"
modules="${modules} args"
## exec
mkdir -p ${dir}
cd ${dir}
ptk bundle node ${modules}