@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/README.md ADDED
@@ -0,0 +1,666 @@
1
+ <div align="center">
2
+ <img width="800" src="https://github.com/user-attachments/assets/29849f19-9e0f-49d0-a188-74714c14b0a4" />
3
+ <h1>Slim</h1>
4
+ <h4 align="center">Slim extends JavaScript with runtime types, structs, operators, and components, compiling to plain Javascript</h4>
5
+ </div>
6
+
7
+ <div align="center">
8
+ <a href="https://codemotion.yurba.one/github">CodeMotion IDE</a>
9
+
10
+ <a href="https://codemotion.yurba.one/telegram">Telegram (News on russian)</a>
11
+ </div>
12
+
13
+ <br>
14
+
15
+ > [!IMPORTANT]
16
+ > Slim is still early in development. Please report bugs in Issues and send pull requests for improvements.
17
+
18
+ ## Why Slim?
19
+
20
+ Slim is for projects where data validation needs to stay in the running program. It compiles to JavaScript and works directly with npm packages and the JavaScript ecosystem.
21
+
22
+ | | JavaScript | TypeScript | Slim |
23
+ |---|---|---|---|
24
+ | Static checks | No | Yes | Yes, where possible |
25
+ | Runtime validation | Manual | Manual or libraries | Built in for typed values and structs |
26
+ | Data modelling | Objects and classes | Interfaces and types | Structs, enums, defaults, inheritance, validators |
27
+ | API and external data | Trust it or validate manually | Types are erased at runtime | Validate at the boundary with a `struct` or `type` |
28
+ | Components | Native APIs or a framework | Native APIs or a framework | Components and custom elements |
29
+ | Tooling | Depends on the project | Depends on the project | Compiler, formatter, test runner, REPL, dev server, and package manager |
30
+
31
+ Slim does not aim to replace TypeScript everywhere. It is useful when runtime guarantees and built-in language tools matter.
32
+
33
+ ## Where Can Slim Be Useful?
34
+
35
+ Slim fits JavaScript projects that need runtime validation at data boundaries: API clients and servers, CLI tools, scripts, UI forms, libraries, and embedded JavaScript environments. It adds structs, validators, and language features that otherwise require separate libraries or conventions.
36
+
37
+ ## How To Install?
38
+
39
+ Requirements:
40
+ - Node.js 22+
41
+ - Git
42
+
43
+ ## Installation
44
+ Create a project folder, then run:
45
+
46
+ - Clone project
47
+
48
+ ```console
49
+ git clone https://github.com/cdmtn/slim-lang.git
50
+ ```
51
+
52
+ - Install all deps
53
+
54
+ ```console
55
+ npm i
56
+ npm link
57
+ ```
58
+ On mac/linux this may cause a permission error, try using `sudo npm link`
59
+
60
+ - Check if all Slim CLI Installed
61
+
62
+ ```console
63
+ slmc --version
64
+ spm --version
65
+ ```
66
+
67
+ - Create Slim config:
68
+
69
+ ```console
70
+ slmc create --config
71
+ slmc config -S main=index
72
+ ```
73
+ - Create `.slim` file:
74
+
75
+ ```console
76
+ slmc create --file index
77
+ slmc run
78
+ ```
79
+
80
+ This creates `slimconfig.json`, sets `index` as the entry point, then compiles and runs `index.slim`.
81
+
82
+ Or scaffold everything at once — `slimconfig.json` plus a starter `index.slim`:
83
+
84
+ ```console
85
+ slmc init
86
+ ```
87
+
88
+ ## Examples
89
+
90
+ Slim keeps type checks at runtime; they are not erased during compilation.
91
+
92
+ **Examples of runtime data validation structures:**
93
+ ```cpp
94
+ struct User {
95
+ name: string | any
96
+ id: int
97
+ roles: string[] // string array (string[])
98
+ }
99
+
100
+ // Let's assume that the data came from an API
101
+ const user = {
102
+ name: "John",
103
+ id: 3,
104
+ roles: [] // null array (null[])
105
+ }
106
+
107
+ User.verify(user) // ❌ StructError: "User.roles" expected string[], got null[]
108
+ ```
109
+
110
+ **Built-in Operators**:
111
+
112
+ ```typescript
113
+ // sizeof
114
+
115
+ log(sizeof [1, 2, 3]) // 3
116
+ log(sizeof { key: "value" }) // 1
117
+
118
+ // empty
119
+
120
+ log(empty []) // true
121
+ log(empty {}) // true
122
+ log(empty null) // true
123
+ log(empty [1, 2, 3]) // false
124
+
125
+ // kindof
126
+
127
+ log(kindof []) // null[]
128
+ log(kindof [1, 2, 3]) // int[]
129
+ log(kindof null) // null
130
+ log(kindof [1, "hello"]) // array
131
+ log(kindof 1.5) // float
132
+
133
+ // or / and (lower into || and &&)
134
+
135
+ log(true or false) // true
136
+ log(true and false) // false
137
+
138
+ // lock
139
+
140
+ const a = { name: "John" }
141
+ lock a;
142
+
143
+ a.name = "Arthur" // ❌ Error: Cannot assign to read only property 'name' of object '#<Object>'
144
+ ```
145
+
146
+ **Basic enum example:**
147
+
148
+ ```cpp
149
+ enum Role {
150
+ Member: 0
151
+ Helper: 1
152
+ Admin: 2
153
+ }
154
+
155
+ const user = {
156
+ name: "John",
157
+ role: 0
158
+ }
159
+
160
+ if(user.role == Role.Member) log(true) // true
161
+ ```
162
+
163
+ Enum members can also be used in struct annotations:
164
+
165
+ ```cpp
166
+ enum Role {
167
+ Member: 0
168
+ Helper: 1
169
+ Admin: 2
170
+ }
171
+
172
+ const user = {
173
+ name: "John",
174
+ role: Role.Admin
175
+ }
176
+
177
+ struct User {
178
+ name: string
179
+ role: Role::Helper
180
+ }
181
+
182
+ User.verify(user) // ❌ StructError: "User.role" expected Role::Helper, got int
183
+ ```
184
+
185
+ Struct fields may use enums and other structs as types.
186
+
187
+ A trailing `?` marks a type nullable (`T?` means `T | null | undefined`):
188
+
189
+ ```cpp
190
+ let name: string? = null // ok
191
+ name = "Slim" // ok
192
+ name = 42 // ❌ TypeError
193
+ ```
194
+
195
+ Tuple types validate a fixed-length array element by element:
196
+
197
+ ```cpp
198
+ let pair: [int, string] = [1, "a"] // ok
199
+ let bad: [int, string] = [1] // ❌ wrong length
200
+ ```
201
+
202
+ You can destructure a typed value; the source is validated before it is unpacked:
203
+
204
+ ```cpp
205
+ struct User { name: string
206
+ id: int }
207
+
208
+ const { name, id }: User = payload // throws if payload is not a valid User
209
+ const [x, y]: [int, int] = point
210
+ ```
211
+
212
+ Types combine with `|` (union — matches any) and `&` (intersection — matches all):
213
+
214
+ ```cpp
215
+ type Positive(v) { return v > 0 }
216
+ type Even(v) { return v % 2 == 0 }
217
+
218
+ let n: Positive & Even = 4 // ok
219
+ let m: Positive & Even = 3 // ❌ TypeError: expected Positive & Even
220
+ ```
221
+
222
+ ## Built-in Runtime Validators
223
+
224
+ Slim ships common validator types you can use in any annotation: `email`, `url`, `uuid`, `positive`, `negative`, `natural`, `nonempty`.
225
+
226
+ ```cpp
227
+ struct Account {
228
+ email: email
229
+ balance: positive
230
+ }
231
+
232
+ let id: uuid = "550e8400-e29b-41d4-a716-446655440000"
233
+ ```
234
+
235
+ ## Struct Defaults, Constructors, and Inheritance
236
+
237
+ Struct fields can declare defaults. `Struct.new(...)` fills them in and validates the result:
238
+
239
+ ```cpp
240
+ struct User {
241
+ name: string
242
+ role: string = "member"
243
+ active: bool = true
244
+ }
245
+
246
+ const u = User.new({ name: "Alice" }) // { name: "Alice", role: "member", active: true }
247
+ ```
248
+
249
+ A struct can `extend` another, inheriting its fields and defaults:
250
+
251
+ ```cpp
252
+ struct Admin extends User {
253
+ level: int
254
+ }
255
+
256
+ const a = Admin.new({ name: "Bob", level: 9 }) // role defaults to "member"
257
+ ```
258
+
259
+ Structs can also carry methods (available on `Struct.new(...)` instances, inherited through `extends`):
260
+
261
+ ```cpp
262
+ struct Greeter {
263
+ name: string
264
+ greet() { return "Hi " + this.name }
265
+ }
266
+
267
+ Greeter.new({ name: "Ada" }).greet() // "Hi Ada"
268
+ ```
269
+
270
+ ## Return Types
271
+
272
+ A function can declare its return type with `-> Type`. Slim checks each `return` and verifies the value at runtime:
273
+
274
+ ```cpp
275
+ func parse(raw: string) -> int {
276
+ return JSON.parse(raw).value
277
+ }
278
+
279
+ parse(`{ "value": 7 }`) // 7
280
+ parse(`{ "value": "seven" }`) // ❌ function "parse" must return int, got string: "seven"
281
+ ```
282
+
283
+ This works with `func`, methods, arrows, and `async` functions. Nested functions keep their own return type:
284
+
285
+ ```cpp
286
+ const double = (n: int) -> int => n * 2
287
+
288
+ async func load(id: int) -> User {
289
+ return await fetchUser(id)
290
+ }
291
+ ```
292
+
293
+ With JSDoc or declarations enabled, the return type is also available to TypeScript (`async` becomes `Promise<T>`).
294
+
295
+ ## Generic Containers
296
+
297
+ `Array<T>`, `Set<T>`, and `Map<K, V>` validate their contents. `Array<T>` and `T[]` are equivalent:
298
+
299
+ ```cpp
300
+ let ids: Array<int> = [1, 2] // ok
301
+ let names: Set<string> = new Set(["a"]) // ok
302
+ let ages: Map<string, int> = new Map([["a", 1]]) // ok
303
+
304
+ let mixed: Array<int> = JSON.parse(`[1, "a"]`) // ❌ rejected: "a" is not an int
305
+ ```
306
+
307
+ `Promise<T>` and bare built-ins (`Map`, `Set`, `Date`, `RegExp`, `Error`) are matched by constructor. A promise resolves too late for its contents to be checked.
308
+
309
+ ## Match Expressions
310
+
311
+ `match` returns the branch whose pattern matches the subject. `_` is the fallback; without it, an unmatched value returns `undefined`. Enum members compare by value.
312
+
313
+ ```cpp
314
+ enum Role {
315
+ Member: 0
316
+ Admin: 2
317
+ }
318
+
319
+ const label = match (user.role) {
320
+ Role.Admin => "administrator",
321
+ Role.Member => "member",
322
+ _ => "guest"
323
+ }
324
+ ```
325
+
326
+ A case can bind the subject and add a `when` guard:
327
+
328
+ ```cpp
329
+ const size = match (n) {
330
+ x when x > 10 => "big",
331
+ x when x > 0 => "small",
332
+ _ => "nonpositive"
333
+ }
334
+ ```
335
+
336
+ A `match` over an enum has to handle every member, or say it does not with `_`:
337
+
338
+ ```cpp
339
+ enum Role { Member: 0
340
+ Admin: 2 }
341
+
342
+ match (role) {
343
+ Role.Admin => "administrator"
344
+ } // ❌ match on "Role" does not handle Role.Member — add the missing case or a "_" fallback
345
+ ```
346
+
347
+ Slim skips this check for guarded cases and non-enum subjects.
348
+
349
+ ## Error Handling
350
+
351
+ An uncaught top-level error is reported and exits the process. Use a handler when the process should stay alive:
352
+
353
+ ```cpp
354
+ onError((err) => {
355
+ log("handled:", err.message)
356
+ })
357
+ ```
358
+
359
+ ## Testing
360
+
361
+ Write tests in `.slim` files named `*.test.slim` using the built-in `test`, `assert`, and `assertEqual`:
362
+
363
+ ```cpp
364
+ test("addition works", () => {
365
+ assertEqual(2 + 2, 4)
366
+ })
367
+ ```
368
+
369
+ Run them all with:
370
+
371
+ ```console
372
+ slmc test
373
+ ```
374
+
375
+ ## Formatting
376
+
377
+ Reindent Slim source (strings, templates, and comments are left untouched):
378
+
379
+ ```console
380
+ slmc fmt index
381
+ ```
382
+
383
+ ## REPL
384
+
385
+ Bindings persist between REPL lines:
386
+
387
+ ```console
388
+ slmc repl
389
+ ```
390
+
391
+ ```
392
+ slim> struct User { name: string }
393
+ slim> log(User.new({ name: "Ada" }).name)
394
+ Ada
395
+ ```
396
+
397
+ ## Static Type Checking
398
+
399
+ Before building, Slim checks literals, arithmetic, arrays, struct instances, and typed parameters. A definite conflict is a compile error, so no output is written to `dist`:
400
+
401
+ ```console
402
+ TypeError: "User.age" expects int, got string
403
+ at index.slim:10:37
404
+
405
+ const u: User = { name: "Ada", age: "old" }
406
+ ^
407
+ ```
408
+
409
+ It catches invalid literals and reassignments, invalid struct fields, missing field access, array or tuple element errors, incompatible returns, incomplete enum matches, and calls with the wrong arguments:
410
+
411
+ ```cpp
412
+ struct User {
413
+ name: string
414
+ age: int
415
+ }
416
+
417
+ func greet(user: User, times: int) { return user.name }
418
+
419
+ let n: int = 1.5 // ❌ expects int, got float
420
+ const u: User = { name: "Ada", age: "old" } // ❌ "User.age" expects int, got string
421
+ greet(u) // ❌ expects 2 arguments, got 1
422
+ log(u.nmae) // ❌ "User" has no field "nmae"
423
+ ```
424
+
425
+ Inference is intentionally conservative. Calls into npm packages, unknown imports, and custom `type` validators remain unknown and are checked at runtime. Slim reports only conflicts it can prove, so normal narrowing with unions remains valid:
426
+
427
+ ```cpp
428
+ let v: int | string = 1
429
+
430
+ if (kindof v == "int") {
431
+ let n: int = v // fine — the runtime confirms it
432
+ }
433
+
434
+ let b: bool = v // ❌ no arm of int | string can ever be a bool
435
+ ```
436
+
437
+ Checks follow `use` across files, so imported structs, enums, custom types, and function signatures are checked at their call sites:
438
+
439
+ ```cpp
440
+ // models.slim
441
+ export struct User {
442
+ name: string
443
+ role: Role
444
+ }
445
+
446
+ export func greet(user: User) -> string { return user.name }
447
+ ```
448
+
449
+ ```cpp
450
+ // index.slim
451
+ use { User, greet } from "./models"
452
+
453
+ const u: User = { name: 1 } // ❌ "User.name" expects string, got int
454
+ let n: int = greet(u) // ❌ "n" expects int, got string
455
+ ```
456
+
457
+ Inherited struct fields and types used only inside an imported module are also resolved. A local declaration takes precedence over an imported name.
458
+
459
+ Static checks run before runtime validation. Disable them with `slmc build --no-check` or in the config:
460
+
461
+ ```json
462
+ {
463
+ "main": "index",
464
+ "check": false
465
+ }
466
+ ```
467
+
468
+ ## Release Mode
469
+
470
+ Typed variables, parameters, and struct fields are validated at runtime by default. Production builds can remove those implicit checks while keeping explicit `struct.verify()` calls, operators, and `lock`:
471
+
472
+ ```console
473
+ slmc run --release
474
+ ```
475
+
476
+ This sets `SLIM_RELEASE=1`. You can use the same variable when running compiled output directly:
477
+
478
+ ```console
479
+ SLIM_RELEASE=1 node dist/index.js
480
+ ```
481
+
482
+ ## TypeScript-Compatible Types (JSDoc)
483
+
484
+ Slim can add JSDoc to compiled JavaScript so editors and `tsc --checkJs` understand Slim types. Enable it in `slimconfig.json`:
485
+
486
+ ```json
487
+ {
488
+ "main": "index",
489
+ "jsdoc": true
490
+ }
491
+ ```
492
+
493
+ Structs, typed functions, and typed variables then compile to plain JS with JSDoc:
494
+
495
+ ```js
496
+ /**
497
+ * @typedef {{ name: string, id: number, roles: string[] }} User
498
+ */
499
+
500
+ /** @param {User} user */
501
+ function greet(user) { /* runtime check + return */ }
502
+
503
+ /** @type {number} */
504
+ let count = 0
505
+ ```
506
+
507
+ Each `struct` provides a runtime validator and a static type. `int` and `float` map to `number`, `bool` to `boolean`, and optional fields to `field?`. Enums and custom types emit typedefs too.
508
+
509
+ With `jsdoc` enabled, Slim adds `// @ts-check`, writes `dist/external/slim-globals.d.ts`, and creates `jsconfig.json` when one does not exist:
510
+
511
+ ```console
512
+ npx tsc -p jsconfig.json
513
+ ```
514
+
515
+ TypeScript flags invalid arguments and property access. Slim continues to validate typed variable initializers and reassignments at runtime.
516
+
517
+ ### Publishing a Slim library for TypeScript
518
+
519
+ For a Slim library consumed from TypeScript, enable declaration files. Each compiled module receives a `.d.ts` sidecar for exported structs, enums, custom types, and typed functions:
520
+
521
+ ```json
522
+ {
523
+ "main": "index",
524
+ "declarations": true
525
+ }
526
+ ```
527
+
528
+ Point `package.json` at the generated entry declaration so consumers pick it up:
529
+
530
+ ```json
531
+ {
532
+ "types": "dist/index.d.ts"
533
+ }
534
+ ```
535
+
536
+ A TypeScript project can then import `User` and `greet` from the package with type checking intact.
537
+
538
+ ## Custom Elements
539
+
540
+ Mark a component as `element` to compile it as both a custom element and a function. `Tab` becomes `slim-tab`, `TabBar` becomes `slim-tab-bar`, and the tag is available as `Tab.tag`:
541
+
542
+ ```cpp
543
+ element component Tab(props) {
544
+ onMount((host) => {
545
+ host.setLabel = (text) => { host.querySelector(".label").textContent = text }
546
+ })
547
+
548
+ onConnect((host) => { log("in the document") })
549
+ onUnmount((host) => { log("removed") })
550
+
551
+ return <div class="tab"><span class="label">${props.label}</span></div>
552
+ }
553
+ ```
554
+
555
+ ```html
556
+ <slim-tab label="Overview"></slim-tab>
557
+ ```
558
+
559
+ The host element is the mount target, and `onMount` receives it. In this example, `host.setLabel` becomes a method of `<slim-tab>`:
560
+ `document.querySelector("slim-tab").setLabel("…")`.
561
+
562
+ `element` also keeps the component callable as a regular function:
563
+
564
+ ```js
565
+ EmptyState({ icon: "inbox" }) // the rendered element, as before
566
+ document.createElement("empty-state") // the same component, as a tag
567
+ ```
568
+
569
+ `onConnect` maps to `connectedCallback`; `onUnmount` maps to `disconnectedCallback`.
570
+
571
+ Interpolating a component preserves the same DOM node, including its methods and listeners:
572
+
573
+ ```cpp
574
+ component SideLeft(props) {
575
+ onMount((el) => { el.setActive = (id) => { /* … */ } })
576
+ return <nav class="side-left">${props.items}</nav>
577
+ }
578
+
579
+ element("music-left") component MusicLeft() {
580
+ return ${SideLeft({ items: playlists() })}
581
+ }
582
+ ```
583
+
584
+ `document.querySelector("music-left").setActive(…)` reaches the inner component method.
585
+
586
+ Props can come from attributes or the `props` property. Properties take precedence, accept any value, and trigger a re-render when reassigned. Values set before the element upgrades are preserved:
587
+
588
+ ```js
589
+ const tab = document.createElement("slim-tab")
590
+ tab.props = { label: "Overview", items: [1, 2, 3] }
591
+ document.body.append(tab)
592
+ ```
593
+
594
+ A tag renders on connection and when `props` changes. There is no hydration; server output is markup, not state. On the server or in an embedded engine, registration is skipped and the component remains a function.
595
+
596
+ ## Embedding Slim in a Host Engine
597
+
598
+ Slim uses one of two runtimes per file:
599
+
600
+ - `external/core.js` — the type system, structs, enums, operators, validators and
601
+ test helpers. It imports no Node built-in, touches no filesystem, and assumes no
602
+ DOM. Every `process` reference is guarded, so the module loads where `process`
603
+ does not exist at all.
604
+ - `external/defaults.js` — the core plus the DOM layer components render through
605
+ (linkedom on the server) and the file reading that puts a source line in an error.
606
+
607
+ Files without components use `core.js`, which also works in hosts such as QuickJS:
608
+
609
+ ```cpp
610
+ struct Enemy {
611
+ name: string
612
+ hp: positive
613
+ }
614
+
615
+ func spawn(payload: string) -> Enemy {
616
+ return JSON.parse(payload) // ❌ throws if the host sent a bad shape
617
+ }
618
+ ```
619
+
620
+ The output is plain ES modules. A host with module support (for QuickJS, `JS_SetModuleLoaderFunc`) needs:
621
+
622
+ - **`console.log`**, which `log()` and friends call. QuickJS's `qjs` has it; an
623
+ embedder linking `libquickjs` usually defines it.
624
+ - **A module loader**, resolving `./external/core.js` relative to the entry.
625
+
626
+ Data from the host is validated when it reaches a Slim struct. Where performance matters, use release mode and keep explicit `verify()` calls.
627
+
628
+ ## Dev Server
629
+
630
+ `slmc server` watches the project and rebuilds on `.slim` changes (`node_modules`, `dist`, and `.git` are ignored). Point the watcher at a specific folder with the `watch` key in `slimconfig.json`:
631
+
632
+ ```json
633
+ {
634
+ "main": "index",
635
+ "watch": "src"
636
+ }
637
+ ```
638
+
639
+ ## Using npm Packages
640
+
641
+ Slim compiles to plain ES modules, so npm packages work directly. `use` resolves a bare specifier to an installed package when no local `.slim` file has that name:
642
+
643
+ ```
644
+ use { Command } from "commander" // → import { Command } from "commander"
645
+ ```
646
+
647
+ A plain JavaScript `import` also passes through. Use a `struct` or `type` to validate data returned by an external library:
648
+
649
+ ```
650
+ use { fetchUser } from "some-api-client"
651
+
652
+ struct User {
653
+ name: string
654
+ id: int
655
+ }
656
+
657
+ const user: User = await fetchUser(3) // ❌ throws if the payload is not a valid User
658
+ ```
659
+
660
+ ## Package Versions
661
+
662
+ `spm i <name>` records each installed package's resolved version and repository in `spm.lock.json`. Inspect them with:
663
+
664
+ ```console
665
+ spm lock
666
+ ```
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@slim-lang/core",
3
+ "version": "1.2.0",
4
+ "description": "Slim extends JavaScript with runtime types, structs, operators, and components, compiling to plain Javascript",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cdmtn/slim-lang.git"
10
+ },
11
+ "scripts": {
12
+ "slim:build": "node src/compile.js",
13
+ "slim": "node src/compile.js && node run-slim.js",
14
+ "slim:server": "node run-dev-slim.js",
15
+ "release:patch": "npm version patch && git push --follow-tags && npm publish",
16
+ "release:minor": "npm version minor && git push --follow-tags && npm publish",
17
+ "release:major": "npm version major && git push --follow-tags && npm publish",
18
+ "test": "node --test"
19
+ },
20
+ "files": [
21
+ "packages",
22
+ "src",
23
+ "run-dev-slim.js",
24
+ "run-slim.js",
25
+ "README.md"
26
+ ],
27
+ "bin": {
28
+ "slmc": "./src/bin/cli.js",
29
+ "spm": "./src/bin/spm.js"
30
+ },
31
+ "keywords": [],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "dependencies": {
35
+ "@babel/generator": "^7.29.7",
36
+ "@babel/parser": "^7.29.7",
37
+ "@babel/traverse": "^7.29.7",
38
+ "@babel/types": "^7.29.7",
39
+ "@jridgewell/gen-mapping": "^0.3.13",
40
+ "@jridgewell/remapping": "^2.3.5",
41
+ "chalk": "^6.0.0",
42
+ "chokidar": "^5.0.0",
43
+ "commander": "^15.0.0",
44
+ "keytar": "^7.9.0",
45
+ "linkedom": "^0.18.13",
46
+ "magic-string": "^0.30.21",
47
+ "open": "^11.0.1",
48
+ "source-map": "^0.7.6",
49
+ "tar": "^7.5.22"
50
+ },
51
+ "devDependencies": {
52
+ "@babel/plugin-transform-typescript": "^7.29.7",
53
+ "typescript": "^5.9.3"
54
+ }
55
+ }