/[sudobot]/branches/6.x/scripts/extensions.js
ViewVC logotype

Annotation of /branches/6.x/scripts/extensions.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 577 - (hide annotations)
Mon Jul 29 18:52:37 2024 UTC (8 months ago) by rakinar2
File MIME type: text/javascript
File size: 8814 byte(s)
chore: add old version archive branches (2.x to 9.x-dev)
1 rakinar2 577 #!/usr/bin/env node
2    
3     /*
4     * This file is part of SudoBot.
5     *
6     * Copyright (C) 2021-2023 OSN Developers.
7     *
8     * SudoBot is free software; you can redistribute it and/or modify it
9     * under the terms of the GNU Affero General Public License as published by
10     * the Free Software Foundation, either version 3 of the License, or
11     * (at your option) any later version.
12     *
13     * SudoBot is distributed in the hope that it will be useful, but
14     * WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU Affero General Public License for more details.
17     *
18     * You should have received a copy of the GNU Affero General Public License
19     * along with SudoBot. If not, see <https://www.gnu.org/licenses/>.
20     */
21    
22     require("module-alias/register");
23     const chalk = require("chalk");
24     const { spawnSync } = require("child_process");
25     const { existsSync, lstatSync, readdirSync, readFileSync, writeFileSync, rmSync } = require("fs");
26     const path = require("path");
27     const { chdir, cwd } = require("process");
28    
29     const extensionsPath = path.resolve(__dirname, "../extensions");
30    
31     function error(...args) {
32     console.error(...args);
33     process.exit(-1);
34     }
35    
36     if (!existsSync(extensionsPath)) {
37     error("You're not using any extension! To get started, create an `extensions` folder in the project root.");
38     }
39    
40     function getRecuriveJavaScriptFiles(dir) {
41     const files = readdirSync(dir);
42     const flat = [];
43    
44     for (const fileName of files) {
45     const file = path.join(dir, fileName);
46     const isDirectory = lstatSync(file).isDirectory();
47    
48     if (isDirectory) {
49     flat.push(...getRecuriveJavaScriptFiles(file));
50     continue;
51     }
52    
53     if (!file.endsWith(".js")) {
54     continue;
55     }
56    
57     flat.push(file);
58     }
59    
60     return flat;
61     }
62    
63     function readMeta(extensionName, extensionDirectory) {
64     const metadataFile = path.join(extensionDirectory, "extension.json");
65    
66     if (!existsSync(metadataFile)) {
67     error(`Extension ${extensionName} does not have a "extension.json" file!`);
68     }
69    
70     const metadata = JSON.parse(readFileSync(metadataFile, { encoding: "utf-8" }));
71     return metadata;
72     }
73    
74     async function writeCacheIndex() {
75     const extensionsOutputArray = [];
76     const meta = [];
77     const extensions = readdirSync(extensionsPath);
78    
79     for await (const extensionName of extensions) {
80     const extensionDirectory = path.resolve(extensionsPath, extensionName);
81     const isDirectory = lstatSync(extensionDirectory).isDirectory();
82    
83     if (!isDirectory) {
84     continue;
85     }
86    
87     console.log("Found extension: ", extensionName);
88    
89     // const metadataFile = path.join(extensionDirectory, "extension.json");
90    
91     // if (!existsSync(metadataFile)) {
92     // error(`Extension ${extensionName} does not have a "extension.json" file!`);
93     // }
94    
95     // const metadata = JSON.parse(readFileSync(metadataFile, { encoding: "utf-8" }));
96     // const {
97     // main_directory = "./build",
98     // commands = `./${main_directory}/commands`,
99     // events = `./${main_directory}/events`,
100     // main = `./${main_directory}/index.js`,
101     // language = "typescript"
102     // } = metadata;
103    
104     const {
105     main_directory = "./build",
106     commands = `./${main_directory}/commands`,
107     events = `./${main_directory}/events`,
108     main = `./${main_directory}/index.js`,
109     language = "typescript"
110     } = readMeta(extensionName, extensionDirectory);
111    
112     const extensionPath = path.join(extensionDirectory, main);
113    
114     const imported = await require(extensionPath);
115     const { default: ExtensionClass } = imported.__esModule ? imported : { default: imported };
116     const extension = new ExtensionClass(this.client);
117     let commandPaths = await extension.commands();
118     let eventPaths = await extension.events();
119    
120     if (commandPaths === null) {
121     const directory = path.join(__dirname, "../extensions", extensionName, commands);
122    
123     if (existsSync(directory)) {
124     commandPaths = getRecuriveJavaScriptFiles(directory);
125     }
126     }
127    
128     if (eventPaths === null) {
129     const directory = path.join(__dirname, "../extensions", extensionName, events);
130    
131     if (existsSync(directory)) {
132     eventPaths = getRecuriveJavaScriptFiles(directory);
133     }
134     }
135    
136     extensionsOutputArray.push({
137     name: extensionName,
138     entry: extensionPath,
139     commands: commandPaths ?? [],
140     events: eventPaths ?? []
141     });
142    
143     meta.push({
144     language
145     });
146     }
147    
148     console.log("Overview of the extensions: ");
149     console.table(
150     extensionsOutputArray.map((e, i) => ({
151     name: e.name,
152     entry: e.entry.replace(path.resolve(__dirname, "../extensions"), "{ROOT}"),
153     commands: e.commands.length,
154     events: e.events.length,
155     language: meta[i].language
156     }))
157     );
158    
159     const indexFile = path.join(__dirname, "../extensions/index.json");
160    
161     writeFileSync(
162     indexFile,
163     JSON.stringify(
164     {
165     extensions: extensionsOutputArray
166     },
167     null,
168     4
169     )
170     );
171    
172     console.log("Wrote cache index file: ", indexFile);
173     console.warn(
174     "Note: If you're getting import errors after caching extensions, please try first by removing or rebuilding them."
175     );
176     }
177    
178     const MAX_CHARS = 7;
179    
180     function actionLog(action, description) {
181     console.log(
182     chalk.green.bold(`${action}${action.length >= MAX_CHARS ? "" : " ".repeat(MAX_CHARS - action.length)} `),
183     description
184     );
185     }
186    
187     function spawnSyncCatchExit(...args) {
188     actionLog("RUN", args[0]);
189     const { status } = spawnSync(...args);
190     if (status !== 0) process.exit(status);
191     }
192    
193     async function buildExtensions() {
194     const startTime = Date.now();
195     const extensions = readdirSync(extensionsPath);
196     const workingDirectory = cwd();
197    
198     for await (const extensionName of extensions) {
199     const extensionDirectory = path.resolve(extensionsPath, extensionName);
200     const isDirectory = lstatSync(extensionDirectory).isDirectory();
201    
202     if (!isDirectory) {
203     continue;
204     }
205    
206     chdir(path.join(extensionsPath, extensionName));
207    
208     actionLog("DEPS", extensionName);
209     spawnSyncCatchExit("npm install -D", {
210     encoding: "utf-8",
211     shell: true,
212     stdio: "inherit"
213     });
214    
215     actionLog("RELINK", extensionName);
216     spawnSyncCatchExit(`npm link --save ${path.relative(cwd(), path.resolve(__dirname, ".."))}`, {
217     encoding: "utf-8",
218     shell: true,
219     stdio: "inherit"
220     });
221    
222     actionLog("BUILD", extensionName);
223     const { build_command } = readMeta(extensionName, extensionDirectory);
224    
225     if (!build_command) {
226     console.log(chalk.cyan.bold("INFO "), "This extension doesn't require building.");
227     continue;
228     }
229    
230     spawnSyncCatchExit(build_command, {
231     encoding: "utf-8",
232     shell: true,
233     stdio: "inherit"
234     });
235     }
236    
237     actionLog("SUCCESS", `in ${((Date.now() - startTime) / 1000).toFixed(2)}s, built ${extensions.length} extensions`);
238     chdir(workingDirectory);
239     }
240    
241     if (process.argv.includes("--clear-cache") || process.argv.includes("--clean") || process.argv.includes("--delcache")) {
242     const indexFile = path.join(__dirname, "../extensions/index.json");
243    
244     if (!existsSync(indexFile)) {
245     error("No cached index file found!");
246     }
247    
248     rmSync(indexFile);
249     console.log("Removed cached index file: ", indexFile);
250     } else if (process.argv.includes("--cache") || process.argv.includes("--index")) {
251     console.time("Finished in");
252     console.log("Creating cache index for all the installed extensions");
253     writeCacheIndex()
254     .then(() => console.timeEnd("Finished in"))
255     .catch(e => {
256     console.log(e);
257     console.timeEnd("Finished in");
258     });
259     } else if (process.argv.includes("--build")) {
260     console.log("Building installed extensions");
261     buildExtensions().catch(e => {
262     console.log(e);
263     process.exit(-1);
264     });
265     } else {
266     console.log("Usage:");
267     console.log(" node extensions.js <options>\n");
268     console.log("Options:");
269     console.log(" --build | Builds all the installed extensions, if needed.");
270     console.log(" --cache | Creates cache indexes for installed extensions, to improve the startup time.");
271     console.log(" --clean | Clears all installed extension cache.");
272     process.exit(-1);
273     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26