This commit is contained in:
fenris 2026-09-18 02:13:11 +02:00
parent 0c4e6ff649
commit 6300c16485
7 changed files with 1358 additions and 212 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/build/
/temp/
/.geany

152
misc/ivaldi.schema.json Normal file
View file

@ -0,0 +1,152 @@
{
"nullable": false,
"type": "object",
"properties": {
"version": {
"nullable": false,
"type": "string"
},
"name": {
"nullable": false,
"type": "string"
},
"kind": {
"nullable": false,
"type": "string",
"enum": ["node", "web"]
},
"dependencies": {
"nullable": true,
"type": "object",
"properties": {
"plankton": {
"nullable": true,
"type": "array",
"item": {
"nullable": false,
"type": "string"
},
"default": null
},
"node_modules": {
"nullable": true,
"type": "array",
"item": {
"nullable": false,
"type": "string"
},
"default": null
}
},
"required": [
],
"default": null
},
"core": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
}
},
"apps": {
"nullable": false,
"type": "object",
"properties": {
"name": {
"nullable": false,
"type": "string"
},
"sources": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
}
},
"additional_dependencies": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
},
"default": []
},
"destination": {
"nullable": false,
"type": "string",
"default": "{{project}}-{{app}"
},
"shebang": {
"nullable": true,
"type": "string",
"default": null
},
"executable": {
"nullable": false,
"type": "boolean",
"default": false
}
},
"additionalProperties": false,
"required": [
"name",
"sources"
]
},
"custom_rules": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "object",
"properties": {
"targets": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
}
},
"dependencies": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
}
},
"actions": {
"nullable": false,
"type": "array",
"item": {
"nullable": false,
"type": "string"
}
},
"info": {
"nullable": true,
"type": "string"
}
},
"additionalProperties": false,
"required": [
"targets",
"dependencies",
"actions"
]
}
}
},
"additionalProperties": false,
"required": [
"version",
"name",
"kind",
"core"
]
}

564
misc/wizard Executable file
View file

@ -0,0 +1,564 @@
#!/usr/bin/env python3
import sys as _sys
import os as _os
import json as _json
import argparse as _argparse
def file_read(path):
handle = open(path, "r")
content = handle.read()
handle.close()
return content
def string_coin(pattern, arguments):
result = pattern
for (key, value, ) in arguments.items():
result = result.replace("{{%s}}" % key, value)
return result
def command_log(content):
return string_coin(
"echo '-- {{content}}'",
{
"content": content,
}
)
def command_mkdir(path):
return string_coin(
"mkdir --parents {{path}}",
{
"path": path,
}
)
def command_cat(source_paths, target_path, options = None):
options = (
{
"append": False,
}
|
(options or {})
)
return string_coin(
"cat {{source_paths}} {{operator}} {{target_path}}",
{
"source_paths": " ".join(source_paths),
"operator": (">>" if options["append"] else ">"),
"target_path": target_path,
}
)
def command_copy(source_path, target_path, options = None):
options = (
{
}
|
(options or {})
)
return string_coin(
"cp -r -u {{source_path}} {{target_path}}",
{
"source_path": source_path,
"target_path": target_path,
}
)
def command_tsc(source_paths, target_path, options = None):
options = (
{
"declaration": False,
"libs": [],
}
|
(options or {})
)
return string_coin(
"tsc{{libs}} {{source_paths}} --outFile {{target_path}}{{declaration}}",
{
"libs": "".join(
map(
lambda name: string_coin(
" --lib {{name}}",
{"name": name}
),
options["libs"]
),
),
"source_paths": " ".join(source_paths),
"target_path": target_path,
"declaration": (" --declaration" if options["declaration"] else ""),
}
)
def gnumake_rule(data):
return string_coin(
"{{phony}}{{targets}}: {{dependencies}}\n{{actions}}\n",
{
"phony": (
string_coin(".PHONY: {{name}}\n", {"name": data["targets"][0]})
if (data["phony"] and (len(data["targets"]) == 1)) else
""
),
"targets": " ".join(data["targets"]),
"dependencies": " ".join(data["dependencies"]),
"actions": "".join(
map(
lambda data_action: string_coin(
"\t{{prefix}}{{name}}\n",
{
"prefix": ("@ " if data_action["silent"] else ""),
"name": data_action["command"],
}
),
data["actions"]
)
),
}
)
def gnumake_sheet(data):
return string_coin(
"{{rules}}",
{
"rules": "".join(
map(
lambda data_rule: gnumake_rule(data_rule),
data["rules"]
)
),
}
)
def action_update_plankton(conf, definition):
directory = _os.path.join(definition["directories"]["lib"], "plankton")
commands = [
command_mkdir(directory),
string_coin("cd {{directory}}", {"directory": directory}),
string_coin(
"ptk bundle {{target}} {{modules}}",
{
"target": definition["kind"],
"modules": " ".join(definition["dependencies"]["plankton"]),
}
),
"cd - > /dev/null",
]
_sys.stdout.write(
"".join(
map(
lambda command: (command + "\n"),
commands
)
)
)
def refine_definition(definition_raw):
return {
"name": definition_raw["name"],
"kind": definition_raw.get("kind", "node"),
"directories": {
"lib": definition_raw.get("directories", {}).get("lib", "lib"),
"misc": definition_raw.get("directories", {}).get("misc", "misc"),
"source": definition_raw.get("directories", {}).get("source", "source"),
},
"dependencies": {
"plankton": definition_raw.get("dependencies", {}).get("plankton", []),
"node": definition_raw.get("dependencies", {}).get("node", []),
},
"core": definition_raw["core"],
"apps": list(
map(
lambda app_raw: {
"name": app_raw["name"],
"sources": app_raw["sources"],
"additional_dependencies": app_raw.get("additional_dependencies", []),
"destination": app_raw.get("destination", "{{project}}-{{app}}"),
"shebang": app_raw.get("shebang", ""),
"executable": app_raw.get("executable", True),
},
definition_raw["apps"]
)
),
"custom_rules": definition_raw.get("custom_rules", []),
}
def action_update_node_modules(conf, definition):
directory = _os.path.join(definition["directories"]["lib"], "node")
commands = [
command_mkdir(directory),
string_coin("cd {{directory}}", {"directory": directory}),
string_coin(
"npm install {{modules}}",
{
"modules": " ".join(definition["dependencies"]["node"]),
}
),
"cd - > /dev/null",
]
_sys.stdout.write(
"".join(
map(
lambda command: (command + "\n"),
commands
)
)
)
def action_build(conf, definition):
data = {
"rules": (
[
{
"phony": True,
"targets": ["_default"],
"dependencies": list(
map(
lambda data_app: string_coin("app-{{name}}", {"name": data_app["name"]}),
definition["apps"]
)
),
"actions": [],
},
{
"phony": True,
"targets": [
"node_modules"
],
"dependencies": [],
"actions": [
{
"silent": True,
"command": command_log("placing node modules …"),
},
{
"silent": True,
"command": command_mkdir(_os.path.join(conf["directories"]["build"], "node_modules")),
},
{
"silent": True,
"command": command_copy(
_os.path.join(definition["directories"]["lib"], "node", "node_modules", "*"),
_os.path.join(conf["directories"]["build"], "node_modules", "")
),
},
]
},
{
"phony": False,
"targets": [
_os.path.join(conf["directories"]["temp"], "core-unlinked.js"),
_os.path.join(conf["directories"]["temp"], "core-unlinked.d.ts"),
],
"dependencies": (
[
_os.path.join(definition["directories"]["lib"], "plankton", "plankton.d.ts"),
]
+
list(
map(
lambda path: _os.path.join(definition["directories"]["source"], path),
definition["core"]
)
)
),
"actions": [
{
"silent": True,
"command": command_log("core | compiling …")
},
{
"silent": True,
"command": command_mkdir("$(dir $@)")
},
{
"silent": True,
"command": command_tsc(
["$^"],
_os.path.join(conf["directories"]["temp"], "core-unlinked.js"),
{
"libs": ["es2020"],
"declaration": True,
}
)
}
]
},
]
+
list(
map(
lambda data_custom_rule: {
"phony": False,
"targets": list(
map(
lambda target: string_coin(
target,
{
"dir_build": conf["directories"]["build"],
}
),
data_custom_rule["targets"]
)
),
"dependencies": list(
map(
lambda dependency: string_coin(
dependency,
{
"dir_source": definition["directories"]["source"],
"dir_misc": definition["directories"]["misc"],
}
),
data_custom_rule["dependencies"]
)
),
"actions": (
[
{
"silent": True,
"command": command_log(
string_coin(
"{{info}} …",
{
"info": data_custom_rule["info"],
}
),
),
}
]
+
list(
map(
lambda action: {
"silent": True,
"command": action,
},
data_custom_rule["actions"]
)
)
),
},
definition["custom_rules"]
)
)
+
list(
map(
lambda data_app: {
"phony": False,
"targets": [
_os.path.join(conf["directories"]["temp"], string_coin("app-{{name}}-unlinked.js", {"name": data_app["name"]})),
],
"dependencies": (
[
_os.path.join(definition["directories"]["lib"], "plankton", "plankton.d.ts"),
_os.path.join(conf["directories"]["temp"], "core-unlinked.d.ts"),
]
+
list(
map(
lambda path: _os.path.join(definition["directories"]["source"], path),
data_app["sources"]
)
)
),
"actions": [
{
"silent": True,
"command": command_log(string_coin("app:{{name}} | compiling …", {"name": data_app["name"]})),
},
{
"silent": True,
"command": command_mkdir("$(dir $@)"),
},
{
"silent": True,
"command": command_tsc(["$^"], "$@", {"libs": ["es2020"]}),
},
],
},
definition["apps"]
)
)
+
list(
map(
lambda data_app: {
"phony": False,
"targets": [
_os.path.join(
conf["directories"]["build"],
string_coin(
data_app["destination"],
{
"project": definition["name"],
"app": data_app["name"],
}
)
),
],
"dependencies": (
[
_os.path.join(definition["directories"]["lib"], "plankton", "plankton.js"),
]
+
[
_os.path.join(conf["directories"]["temp"], "core-unlinked.js"),
_os.path.join(conf["directories"]["temp"], string_coin("app-{{name}}-unlinked.js", {"name": data_app["name"]})),
]
),
"actions": (
[
{
"silent": True,
"command": command_log(string_coin("app:{{name}} | linking …", {"name": data_app["name"]}))
},
{
"silent": True,
"command": command_mkdir("$(dir $@)")
},
{
"silent": True,
"command": string_coin(
"echo '{{shebang}}' > $@",
{
"shebang": data_app["shebang"],
}
)
},
{
"silent": True,
"command": command_cat(["$^"], "$@", {"append": True})
},
]
+
(
[
{
"silent": True,
"command": "chmod +x $@",
},
]
if data_app["executable"] else
[
]
)
)
},
definition["apps"]
)
)
+
list(
map(
lambda data_app: {
"phony": True,
"targets": [
string_coin(
"app-{{name}}",
{
"name": data_app["name"],
}
),
],
"dependencies": (
[
"node_modules",
_os.path.join(
conf["directories"]["build"],
string_coin(
data_app["destination"],
{
"project": definition["name"],
"name": data_app["name"],
}
)
),
]
+
list(
map(
lambda dependency: string_coin(
dependency,
{
"dir_source": definition["directories"]["source"],
"dir_misc": definition["directories"]["misc"],
"dir_temp": conf["directories"]["temp"],
"dir_build": conf["directories"]["build"],
}
),
data_app["additional_dependencies"]
)
)
),
"actions": []
},
definition["apps"]
)
)
)
}
_sys.stdout.write(gnumake_sheet(data))
def main():
## args
argument_parser = _argparse.ArgumentParser()
argument_parser.add_argument(
"action",
type = str,
metavar = "<action>",
)
argument_parser.add_argument(
"-d",
"--definition-path",
type = str,
default = "prj.json",
metavar = "<definition-path>",
)
argument_parser.add_argument(
"-b",
"--build-directory-pattern",
type = str,
default = "/tmp/{{project}}",
metavar = "<build-directory-pattern>",
)
args = argument_parser.parse_args()
## vars
definition = refine_definition(_json.loads(file_read(args.definition_path)))
conf = {
"directories": {
"temp": string_coin("/tmp/{{project}}-temp", {"project": definition["name"]}),
"build": string_coin(args.build_directory_pattern, {"project": definition["name"]}),
}
}
## exec
### todo: refine
if (args.action == "update-plankton"):
action_update_plankton(conf, definition)
elif (args.action == "update-node"):
action_update_node_modules(conf, definition)
elif (args.action == "build"):
action_build(conf, definition)
else:
_sys.stderr.write("unhandled action")
main()

185
source/helpers.ts Normal file
View file

@ -0,0 +1,185 @@
/*
This file is part of »ivaldi«.
Copyright 2025 'Fenris Wolf' <fenris@folksprak.org>
»ivaldi« 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.
»ivaldi« 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 »ivaldi«. If not, see <http://www.gnu.org/licenses/>.
*/
namespace _ivaldi
{
/**
*/
export function command_log(
content : string
) : string
{
return lib_plankton.string.coin(
"echo '-- {{content}}'",
{
"content": content,
}
)
}
/**
*/
export function command_delete(
path : string
) : string
{
return lib_plankton.string.coin(
"rm --recursive --force {{path}}",
{
"path": path,
}
)
}
/**
*/
export function command_mkdir(
path : string
) : string
{
return lib_plankton.string.coin(
"mkdir --parents {{path}}",
{
"path": path,
}
)
}
/**
*/
export function command_cat(
source_paths : Array<string>,
target_path : string,
{
"append": append = false,
}
:
{
append ?: boolean;
}
=
{
}
) : string
{
return lib_plankton.string.coin(
"cat {{source_paths}} {{operator}} {{target_path}}",
{
"source_paths": source_paths.join(" "),
"operator": (append ? ">>" : ">"),
"target_path": target_path,
}
)
}
/**
*/
export function command_copy(
source_path : string,
target_path : string
) : string
{
return lib_plankton.string.coin(
"cp -r -u {{source_path}} {{target_path}}",
{
"source_path": source_path,
"target_path": target_path,
}
)
}
/**
*/
export function command_ptk_bundle(
target : string,
modules : Array<string>
) : string
{
return lib_plankton.string.coin(
"ptk bundle {{target}} {{modules}}",
{
"target": target,
"modules": modules.join(" "),
}
)
}
/**
*/
export function command_npm_install(
modules : Array<string>
) : string
{
return lib_plankton.string.coin(
"npm install {{modules}}",
{
"modules": modules.join(" "),
}
)
}
/**
*/
export function command_tsc(
source_paths : Array<string>,
target_path : string,
{
"declaration": declaration = false,
"libs": libs = [],
}
:
{
declaration ?: boolean;
libs ?: Array<string>;
}
=
{
}
) : string
{
return lib_plankton.string.coin(
"tsc{{libs}} {{source_paths}} --outFile {{target_path}}{{declaration}}",
{
"libs": (
libs
.map(
name => lib_plankton.string.coin(
" --lib {{name}}",
{"name": name}
),
)
.join("")
),
"source_paths": source_paths.join(" "),
"target_path": target_path,
"declaration": (declaration ? " --declaration" : ""),
}
)
}
}

View file

@ -91,6 +91,21 @@ namespace _ivaldi
)
);
}
/**
*/
function get_app_facade_name(
app : type_app
) : string
{
return lib_plankton.string.coin(
"app-{{name}}",
{
"name": app.name,
}
);
}
/**
@ -179,7 +194,8 @@ namespace _ivaldi
);
// exec
if (args.help) {
if (args.help)
{
process.stdout.write(
arg_handler.generate_help(
{
@ -190,14 +206,13 @@ namespace _ivaldi
)
);
}
else {
else
{
const content : string = await lib_plankton.file.read(args.data_path);
const data_raw : any = lib_plankton.json.decode(content);
const data : _ivaldi.type_data = {
"version": data_raw["version"],
"name": data_raw["name"],
"libs": (data_raw["libs"] ?? {}),
"sources": data_raw["sources"],
"target": (
data_raw["target"]
??
@ -214,6 +229,11 @@ namespace _ivaldi
??
"source"
),
"misc": (
(data_raw["misc"] ?? {})["misc"]
??
"misc"
),
"temp": (
(data_raw["misc"] ?? {})["temp"]
??
@ -227,7 +247,7 @@ namespace _ivaldi
"makefile": (
(data_raw["misc"] ?? {})["makefile"]
??
"/tmp"
lib_plankton.string.coin("/tmp/", {})
),
},
"commands": {
@ -237,89 +257,122 @@ namespace _ivaldi
"tsc"
),
},
"libs": (data_raw["libs"] ?? {}),
"core": data_raw["core"],
"apps": (
data_raw["apps"].map(
(app_raw : Record<string, any>) => (
{
"name": app_raw["name"],
"sources": app_raw["sources"],
"additional_dependencies": (app_raw["additional_dependencies"] ?? []),
"destination": (app_raw["destination"] ?? "{{project}}-{{app}}"),
"shebang": (app_raw["shebang"] ?? null),
"executable": (app_raw["executable"] ?? false),
}
)
)
),
"custom_rules": (
(data_raw["custom_rules"] ?? []).map(
(custom_rule_raw : Record<string, any>) => (
{
"targets": custom_rule_raw["targets"],
"dependencies": custom_rule_raw["dependencies"],
"actions": custom_rule_raw["actions"],
"info": (custom_rule_raw["info"] ?? null),
}
)
)
),
};
switch (args.action) {
default: {
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": {
const directory : string = lib_plankton.string.coin(
"{{directory}}/plankton",
{
"directory": data.directories.lib,
}
);
/**
* @todo do not use "execute" for that
*/
await execute(
lib_plankton.string.coin(
"mkdir -p {{directory}}",
{
"directory": directory,
}
)
);
await execute(
lib_plankton.string.coin(
"ptk bundle {{target}}} {{modules}}",
{
"target": data.target,
"modules": lib.data.modules.join(" "),
}
),
{
"working_directory": directory,
}
);
break;
}
case "node": {
/**
* @todo
*/
throw (new Error("unhanled lib kind: " + "node"));
break;
}
default: {
throw (new Error("unhanled lib kind: " + lib["kind"]));
break;
}
case "fetch":
{
// plankton
{
if (data.libs.plankton === null)
{
// do nothing
}
else
{
const directory : string = lib_plankton.string.coin(
"{{directory}}/plankton",
{
"directory": data.directories.lib,
}
);
/**
* @todo do not use "execute" for that
*/
await execute(
_ivaldi.command_mkdir(directory),
);
await execute(
_ivaldi.command_ptk_bundle(data.target, data.libs.plankton),
{
"working_directory": directory,
}
);
}
}
// node
{
if (data.libs.node === null)
{
// do nothing
}
else
{
const directory : string = lib_plankton.string.coin(
"{{directory}}/node",
{
"directory": data.directories.lib,
}
);
/**
* @todo do not use "execute" for that
*/
await execute(
_ivaldi.command_mkdir(directory)
);
await execute(
_ivaldi.command_npm_install(data.libs.node),
{
"working_directory": directory,
}
);
}
}
return Promise.resolve<void>(undefined);
break;
}
case "clear": {
case "clear":
{
const commands : Array<string> = [
lib_plankton.string.coin(
"rm -r -f {{directory}}",
{
"directory": data.directories.temp,
}
),
lib_plankton.string.coin(
"rm -r -f {{directory}}",
{
"directory": data.directories.build,
}
),
_ivaldi.command_delete(data.directories.temp),
_ivaldi.command_delete(data.directories.build),
];
/**
* @todo do not use "execute" for that
*/
for (const command of commands) {
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"));
case "build":
{
const has_plankton : boolean = ((data.libs.plankton !== null) && (data.libs.plankton.length > 0));
const path_plankton_declaration : string = lib_plankton.string.coin(
"{{directory_libs}}/plankton/plankton.d.ts",
{
@ -332,165 +385,326 @@ namespace _ivaldi
"directory_libs": data.directories.lib,
}
);
const paths_sources : Array<string> = (
data.sources
.map(
source => lib_plankton.string.coin(
"{{directory}}/{{name}}",
{
"directory": data.directories.source,
"name": source,
}
)
)
);
const path_unlinked : string = lib_plankton.string.coin(
"{{directory_temp}}/{{name}}-unlinked.js",
{
"directory_temp": data.directories.temp,
"name": data.name,
}
);
const path_head : string = lib_plankton.string.coin(
"{{directory_temp}}/head.js",
const path_core_declaration : string = lib_plankton.string.coin(
"{{directory_temp}}/core-unlinked.d.ts",
{
"directory_temp": data.directories.temp,
}
);
const path_result : string = lib_plankton.string.coin(
"{{directory_build}}/{{name}}",
const path_core_implementation : string = lib_plankton.string.coin(
"{{directory_temp}}/core-unlinked.js",
{
"directory_build": data.directories.build,
"name": data.name,
"directory_temp": data.directories.temp,
}
);
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": [
"rules": (
(new Array<Array<lib_plankton.gnu_make.type_rule>>)
// 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 …",
.concat(
[
{
"kind": "meta",
"data": {
"phony": true,
"name": "_root",
"dependencies": data.apps.map(app => get_app_facade_name(app)),
}
},
]
)
// node modules
.concat(
[
{
"kind": "concrete",
"data": {
"targets": [
"node_modules"
],
"dependencies": [],
"actions": [
_ivaldi.command_log("placing node modules …"),
_ivaldi.command_mkdir(
lib_plankton.string.coin("{{directory}}/node_modules", {"directory": data.directories.build})
),
_ivaldi.command_copy(
lib_plankton.string.coin("{{directory}}/node/node_modules/*", {"directory": data.directories.lib}),
lib_plankton.string.coin("{{directory}}/node_modules/", {"directory": data.directories.build})
)
]
}
},
]
)
// custom rules
.concat(
[
data.custom_rules
.map(
data_custom_rule => ({
"kind": "concrete",
"data": {
"targets": (
data_custom_rule.targets
.map(
target => lib_plankton.string.coin(
target,
{
"dir_build": data.directories.build,
}
)
)
),
"dependencies": (
data_custom_rule.dependencies.map(
dependency => lib_plankton.string.coin(
dependency,
{
"dir_source": data.directories.source,
"dir_misc": data.directories.misc,
}
),
)
),
"actions": (
(new Array<string>())
.concat(
(data_custom_rule.info === null)
?
[]
:
[
_ivaldi.command_log(
lib_plankton.string.coin(
"{{info}} …",
{
"info": data_custom_rule.info,
}
),
)
]
)
.concat(
/**
* @todo coin?
*/
data_custom_rule.actions
)
),
}
),
// mkdir
lib_plankton.string.coin(
"mkdir -p $(dir $@)",
{
})
)
]
)
// core
.concat(
[
[
{
"kind": "concrete",
"data": {
"targets": [
path_core_declaration,
path_core_implementation,
],
"dependencies": (
(new Array<string>())
.concat(has_plankton ? [path_plankton_declaration] : [])
.concat(
data.core.map(
path => lib_plankton.string.coin(
"{{directory}}/{{path}}",
{
"directory": data.directories.source,
"path": path,
}
)
)
)
),
"actions": [
// log
_ivaldi.command_log("core | compiling …"),
// mkdir
_ivaldi.command_mkdir("$(dir $@)"),
// command
_ivaldi.command_tsc(
["$^"],
path_core_implementation,
{
"libs": ["es2020"],
"declaration": true,
}
),
]
}
),
// command
lib_plankton.string.coin(
"{{command}} $^ --lib es2020,dom --target es6 --outFile $@",
{
"command": data.commands.typescript_compile,
}
),
],
}
},
// 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}}'",
]
)
// apps
.concat(
data.apps
.map(
app => {
const paths_sources : Array<string> = (
app.sources
.map(
source => lib_plankton.string.coin(
"{{directory}}/{{name}}",
{
"directory": data.directories.source,
"name": source,
}
)
)
);
const path_unlinked : string = lib_plankton.string.coin(
"{{directory_temp}}/app-{{name}}-unlinked.js",
{
"message": "linking …",
"directory_temp": data.directories.temp,
"name": app.name,
}
),
// mkdir
lib_plankton.string.coin(
"mkdir -p $(dir $@)",
);
/*
const path_head : string = lib_plankton.string.coin(
"{{directory_temp}}/head.js",
{
"directory_temp": data.directories.temp,
}
),
// command
lib_plankton.string.coin(
"cat $^ > $@",
);
*/
const path_result : string = lib_plankton.string.coin(
"{{directory_build}}/{{destination}}",
{
"directory_build": data.directories.build,
"destination": lib_plankton.string.coin(
app.destination,
{
"project": data.name,
"app": app.name,
}
),
}
),
// make executable
lib_plankton.string.coin(
"chmod +x {{path}}",
);
return [
// compile
{
"path": path_result,
}
),
],
},
},
],
"kind": "concrete",
"data": {
"targets": [
path_unlinked,
],
"dependencies": (
(new Array<string>())
.concat(has_plankton ? [path_plankton_declaration] : [])
.concat([path_core_declaration])
.concat(paths_sources)
),
"actions": [
_ivaldi.command_log(lib_plankton.string.coin("app:{{name}} | compiling …", {"name": app.name})),
_ivaldi.command_mkdir("$(dir $@)"),
_ivaldi.command_tsc(
["$^"],
"$@",
{
/**
* @todo var
*/
"libs": ["es2020"],
}
),
]
}
},
// link
{
"kind": "concrete",
"data": {
"targets": [
path_result,
],
"dependencies": (
(new Array<string>())
// .concat([path_head])
.concat(has_plankton ? [path_plankton_implementation] : [])
.concat([path_unlinked])
),
"actions": (
(new Array<string>())
.concat(
[
_ivaldi.command_log(lib_plankton.string.coin("app:{{name}} | linking …", {"name": app.name})),
_ivaldi.command_mkdir("$(dir $@)"),
lib_plankton.string.coin("echo '{{head}}' > {{path}}", {"head": (app.shebang ?? ""), "path": path_result}),
_ivaldi.command_cat(["$^"], "$@", {"append": true}),
]
)
// make executable
.concat(
(
app.executable
?
[
/**
* @todo _ivaldi.command_
*/
"chmod +x $@",
]
:
[
]
)
)
)
},
},
// facade
{
"kind": "meta",
"data": {
"phony": true,
"name": get_app_facade_name(app),
"dependencies": (
(new Array<string>())
.concat(
[
/**
* @todo variable
*/
"node_modules",
path_result,
]
)
// additional dependencies
.concat(
app.additional_dependencies
.map(
dependency => lib_plankton.string.coin(
dependency,
{
"dir_source": data.directories.source,
"dir_misc": data.directories.misc,
"dir_temp": data.directories.temp,
"dir_build": data.directories.build,
}
)
)
)
),
},
},
];
}
)
)
.reduce(
(x, y) => x.concat(y),
[]
)
),
};
const makefile : string = lib_plankton.gnu_make.render_sheet(
gnu_make_sheet,
@ -522,7 +736,8 @@ namespace _ivaldi
return Promise.resolve<void>(undefined);
break;
}
case "deploy": {
case "deploy":
{
return Promise.reject<void>(new Error("not yet implemented"));
break;
}

View file

@ -37,6 +37,18 @@ namespace _ivaldi
};
}
);
/**
*/
export type type_app = {
name : string;
sources : Array<string>;
additional_dependencies : Array<string>;
destination : string;
shebang : (null | string);
executable : boolean;
};
/**
@ -44,12 +56,11 @@ namespace _ivaldi
export type type_data = {
version : string;
name : string;
libs : Array<type_lib>;
sources : Array<string>;
target : ("node" | "web");
directories : {
lib : string;
source : string;
misc : string;
temp : string;
build : string;
makefile : string;
@ -57,6 +68,23 @@ namespace _ivaldi
commands : {
typescript_compile : string;
};
libs : {
plankton : (null | Array<string>);
node : (null | Array<string>);
};
core : Array<string>;
apps : Array<
type_app
>;
custom_rules: Array<
{
targets : Array<string>;
dependencies : Array<string>;
actions : Array<string>;
info : (null | string);
}
>;
};
}

View file

@ -17,6 +17,7 @@ _default: _root
temp/ivaldi-unlinked.js: \
libs/plankton/plankton.d.ts \
source/types.ts \
source/helpers.ts \
source/main.ts
@ ${cmd_log} "compiling …"
@ ${cmd_mkdir} temp