@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/compile.js ADDED
@@ -0,0 +1,230 @@
1
+ import "./handlers/errorHandler.js"
2
+ import fs from "fs"
3
+ import path from "path"
4
+ import { transform } from "./transform.js"
5
+
6
+ import { readFile } from 'fs/promises';
7
+ import { Debug } from "./external/core.js";
8
+ import { stripComments } from "./parser.js";
9
+ import { UseError } from "./external/classErrors.js";
10
+ import { getDistPath, resolveSlimSource } from "./modulePaths.js";
11
+
12
+ const compiled = new Set()
13
+
14
+ let usePackages = true
15
+ let jsdoc = false
16
+ let declarations = false
17
+ let check = true
18
+ let useStyle = "import"
19
+
20
+ function syncExternal() {
21
+ const srcExternal = path.resolve("src/external")
22
+ const distExternal = path.resolve("dist/external")
23
+
24
+ if (!fs.existsSync(srcExternal)) return
25
+
26
+ function syncDir(srcDir, distDir) {
27
+ fs.mkdirSync(distDir, { recursive: true })
28
+
29
+ for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
30
+ const srcFull = path.join(srcDir, entry.name)
31
+ const distFull = path.join(distDir, entry.name)
32
+
33
+ if (entry.isDirectory()) {
34
+ syncDir(srcFull, distFull)
35
+ continue
36
+ }
37
+
38
+ if (fs.existsSync(distFull)) {
39
+ const srcMtime = fs.statSync(srcFull).mtimeMs
40
+ const distMtime = fs.statSync(distFull).mtimeMs
41
+ if (srcMtime <= distMtime) continue
42
+ }
43
+
44
+ fs.copyFileSync(srcFull, distFull)
45
+ Debug.log(`Synced: ${path.relative(".", distFull)}`)
46
+ }
47
+ }
48
+
49
+ syncDir(srcExternal, distExternal)
50
+ }
51
+
52
+ function extractUses(code) {
53
+ code = stripComments(code)
54
+ const uses = []
55
+
56
+ const patterns = [
57
+ /\buse\s+(@[\w$\/.-]+)\s*;?$/gm,
58
+ /\buse\s+(?:\{[^}]+\}|\*\s+as\s+[\w$]+|[\w$]+\s+as\s+[\w$]+|[\w$]+)\s+from\s+(@[\w$\/.-]+)\s*;?$/gm,
59
+ /\buse\s+(?:\{[^}]+\}|\*\s+as\s+[\w$]+|[\w$]+\s+as\s+[\w$]+|[\w$]+)\s+from\s+["']([^"']+)["']\s*;?$/gm,
60
+ /\buse\s+["']([^"']+)["']\s*;?$/gm,
61
+ ]
62
+
63
+ for (const pattern of patterns) {
64
+ let match
65
+ while ((match = pattern.exec(code)) !== null) {
66
+ const raw = match[1]
67
+ if (raw) uses.push(raw)
68
+ }
69
+ }
70
+
71
+ return [...new Set(uses)]
72
+ }
73
+
74
+ function compileFile(slimFile, isEntry = false, mainEntry = null) {
75
+ const hasSilmExtension = slimFile.endsWith(".slim")
76
+ const file = hasSilmExtension ? slimFile : slimFile + ".slim"
77
+ const abs = path.resolve(file)
78
+
79
+ if (compiled.has(abs)) return
80
+ compiled.add(abs)
81
+
82
+ if (!fs.existsSync(abs)) {
83
+ console.error(`\nError: File not found: ${abs}\n`)
84
+ process.exit(1)
85
+ }
86
+
87
+ const code = fs.readFileSync(abs, "utf8")
88
+
89
+ const uses = extractUses(code)
90
+
91
+ if (!usePackages && uses.length > 0) {
92
+ const err = new UseError(
93
+ `The "use" feature is disabled. Set "usePackages": true in slimconfig.json to enable imports (in ${abs}, found: use ${uses[0]})`
94
+ )
95
+ console.error(err)
96
+ process.exit(1)
97
+ }
98
+
99
+ for (const raw of uses) {
100
+ const depPath = resolveSlimSource(raw, abs)
101
+
102
+ if (depPath === null) continue
103
+
104
+ if (!fs.existsSync(depPath)) {
105
+ const err = new UseError(`No lib/file founded by the path: ${raw}`)
106
+ console.error(err)
107
+ process.exit(1)
108
+ }
109
+
110
+ if (depPath === abs) {
111
+ console.error(`\nError: Circular dependency detected in ${abs}\n`)
112
+ process.exit(1)
113
+ }
114
+ compileFile(depPath, false, mainEntry)
115
+ }
116
+
117
+ const { code: output, declarations: dts } = transform(code, abs, { jsdoc, declarations, check, uses: useStyle })
118
+
119
+ const outputPath = isEntry
120
+ ? path.resolve(`dist/${mainEntry}.js`)
121
+ : getDistPath(abs)
122
+
123
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true })
124
+ fs.writeFileSync(outputPath, output)
125
+
126
+ if (dts) fs.writeFileSync(outputPath.replace(/\.js$/, ".d.ts"), dts)
127
+ }
128
+
129
+ function cleanDist(slimFileClear) {
130
+ const keep = new Set([
131
+ path.resolve(`dist/${slimFileClear}.js`),
132
+ path.resolve("dist/mappings.json"),
133
+ ])
134
+
135
+ function addDirToKeep(dir) {
136
+ if (!fs.existsSync(dir)) return
137
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
138
+ const full = path.resolve(dir, entry.name)
139
+ keep.add(full)
140
+ if (entry.isDirectory()) addDirToKeep(full)
141
+ }
142
+ }
143
+ addDirToKeep(path.resolve("dist/external"))
144
+
145
+ for (const slimFile of compiled) {
146
+ if (slimFile === path.resolve(`${slimFileClear}.slim`)) {
147
+ keep.add(path.resolve(`dist/${slimFileClear}.js`))
148
+ } else {
149
+ keep.add(getDistPath(slimFile))
150
+ }
151
+ }
152
+
153
+ for (const kept of [...keep]) {
154
+ if (kept.endsWith(".js")) keep.add(kept.replace(/\.js$/, ".d.ts"))
155
+ }
156
+
157
+ function walkAndClean(dir) {
158
+ if (!fs.existsSync(dir)) return
159
+
160
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
161
+ const full = path.resolve(dir, entry.name)
162
+
163
+ if (entry.isDirectory()) {
164
+ walkAndClean(full)
165
+
166
+ if (fs.readdirSync(full).length === 0) {
167
+ fs.rmdirSync(full)
168
+ }
169
+ } else if (!keep.has(full)) {
170
+ fs.rmSync(full)
171
+ Debug.log(`Cleaned: ${path.relative(".", full)}`)
172
+ }
173
+ }
174
+ }
175
+
176
+ walkAndClean(path.resolve("dist"))
177
+ }
178
+
179
+ function writeJsConfig() {
180
+ const configPath = path.resolve("jsconfig.json")
181
+ if (fs.existsSync(configPath)) return
182
+
183
+ const config = {
184
+ compilerOptions: {
185
+ allowJs: true,
186
+ checkJs: false,
187
+ noEmit: true,
188
+ module: "esnext",
189
+ target: "es2020",
190
+ moduleResolution: "bundler",
191
+ strict: false,
192
+ skipLibCheck: true
193
+ },
194
+ include: ["dist"]
195
+ }
196
+
197
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 4) + "\n")
198
+ Debug.log("Created jsconfig.json for TypeScript checking")
199
+ }
200
+
201
+ async function main() {
202
+ let data
203
+
204
+ try {
205
+ const contents = await readFile("slimconfig.json", "utf8")
206
+ data = JSON.parse(contents)
207
+ } catch (error) {
208
+ console.error("Error reading or parsing slimconfig.json:", error)
209
+ process.exit(1)
210
+ }
211
+
212
+ usePackages = data.usePackages !== false
213
+ jsdoc = data.jsdoc === true
214
+ declarations = data.declarations === true
215
+ check = data.check !== false && process.env.SLIM_NO_CHECK !== "1"
216
+ useStyle = typeof data.uses === "string" ? data.uses : "import"
217
+
218
+ if (!("main" in data)) return
219
+
220
+ try {
221
+ syncExternal()
222
+ compileFile(data.main, true, data.main)
223
+ cleanDist(data.main)
224
+ if (jsdoc) writeJsConfig()
225
+ } catch (error) {
226
+ console.error(`\n${error.slimTypeErrors || error.slimSyntaxError ? error.message : error}\n`)
227
+ process.exit(1)
228
+ }
229
+ }
230
+ main()
@@ -0,0 +1,202 @@
1
+ // Source frames require a host-provided file reader.
2
+ let readSource = null
3
+
4
+ export function setSourceReader(reader) {
5
+ readSource = typeof reader === "function" ? reader : null
6
+ }
7
+
8
+ let __mappings__ = null
9
+ let __sourceFile__ = null
10
+
11
+ function loadMappings() {
12
+ if (__mappings__) return
13
+ try {
14
+ const data = JSON.parse(readSource(new URL("../../dist/mappings.json", import.meta.url)))
15
+ __mappings__ = data.mappings
16
+ __sourceFile__ = data.sourceFile
17
+ } catch {
18
+ __mappings__ = []
19
+ }
20
+ }
21
+
22
+ function resolveOriginalLine(generatedLine) {
23
+ loadMappings()
24
+ if (!__mappings__?.length) return { line: generatedLine, file: __sourceFile__ }
25
+
26
+ const mapping = __mappings__
27
+ .filter(m => m.generatedLine <= generatedLine)
28
+ .at(-1)
29
+
30
+ return {
31
+ line: mapping?.originalLine ?? generatedLine,
32
+ file: __sourceFile__
33
+ }
34
+ }
35
+
36
+ function isInternalFrame(normalized) {
37
+ return (
38
+ normalized.includes("node_modules") ||
39
+ normalized.includes("node:") ||
40
+ normalized.includes("/external/") ||
41
+ normalized.includes("/src/handlers/") ||
42
+ normalized.includes("/src/compile.js") ||
43
+ normalized.includes("/src/transform.js") ||
44
+ normalized.includes("/src/parser.js")
45
+ )
46
+ }
47
+
48
+ function parseStack(stack, skip = 0) {
49
+ if (!stack) return null
50
+
51
+ const frames = []
52
+ for (const line of stack.split("\n")) {
53
+ const match = line.match(/at .+ \((.+):(\d+):(\d+)\)/)
54
+ ?? line.match(/at (.+):(\d+):(\d+)/)
55
+
56
+ if (!match) continue
57
+
58
+ const [, file, ln, col] = match
59
+ const normalized = file.replace(/^file:\/\/\//, "").replace(/\\/g, "/")
60
+
61
+ if (isInternalFrame(normalized)) continue
62
+
63
+ frames.push({ file: normalized, line: parseInt(ln), col: parseInt(col) })
64
+ }
65
+
66
+ if (frames.length) {
67
+ const slimFrames = frames.filter(f => f.file.endsWith(".slim"))
68
+ const pool = slimFrames.length ? slimFrames : frames
69
+ return pool[Math.min(skip, pool.length - 1)]
70
+ }
71
+
72
+ for (const line of stack.split("\n")) {
73
+ const match = line.match(/at .+ \((.+dist\/output\.js):(\d+):(\d+)\)/)
74
+ ?? line.match(/at (.+dist\/output\.js):(\d+):(\d+)/)
75
+
76
+ if (!match) continue
77
+
78
+ const [, , ln, col] = match
79
+ const resolved = resolveOriginalLine(parseInt(ln))
80
+
81
+ return {
82
+ file: resolved.file,
83
+ line: resolved.line,
84
+ col: parseInt(col)
85
+ }
86
+ }
87
+
88
+ return null
89
+ }
90
+
91
+ function getSourceLine(file, line) {
92
+ if (!readSource) return null
93
+
94
+ try {
95
+ const content = readSource(file.replace(/\\/g, "/"))
96
+ return content?.split("\n")[line - 1]?.replace(/\r$/, "") ?? null
97
+ } catch {
98
+ return null
99
+ }
100
+ }
101
+
102
+ export function resolveErrorLocation(err) {
103
+ if (err?.file && err?.line) {
104
+ return {
105
+ file: err.file,
106
+ line: err.line,
107
+ col: err.col ?? null,
108
+ sourceLine: err.sourceLine ?? getSourceLine(err.file, err.line)
109
+ }
110
+ }
111
+
112
+ const loc = parseStack(err?.stack)
113
+ if (!loc) return null
114
+
115
+ return {
116
+ file: loc.file,
117
+ line: loc.line,
118
+ col: loc.col ?? null,
119
+ sourceLine: getSourceLine(loc.file, loc.line)
120
+ }
121
+ }
122
+
123
+ export function formatError(tag, message, file, line, col, sourceLine) {
124
+ const parts = [`\n${tag}: ${message}`]
125
+
126
+ if (file && line) {
127
+ parts.push(` at ${file}:${line}:${col ?? 1}`)
128
+ }
129
+
130
+ if (sourceLine) {
131
+ const trimmed = sourceLine.trim()
132
+ const indent = sourceLine.search(/\S/)
133
+ const pointer = col
134
+ ? " ".repeat(Math.max(0, col - indent - 1)) + "^"
135
+ : "^"
136
+
137
+ parts.push(`\n ${trimmed}`)
138
+ parts.push(` ${pointer}`)
139
+ }
140
+
141
+ parts.push("")
142
+ return parts.join("\n")
143
+ }
144
+
145
+ export class LangError extends Error {
146
+ constructor(message, tag = "Error", meta = {}) {
147
+ super(message)
148
+ this.tag = tag
149
+ this.name = tag
150
+
151
+ const loc = parseStack(this.stack, meta.skipUserFrames ?? 0)
152
+
153
+ this.file = meta.file ?? loc?.file ?? null
154
+ this.line = meta.line ?? loc?.line ?? null
155
+ this.col = meta.col ?? loc?.col ?? null
156
+ this.sourceLine = meta.sourceLine ?? (
157
+ this.file && this.line
158
+ ? getSourceLine(this.file, this.line)
159
+ : null
160
+ )
161
+
162
+ if (Error.captureStackTrace) {
163
+ Error.captureStackTrace(this, this.constructor)
164
+ }
165
+ }
166
+ }
167
+
168
+ export class StructError extends LangError {
169
+ constructor(m, meta) { super(m, "StructError", meta) }
170
+ }
171
+ export class StructPassedError extends LangError {
172
+ constructor(m, meta) { super(m, "StructPassedError", meta) }
173
+ }
174
+ export class StructExpectError extends LangError {
175
+ constructor(m, meta) { super(m, "StructExpectError", meta) }
176
+ }
177
+ export class StructResultError extends StructError {
178
+ constructor(m, meta) { super(m, "StructResultError", meta) }
179
+ }
180
+
181
+ export class TypeError_ extends LangError {
182
+ constructor(m, meta) { super(m, "TypeError", meta) }
183
+ }
184
+ export class RuntimeError extends LangError {
185
+ constructor(m, meta) { super(m, "RuntimeError", meta) }
186
+ }
187
+
188
+ export class ArgumentDeclarationTypeError extends TypeError_ {
189
+ constructor(m, meta) { super(m, meta) }
190
+ }
191
+
192
+ export class EnumError extends LangError {
193
+ constructor(m, meta) { super(m, "EnumError", meta) }
194
+ }
195
+
196
+ export class UseError extends Error {
197
+ constructor(m, meta) { super(m, "UseError", meta) }
198
+ }
199
+
200
+ export class TypeDefError extends TypeError {
201
+ constructor(m, meta) { super(m, "TypeDefError", meta) }
202
+ }
@@ -0,0 +1,38 @@
1
+ // Browser-safe runtime subset serialized into client handlers.
2
+
3
+ export function log(...args) { console.log(...args) }
4
+ export function warn(...args) { console.warn(...args) }
5
+ export function error(...args) { console.error(...args) }
6
+ export function info(...args) { console.info(...args) }
7
+ export function debug(...args) { console.log(...args) }
8
+
9
+ export function type(obj) {
10
+ if (obj === null) return "null"
11
+ if (obj === undefined) return "undefined"
12
+ if (typeof obj === "number" && Number.isNaN(obj)) return "NaN"
13
+
14
+ if (typeof HTMLElement !== "undefined" && obj instanceof HTMLElement) return "element"
15
+ if (obj && typeof obj === "object" && obj.nodeType === 11) return "fragment"
16
+
17
+ if (typeof obj === "function" && obj.__type__ === true) return "type"
18
+ if (typeof obj === "function" && obj.__component__ === true) return "component"
19
+
20
+ if (Array.isArray(obj)) {
21
+ if (obj.length > 0) {
22
+ const first = type(obj[0])
23
+ if (obj.every(el => type(el) === first)) return first + "[]"
24
+ }
25
+ return "array"
26
+ }
27
+
28
+ if (typeof obj === "object") return "object"
29
+ if (typeof obj === "string") return "string"
30
+ if (typeof obj === "number") return Number.isInteger(obj) ? "int" : "float"
31
+ if (typeof obj === "boolean") return "bool"
32
+ if (typeof obj === "function") return /^\s*class\s+/.test(obj.toString()) ? "class" : "function"
33
+
34
+ return undefined
35
+ }
36
+
37
+ // Client declarations are emitted before the handler table.
38
+ export const CLIENT_RUNTIME = [log, warn, error, info, debug, type]