@slim-lang/core 1.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 +666 -0
- package/package.json +55 -0
- package/packages/slim/.spm +7 -0
- package/packages/slim/converters/main.slim +106 -0
- package/packages/slim/helpers/array.slim +25 -0
- package/packages/slim/helpers/path.slim +3 -0
- package/packages/slim/helpers/request.slim +102 -0
- package/packages/slim/helpers/string.slim +27 -0
- package/packages/slim/main.slim +42 -0
- package/packages/slim/parse/main.slim +25 -0
- package/packages/slim/server/main.slim +423 -0
- package/packages/slim/time/main.slim +66 -0
- package/packages/slim/types/common.slim +6 -0
- package/packages/slim/types/formats.slim +23 -0
- package/packages/slim/types/hash.slim +6 -0
- package/packages/slim/types/mails.slim +3 -0
- package/packages/slim/types/numerical.slim +9 -0
- package/packages/slim/types/time.slim +3 -0
- package/run-dev-slim.js +133 -0
- package/run-slim.js +20 -0
- package/src/bin/api/github_auth.js +89 -0
- package/src/bin/api/github_get.js +139 -0
- package/src/bin/api/github_req.js +455 -0
- package/src/bin/api/lock.js +37 -0
- package/src/bin/api/spm.js +103 -0
- package/src/bin/api/storage.js +30 -0
- package/src/bin/cli.js +404 -0
- package/src/bin/config.default.json +5 -0
- package/src/bin/helpers.js +147 -0
- package/src/bin/parsers/spm.js +174 -0
- package/src/bin/spm.js +519 -0
- package/src/checker.js +926 -0
- package/src/compile.js +230 -0
- package/src/external/classErrors.js +202 -0
- package/src/external/client.js +38 -0
- package/src/external/core.js +861 -0
- package/src/external/defaults.js +25 -0
- package/src/external/helpers.js +541 -0
- package/src/external/slim-globals.d.ts +65 -0
- package/src/external/types.js +38 -0
- package/src/format.js +81 -0
- package/src/handlers/errorHandler.js +43 -0
- package/src/handlers/parser/components.js +250 -0
- package/src/handlers/parserHandler.js +793 -0
- package/src/jsdoc.js +273 -0
- package/src/lexer.js +174 -0
- package/src/modulePaths.js +74 -0
- package/src/parser.js +818 -0
- package/src/repl.js +32 -0
- package/src/sourcemap.js +0 -0
- package/src/test-runner.js +62 -0
- package/src/transform.js +765 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const API_URL = "https://codemotion.yurba.one/api/spm"
|
|
2
|
+
|
|
3
|
+
export async function publishPackage({ token, name, version, github, description = "" }) {
|
|
4
|
+
const res = await fetch(`${API_URL}/publish`, {
|
|
5
|
+
method: "POST",
|
|
6
|
+
headers: { "Content-Type": "application/json" },
|
|
7
|
+
body: JSON.stringify({
|
|
8
|
+
github_token: token,
|
|
9
|
+
name,
|
|
10
|
+
description,
|
|
11
|
+
version,
|
|
12
|
+
github,
|
|
13
|
+
}),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const data = await res.json();
|
|
17
|
+
|
|
18
|
+
if (!data.success) {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
content: data.result
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
content: data.result
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function getPackage({ name }) {
|
|
32
|
+
const res = await fetch(`${API_URL}/install?name=${name}`, {
|
|
33
|
+
method: "GET",
|
|
34
|
+
headers: { "Content-Type": "application/json" }
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const data = await res.json();
|
|
38
|
+
|
|
39
|
+
if (!data.success) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
content: data.result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
content: data.result
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function updatePackage({ token, name, description, version, github }) {
|
|
53
|
+
const res = await fetch(`${API_URL}/update`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
github_token: token,
|
|
58
|
+
name,
|
|
59
|
+
description,
|
|
60
|
+
version,
|
|
61
|
+
github,
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const data = await res.json();
|
|
66
|
+
|
|
67
|
+
if (!data.success) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
content: data.result
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
content: data.result
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function removePackage({ token, name }) {
|
|
81
|
+
const res = await fetch(`${API_URL}/remove`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: { "Content-Type": "application/json" },
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
github_token: token,
|
|
86
|
+
name
|
|
87
|
+
}),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const data = await res.json();
|
|
91
|
+
|
|
92
|
+
if (!data.success) {
|
|
93
|
+
return {
|
|
94
|
+
success: false,
|
|
95
|
+
content: data.result
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
content: data.result
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import keytar from "keytar";
|
|
2
|
+
|
|
3
|
+
const SERVICE = "slim-package-manager";
|
|
4
|
+
const ACCOUNT = "github";
|
|
5
|
+
|
|
6
|
+
export async function saveGitHubToken(token) {
|
|
7
|
+
if (!token) {
|
|
8
|
+
throw new Error("GitHub token is empty");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
await keytar.setPassword(
|
|
12
|
+
SERVICE,
|
|
13
|
+
ACCOUNT,
|
|
14
|
+
token
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function getGitHubToken() {
|
|
19
|
+
return await keytar.getPassword(
|
|
20
|
+
SERVICE,
|
|
21
|
+
ACCOUNT
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function deleteGitHubToken() {
|
|
26
|
+
return await keytar.deletePassword(
|
|
27
|
+
SERVICE,
|
|
28
|
+
ACCOUNT
|
|
29
|
+
);
|
|
30
|
+
}
|
package/src/bin/cli.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import path from "node:path"
|
|
7
|
+
import fs from "node:fs"
|
|
8
|
+
|
|
9
|
+
import { log, error, parseValue } from "./helpers.js"
|
|
10
|
+
import { runTests } from "../test-runner.js"
|
|
11
|
+
import { formatFile } from "../format.js"
|
|
12
|
+
|
|
13
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
14
|
+
import defaultConfig from "./config.default.json" with { type: "json" };
|
|
15
|
+
|
|
16
|
+
const root = process.cwd()
|
|
17
|
+
const program = new Command();
|
|
18
|
+
const slimConfigPath = path.join(root, "slimconfig.json")
|
|
19
|
+
const packagePath = path.join(root, "package.json")
|
|
20
|
+
const spaceRegex = /\s/gm
|
|
21
|
+
|
|
22
|
+
function slimConfigCheck() {
|
|
23
|
+
packageCheck()
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(slimConfigPath)) {
|
|
26
|
+
error(`The configuration file does not exist, or it is not in the root directory. You can create the configuration file:
|
|
27
|
+
${program.name()} create --config`)
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
function slimConfigRead() {
|
|
34
|
+
slimConfigCheck()
|
|
35
|
+
|
|
36
|
+
return JSON.parse(fs.readFileSync(slimConfigPath, "utf8"));
|
|
37
|
+
}
|
|
38
|
+
// slimserver.json lives next to the entry file named by slimconfig's "main".
|
|
39
|
+
function serverConfigRead() {
|
|
40
|
+
try {
|
|
41
|
+
const config = JSON.parse(fs.readFileSync(slimConfigPath, "utf8"))
|
|
42
|
+
if (!config.main) return {}
|
|
43
|
+
|
|
44
|
+
const dir = path.dirname(path.resolve(config.main))
|
|
45
|
+
return JSON.parse(fs.readFileSync(path.join(dir, "slimserver.json"), "utf8"))
|
|
46
|
+
} catch {
|
|
47
|
+
return {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function packageCheck() {
|
|
51
|
+
if (!fs.existsSync(packagePath)) {
|
|
52
|
+
error(`package.json was not found in the root folder of the directory`)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
program
|
|
60
|
+
.name("slmc")
|
|
61
|
+
.description(`Slim Language CLI (SLMC ${pkg.version})`)
|
|
62
|
+
.version(pkg.version);
|
|
63
|
+
|
|
64
|
+
program
|
|
65
|
+
.command("build")
|
|
66
|
+
.description("Build a Slim project")
|
|
67
|
+
.option("-S, --silent", "Build without log")
|
|
68
|
+
.option("--no-check", "Build without static type checking")
|
|
69
|
+
.action((params) => {
|
|
70
|
+
slimConfigCheck()
|
|
71
|
+
|
|
72
|
+
if(!params.check) process.env.SLIM_NO_CHECK = "1"
|
|
73
|
+
|
|
74
|
+
if(!params.silent) log("Building...")
|
|
75
|
+
execSync("node src/compile.js", { stdio: "inherit" });
|
|
76
|
+
if(!params.silent) log("Ready!")
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
program
|
|
80
|
+
.command("run")
|
|
81
|
+
.description("Build and run a Slim project")
|
|
82
|
+
.option("-S, --silent", "Run without log")
|
|
83
|
+
.option("-D, --dev", "Run with dev features enabled (SLIM_DEV)")
|
|
84
|
+
.option("-R, --release", "Run without runtime type checks")
|
|
85
|
+
.option("--no-check", "Run without static type checking")
|
|
86
|
+
.action((params) => {
|
|
87
|
+
slimConfigCheck()
|
|
88
|
+
|
|
89
|
+
if(params.release) process.env.SLIM_RELEASE = "1"
|
|
90
|
+
if(params.dev) process.env.SLIM_DEV = "1"
|
|
91
|
+
if(!params.check) process.env.SLIM_NO_CHECK = "1"
|
|
92
|
+
if(!params.silent) log(params.dev ? "Building and running (dev)..." : "Building and running...")
|
|
93
|
+
execSync("node src/compile.js && node run-slim.js", { stdio: "inherit" });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
program
|
|
97
|
+
.command("view")
|
|
98
|
+
.description("View the current file to be compiled")
|
|
99
|
+
.option("-L, --line <line>", "View on line")
|
|
100
|
+
.action((params) => {
|
|
101
|
+
const config = slimConfigRead()
|
|
102
|
+
|
|
103
|
+
if("main" in config) {
|
|
104
|
+
const mainContent = fs.readFileSync(config.main + ".slim", "utf8")
|
|
105
|
+
|
|
106
|
+
if(params["line"]) {
|
|
107
|
+
try {
|
|
108
|
+
let line = parseInt(params.line) - 1
|
|
109
|
+
const lines = mainContent.split("\n")
|
|
110
|
+
const lineContent = lines[line]
|
|
111
|
+
|
|
112
|
+
if(lineContent != undefined) {
|
|
113
|
+
console.log(mainContent.split("\n")[line])
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
error(`Line ${line + 1} does not exist. The file contains between 1 and ${lines.length} line(-s)`)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch(e) {
|
|
120
|
+
error(e)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(mainContent)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
program
|
|
131
|
+
.command("server")
|
|
132
|
+
.description("Run a Slim server (prod by default; --dev for watch + live reload)")
|
|
133
|
+
.option("-D, --dev", "Dev mode: watch, rebuild, live reload, request logs")
|
|
134
|
+
.option("-H, --hot", "Dev mode with hot reload")
|
|
135
|
+
.option("-R, --release", "Run without runtime type checks")
|
|
136
|
+
.action((params) => {
|
|
137
|
+
slimConfigCheck()
|
|
138
|
+
|
|
139
|
+
// Dev is on when requested on the CLI or set in slimserver.json ("dev": true).
|
|
140
|
+
const dev = Boolean(params.dev || params.hot || serverConfigRead().dev === true)
|
|
141
|
+
|
|
142
|
+
if(params.release) process.env.SLIM_RELEASE = "1"
|
|
143
|
+
|
|
144
|
+
if(dev) {
|
|
145
|
+
log("Starting Slim dev server (watch + live reload)...")
|
|
146
|
+
execSync(`node run-dev-slim.js${params.hot ? " --hot" : ""}`, { stdio: "inherit" });
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
log("Starting Slim server...")
|
|
150
|
+
execSync("node src/compile.js && node run-slim.js", { stdio: "inherit" });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
program
|
|
155
|
+
.command("config")
|
|
156
|
+
.description("View current Slim config")
|
|
157
|
+
.option("-K, --keys <keys>", "Show only specific keys")
|
|
158
|
+
.option("-S, --set <key>=<value>", "Set key value")
|
|
159
|
+
.option("-R, --remove <key>", "Remove key")
|
|
160
|
+
.action(async (params) => {
|
|
161
|
+
slimConfigCheck()
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
let data = await readFile(path.join(root, "slimconfig.json"), 'utf8');
|
|
165
|
+
const res = JSON.parse(data);
|
|
166
|
+
|
|
167
|
+
if(params["keys"]) {
|
|
168
|
+
const args = params["keys"].split(spaceRegex).map(item => item.trim())
|
|
169
|
+
|
|
170
|
+
if(args.length > 0) {
|
|
171
|
+
args.forEach(a => {
|
|
172
|
+
a = a.replaceAll("--", "")
|
|
173
|
+
if(a in res) {
|
|
174
|
+
console.log(res[a])
|
|
175
|
+
}
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
console.log(res)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else if(params["set"]) {
|
|
183
|
+
const config = JSON.parse(fs.readFileSync(slimConfigPath, "utf8"));
|
|
184
|
+
const args = params["set"].split(spaceRegex).map(item => item.trim());
|
|
185
|
+
|
|
186
|
+
args.forEach(arg => {
|
|
187
|
+
if (!arg.includes("=")) return;
|
|
188
|
+
|
|
189
|
+
const [key, ...valueParts] = arg.split("=");
|
|
190
|
+
|
|
191
|
+
const value = valueParts.join("=").trim();
|
|
192
|
+
|
|
193
|
+
config[key.trim()] = parseValue(value);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
slimConfigPath,
|
|
198
|
+
JSON.stringify(config, null, 4),
|
|
199
|
+
"utf8"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
else if (params["remove"]) {
|
|
203
|
+
const config = JSON.parse(fs.readFileSync(slimConfigPath, "utf8"));
|
|
204
|
+
const keys = params["remove"]
|
|
205
|
+
.split(spaceRegex)
|
|
206
|
+
.map(item => item.trim())
|
|
207
|
+
.filter(Boolean);
|
|
208
|
+
|
|
209
|
+
keys.forEach(key => {
|
|
210
|
+
delete config[key];
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
fs.writeFileSync(
|
|
214
|
+
slimConfigPath,
|
|
215
|
+
JSON.stringify(config, null, 4),
|
|
216
|
+
"utf8"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
const config = JSON.parse(fs.readFileSync(slimConfigPath, "utf8"));
|
|
221
|
+
console.log(config)
|
|
222
|
+
}
|
|
223
|
+
} catch (err) {
|
|
224
|
+
console.error(err);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
program
|
|
229
|
+
.command("create")
|
|
230
|
+
.description("Workspace creating")
|
|
231
|
+
.option("--cfg, --config", "Create config")
|
|
232
|
+
.option("--srv, --server", "Create a slimserver.json next to the entry file")
|
|
233
|
+
.option("--file <name>", "Create file")
|
|
234
|
+
.action((params) => {
|
|
235
|
+
if(params.config) {
|
|
236
|
+
try {
|
|
237
|
+
fs.writeFileSync(slimConfigPath, JSON.stringify(defaultConfig, null, 4), 'utf8');
|
|
238
|
+
log('Slim config create in root dir');
|
|
239
|
+
} catch (err) {
|
|
240
|
+
error('An error occurred while creating config:', err);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if(params.server) {
|
|
244
|
+
try {
|
|
245
|
+
const config = slimConfigRead()
|
|
246
|
+
const dir = config.main ? path.dirname(path.resolve(config.main)) : root
|
|
247
|
+
const serverConfigPath = path.join(dir, "slimserver.json")
|
|
248
|
+
|
|
249
|
+
if(fs.existsSync(serverConfigPath)) {
|
|
250
|
+
log(`slimserver.json already exists at ${path.relative(root, serverConfigPath)}, leaving it untouched`)
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
const template = {
|
|
254
|
+
port: 3000,
|
|
255
|
+
dev: false,
|
|
256
|
+
statics: [{ from: "/public", to: "./public" }],
|
|
257
|
+
redirects: [{ from: "/github", to: "https://example.com" }]
|
|
258
|
+
}
|
|
259
|
+
fs.writeFileSync(serverConfigPath, JSON.stringify(template, null, 4) + "\n", 'utf8')
|
|
260
|
+
log(`Created ${path.relative(root, serverConfigPath)}`)
|
|
261
|
+
}
|
|
262
|
+
} catch (err) {
|
|
263
|
+
error('An error occurred while creating slimserver.json:', err);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if(params.file) {
|
|
267
|
+
try {
|
|
268
|
+
fs.writeFileSync(params.file + ".slim", "", 'utf8');
|
|
269
|
+
log(`File ${params.file}.slim created`);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
error('An error occurred while creating file:', err);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if(!params.config && !params.server && !params.file) {
|
|
275
|
+
log(
|
|
276
|
+
`Please use the following arguments to create files:
|
|
277
|
+
${program.name()} help create
|
|
278
|
+
`)
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
program
|
|
283
|
+
.command("version")
|
|
284
|
+
.option("--check", "Check actual version")
|
|
285
|
+
.action(async (params) => {
|
|
286
|
+
packageCheck()
|
|
287
|
+
|
|
288
|
+
if(params.check) {
|
|
289
|
+
const githubRepo = pkg.repository.url.split("git+https://github.com/")[1].trim().split(".git")[0]
|
|
290
|
+
const res = await fetch("https://raw.githubusercontent.com/" + githubRepo + "/main/package.json")
|
|
291
|
+
const githubPkg = await res.json()
|
|
292
|
+
|
|
293
|
+
if(githubPkg.version != pkg.version) {
|
|
294
|
+
log(`Your version is not compatible with the latest version of Slim:
|
|
295
|
+
Current: ${githubPkg.version}
|
|
296
|
+
Your's: ${pkg.version}`)
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
log(`You on the latest Slim version`)
|
|
300
|
+
}
|
|
301
|
+
return
|
|
302
|
+
}
|
|
303
|
+
console.log(program.version())
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
program
|
|
307
|
+
.command("log")
|
|
308
|
+
.argument('<string>')
|
|
309
|
+
.action((str) => {
|
|
310
|
+
log(str)
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
program
|
|
314
|
+
.command("check")
|
|
315
|
+
.action(() => {
|
|
316
|
+
if(slimConfigCheck() != false) {
|
|
317
|
+
log("Everything is OK")
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
program
|
|
322
|
+
.command("init")
|
|
323
|
+
.description("Scaffold a new Slim project")
|
|
324
|
+
.action(() => {
|
|
325
|
+
packageCheck()
|
|
326
|
+
|
|
327
|
+
if (!fs.existsSync(slimConfigPath)) {
|
|
328
|
+
fs.writeFileSync(slimConfigPath, JSON.stringify({ main: "index", usePackages: true }, null, 4), "utf8")
|
|
329
|
+
log("Created slimconfig.json")
|
|
330
|
+
} else {
|
|
331
|
+
log("slimconfig.json already exists, leaving it untouched")
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const entryFile = path.join(root, "index.slim")
|
|
335
|
+
if (!fs.existsSync(entryFile)) {
|
|
336
|
+
fs.writeFileSync(entryFile,
|
|
337
|
+
`struct User {
|
|
338
|
+
name: string
|
|
339
|
+
id: int
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const user: User = { name: "Slim", id: 1 }
|
|
343
|
+
log(\`Hello, \${user.name}!\`)
|
|
344
|
+
`, "utf8")
|
|
345
|
+
log("Created index.slim")
|
|
346
|
+
} else {
|
|
347
|
+
log("index.slim already exists, leaving it untouched")
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
log("Done. Run your project with: slmc run")
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
program
|
|
354
|
+
.command("repl")
|
|
355
|
+
.description("Start an interactive Slim REPL")
|
|
356
|
+
.action(async () => {
|
|
357
|
+
const { startRepl } = await import("../repl.js")
|
|
358
|
+
startRepl()
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
program
|
|
362
|
+
.command("test")
|
|
363
|
+
.description("Run Slim test files (*.test.slim)")
|
|
364
|
+
.argument("[file]", "Run a specific test file")
|
|
365
|
+
.action((file) => {
|
|
366
|
+
process.exitCode = runTests(file)
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
program
|
|
370
|
+
.command("fmt")
|
|
371
|
+
.description("Format Slim source")
|
|
372
|
+
.argument("[file]", "File to format (defaults to the config main)")
|
|
373
|
+
.action((file) => {
|
|
374
|
+
let target = file
|
|
375
|
+
if (!target) {
|
|
376
|
+
const config = slimConfigRead()
|
|
377
|
+
if (!("main" in config)) {
|
|
378
|
+
error(`No file given and no "main" in slimconfig.json`)
|
|
379
|
+
process.exitCode = 1
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
target = config.main
|
|
383
|
+
}
|
|
384
|
+
if (!target.endsWith(".slim")) target += ".slim"
|
|
385
|
+
target = path.resolve(target)
|
|
386
|
+
|
|
387
|
+
if (!fs.existsSync(target)) {
|
|
388
|
+
error(`File not found: ${target}`)
|
|
389
|
+
process.exitCode = 1
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const changed = formatFile(target)
|
|
394
|
+
log(changed ? `Formatted ${path.relative(root, target)}` : `${path.relative(root, target)} already formatted`)
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
program.parse();
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
error(err?.message ?? err)
|
|
403
|
+
process.exitCode = 1
|
|
404
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { rm, mkdir } from 'node:fs/promises';
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
|
|
6
|
+
export const formatError = chalk.red.bold
|
|
7
|
+
export const formatSuccess = chalk.green.bold
|
|
8
|
+
export const formatBgWhite = chalk.bgWhiteBright
|
|
9
|
+
export const formatItalic = chalk.italic
|
|
10
|
+
export const formatBold = chalk.bold
|
|
11
|
+
export const rootPath = process.cwd()
|
|
12
|
+
export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
13
|
+
|
|
14
|
+
export function parseValue(value) {
|
|
15
|
+
if(value == "true" || value == "false") {
|
|
16
|
+
return value == "true"
|
|
17
|
+
}
|
|
18
|
+
if(/^-?\d*\.?\d*$/.test(value)) {
|
|
19
|
+
return parseFloat(value)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function log(...text) {
|
|
26
|
+
console.log(`[SLIM CLI]`, ...text)
|
|
27
|
+
}
|
|
28
|
+
export function error(...text) {
|
|
29
|
+
console.error(`[SLIM CLI]`, ...text)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isPackageExists(name) {
|
|
33
|
+
return fs.existsSync(path.join(rootPath, "packages", name))
|
|
34
|
+
}
|
|
35
|
+
export function getPackagePath(name) {
|
|
36
|
+
return path.join(rootPath, "packages", name)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function deleteDirectory(dirPath) {
|
|
40
|
+
try {
|
|
41
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
42
|
+
return true
|
|
43
|
+
} catch (err) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function loading({ startMsg, callback }) {
|
|
49
|
+
function fail(...args) {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
msg: args.join(" ")
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function ok(...args) {
|
|
56
|
+
return {
|
|
57
|
+
success: true,
|
|
58
|
+
msg: args.join(" ")
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const spinChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
63
|
+
let i = 0;
|
|
64
|
+
|
|
65
|
+
const render = (char, msg) => {
|
|
66
|
+
process.stdout.write(
|
|
67
|
+
`\r\x1b[2K${chalk.green.bold(`[SPM] ${char} ${msg}`)}`
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const spinnerInterval = setInterval(() => {
|
|
72
|
+
render(spinChars[i++ % spinChars.length], startMsg);
|
|
73
|
+
}, 100);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const result = await callback({ fail, ok });
|
|
77
|
+
|
|
78
|
+
clearInterval(spinnerInterval);
|
|
79
|
+
|
|
80
|
+
process.stdout.write('\r\x1b[2K');
|
|
81
|
+
|
|
82
|
+
if (!result || typeof result.success !== 'boolean') {
|
|
83
|
+
return {
|
|
84
|
+
success: false,
|
|
85
|
+
msg: 'Invalid callback result'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const icon = result.success ? '✓' : '✗';
|
|
90
|
+
|
|
91
|
+
process.stdout.write(
|
|
92
|
+
`${chalk.green.bold(`[SPM] ${icon} ${result.msg}`)}\n`
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return result;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
clearInterval(spinnerInterval);
|
|
98
|
+
|
|
99
|
+
process.stdout.write('\r\x1b[2K');
|
|
100
|
+
|
|
101
|
+
const result = {
|
|
102
|
+
success: false,
|
|
103
|
+
msg: error instanceof Error ? error.message : String(error)
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
process.stdout.write(
|
|
107
|
+
`${chalk.red.bold(`[SPM] ✗ ${result.msg}`)}\n`
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function createFolder(path) {
|
|
115
|
+
try {
|
|
116
|
+
await mkdir(path, { recursive: true });
|
|
117
|
+
return { success: true }
|
|
118
|
+
} catch (err) {
|
|
119
|
+
return {
|
|
120
|
+
success: false,
|
|
121
|
+
content: err.message
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function createFile(filePath, content) {
|
|
127
|
+
try {
|
|
128
|
+
const normalized = content
|
|
129
|
+
.replace(/^\s*\n/, "")
|
|
130
|
+
.replace(/\n\s*$/, "")
|
|
131
|
+
.replace(/^[ \t]+/gm, line => {
|
|
132
|
+
return line.replace(/^[ \t]{0,7}/, "");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
fs.writeFileSync(filePath, normalized);
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
success: true
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
content: err.message
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|