@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.
Files changed (52) hide show
  1. package/README.md +666 -0
  2. package/package.json +55 -0
  3. package/packages/slim/.spm +7 -0
  4. package/packages/slim/converters/main.slim +106 -0
  5. package/packages/slim/helpers/array.slim +25 -0
  6. package/packages/slim/helpers/path.slim +3 -0
  7. package/packages/slim/helpers/request.slim +102 -0
  8. package/packages/slim/helpers/string.slim +27 -0
  9. package/packages/slim/main.slim +42 -0
  10. package/packages/slim/parse/main.slim +25 -0
  11. package/packages/slim/server/main.slim +423 -0
  12. package/packages/slim/time/main.slim +66 -0
  13. package/packages/slim/types/common.slim +6 -0
  14. package/packages/slim/types/formats.slim +23 -0
  15. package/packages/slim/types/hash.slim +6 -0
  16. package/packages/slim/types/mails.slim +3 -0
  17. package/packages/slim/types/numerical.slim +9 -0
  18. package/packages/slim/types/time.slim +3 -0
  19. package/run-dev-slim.js +133 -0
  20. package/run-slim.js +20 -0
  21. package/src/bin/api/github_auth.js +89 -0
  22. package/src/bin/api/github_get.js +139 -0
  23. package/src/bin/api/github_req.js +455 -0
  24. package/src/bin/api/lock.js +37 -0
  25. package/src/bin/api/spm.js +103 -0
  26. package/src/bin/api/storage.js +30 -0
  27. package/src/bin/cli.js +404 -0
  28. package/src/bin/config.default.json +5 -0
  29. package/src/bin/helpers.js +147 -0
  30. package/src/bin/parsers/spm.js +174 -0
  31. package/src/bin/spm.js +519 -0
  32. package/src/checker.js +926 -0
  33. package/src/compile.js +230 -0
  34. package/src/external/classErrors.js +202 -0
  35. package/src/external/client.js +38 -0
  36. package/src/external/core.js +861 -0
  37. package/src/external/defaults.js +25 -0
  38. package/src/external/helpers.js +541 -0
  39. package/src/external/slim-globals.d.ts +65 -0
  40. package/src/external/types.js +38 -0
  41. package/src/format.js +81 -0
  42. package/src/handlers/errorHandler.js +43 -0
  43. package/src/handlers/parser/components.js +250 -0
  44. package/src/handlers/parserHandler.js +793 -0
  45. package/src/jsdoc.js +273 -0
  46. package/src/lexer.js +174 -0
  47. package/src/modulePaths.js +74 -0
  48. package/src/parser.js +818 -0
  49. package/src/repl.js +32 -0
  50. package/src/sourcemap.js +0 -0
  51. package/src/test-runner.js +62 -0
  52. package/src/transform.js +765 -0
package/src/jsdoc.js ADDED
@@ -0,0 +1,273 @@
1
+ import _traverse from "@babel/traverse"
2
+ import * as t from "@babel/types"
3
+
4
+ const traverse = _traverse.default ?? _traverse
5
+
6
+ const PRIMITIVES = {
7
+ int: "number", float: "number", number: "number",
8
+ string: "string", bool: "boolean", null: "null",
9
+ any: "any", object: "object", array: "any[]",
10
+ function: "Function", element: "HTMLElement"
11
+ }
12
+
13
+ function mapType(label) {
14
+ label = String(label).trim()
15
+ if (!label) return "any"
16
+ if (label.length > 2 && label.startsWith("[") && label.endsWith("]")) {
17
+ return `[${label.slice(1, -1).split(",").map(part => mapType(part.trim())).join(", ")}]`
18
+ }
19
+ if (label.endsWith("?")) return mapType(label.slice(0, -1)) + " | null | undefined"
20
+ if (label.endsWith("[]")) return mapType(label.slice(0, -2)) + "[]"
21
+ if (label.includes("|")) return [...new Set(label.split("|").map(mapType))].join(" | ")
22
+ if (label.includes("&")) return [...new Set(label.split("&").map(mapType))].join(" & ")
23
+ if (label.includes("::")) return "number | string"
24
+ return PRIMITIVES[label] ?? label
25
+ }
26
+
27
+ export function jsdocComment(lines) {
28
+ return "*\n" + lines.map(line => ` * ${line}`).join("\n") + "\n "
29
+ }
30
+
31
+ function enumTypedef(name, schema) {
32
+ const types = new Set()
33
+
34
+ for (const property of schema.properties) {
35
+ if (!t.isObjectProperty(property)) continue
36
+ if (t.isStringLiteral(property.value)) types.add("string")
37
+ else types.add("number")
38
+ }
39
+
40
+ const type = types.size ? [...types].join(" | ") : "number | string"
41
+ return `@typedef {${type}} ${name}`
42
+ }
43
+
44
+ function structTypedef(name, schema) {
45
+ const fields = []
46
+
47
+ for (const property of schema.properties) {
48
+ if (!t.isObjectProperty(property) || !t.isStringLiteral(property.value)) continue
49
+
50
+ const rawKey = t.isStringLiteral(property.key)
51
+ ? property.key.value
52
+ : t.isIdentifier(property.key)
53
+ ? property.key.name
54
+ : null
55
+ if (rawKey === null) continue
56
+
57
+ const optional = rawKey.startsWith("*")
58
+ const field = optional ? rawKey.slice(1).trim() : rawKey
59
+ fields.push(`${field}${optional ? "?" : ""}: ${mapType(property.value.value)}`)
60
+ }
61
+
62
+ return `@typedef {{ ${fields.join(", ")} }} ${name}`
63
+ }
64
+
65
+ function returnLabel(fn) {
66
+ if (!t.isBlockStatement(fn.body)) return null
67
+
68
+ for (const statement of fn.body.body) {
69
+ if (!t.isExpressionStatement(statement)) continue
70
+ const call = statement.expression
71
+ if (!t.isCallExpression(call) || !t.isIdentifier(call.callee, { name: "__declare_return__" })) continue
72
+ if (t.isStringLiteral(call.arguments[0])) return call.arguments[0].value
73
+ }
74
+ return null
75
+ }
76
+
77
+ function paramLines(fn) {
78
+ if (!t.isBlockStatement(fn.body)) return []
79
+
80
+ const lines = []
81
+ for (const statement of fn.body.body) {
82
+ if (!t.isExpressionStatement(statement)) continue
83
+ const call = statement.expression
84
+ if (!t.isCallExpression(call) || !t.isIdentifier(call.callee, { name: "__typed_parameter__" })) continue
85
+
86
+ const [value, typeArg] = call.arguments
87
+ if (!t.isIdentifier(value) || !t.isStringLiteral(typeArg)) continue
88
+
89
+ lines.push(`@param {${mapType(typeArg.value)}} ${value.name}`)
90
+ }
91
+
92
+ const returns = returnLabel(fn)
93
+ if (returns) {
94
+ const asyncReturn = fn.async ? `Promise<${mapType(returns)}>` : mapType(returns)
95
+ lines.push(`@returns {${asyncReturn}}`)
96
+ }
97
+
98
+ return lines
99
+ }
100
+
101
+ function structInterface(schema) {
102
+ const fields = []
103
+
104
+ for (const property of schema.properties) {
105
+ if (!t.isObjectProperty(property) || !t.isStringLiteral(property.value)) continue
106
+
107
+ const rawKey = t.isStringLiteral(property.key)
108
+ ? property.key.value
109
+ : t.isIdentifier(property.key)
110
+ ? property.key.name
111
+ : null
112
+ if (rawKey === null) continue
113
+
114
+ const optional = rawKey.startsWith("*")
115
+ const field = optional ? rawKey.slice(1).trim() : rawKey
116
+ fields.push(`${field}${optional ? "?" : ""}: ${mapType(property.value.value)}`)
117
+ }
118
+
119
+ return `{ ${fields.join("; ")} }`
120
+ }
121
+
122
+ function enumValueType(schema) {
123
+ const types = new Set()
124
+ for (const property of schema.properties) {
125
+ if (!t.isObjectProperty(property)) continue
126
+ types.add(t.isStringLiteral(property.value) ? "string" : "number")
127
+ }
128
+ return types.size ? [...types].join(" | ") : "number | string"
129
+ }
130
+
131
+ function paramTypes(fn) {
132
+ const types = new Map()
133
+ if (!t.isBlockStatement(fn.body)) return types
134
+
135
+ for (const statement of fn.body.body) {
136
+ if (!t.isExpressionStatement(statement)) continue
137
+ const call = statement.expression
138
+ if (!t.isCallExpression(call) || !t.isIdentifier(call.callee, { name: "__typed_parameter__" })) continue
139
+
140
+ const [value, typeArg] = call.arguments
141
+ if (t.isIdentifier(value) && t.isStringLiteral(typeArg)) types.set(value.name, typeArg.value)
142
+ }
143
+ return types
144
+ }
145
+
146
+ function functionSignature(name, fn) {
147
+ const types = paramTypes(fn)
148
+
149
+ const params = fn.params.map(param => {
150
+ if (t.isAssignmentPattern(param) && t.isIdentifier(param.left)) {
151
+ const label = types.get(param.left.name)
152
+ return `${param.left.name}?: ${label ? mapType(label) : "any"}`
153
+ }
154
+ if (t.isRestElement(param) && t.isIdentifier(param.argument)) {
155
+ return `...${param.argument.name}: any[]`
156
+ }
157
+ if (t.isIdentifier(param)) {
158
+ const label = types.get(param.name)
159
+ return `${param.name}: ${label ? mapType(label) : "any"}`
160
+ }
161
+ return "arg: any"
162
+ })
163
+
164
+ const returns = returnLabel(fn)
165
+ const result = returns
166
+ ? (fn.async ? `Promise<${mapType(returns)}>` : mapType(returns))
167
+ : "any"
168
+
169
+ return `export declare function ${name}(${params.join(", ")}): ${result}`
170
+ }
171
+
172
+ export function emitDeclarations(ast) {
173
+ const lines = []
174
+ let usesStruct = false
175
+
176
+ traverse(ast, {
177
+ ExportNamedDeclaration(path_) {
178
+ const decl = path_.node.declaration
179
+ if (!decl) return
180
+
181
+ if (t.isFunctionDeclaration(decl) && decl.id) {
182
+ lines.push(functionSignature(decl.id.name, decl))
183
+ return
184
+ }
185
+
186
+ if (!t.isVariableDeclaration(decl)) return
187
+
188
+ for (const declarator of decl.declarations) {
189
+ if (!t.isIdentifier(declarator.id)) continue
190
+ const name = declarator.id.name
191
+ const init = declarator.init
192
+
193
+ if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
194
+ lines.push(functionSignature(name, init))
195
+ continue
196
+ }
197
+
198
+ if (!t.isCallExpression(init)) continue
199
+
200
+ if (t.isIdentifier(init.callee, { name: "__def_struct__" }) && t.isObjectExpression(init.arguments[1])) {
201
+ usesStruct = true
202
+ lines.push(`export interface ${name} ${structInterface(init.arguments[1])}`)
203
+ lines.push(`export declare const ${name}: SlimStruct<${name}>`)
204
+ } else if (t.isIdentifier(init.callee, { name: "__def_enum__" }) && t.isObjectExpression(init.arguments[1])) {
205
+ lines.push(`export type ${name} = ${enumValueType(init.arguments[1])}`)
206
+ lines.push(`export declare const ${name}: Record<string, any>`)
207
+ } else if (t.isIdentifier(init.callee, { name: "__type_def__" })) {
208
+ lines.push(`export type ${name} = any`)
209
+ lines.push(`export declare const ${name}: (value: unknown) => boolean`)
210
+ }
211
+ }
212
+ }
213
+ })
214
+
215
+ if (lines.length === 0) return null
216
+
217
+ const header = usesStruct
218
+ ? "type SlimStruct<T> = { verify(value: unknown): T; verifySafe(value: unknown): { success: boolean; result: T } }\n\n"
219
+ : ""
220
+
221
+ return header + lines.join("\n") + "\n"
222
+ }
223
+
224
+ export function emitJsDoc(ast) {
225
+ const typedefs = []
226
+
227
+ traverse(ast, {
228
+ VariableDeclaration(path_) {
229
+ for (const declarator of path_.node.declarations) {
230
+ const init = declarator.init
231
+ if (!t.isCallExpression(init)) continue
232
+
233
+ if (t.isIdentifier(init.callee, { name: "__def_struct__" }) &&
234
+ t.isStringLiteral(init.arguments[0]) &&
235
+ t.isObjectExpression(init.arguments[1])) {
236
+ typedefs.push(structTypedef(init.arguments[0].value, init.arguments[1]))
237
+ }
238
+
239
+ if (t.isIdentifier(init.callee, { name: "__def_enum__" }) &&
240
+ t.isStringLiteral(init.arguments[0]) &&
241
+ t.isObjectExpression(init.arguments[1])) {
242
+ typedefs.push(enumTypedef(init.arguments[0].value, init.arguments[1]))
243
+ }
244
+
245
+ if (t.isIdentifier(init.callee, { name: "__type_def__" }) &&
246
+ t.isStringLiteral(init.arguments[0])) {
247
+ typedefs.push(`@typedef {any} ${init.arguments[0].value}`)
248
+ }
249
+
250
+ if (t.isIdentifier(init.callee, { name: "__typed_variable__" }) &&
251
+ t.isStringLiteral(init.arguments[1])) {
252
+ t.addComment(path_.node, "leading",
253
+ jsdocComment([`@type {${mapType(init.arguments[1].value)}}`]), false)
254
+ }
255
+ }
256
+ },
257
+
258
+ Function(path_) {
259
+ const lines = paramLines(path_.node)
260
+ if (lines.length === 0) return
261
+
262
+ const target = t.isFunctionDeclaration(path_.node)
263
+ ? path_.node
264
+ : path_.parentPath.isVariableDeclarator()
265
+ ? path_.parentPath.parentPath.node
266
+ : path_.node
267
+
268
+ t.addComment(target, "leading", jsdocComment(lines), false)
269
+ }
270
+ })
271
+
272
+ return typedefs
273
+ }
package/src/lexer.js ADDED
@@ -0,0 +1,174 @@
1
+ const KEYWORDS = new Set([
2
+ "struct", "enum", "type", "func", "component", "use", "lock", "mode", "elif",
3
+ "and", "or", "sizeof", "kindof", "empty", "copyof",
4
+ "break", "case", "catch", "class", "const", "continue", "debugger", "default",
5
+ "delete", "do", "else", "export", "extends", "finally", "for", "function",
6
+ "if", "import", "in", "instanceof", "new", "return", "super", "switch", "this",
7
+ "throw", "try", "typeof", "var", "void", "while", "with", "yield", "let",
8
+ "static", "async", "await", "of", "as", "from"
9
+ ])
10
+
11
+ const REGEX_KEYWORDS = new Set([
12
+ "return", "typeof", "instanceof", "in", "of", "new", "delete", "void",
13
+ "do", "else", "yield", "await", "case", "throw"
14
+ ])
15
+
16
+ const PUNCTUATORS = [
17
+ ">>>=", "===", "!==", ">>>", "**=", "<<=", ">>=", "&&=", "||=", "??=", "...",
18
+ "~/", "::", "=>", "==", "!=", "<=", ">=", "&&", "||", "??", "?.", "**", "++",
19
+ "--", "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<", ">>",
20
+ "{", "}", "(", ")", "[", "]", ";", ",", "<", ">", "+", "-", "*", "/", "%",
21
+ "&", "|", "^", "!", "~", "?", ":", "=", ".", "@"
22
+ ]
23
+
24
+ const NUMBER = /(?:0[xX][0-9a-fA-F_]+|0[oO][0-7_]+|0[bB][01_]+|(?:\d[\d_]*)?\.?\d[\d_]*(?:[eE][+-]?\d+)?)n?/y
25
+ const NAME = /[A-Za-z_$][\w$]*/y
26
+
27
+ function regexAllowed(prev) {
28
+ if (!prev) return true
29
+ if (prev.type === "number" || prev.type === "string" || prev.type === "regex" || prev.type === "template") {
30
+ return false
31
+ }
32
+ if (prev.type === "name") {
33
+ return prev.keyword && REGEX_KEYWORDS.has(prev.value)
34
+ }
35
+ if (prev.type === "punct") {
36
+ return prev.value !== ")" && prev.value !== "]"
37
+ }
38
+ return true
39
+ }
40
+
41
+ function scanRegex(code, start) {
42
+ let i = start + 1
43
+ let inClass = false
44
+
45
+ while (i < code.length) {
46
+ const c = code[i]
47
+ if (c === "\n") return start
48
+ if (c === "\\") { i += 2; continue }
49
+ if (c === "[") inClass = true
50
+ else if (c === "]") inClass = false
51
+ else if (c === "/" && !inClass) { i++; break }
52
+ i++
53
+ }
54
+
55
+ if (i > code.length) return start
56
+ while (i < code.length && /[a-z]/i.test(code[i])) i++
57
+ return i
58
+ }
59
+
60
+ export function tokenize(code) {
61
+ const tokens = []
62
+ const modes = [{ kind: "code", brace: 0, interp: false }]
63
+ let prev = null
64
+ let i = 0
65
+
66
+ const emit = (type, start, end, keyword = false) => {
67
+ const token = { type, value: code.slice(start, end), start, end }
68
+ if (keyword) token.keyword = true
69
+ tokens.push(token)
70
+ if (type !== "ws" && type !== "newline" && type !== "comment") prev = token
71
+ return token
72
+ }
73
+
74
+ while (i < code.length) {
75
+ const mode = modes[modes.length - 1]
76
+
77
+ if (mode.kind === "template") {
78
+ const start = i
79
+ while (i < code.length) {
80
+ const c = code[i]
81
+ if (c === "\\") { i += 2; continue }
82
+ if (c === "`" || (c === "$" && code[i + 1] === "{")) break
83
+ i++
84
+ }
85
+ if (i > start) emit("template", start, i)
86
+
87
+ if (code[i] === "`") {
88
+ emit("punct", i, i + 1); i++
89
+ modes.pop()
90
+ } else if (code[i] === "$") {
91
+ emit("punct", i, i + 2); i += 2
92
+ modes.push({ kind: "code", brace: 0, interp: true })
93
+ }
94
+ continue
95
+ }
96
+
97
+ const c = code[i]
98
+
99
+ if (c === " " || c === "\t" || c === "\r") {
100
+ const start = i
101
+ while (i < code.length && (code[i] === " " || code[i] === "\t" || code[i] === "\r")) i++
102
+ emit("ws", start, i); continue
103
+ }
104
+
105
+ if (c === "\n") { emit("newline", i, i + 1); i++; continue }
106
+
107
+ if (c === "/" && code[i + 1] === "/") {
108
+ const start = i
109
+ while (i < code.length && code[i] !== "\n") i++
110
+ emit("comment", start, i); continue
111
+ }
112
+
113
+ if (c === "/" && code[i + 1] === "*") {
114
+ const start = i
115
+ i += 2
116
+ while (i < code.length && !(code[i] === "*" && code[i + 1] === "/")) i++
117
+ i = Math.min(i + 2, code.length)
118
+ emit("comment", start, i); continue
119
+ }
120
+
121
+ if (c === "'" || c === '"') {
122
+ const start = i
123
+ i++
124
+ while (i < code.length) {
125
+ if (code[i] === "\\") { i += 2; continue }
126
+ if (code[i] === c || code[i] === "\n") { break }
127
+ i++
128
+ }
129
+ if (code[i] === c) i++
130
+ emit("string", start, i); continue
131
+ }
132
+
133
+ if (c === "`") {
134
+ emit("punct", i, i + 1); i++
135
+ modes.push({ kind: "template" }); continue
136
+ }
137
+
138
+ if (c === "}" && mode.interp && mode.brace === 0) {
139
+ emit("punct", i, i + 1); i++
140
+ modes.pop(); continue
141
+ }
142
+ if (c === "{" && mode.interp) mode.brace++
143
+ if (c === "}" && mode.interp) mode.brace--
144
+
145
+ if (c === "/" && regexAllowed(prev)) {
146
+ const end = scanRegex(code, i)
147
+ if (end > i) { emit("regex", i, end); i = end; continue }
148
+ }
149
+
150
+ NUMBER.lastIndex = i
151
+ const num = NUMBER.exec(code)
152
+ if (num && num.index === i && num[0].length > 0) {
153
+ emit("number", i, i + num[0].length); i += num[0].length; continue
154
+ }
155
+
156
+ NAME.lastIndex = i
157
+ const name = NAME.exec(code)
158
+ if (name && name.index === i) {
159
+ emit("name", i, i + name[0].length, KEYWORDS.has(name[0])); i += name[0].length; continue
160
+ }
161
+
162
+ let matched = null
163
+ for (const p of PUNCTUATORS) {
164
+ if (code.startsWith(p, i)) { matched = p; break }
165
+ }
166
+ if (matched) {
167
+ emit("punct", i, i + matched.length); i += matched.length; continue
168
+ }
169
+
170
+ emit("other", i, i + 1); i++
171
+ }
172
+
173
+ return tokens
174
+ }
@@ -0,0 +1,74 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import { isBuiltin } from "node:module"
4
+
5
+ const slimExtension = ".slim"
6
+
7
+ function isWithin(parent, target) {
8
+ const relative = path.relative(parent, target)
9
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative)
10
+ }
11
+
12
+ function bareName(raw) {
13
+ const segments = raw.split("/")
14
+ return raw.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0]
15
+ }
16
+
17
+ function isNodeModule(raw) {
18
+ if (raw.startsWith(".") || path.isAbsolute(raw)) return false
19
+ return fs.existsSync(path.resolve("node_modules", bareName(raw)))
20
+ }
21
+
22
+ export function getDistPath(slimFile) {
23
+ const abs = path.resolve(slimFile)
24
+ const srcRoot = path.resolve("src")
25
+ const projectRoot = path.resolve(".")
26
+ const relative = isWithin(srcRoot, abs)
27
+ ? path.relative(srcRoot, abs)
28
+ : path.relative(projectRoot, abs)
29
+
30
+ return path.resolve("dist", relative.replace(/\.slim$/, ".js"))
31
+ }
32
+
33
+ export function resolveSlimSource(raw, fromFile) {
34
+ // Node builtins (e.g. "node:crypto", "fs", "path") are not Slim sources;
35
+ // leave them for the JS import to resolve untouched.
36
+ if (isBuiltin(raw)) return null
37
+
38
+ if (raw.startsWith("@")) {
39
+ const packagesRoot = path.resolve("packages")
40
+ const packageName = raw.slice("@".length)
41
+ const fileSource = path.resolve(packagesRoot, packageName + slimExtension)
42
+
43
+ if (fs.existsSync(fileSource)) return fileSource
44
+
45
+ const directorySource = path.resolve(packagesRoot, packageName, "main.slim")
46
+ if (fs.existsSync(directorySource)) return directorySource
47
+
48
+ if (isNodeModule(raw)) return null
49
+
50
+ return fileSource
51
+ }
52
+
53
+ if (raw.endsWith(".js")) return null
54
+
55
+ const localSource = path.resolve(path.dirname(fromFile), raw + slimExtension)
56
+ if (fs.existsSync(localSource)) return localSource
57
+
58
+ if (isNodeModule(raw)) return null
59
+
60
+ return localSource
61
+ }
62
+
63
+ export function resolveSlimImport(raw, fromFile) {
64
+ if (raw.endsWith(".js")) return raw
65
+
66
+ const slimSource = resolveSlimSource(raw, fromFile)
67
+ if (slimSource === null) return raw
68
+
69
+ const distTarget = getDistPath(slimSource)
70
+ const distFrom = getDistPath(fromFile)
71
+ const relative = path.relative(path.dirname(distFrom), distTarget).replace(/\\/g, "/")
72
+
73
+ return relative.startsWith(".") ? relative : "./" + relative
74
+ }