@slim-lang/core 1.2.0 → 1.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slim-lang/core",
3
- "version": "1.2.0",
3
+ "version": "1.2.3",
4
4
  "description": "Slim extends JavaScript with runtime types, structs, operators, and components, compiling to plain Javascript",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,43 @@
1
+ import "./C:/Users/zeral/AppData/Local/Temp/claude/D--dev-slim-github/72243d74-23bd-4f88-a955-b7ed76b7e749/scratchpad/myapp/dist/external/core.js";
2
+ import * as crypto from "node:crypto";
3
+ import { SHA256, QuickHash } from "../types/hash.js";
4
+ let _slimType = {};
5
+ export const StringHelper = __typed_variable__({
6
+ len(str) {
7
+ let _slimType2 = {};
8
+ __typed_parameter__(str, __type_spec__(__type_ref__("string")), _slimType2, "str", false, `function "len": argument<0> "str" expected string, got ${type(str)}`);
9
+ if (type(str) == "string") return __typed_return__(str.length, __type_spec__(__type_ref__("int")), "len");
10
+ },
11
+ truncate(str, maxLength) {
12
+ let _slimType3 = {},
13
+ _slimType4 = {};
14
+ __typed_parameter__(str, __type_spec__(__type_ref__("string")), _slimType3, "str", false, `function "truncate": argument<0> "str" expected string, got ${type(str)}`);
15
+ __typed_parameter__(maxLength, __type_spec__(__type_ref__("int")), _slimType4, "maxLength", false, `function "truncate": argument<1> "maxLength" expected int, got ${type(maxLength)}`);
16
+ if (str.length > maxLength) {
17
+ return __typed_return__(str.slice(0, maxLength) + '...', __type_spec__(__type_ref__("string")), "truncate");
18
+ }
19
+ return __typed_return__(str, __type_spec__(__type_ref__("string")), "truncate");
20
+ },
21
+ hash(str) {
22
+ let _slimType5 = {};
23
+ __typed_parameter__(str, __type_spec__(__type_ref__("string")), _slimType5, "str", false, `function "hash": argument<0> "str" expected string, got ${type(str)}`);
24
+ return __typed_return__(crypto.createHash("sha256").update(str).digest("hex"), __type_spec__(__type_ref__("SHA256", () => SHA256)), "hash");
25
+ },
26
+ quickHash(str) {
27
+ let _slimType6 = {};
28
+ __typed_parameter__(str, __type_spec__(__type_ref__("string")), _slimType6, "str", false, `function "quickHash": argument<0> "str" expected string, got ${type(str)}`);
29
+ let hash = 0;
30
+ for (let i = 0; i < str.length; i++) {
31
+ hash = (hash << 5) - hash + str.charCodeAt(i);
32
+ hash |= 0;
33
+ }
34
+ return __typed_return__(hash, __type_spec__(__type_ref__("QuickHash", () => QuickHash)), "quickHash");
35
+ }
36
+ }, __type_spec__(__type_ref__("object")), _slimType, "StringHelper");
37
+ export { StringHelper as default };
38
+ try {
39
+ __lock_object__(StringHelper);
40
+ } catch (__err__) {
41
+ __handle_sync_error__(__err__);
42
+ }
43
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6Ijs7OztBQUdBLGFBQUFBLFlBQUEsR0FBQUMsa0JBQUEsQ0FBNEM7RUFDNUNDLElBQUFDLEdBQUE7SUFBQSxJQUFBQyxVQUFBO0lBQUFDLG1CQUFBLENBQUFGLEdBQUEsRUFBQUcsYUFBQSxDQUFBQyxZQUFBLGFBQUFILFVBQUEsMEVBQUFJLElBQUEsQ0FBQUwsR0FBQTtJQUNBLElBQUFLLElBQUEsQ0FBQUwsR0FBQSxDQUFxQixxQkFBQU0sZ0JBQUEsQ0FBQU4sR0FBQSxDQUFBTyxNQUFBLEVBQUFKLGFBQUEsQ0FBQUMsWUFBQTtFQUNyQjtFQUNBSSxTQUFBUixHQUFBLEVBQUFTLFNBQUE7SUFBQSxJQUFBQyxVQUFBO01BQUFDLFVBQUE7SUFBQVQsbUJBQUEsQ0FBQUYsR0FBQSxFQUFBRyxhQUFBLENBQUFDLFlBQUEsYUFBQU0sVUFBQSwrRUFBQUwsSUFBQSxDQUFBTCxHQUFBO0lBQUFFLG1CQUFBLENBQUFPLFNBQUEsRUFBQU4sYUFBQSxDQUFBQyxZQUFBLFVBQUFPLFVBQUEsd0ZBQUFOLElBQUEsQ0FBQUksU0FBQTtJQUNBLElBQUFULEdBQUEsQ0FBQU8sTUFBQSxHQUFBRSxTQUFBO01BQ0EsT0FBQUgsZ0JBQUEsQ0FBQU4sR0FBQSxDQUFBWSxLQUFBLElBQUFILFNBQUEsV0FBQU4sYUFBQSxDQUFBQyxZQUFBO0lBQ0E7SUFDQSxPQUFBRSxnQkFBQSxDQUFBTixHQUFBLEVBQUFHLGFBQUEsQ0FBQUMsWUFBQTtFQUNBO0VBQ0FTLEtBQUFiLEdBQUE7SUFBQSxJQUFBYyxVQUFBO0lBQUFaLG1CQUFBLENBQUFGLEdBQUEsRUFBQUcsYUFBQSxDQUFBQyxZQUFBLGFBQUFVLFVBQUEsMkVBQUFULElBQUEsQ0FBQUwsR0FBQTtJQUNBLE9BQUFNLGdCQUFBLENBQUFTLE1BQUEsQ0FBQUMsVUFBQSxXQUFBQyxNQUFBLENBQUFqQixHQUFBLEVBQUFrQixNQUFBLFNBQUFmLGFBQUEsQ0FBQUMsWUFBQSxpQkFBQWUsTUFBQTtFQUNBO0VBQ0FDLFVBQUFwQixHQUFBO0lBQUEsSUFBQXFCLFVBQUE7SUFBQW5CLG1CQUFBLENBQUFGLEdBQUEsRUFBQUcsYUFBQSxDQUFBQyxZQUFBLGFBQUFpQixVQUFBLGdGQUFBaEIsSUFBQSxDQUFBTCxHQUFBO0lBQ0EsSUFBQWEsSUFBQTtJQUNBLFNBQUFTLENBQUEsTUFBQUEsQ0FBQSxHQUFBdEIsR0FBQSxDQUFBTyxNQUFBLEVBQUFlLENBQUE7TUFDQVQsSUFBQSxJQUFBQSxJQUFBLFNBQUFBLElBQUEsR0FBQWIsR0FBQSxDQUFBdUIsVUFBQSxDQUFBRCxDQUFBO01BQ0FULElBQUE7SUFDQTtJQUNBLE9BQUFQLGdCQUFBLENBQUFPLElBQUEsRUFBQVYsYUFBQSxDQUFBQyxZQUFBLG9CQUFBb0IsU0FBQTtFQUNBO0FBQ0EsQ0FyQkEsRUFBQXJCLGFBQUEsQ0FBQUMsWUFBQSxhQUFBcUIsU0FBQTtBQUFBLFNBQUE1QixZQUFBLElBQUE2QixPQUFBO0FBcUJFO0VBRUZDLGVBQUEsQ0FBQTlCLFlBQUEsQ0FBaUI7QUFBQSxTQUFBK0IsT0FBQTtFQUFBQyxxQkFBQSxDQUFBRCxPQUFBO0FBQUEiLCJuYW1lcyI6WyJTdHJpbmdIZWxwZXIiLCJfX3R5cGVkX3ZhcmlhYmxlX18iLCJsZW4iLCJzdHIiLCJfc2xpbVR5cGUyIiwiX190eXBlZF9wYXJhbWV0ZXJfXyIsIl9fdHlwZV9zcGVjX18iLCJfX3R5cGVfcmVmX18iLCJ0eXBlIiwiX190eXBlZF9yZXR1cm5fXyIsImxlbmd0aCIsInRydW5jYXRlIiwibWF4TGVuZ3RoIiwiX3NsaW1UeXBlMyIsIl9zbGltVHlwZTQiLCJzbGljZSIsImhhc2giLCJfc2xpbVR5cGU1IiwiY3J5cHRvIiwiY3JlYXRlSGFzaCIsInVwZGF0ZSIsImRpZ2VzdCIsIlNIQTI1NiIsInF1aWNrSGFzaCIsIl9zbGltVHlwZTYiLCJpIiwiY2hhckNvZGVBdCIsIlF1aWNrSGFzaCIsIl9zbGltVHlwZSIsImRlZmF1bHQiLCJfX2xvY2tfb2JqZWN0X18iLCJfX2Vycl9fIiwiX19oYW5kbGVfc3luY19lcnJvcl9fIl0sImlnbm9yZUxpc3QiOltdLCJzb3VyY2VzIjpbIkQ6XFxkZXZcXHNsaW0tZ2l0aHViXFxwYWNrYWdlc1xcc2xpbVxcaGVscGVyc1xcc3RyaW5nLnNsaW0iXSwic291cmNlc0NvbnRlbnQiOlsidXNlICogYXMgY3J5cHRvIGZyb20gXCJub2RlOmNyeXB0b1wiXHJcbnVzZSB7IFNIQTI1NiwgUXVpY2tIYXNoIH0gZnJvbSBAc2xpbS90eXBlcy9oYXNoXHJcblxyXG5leHBvcnQgZGVmYXVsdCBjb25zdCBTdHJpbmdIZWxwZXI6IG9iamVjdCA9IHtcclxuICAgIGxlbihzdHI6IHN0cmluZyk6IGludCB7XHJcbiAgICAgICAgaWYoa2luZG9mIHN0ciA9PSBcInN0cmluZ1wiKSByZXR1cm4gc3RyLmxlbmd0aFxyXG4gICAgfSxcclxuICAgIHRydW5jYXRlKHN0cjogc3RyaW5nLCBtYXhMZW5ndGg6IGludCk6IHN0cmluZyB7XHJcbiAgICAgICAgaWYgKHN0ci5sZW5ndGggPiBtYXhMZW5ndGgpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHN0ci5zbGljZSgwLCBtYXhMZW5ndGgpICsgJy4uLic7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiBzdHI7XHJcbiAgICB9LFxyXG4gICAgaGFzaChzdHI6IHN0cmluZyk6IFNIQTI1NiB7XHJcbiAgICAgICAgcmV0dXJuIGNyeXB0by5jcmVhdGVIYXNoKFwic2hhMjU2XCIpLnVwZGF0ZShzdHIpLmRpZ2VzdChcImhleFwiKVxyXG4gICAgfSxcclxuICAgIHF1aWNrSGFzaChzdHI6IHN0cmluZyk6IFF1aWNrSGFzaCB7XHJcbiAgICAgICAgbGV0IGhhc2ggPSAwO1xyXG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICAgIGhhc2ggPSAoaGFzaCA8PCA1KSAtIGhhc2ggKyBzdHIuY2hhckNvZGVBdChpKTtcclxuICAgICAgICAgICAgaGFzaCB8PSAwO1xyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gaGFzaDtcclxuICAgIH1cclxufVxyXG5cclxubG9jayBTdHJpbmdIZWxwZXI7Il19
@@ -0,0 +1,11 @@
1
+ import "./C:/Users/zeral/AppData/Local/Temp/claude/D--dev-slim-github/72243d74-23bd-4f88-a955-b7ed76b7e749/scratchpad/myapp/dist/external/core.js";
2
+ export const SHA256 = __type_def__("SHA256", v => {
3
+ return __sizeof__(v) == 64;
4
+ }, {});
5
+ export const QuickHash = __type_def__("QuickHash", v => {
6
+ return __sizeof__(v) == 8;
7
+ }, {});
8
+ try {} catch (__err__) {
9
+ __handle_sync_error__(__err__);
10
+ }
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJtYXBwaW5ncyI6IjtBQUFBLGFBQUFBLE1BQUEsR0FBQUMsWUFBQSxXQUFBQyxDQUFBO0VBQUEsT0FBQUMsVUFBQSxDQUFBRCxDQUFBO0FBQUEsTUFFQztBQUNELGFBQUFFLFNBQUEsR0FBQUgsWUFBQSxjQUFBQyxDQUFBO0VBQUEsT0FBQUMsVUFBQSxDQUFBRCxDQUFBO0FBQUE7QUFBQSxjQUFBRyxPQUFBO0VBQUFDLHFCQUFBLENBQUFELE9BQUE7QUFBQSIsIm5hbWVzIjpbIlNIQTI1NiIsIl9fdHlwZV9kZWZfXyIsInYiLCJfX3NpemVvZl9fIiwiUXVpY2tIYXNoIiwiX19lcnJfXyIsIl9faGFuZGxlX3N5bmNfZXJyb3JfXyJdLCJpZ25vcmVMaXN0IjpbXSwic291cmNlcyI6WyJEOlxcZGV2XFxzbGltLWdpdGh1YlxccGFja2FnZXNcXHNsaW1cXHR5cGVzXFxoYXNoLnNsaW0iXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHR5cGUgU0hBMjU2KHYpIHtcclxuICAgIHJldHVybiBzaXplb2YgdiA9PSA2NFxyXG59XHJcbmV4cG9ydCB0eXBlIFF1aWNrSGFzaCh2KSB7XHJcbiAgICByZXR1cm4gc2l6ZW9mIHYgPT0gOFxyXG59Il19
package/run-dev-slim.js CHANGED
@@ -1,7 +1,13 @@
1
1
  import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
2
4
  import chokidar from "chokidar";
3
5
  import { spawn } from "child_process";
4
6
 
7
+ // This script ships in the package; the compiler sits next to it there.
8
+ const packageRoot = path.dirname(fileURLToPath(import.meta.url));
9
+ const compileScript = path.join(packageRoot, "src", "compile.js");
10
+
5
11
  const config = JSON.parse(
6
12
  fs.readFileSync("slimconfig.json", "utf8")
7
13
  );
@@ -68,7 +74,7 @@ function stop() {
68
74
 
69
75
  function compile() {
70
76
  return new Promise(resolve => {
71
- const compiler = spawn("node", ["src/compile.js"], {
77
+ const compiler = spawn("node", [compileScript], {
72
78
  stdio: "inherit"
73
79
  });
74
80
 
package/run-slim.js CHANGED
@@ -7,10 +7,12 @@ import { fileURLToPath } from 'url';
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
9
9
 
10
- const config = JSON.parse(readFileSync(path.join(__dirname, 'slimconfig.json'), 'utf8'));
10
+ const root = process.cwd();
11
+
12
+ const config = JSON.parse(readFileSync(path.join(root, 'slimconfig.json'), 'utf8'));
11
13
  const mainFile = config.main;
12
14
 
13
- const dist = path.join(__dirname, 'dist', `${mainFile}.js`);
15
+ const dist = path.join(root, 'dist', `${mainFile}.js`);
14
16
  const args = ['--enable-source-maps', '--no-warnings', dist];
15
17
 
16
18
  const proc = spawn('node', args, { stdio: 'inherit' });
package/src/bin/cli.js CHANGED
@@ -5,6 +5,7 @@ import { execSync } from "child_process";
5
5
  import { readFile } from 'node:fs/promises';
6
6
  import path from "node:path"
7
7
  import fs from "node:fs"
8
+ import { fileURLToPath } from "node:url"
8
9
 
9
10
  import { log, error, parseValue } from "./helpers.js"
10
11
  import { runTests } from "../test-runner.js"
@@ -14,6 +15,14 @@ import pkg from "../../package.json" with { type: "json" };
14
15
  import defaultConfig from "./config.default.json" with { type: "json" };
15
16
 
16
17
  const root = process.cwd()
18
+ // Toolchain scripts (compiler, runners) live inside the package, which is only
19
+ // the same as the project root during local development. Resolve them from this
20
+ // file's location so the commands work when installed from npm too.
21
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..")
22
+ const compileScript = JSON.stringify(path.join(packageRoot, "src", "compile.js"))
23
+ const runScript = JSON.stringify(path.join(packageRoot, "run-slim.js"))
24
+ const devScript = JSON.stringify(path.join(packageRoot, "run-dev-slim.js"))
25
+
17
26
  const program = new Command();
18
27
  const slimConfigPath = path.join(root, "slimconfig.json")
19
28
  const packagePath = path.join(root, "package.json")
@@ -72,7 +81,7 @@ program
72
81
  if(!params.check) process.env.SLIM_NO_CHECK = "1"
73
82
 
74
83
  if(!params.silent) log("Building...")
75
- execSync("node src/compile.js", { stdio: "inherit" });
84
+ execSync(`node ${compileScript}`, { stdio: "inherit" });
76
85
  if(!params.silent) log("Ready!")
77
86
  });
78
87
 
@@ -90,7 +99,7 @@ program
90
99
  if(params.dev) process.env.SLIM_DEV = "1"
91
100
  if(!params.check) process.env.SLIM_NO_CHECK = "1"
92
101
  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" });
102
+ execSync(`node ${compileScript} && node ${runScript}`, { stdio: "inherit" });
94
103
  });
95
104
 
96
105
  program
@@ -143,11 +152,11 @@ program
143
152
 
144
153
  if(dev) {
145
154
  log("Starting Slim dev server (watch + live reload)...")
146
- execSync(`node run-dev-slim.js${params.hot ? " --hot" : ""}`, { stdio: "inherit" });
155
+ execSync(`node ${devScript}${params.hot ? " --hot" : ""}`, { stdio: "inherit" });
147
156
  }
148
157
  else {
149
158
  log("Starting Slim server...")
150
- execSync("node src/compile.js && node run-slim.js", { stdio: "inherit" });
159
+ execSync(`node ${compileScript} && node ${runScript}`, { stdio: "inherit" });
151
160
  }
152
161
  });
153
162
 
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "main": "path/to/your/files",
3
3
  "usePackages": true,
4
- "uses": "import"
4
+ "uses": "import",
5
+ "packages": "packages"
5
6
  }
@@ -2,6 +2,9 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { rm, mkdir } from 'node:fs/promises';
4
4
  import chalk from "chalk";
5
+ import { projectPackagesDir } from "../modulePaths.js";
6
+
7
+ export { projectPackagesDir };
5
8
 
6
9
  export const formatError = chalk.red.bold
7
10
  export const formatSuccess = chalk.green.bold
@@ -30,10 +33,10 @@ export function error(...text) {
30
33
  }
31
34
 
32
35
  export function isPackageExists(name) {
33
- return fs.existsSync(path.join(rootPath, "packages", name))
36
+ return fs.existsSync(path.join(projectPackagesDir(), name))
34
37
  }
35
38
  export function getPackagePath(name) {
36
- return path.join(rootPath, "packages", name)
39
+ return path.join(projectPackagesDir(), name)
37
40
  }
38
41
 
39
42
  export async function deleteDirectory(dirPath) {
package/src/bin/spm.js CHANGED
@@ -1,9 +1,9 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { Command } from "commander";
4
4
  import { formatError, formatBold, formatItalic, formatSuccess, createFolder, createFile } from "./helpers.js"
5
5
  import pkg from "../../package.json" with { type: "json" };
6
- import { log, isPackageExists, rootPath, deleteDirectory, loading, delay } from "./helpers.js";
6
+ import { log, isPackageExists, rootPath, deleteDirectory, loading, delay, projectPackagesDir } from "./helpers.js";
7
7
 
8
8
  import fs from "node:fs"
9
9
  import path from "node:path"
@@ -27,7 +27,7 @@ function errlog(...args) {
27
27
  console.log(formatError(...args))
28
28
  }
29
29
 
30
- const packagesPath = path.join(rootPath, "packages")
30
+ const packagesPath = projectPackagesDir()
31
31
 
32
32
  function renderObject(obj, indent = 0) {
33
33
  let output = "";
@@ -73,7 +73,7 @@ spm
73
73
  spm
74
74
  .command("list")
75
75
  .action(() => {
76
- const targetDir = path.join(rootPath, "packages")
76
+ const targetDir = packagesPath
77
77
  if (!fs.existsSync(targetDir)) {
78
78
  spmlog("No packages installed (the packages/ directory does not exist)")
79
79
  return
@@ -186,7 +186,7 @@ spm
186
186
  }
187
187
  }
188
188
 
189
- const res = await deleteDirectory(path.join(rootPath, "packages", name))
189
+ const res = await deleteDirectory(path.join(packagesPath, name))
190
190
 
191
191
  if (res) {
192
192
  removeLockEntry(name)
@@ -257,7 +257,7 @@ spm
257
257
  await loading({
258
258
  startMsg: `Creating path for ${name}`,
259
259
  callback: async ({ fail, ok }) => {
260
- const packagePath = path.join(rootPath, "packages", name)
260
+ const packagePath = path.join(packagesPath, name)
261
261
 
262
262
  if (isExists) return fail("Package is already exists")
263
263
  else {
@@ -336,7 +336,7 @@ spm
336
336
  const githubRepo = SPMContent.github.repo
337
337
  const version = SPMContent.version
338
338
  const description = SPMContent.description
339
- const packagePath = path.join("packages", name)
339
+ const packagePath = path.join(packagesPath, name)
340
340
 
341
341
  if ("github" in SPMContent && "repo" in SPMContent.github) {
342
342
  await loading({
@@ -445,7 +445,7 @@ spm
445
445
  spmlog(formatError("No GitHub repository specified"));
446
446
  }
447
447
  } else {
448
- spmlog(formatError(`No package founded: /packages/${name}`));
448
+ spmlog(formatError(`No package founded: ${path.relative(process.cwd(), path.join(packagesPath, name)) || name}`));
449
449
  }
450
450
  });
451
451
 
package/src/checker.js CHANGED
@@ -5,8 +5,6 @@ import { computeLineStarts, offsetToLineCol } from "./sourcemap.js"
5
5
 
6
6
  const traverse = _traverse.default ?? _traverse
7
7
 
8
- // Check annotations before lowering; report only definite type conflicts.
9
-
10
8
  const NUMERIC = new Set(["int", "float", "number"])
11
9
  const KNOWN = new Set([
12
10
  "int", "float", "number", "string", "bool", "null", "undefined",
@@ -87,7 +85,6 @@ function normalize(label) {
87
85
  return trimmed
88
86
  }
89
87
 
90
- // Resolve inherited fields so structs can cross module boundaries.
91
88
  function resolveStruct(name, env, seen = new Set()) {
92
89
  const definition = env.structs.get(name)
93
90
  if (!definition) return null
@@ -135,7 +132,6 @@ export function createEnvironment() {
135
132
  }
136
133
  }
137
134
 
138
- // Module declarations keyed by absolute source path.
139
135
  const moduleTypes = new Map()
140
136
 
141
137
  function signatureFor(name, path_, env) {
@@ -149,7 +145,7 @@ const BUILTIN_CLASSES = new Set(["Map", "Set", "Date", "Promise", "RegExp", "Err
149
145
  function isOpen(label, env) {
150
146
  const base = baseName(label)
151
147
  if (!base) return true
152
- // Custom validators shadow built-ins and cannot be evaluated statically.
148
+
153
149
  if (env.customTypes.has(base)) return true
154
150
  if (KNOWN.has(base) || BUILTIN_CLASSES.has(base)) return false
155
151
  if (env.structs.has(base) || env.enums.has(base)) return false
@@ -217,7 +213,6 @@ function acceptsAtom(target, source, env) {
217
213
  return false
218
214
  }
219
215
 
220
- // A union needs one compatible arm; array literals need every element to fit.
221
216
  export function accepts(target, source, env, requireAll = false) {
222
217
  if (!target || !source) return true
223
218
 
@@ -598,7 +593,6 @@ function checkStructLiteral(structName, node, path_, env, report) {
598
593
  }
599
594
  }
600
595
 
601
- // Inspect lowered match expressions to preserve exhaustiveness checks.
602
596
  function matchExpression(node) {
603
597
  if (!t.isCallExpression(node) || node.arguments.length !== 1) return null
604
598
 
@@ -839,7 +833,6 @@ function buildModuleTypes(ast, env) {
839
833
  return record
840
834
  }
841
835
 
842
- // Import known declarations; unresolved modules remain unchecked.
843
836
  function seedImports(env, imports, wildcards) {
844
837
  const merge = (record, name, local) => {
845
838
  if (record.structs.has(name)) env.structs.set(local, record.structs.get(name))
@@ -850,7 +843,6 @@ function seedImports(env, imports, wildcards) {
850
843
 
851
844
  const sources = new Set([...wildcards, ...imports.map(entry => entry.source)])
852
845
 
853
- // Local declarations override explicit and transitive imports.
854
846
  for (const source of sources) {
855
847
  const record = moduleTypes.get(source)
856
848
  if (!record) continue
package/src/compile.js CHANGED
@@ -7,7 +7,7 @@ import { readFile } from 'fs/promises';
7
7
  import { Debug } from "./external/core.js";
8
8
  import { stripComments } from "./parser.js";
9
9
  import { UseError } from "./external/classErrors.js";
10
- import { getDistPath, resolveSlimSource } from "./modulePaths.js";
10
+ import { getDistPath, resolveSlimSource, PACKAGE_ROOT } from "./modulePaths.js";
11
11
 
12
12
  const compiled = new Set()
13
13
 
@@ -18,7 +18,7 @@ let check = true
18
18
  let useStyle = "import"
19
19
 
20
20
  function syncExternal() {
21
- const srcExternal = path.resolve("src/external")
21
+ const srcExternal = path.join(PACKAGE_ROOT, "src/external")
22
22
  const distExternal = path.resolve("dist/external")
23
23
 
24
24
  if (!fs.existsSync(srcExternal)) return
@@ -1,9 +1,35 @@
1
1
  import fs from "node:fs"
2
2
  import path from "node:path"
3
3
  import { isBuiltin } from "node:module"
4
+ import { fileURLToPath } from "node:url"
4
5
 
5
6
  const slimExtension = ".slim"
6
7
 
8
+ export const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..")
9
+
10
+ let projectPackagesCache = null
11
+ export function projectPackagesDir() {
12
+ if (projectPackagesCache) return projectPackagesCache
13
+
14
+ let dir = "packages"
15
+ try {
16
+ const config = JSON.parse(fs.readFileSync(path.join(process.cwd(), "slimconfig.json"), "utf8"))
17
+ if (typeof config.packages === "string" && config.packages.trim()) dir = config.packages
18
+ } catch {}
19
+
20
+ projectPackagesCache = path.resolve(dir)
21
+ return projectPackagesCache
22
+ }
23
+
24
+ function packageRoots() {
25
+ const projectPackages = projectPackagesDir()
26
+ const shippedPackages = path.join(PACKAGE_ROOT, "packages")
27
+
28
+ return projectPackages === shippedPackages
29
+ ? [projectPackages]
30
+ : [projectPackages, shippedPackages]
31
+ }
32
+
7
33
  function isWithin(parent, target) {
8
34
  const relative = path.relative(parent, target)
9
35
  return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative)
@@ -31,23 +57,23 @@ export function getDistPath(slimFile) {
31
57
  }
32
58
 
33
59
  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
60
  if (isBuiltin(raw)) return null
37
61
 
38
62
  if (raw.startsWith("@")) {
39
- const packagesRoot = path.resolve("packages")
40
63
  const packageName = raw.slice("@".length)
41
- const fileSource = path.resolve(packagesRoot, packageName + slimExtension)
64
+ const roots = packageRoots()
42
65
 
43
- if (fs.existsSync(fileSource)) return fileSource
66
+ for (const packagesRoot of roots) {
67
+ const fileSource = path.join(packagesRoot, packageName + slimExtension)
68
+ if (fs.existsSync(fileSource)) return fileSource
44
69
 
45
- const directorySource = path.resolve(packagesRoot, packageName, "main.slim")
46
- if (fs.existsSync(directorySource)) return directorySource
70
+ const directorySource = path.join(packagesRoot, packageName, "main.slim")
71
+ if (fs.existsSync(directorySource)) return directorySource
72
+ }
47
73
 
48
74
  if (isNodeModule(raw)) return null
49
75
 
50
- return fileSource
76
+ return path.join(roots[0], packageName + slimExtension)
51
77
  }
52
78
 
53
79
  if (raw.endsWith(".js")) return null
package/src/parser.js CHANGED
@@ -43,7 +43,6 @@ function wordOperatorEdits(code) {
43
43
  for (const token of tokenize(code)) {
44
44
  if (token.type === "ws" || token.type === "newline" || token.type === "comment") continue
45
45
 
46
- // Lower word operators only after an operand.
47
46
  const replacement = token.type === "name" ? replacements[token.value] : undefined
48
47
  if (replacement && endsExpression(prev)) {
49
48
  edits.push({ start: token.start, end: token.end, replacement })
@@ -290,7 +289,6 @@ function functionEdits(text) {
290
289
  })
291
290
  }
292
291
 
293
- // Cache tokens for whole-file and extracted-body scans.
294
292
  const tokenCache = new Map()
295
293
 
296
294
  function tokensFor(src) {
@@ -309,7 +307,6 @@ function tokensFor(src) {
309
307
  return entry
310
308
  }
311
309
 
312
- // Match brackets by tokens so literals and comments do not affect depth.
313
310
  function readBalanced(src, pos, open = "{", close = "}") {
314
311
  const { tokens, starts } = tokensFor(src)
315
312
  let index = starts.get(pos)
@@ -329,8 +326,6 @@ function readBalanced(src, pos, open = "{", close = "}") {
329
326
  return -1
330
327
  }
331
328
 
332
- // Read balanced arguments after matching a construct head.
333
- // Return type may be introduced by `->` or `:` (e.g. `func f(): number`).
334
329
  const returnArrow = /\s*(?:->|:)\s*/y
335
330
 
336
331
  function headMatcher(head, tail) {
@@ -462,7 +457,6 @@ function topLevelWhen(str) {
462
457
  return -1
463
458
  }
464
459
 
465
- // Recursively lower nested match expressions without touching strings.
466
460
  function lowerMatches(str) {
467
461
  if (!str.includes("match")) return str
468
462
 
@@ -627,8 +621,6 @@ function structuralEdits(text) {
627
621
  let rest = line.slice(idx + 1).trim()
628
622
  if (!field) continue
629
623
 
630
- // Optional fields may be written as `*key` or `key?`; normalize
631
- // both to the `*key` form the struct runtime understands.
632
624
  let optional = false
633
625
  if (field.startsWith("*")) { optional = true; field = field.slice(1).trim() }
634
626
  if (field.endsWith("?")) { optional = true; field = field.slice(0, -1).trim() }
@@ -688,8 +680,6 @@ function structuralEdits(text) {
688
680
  const head = declarationHead.exec(src)
689
681
  if (!head) return null
690
682
 
691
- // `export default const X: T = ...` is not valid JS as a single
692
- // statement; lower it to a typed declaration plus `export default X`.
693
683
  const leadingDefault = src.slice(0, i).match(/export\s+default\s+$/)
694
684
  const start = leadingDefault ? i - leadingDefault[0].length : i
695
685
 
@@ -708,10 +698,6 @@ function structuralEdits(text) {
708
698
  },
709
699
  ({ keyword, name, type, expr, exprStart, defaultExport }) => {
710
700
  const pattern = name.startsWith("{") || name.startsWith("[")
711
- // Keep the declaration exported so the module wrapper leaves it at
712
- // top level, then re-export the binding as default. Emitting a bare
713
- // `const` plus `export default X` would trap the `const` in the
714
- // module's try/catch, leaving the default export undefined.
715
701
  const prefix = defaultExport ? "export " : ""
716
702
  const head = pattern
717
703
  ? `${prefix}${keyword} ${name} = __typed_pattern__(`
@@ -730,7 +716,6 @@ function structuralEdits(text) {
730
716
  /(\w[\w$.]*(?:\[.*?\])?)\s*(?:=>\s*([\w$]+))?\s*\n((?:\s*\|(?!\|)[^\n]+\n?)+)/g,
731
717
  (match, source, alias, pipes) => {
732
718
  const steps = [...pipes.matchAll(/\|\s*([\w$]+)\(([^)]*)\)/g)]
733
- // Preserve non-pipe bars, such as multiline union types.
734
719
  if (steps.length === 0) return match
735
720
 
736
721
  const callbackMethods = new Set([
package/src/transform.js CHANGED
@@ -28,9 +28,6 @@ function getTypeReferenceName(typeText) {
28
28
  return withoutGeneric.split("::")[0]
29
29
  }
30
30
 
31
- // Builtin type names are always matched by label; they must never be resolved
32
- // through a same-named binding in scope (e.g. a `const string` variable would
33
- // otherwise shadow the primitive `string` type).
34
31
  const BUILTIN_TYPE_NAMES = new Set([
35
32
  "int", "float", "number", "string", "bool", "null", "undefined",
36
33
  "object", "array", "function", "any", "element"
@@ -439,7 +436,6 @@ function getRuntimePath(sourceFile, entry) {
439
436
  return relFixed.startsWith(".") ? relFixed : "./" + relFixed
440
437
  }
441
438
 
442
- // DOM globals select the full runtime; pure code uses the portable core.
443
439
  const DOM_RUNTIME = new Set([
444
440
  "htmlToVdom", "HTMLElement", "__html__",
445
441
  "__flush_events__", "__bind_events__", "__lifecycle__",
@@ -460,13 +456,6 @@ function usesDom(ast) {
460
456
  return found
461
457
  }
462
458
 
463
- // `use` mirrors `import`: `use { X } from Y` is a named import, `use * as X`
464
- // a namespace import, and a bare `use X from Y` a default import — exactly like
465
- // `import X from Y`. Named exports (including Slim's `export const`/`export func`)
466
- // must therefore be brought in with braces: `use { X } from Y`.
467
- //
468
- // When `bareIsDefault` is false (the legacy `"uses": "named"` config style), a
469
- // bare `use X from Y` instead lowers to a named import `import { X } from Y`.
470
459
  function parseSpecifiers(name, bareIsDefault = true) {
471
460
  const trimmed = name.trim()
472
461
 
@@ -485,7 +474,6 @@ function parseSpecifiers(name, bareIsDefault = true) {
485
474
  })
486
475
  }
487
476
 
488
- // `use X as Y from Z` renames a named export (there is no `import X as Y`).
489
477
  const aliasMatch = trimmed.match(/^([\w$]+)\s+as\s+([\w$]+)$/)
490
478
  if (aliasMatch) {
491
479
  return [t.importSpecifier(t.identifier(aliasMatch[2]), t.identifier(aliasMatch[1]))]
@@ -530,8 +518,6 @@ function formatSyntaxError(err, originalCode, sourceFile, mapped) {
530
518
  }
531
519
 
532
520
  export function transform(code, sourceFile = "input.ps", options = {}) {
533
- // `"uses": "named"` (or "legacy") restores the old bare-import style where a
534
- // bare `use X from Y` is a named import; the default mirrors `import`.
535
521
  const bareIsDefault = options.uses !== "named" && options.uses !== "legacy"
536
522
  const asyncFunctions = new Set()
537
523
  const imports = new Map()
@@ -601,7 +587,6 @@ export function transform(code, sourceFile = "input.ps", options = {}) {
601
587
  }
602
588
  })
603
589
 
604
- // Check annotations before lowering or writing output.
605
590
  if (options.check !== false) {
606
591
  const moduleImports = []
607
592
  const moduleWildcards = []