@microlight/core 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/bin/microlight-core.js +70 -0
- package/dist/scripts/generate-folder-index.js +120 -0
- package/dist/scripts/generate-task-imports.js +64 -0
- package/dist/scripts/generate-task-index.js +61 -0
- package/dist/scripts/prepareFolders.js +119 -0
- package/dist/scripts/prepareServer.js +34 -0
- package/dist/scripts/prepareTasks.js +114 -0
- package/dist/server/app/api/tasks/[slug]/route.js +54 -0
- package/dist/server/app/layout.js +41 -0
- package/dist/server/app/library/[[...f_path]]/ViewFolder.js +113 -0
- package/dist/server/app/library/[[...f_path]]/page.js +42 -0
- package/dist/server/app/page.js +4 -0
- package/dist/server/app/tasks/[slug]/ViewTask.js +252 -0
- package/dist/server/app/tasks/[slug]/action.js +44 -0
- package/dist/server/app/tasks/[slug]/page.js +33 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/ViewRun.js +230 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/DropdownActions.js +46 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/action.js +35 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/page.js +43 -0
- package/dist/server/components/Icon.js +22 -0
- package/dist/server/components/Link.js +52 -0
- package/dist/server/components/MLInput.js +29 -0
- package/dist/server/components/Navbar/Navbar.js +38 -0
- package/dist/server/components/Navbar/NavbarContainer.js +26 -0
- package/dist/server/components/PageHeader.js +87 -0
- package/dist/server/components/StatusChip.js +11 -0
- package/dist/server/components/Test.js +5 -0
- package/dist/server/components/TopLoader.js +8 -0
- package/dist/server/database/microlight/index.js +52 -0
- package/dist/server/database/microlight/tables/Logs.model.js +34 -0
- package/dist/server/database/microlight/tables/Runs.model.js +61 -0
- package/dist/server/instrumentation.js +16 -0
- package/dist/server/lib/executeRun.js +80 -0
- package/dist/server/lib/generateDisplayFunctions.js +89 -0
- package/dist/server/lib/getAllTasks.js +32 -0
- package/dist/server/lib/getTaskDetails.js +17 -0
- package/dist/server/lib/loadSchedules.js +77 -0
- package/dist/server/tasks/1.intro/hello_world2.task.js +21 -0
- package/dist/server/tasks/1.intro/microlight.folder.js +5 -0
- package/dist/server/tasks/1.intro/ml.task.js +31 -0
- package/dist/server/tasks/1.intro/scheduled.task.js +18 -0
- package/dist/server/tasks/1.intro/takes_time.task.js +28 -0
- package/dist/server/tasks/1.intro/test/microlight.folder.js +5 -0
- package/dist/server/tasks/1.intro/test/takes_time2.task.js +28 -0
- package/dist/server/tasks/index.js +33 -0
- package/dist/server/tasks/microlight.folder.js +5 -0
- package/index.js +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
- [Docs](/docs/docs.md)
|
|
4
|
+
- [Change Log](/docs/changelog.md)
|
|
5
|
+
|
|
6
|
+
## About
|
|
7
|
+
Microlight is a simple task(cron and adhoc) runner. The idea is this, there are tasks that needs to be done. For developers its easy to write a small script to execute the task - e.g. restart AWS redis. But to ask marketing team/sales team to restart redis by running a script is not practical. At the same time, as a small team, the devops guy getting called by the sales team to restart redis is also not practical.
|
|
8
|
+
|
|
9
|
+
This is a solution for small teams. This enables tech team to offload operational tasks to the actual team who needs the task to be executed. This also allows the stack to be light weight and tech teams can write simple scripts to do this.
|
|
10
|
+
|
|
11
|
+
Example usecases,
|
|
12
|
+
- restart redis/some server when overloaded.
|
|
13
|
+
- get fresh data . e.g. customer support person wants to know if customer has made a payment. Lets assume your system is not realtime and the CS person wants to trigger a sync.
|
|
14
|
+
- cron jobs
|
|
15
|
+
|
|
16
|
+
Microlight is designed with the following principles in mind
|
|
17
|
+
- Microlight is designed for light work loads such as
|
|
18
|
+
- personal use,
|
|
19
|
+
- small company - less than 10 people
|
|
20
|
+
- simple data pipeline usecases
|
|
21
|
+
- simple automation/devops usecases
|
|
22
|
+
- Running tasks should feel simple and easy
|
|
23
|
+
- Developers gets to write scripts to automate a task.
|
|
24
|
+
- Business team members gets to trigger the job at their convinience using a UI.
|
|
25
|
+
- Webhooks for other subsystems to trigger jobs
|
|
26
|
+
- Setting up and running microlight should be very easy and should have small footprint
|
|
27
|
+
- Single instance by design - no cluster config overhead
|
|
28
|
+
- uses sqlite only - host everything in one single server/container
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Microlight also provides a UI for users to run cron jobs
|
|
32
|
+
|
|
33
|
+
Ways to trigger a job:
|
|
34
|
+
- Trigger jobs from UI
|
|
35
|
+
- Trigger jobs via webhooks
|
|
36
|
+
- Trigger jos from cronjob
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Microlight can do
|
|
40
|
+
- Execution logic has to be written with code (will not no support no code ever)
|
|
41
|
+
- Tasks can be executed by non techincal people from a GUI
|
|
42
|
+
- Tasks can be scheduled
|
|
43
|
+
- Show console logs and execution output
|
|
44
|
+
|
|
45
|
+
thats it
|
|
46
|
+
|
|
47
|
+
### Microlight cannot and will not do
|
|
48
|
+
- No code support
|
|
49
|
+
- Multiple server or master/worker mode
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Simplicity
|
|
54
|
+
|
|
55
|
+
Microlight is designed as simple solution. This is not designed for massive scale. Intentionally Microlight does not support master/worker node. The Microlight server and the execution of task happens on the same machine. If you are looking for a multi node setup, then we recomment using rundeck. For most small startups, rundeck is an overkill. Scaling microlight can only be done by vertical scaling (increasing the size of the server). Microlight does not support horizontal scaling. Microlight is **NOT** designed to scale horizontally. This is intentional.
|
|
56
|
+
|
|
57
|
+
Microlight also uses sqlite. So you can run all microlight dependancies on one machine/container.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Alternatives to microlight
|
|
61
|
+
|
|
62
|
+
- trigger.dev
|
|
63
|
+
- rundeck.com
|
|
64
|
+
- activepieces.com
|
|
65
|
+
|
|
66
|
+
Microlight is the stripped down, simple version of these meant to be run on a single instance.
|
|
67
|
+
|
|
68
|
+
Compared to these solutions,
|
|
69
|
+
- microlight does **NOT** support horizontal scaling
|
|
70
|
+
- microlight does **NOT** have a hosted solution
|
|
71
|
+
- microlight does **NOT** support drag and drop UI tasks.
|
|
72
|
+
- microlight requires you to write scripts using a text editor and commit the code
|
|
73
|
+
- microlight is suitable for one man tech team
|
|
74
|
+
|
|
75
|
+
You should choose one of the alternatives if any of the above points are deal breakers.
|
|
76
|
+
|
|
77
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
import { packageUp } from "package-up";
|
|
7
|
+
|
|
8
|
+
import { prepareTasks } from "../scripts/prepareTasks.js";
|
|
9
|
+
import { prepareServer } from "../scripts/prepareServer.js";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
const program = new Command();
|
|
14
|
+
|
|
15
|
+
let basePath = await packageUp();
|
|
16
|
+
basePath = basePath.split("/");
|
|
17
|
+
basePath.pop();
|
|
18
|
+
const tasksDir = path.join("/", ...basePath, "src", "tasks");
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name("microlight-core")
|
|
22
|
+
.description("Microlight core utilities")
|
|
23
|
+
.version("1.0.0");
|
|
24
|
+
|
|
25
|
+
// Define the `prepare` command
|
|
26
|
+
const prepareCommand = program
|
|
27
|
+
.command("prepare")
|
|
28
|
+
.description("Prepare the environment");
|
|
29
|
+
// .action(() => {
|
|
30
|
+
// console.log("Preparing environment...");
|
|
31
|
+
// });
|
|
32
|
+
|
|
33
|
+
prepareCommand
|
|
34
|
+
.command("all")
|
|
35
|
+
.description("Perform all prepare commands")
|
|
36
|
+
.action(async() => {
|
|
37
|
+
await prepareTasks();
|
|
38
|
+
console.log('\n');
|
|
39
|
+
const { prepareFolders } = await import("../scripts/prepareFolders.js");
|
|
40
|
+
await prepareFolders();
|
|
41
|
+
|
|
42
|
+
console.log('\n');
|
|
43
|
+
await prepareServer();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
prepareCommand
|
|
47
|
+
.command("tasks")
|
|
48
|
+
.description("Index tasks and create an importer")
|
|
49
|
+
.action(async () => {
|
|
50
|
+
await prepareTasks();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
prepareCommand
|
|
54
|
+
.command("folders")
|
|
55
|
+
.description("Index folders")
|
|
56
|
+
.action(async () => {
|
|
57
|
+
const { prepareFolders } = await import("../scripts/prepareFolders.js");
|
|
58
|
+
await prepareFolders();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
prepareCommand
|
|
63
|
+
.command("server")
|
|
64
|
+
.description("Setup the server")
|
|
65
|
+
.action(async () => {
|
|
66
|
+
prepareServer();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Parse CLI arguments
|
|
70
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
// import taskMap from '@/tasks/tasks';
|
|
6
|
+
|
|
7
|
+
// Ensure we're looking in the right directory relative to the script
|
|
8
|
+
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
9
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'folderMap.js');
|
|
10
|
+
const taskMapFile = path.resolve(process.cwd(), '.microlight', 'taskMap.js');
|
|
11
|
+
const taskMap = (await import(taskMapFile))?.default;
|
|
12
|
+
let taskMapByFileName = {};
|
|
13
|
+
Object.keys(taskMap).forEach(function (slug) {
|
|
14
|
+
const task = taskMap[slug];
|
|
15
|
+
taskMapByFileName[task.__folder + '/' + task.__file_name] = task;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// console.log(taskMapByFileName);
|
|
19
|
+
|
|
20
|
+
// Create tasks directory if it doesn't exist
|
|
21
|
+
if (!fs.existsSync(tasksDir)) {
|
|
22
|
+
fs.mkdirSync(tasksDir, {
|
|
23
|
+
recursive: true
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Find all folder files
|
|
28
|
+
const folderPatterns = ['__ml.js', 'ml.js', 'ml.folder.js', '**/microlight.folder.js'];
|
|
29
|
+
const folderFiles = glob.sync(folderPatterns, {
|
|
30
|
+
cwd: tasksDir,
|
|
31
|
+
absolute: false
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// console.log(folderFiles);
|
|
35
|
+
|
|
36
|
+
// Import all task files dynamically
|
|
37
|
+
const folders = await Promise.all(folderFiles.map(async file => {
|
|
38
|
+
const filePath = path.resolve(tasksDir, file);
|
|
39
|
+
let folderName = file.split('/');
|
|
40
|
+
folderName.pop();
|
|
41
|
+
folderName = folderName.join('/');
|
|
42
|
+
console.log(folderName);
|
|
43
|
+
let folder = (await import(filePath))?.default;
|
|
44
|
+
// console.log(folder.default);
|
|
45
|
+
// const taskName = path.basename(filePath, '.task.js');
|
|
46
|
+
return [folderName, folder];
|
|
47
|
+
// return [task?.default?.slug, {...task?.default,...{file_name:taskName}}];
|
|
48
|
+
}));
|
|
49
|
+
// console.log(folders);
|
|
50
|
+
|
|
51
|
+
// Convert array of entries to an object
|
|
52
|
+
const folderMap = Object.fromEntries(folders);
|
|
53
|
+
// console.log(folderMap);
|
|
54
|
+
|
|
55
|
+
Object.keys(folderMap).map(folderName => {
|
|
56
|
+
folderMap[folderName].contents = [];
|
|
57
|
+
const contentList = fs.readdirSync(path.resolve(tasksDir, folderName));
|
|
58
|
+
// console.log(contentList);
|
|
59
|
+
for (const filename of contentList) {
|
|
60
|
+
const file = path.resolve(tasksDir, folderName, filename);
|
|
61
|
+
// const file = project_folder+`${dir}/${filename}`
|
|
62
|
+
// console.log(file);
|
|
63
|
+
if (fs.statSync(file).isDirectory()) {
|
|
64
|
+
// console.log('\n\n\n====');
|
|
65
|
+
// console.log(file);
|
|
66
|
+
const subFolderName = file.split('/src/tasks/')[1];
|
|
67
|
+
// console.log(subFolderName);
|
|
68
|
+
if (folderMap[subFolderName]) {
|
|
69
|
+
folderMap[folderName].contents.push({
|
|
70
|
+
type: 'folder',
|
|
71
|
+
slug: subFolderName,
|
|
72
|
+
...folderMap[subFolderName]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
if (filename.indexOf('.task.js') > -1) {
|
|
77
|
+
// console.log('\n\n\n====');
|
|
78
|
+
// console.log(filename);
|
|
79
|
+
const taskName = path.basename(filename, '.task.js');
|
|
80
|
+
// console.log(taskMapByFileName[folderName+'/'+filename]);
|
|
81
|
+
let item = {
|
|
82
|
+
type: 'task',
|
|
83
|
+
slug: taskName,
|
|
84
|
+
...taskMapByFileName[folderName + '/' + filename]
|
|
85
|
+
};
|
|
86
|
+
folderMap[folderName].contents.push(item);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// console.log(filename);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// folderMap.map(folder=>{
|
|
93
|
+
|
|
94
|
+
//
|
|
95
|
+
// folder.content = [
|
|
96
|
+
// {
|
|
97
|
+
// type:'task',
|
|
98
|
+
// slug:'takes_time'
|
|
99
|
+
// },
|
|
100
|
+
// {
|
|
101
|
+
// type:'folder',
|
|
102
|
+
// slug:'1.intro'
|
|
103
|
+
// }
|
|
104
|
+
// ]
|
|
105
|
+
// })
|
|
106
|
+
|
|
107
|
+
// Write the generated code to a file
|
|
108
|
+
const outputCode = 'export default ' + JSON.stringify(folderMap, null, 2);
|
|
109
|
+
|
|
110
|
+
// Create the output directory if it doesn't exist
|
|
111
|
+
const outputDir = path.dirname(outputFile);
|
|
112
|
+
if (!fs.existsSync(outputDir)) {
|
|
113
|
+
fs.mkdirSync(outputDir, {
|
|
114
|
+
recursive: true
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Write the file
|
|
119
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
120
|
+
console.log(`Generated folder index at: ${outputFile}`);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
// import { join, relative, dirname } from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
// Ensure we're looking in the right directory relative to the script
|
|
8
|
+
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
9
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'importTaskModule.js');
|
|
10
|
+
|
|
11
|
+
// Create tasks directory if it doesn't exist
|
|
12
|
+
if (!fs.existsSync(tasksDir)) {
|
|
13
|
+
fs.mkdirSync(tasksDir, {
|
|
14
|
+
recursive: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Find all task files
|
|
19
|
+
const taskFiles = glob.sync('**/*.task.js', {
|
|
20
|
+
cwd: tasksDir,
|
|
21
|
+
absolute: false
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Generate the import switch statement
|
|
25
|
+
const generateImportSwitch = async tasks => {
|
|
26
|
+
let cases = await Promise.all(tasks.map(async file => {
|
|
27
|
+
const filePath = path.resolve(tasksDir, file);
|
|
28
|
+
const task = await import(filePath);
|
|
29
|
+
// console.log(task);
|
|
30
|
+
const taskName = path.basename(file, '.task.js');
|
|
31
|
+
const taskSlug = task?.default?.slug;
|
|
32
|
+
// const taskName = path.basename(filePath, '.task.js');
|
|
33
|
+
// return [taskName, task.default];
|
|
34
|
+
// return [task?.default?.slug, {...task?.default,...{file_name:taskName}}];
|
|
35
|
+
|
|
36
|
+
return ` case "${taskSlug || taskName}":\n return await import("../src/tasks/${file}");`;
|
|
37
|
+
}));
|
|
38
|
+
cases = cases.join('\n');
|
|
39
|
+
// console.log(cases);
|
|
40
|
+
|
|
41
|
+
return `
|
|
42
|
+
export const importTaskModule = async (task_slug) => {
|
|
43
|
+
switch (task_slug) {
|
|
44
|
+
${cases}
|
|
45
|
+
default:
|
|
46
|
+
return { default: { name: "Library", description: "All executable tasks" } };
|
|
47
|
+
}
|
|
48
|
+
};`.trim();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Write the generated code to a file
|
|
52
|
+
const outputCode = await generateImportSwitch(taskFiles);
|
|
53
|
+
|
|
54
|
+
// Create the output directory if it doesn't exist
|
|
55
|
+
const outputDir = path.dirname(outputFile);
|
|
56
|
+
if (!fs.existsSync(outputDir)) {
|
|
57
|
+
fs.mkdirSync(outputDir, {
|
|
58
|
+
recursive: true
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Write the file
|
|
63
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
64
|
+
console.log(`Generated import switch at: ${outputFile}`);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { glob } from 'glob';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
// import { join, relative, dirname } from 'path';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
// Ensure we're looking in the right directory relative to the script
|
|
8
|
+
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
9
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'taskMap.js');
|
|
10
|
+
|
|
11
|
+
// Create tasks directory if it doesn't exist
|
|
12
|
+
if (!fs.existsSync(tasksDir)) {
|
|
13
|
+
fs.mkdirSync(tasksDir, {
|
|
14
|
+
recursive: true
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Find all task files
|
|
19
|
+
const taskFiles = glob.sync('**/*.task.js', {
|
|
20
|
+
cwd: tasksDir,
|
|
21
|
+
absolute: false
|
|
22
|
+
});
|
|
23
|
+
console.log(taskFiles);
|
|
24
|
+
|
|
25
|
+
// Import all task files dynamically
|
|
26
|
+
const tasks = await Promise.all(taskFiles.map(async file => {
|
|
27
|
+
const filePath = path.resolve(tasksDir, file);
|
|
28
|
+
const task = await import(filePath);
|
|
29
|
+
const taskName = path.basename(filePath);
|
|
30
|
+
let folderName = file.split('/');
|
|
31
|
+
folderName.pop();
|
|
32
|
+
folderName = folderName.join('/');
|
|
33
|
+
// return [taskName, task.default];
|
|
34
|
+
return [task?.default?.slug, {
|
|
35
|
+
...task?.default,
|
|
36
|
+
...{
|
|
37
|
+
__file_name: taskName,
|
|
38
|
+
__folder: folderName
|
|
39
|
+
}
|
|
40
|
+
}];
|
|
41
|
+
}));
|
|
42
|
+
// console.log(tasks);
|
|
43
|
+
|
|
44
|
+
// Convert array of entries to an object
|
|
45
|
+
const taskMap = Object.fromEntries(tasks);
|
|
46
|
+
console.log(taskMap);
|
|
47
|
+
|
|
48
|
+
// Write the generated code to a file
|
|
49
|
+
const outputCode = 'export default ' + JSON.stringify(taskMap, null, 2);
|
|
50
|
+
|
|
51
|
+
// Create the output directory if it doesn't exist
|
|
52
|
+
const outputDir = path.dirname(outputFile);
|
|
53
|
+
if (!fs.existsSync(outputDir)) {
|
|
54
|
+
fs.mkdirSync(outputDir, {
|
|
55
|
+
recursive: true
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Write the file
|
|
60
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
61
|
+
console.log(`Generated tasks index at: ${outputFile}`);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
// import { join, relative, dirname } from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
6
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'folderMap.js');
|
|
7
|
+
const taskMapFile = path.resolve(process.cwd(), '.microlight', 'taskMap.js');
|
|
8
|
+
const taskMap = (await import(taskMapFile))?.default;
|
|
9
|
+
let taskMapByFileName = {};
|
|
10
|
+
Object.keys(taskMap).forEach(function (slug) {
|
|
11
|
+
const task = taskMap[slug];
|
|
12
|
+
taskMapByFileName[task.__folder + '/' + task.__file_name] = task;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Create tasks directory if it doesn't exist
|
|
16
|
+
if (!fs.existsSync(tasksDir)) {
|
|
17
|
+
fs.mkdirSync(tasksDir, {
|
|
18
|
+
recursive: true
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const getFolderFiles = function () {
|
|
22
|
+
// Find all folder files
|
|
23
|
+
const folderPatterns = ['__ml.js', 'ml.js', 'ml.folder.js', '**/microlight.folder.js'];
|
|
24
|
+
const folderFiles = glob.sync(folderPatterns, {
|
|
25
|
+
cwd: tasksDir,
|
|
26
|
+
absolute: false
|
|
27
|
+
});
|
|
28
|
+
return folderFiles;
|
|
29
|
+
};
|
|
30
|
+
export async function prepareFolders() {
|
|
31
|
+
console.log('Preparing folders now');
|
|
32
|
+
const folderFiles = getFolderFiles();
|
|
33
|
+
|
|
34
|
+
// Import all task files dynamically
|
|
35
|
+
const folders = await Promise.all(folderFiles.map(async file => {
|
|
36
|
+
const filePath = path.resolve(tasksDir, file);
|
|
37
|
+
let folderName = file.split('/');
|
|
38
|
+
folderName.pop();
|
|
39
|
+
folderName = folderName.join('/');
|
|
40
|
+
// console.log(folderName);
|
|
41
|
+
let folder = (await import(filePath))?.default;
|
|
42
|
+
// console.log(folder.default);
|
|
43
|
+
// const taskName = path.basename(filePath, '.task.js');
|
|
44
|
+
return [folderName, folder];
|
|
45
|
+
// return [task?.default?.slug, {...task?.default,...{file_name:taskName}}];
|
|
46
|
+
}));
|
|
47
|
+
// console.log(folders);
|
|
48
|
+
|
|
49
|
+
// Convert array of entries to an object
|
|
50
|
+
const folderMap = Object.fromEntries(folders);
|
|
51
|
+
// console.log(folderMap);
|
|
52
|
+
|
|
53
|
+
Object.keys(folderMap).map(folderName => {
|
|
54
|
+
folderMap[folderName].contents = [];
|
|
55
|
+
const contentList = fs.readdirSync(path.resolve(tasksDir, folderName));
|
|
56
|
+
// console.log(contentList);
|
|
57
|
+
for (const filename of contentList) {
|
|
58
|
+
const file = path.resolve(tasksDir, folderName, filename);
|
|
59
|
+
// const file = project_folder+`${dir}/${filename}`
|
|
60
|
+
// console.log(file);
|
|
61
|
+
if (fs.statSync(file).isDirectory()) {
|
|
62
|
+
// console.log('\n\n\n====');
|
|
63
|
+
// console.log(file);
|
|
64
|
+
const subFolderName = file.split('/src/tasks/')[1];
|
|
65
|
+
// console.log(subFolderName);
|
|
66
|
+
if (folderMap[subFolderName]) {
|
|
67
|
+
folderMap[folderName].contents.push({
|
|
68
|
+
type: 'folder',
|
|
69
|
+
slug: subFolderName,
|
|
70
|
+
...folderMap[subFolderName]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
if (filename.indexOf('.task.js') > -1) {
|
|
75
|
+
// console.log('\n\n\n====');
|
|
76
|
+
// console.log(filename);
|
|
77
|
+
const taskName = path.basename(filename, '.task.js');
|
|
78
|
+
// console.log(taskMapByFileName[folderName+'/'+filename]);
|
|
79
|
+
let item = {
|
|
80
|
+
type: 'task',
|
|
81
|
+
slug: taskName,
|
|
82
|
+
...taskMapByFileName[folderName + '/' + filename]
|
|
83
|
+
};
|
|
84
|
+
folderMap[folderName].contents.push(item);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// console.log(filename);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
// folderMap.map(folder=>{
|
|
91
|
+
|
|
92
|
+
//
|
|
93
|
+
// folder.content = [
|
|
94
|
+
// {
|
|
95
|
+
// type:'task',
|
|
96
|
+
// slug:'takes_time'
|
|
97
|
+
// },
|
|
98
|
+
// {
|
|
99
|
+
// type:'folder',
|
|
100
|
+
// slug:'1.intro'
|
|
101
|
+
// }
|
|
102
|
+
// ]
|
|
103
|
+
// })
|
|
104
|
+
|
|
105
|
+
// Write the generated code to a file
|
|
106
|
+
const outputCode = 'export default ' + JSON.stringify(folderMap, null, 2);
|
|
107
|
+
|
|
108
|
+
// Create the output directory if it doesn't exist
|
|
109
|
+
const outputDir = path.dirname(outputFile);
|
|
110
|
+
if (!fs.existsSync(outputDir)) {
|
|
111
|
+
fs.mkdirSync(outputDir, {
|
|
112
|
+
recursive: true
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Write the file
|
|
117
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
118
|
+
console.log(`Generated folder index at: ${outputFile}`);
|
|
119
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import { copySync } from "fs-extra";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
const require = createRequire(import.meta.url); // Required for resolving CommonJS modules
|
|
6
|
+
|
|
7
|
+
export async function prepareServer() {
|
|
8
|
+
console.log('preparing the server');
|
|
9
|
+
const coreModulePath = require.resolve("@microlight/core/package.json"); // Find package.json
|
|
10
|
+
const coreRoot = path.dirname(coreModulePath); // Get package root
|
|
11
|
+
console.log(coreModulePath);
|
|
12
|
+
console.log(coreRoot);
|
|
13
|
+
const serverSrcDir = path.join(coreRoot, "dist", "server"); // Resolve /dist/server
|
|
14
|
+
console.log(serverSrcDir);
|
|
15
|
+
|
|
16
|
+
// Destination path
|
|
17
|
+
const processDir = process.cwd();
|
|
18
|
+
const serverDestDir = path.join(processDir, ".microlight", "server");
|
|
19
|
+
console.log("serverSrcDir:", serverSrcDir);
|
|
20
|
+
console.log("serverDestDir:", serverDestDir);
|
|
21
|
+
|
|
22
|
+
// Ensure destination exists
|
|
23
|
+
if (!fs.existsSync(serverDestDir)) {
|
|
24
|
+
fs.mkdirSync(serverDestDir, {
|
|
25
|
+
recursive: true
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Copy files
|
|
30
|
+
copySync(serverSrcDir, serverDestDir, {
|
|
31
|
+
overwrite: true
|
|
32
|
+
});
|
|
33
|
+
console.log("Server files copied successfully!");
|
|
34
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { glob } from 'glob';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
// import { join, relative, dirname } from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
const tasksDir = path.join(process.cwd(), 'src', 'tasks');
|
|
6
|
+
|
|
7
|
+
// Create tasks directory if it doesn't exist
|
|
8
|
+
if (!fs.existsSync(tasksDir)) {
|
|
9
|
+
fs.mkdirSync(tasksDir, {
|
|
10
|
+
recursive: true
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const getTaskFiles = function () {
|
|
14
|
+
// Find all task files
|
|
15
|
+
const taskFiles = glob.sync('**/*.task.js', {
|
|
16
|
+
cwd: tasksDir,
|
|
17
|
+
absolute: false
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// console.log(taskFiles);
|
|
21
|
+
return taskFiles;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Generate the import switch statement
|
|
25
|
+
const generateImportSwitch = async tasks => {
|
|
26
|
+
let cases = await Promise.all(tasks.map(async file => {
|
|
27
|
+
const filePath = path.resolve(tasksDir, file);
|
|
28
|
+
const task = await import(filePath);
|
|
29
|
+
// console.log(task);
|
|
30
|
+
const taskName = path.basename(file, '.task.js');
|
|
31
|
+
const taskSlug = task?.default?.slug;
|
|
32
|
+
// const taskName = path.basename(filePath, '.task.js');
|
|
33
|
+
// return [taskName, task.default];
|
|
34
|
+
// return [task?.default?.slug, {...task?.default,...{file_name:taskName}}];
|
|
35
|
+
|
|
36
|
+
return ` case "${taskSlug || taskName}":\n return await import("../src/tasks/${file}");`;
|
|
37
|
+
}));
|
|
38
|
+
cases = cases.join('\n');
|
|
39
|
+
// console.log(cases);
|
|
40
|
+
|
|
41
|
+
return `
|
|
42
|
+
export const importTaskModule = async (task_slug) => {
|
|
43
|
+
switch (task_slug) {
|
|
44
|
+
${cases}
|
|
45
|
+
default:
|
|
46
|
+
return { default: { name: "Library", description: "All executable tasks" } };
|
|
47
|
+
}
|
|
48
|
+
};`.trim();
|
|
49
|
+
};
|
|
50
|
+
export async function prepareTasks() {
|
|
51
|
+
console.log('Preparing tasks now');
|
|
52
|
+
await prepareTasksIndex();
|
|
53
|
+
await prepareTasksImports();
|
|
54
|
+
}
|
|
55
|
+
export async function prepareTasksIndex() {
|
|
56
|
+
const taskFiles = getTaskFiles();
|
|
57
|
+
|
|
58
|
+
// Import all task files dynamically
|
|
59
|
+
const tasks = await Promise.all(taskFiles.map(async file => {
|
|
60
|
+
const filePath = path.resolve(tasksDir, file);
|
|
61
|
+
const task = await import(filePath);
|
|
62
|
+
const taskName = path.basename(filePath);
|
|
63
|
+
let folderName = file.split('/');
|
|
64
|
+
folderName.pop();
|
|
65
|
+
folderName = folderName.join('/');
|
|
66
|
+
// return [taskName, task.default];
|
|
67
|
+
return [task?.default?.slug, {
|
|
68
|
+
...task?.default,
|
|
69
|
+
...{
|
|
70
|
+
__file_name: taskName,
|
|
71
|
+
__folder: folderName
|
|
72
|
+
}
|
|
73
|
+
}];
|
|
74
|
+
}));
|
|
75
|
+
// console.log(tasks);
|
|
76
|
+
|
|
77
|
+
// Convert array of entries to an object
|
|
78
|
+
const taskMap = Object.fromEntries(tasks);
|
|
79
|
+
|
|
80
|
+
// console.log(taskMap);
|
|
81
|
+
|
|
82
|
+
// Write the generated code to a file
|
|
83
|
+
const outputCode = 'export default ' + JSON.stringify(taskMap, null, 2);
|
|
84
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'taskMap.js');
|
|
85
|
+
// Create the output directory if it doesn't exist
|
|
86
|
+
const outputDir = path.dirname(outputFile);
|
|
87
|
+
if (!fs.existsSync(outputDir)) {
|
|
88
|
+
fs.mkdirSync(outputDir, {
|
|
89
|
+
recursive: true
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Write the file
|
|
94
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
95
|
+
console.log(`Generated tasks index at: ${outputFile}`);
|
|
96
|
+
}
|
|
97
|
+
export async function prepareTasksImports() {
|
|
98
|
+
const taskFiles = getTaskFiles();
|
|
99
|
+
|
|
100
|
+
// Write the generated code to a file
|
|
101
|
+
const outputCode = await generateImportSwitch(taskFiles);
|
|
102
|
+
const outputFile = path.resolve(process.cwd(), '.microlight', 'importTaskModule.js');
|
|
103
|
+
// Create the output directory if it doesn't exist
|
|
104
|
+
const outputDir = path.dirname(outputFile);
|
|
105
|
+
if (!fs.existsSync(outputDir)) {
|
|
106
|
+
fs.mkdirSync(outputDir, {
|
|
107
|
+
recursive: true
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Write the file
|
|
112
|
+
fs.writeFileSync(outputFile, outputCode, 'utf-8');
|
|
113
|
+
console.log(`Generated importTaskModule at: ${outputFile}`);
|
|
114
|
+
}
|