kimchilang 1.0.1

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 (90) hide show
  1. package/.github/workflows/ci.yml +66 -0
  2. package/README.md +1547 -0
  3. package/create-kimchi-app/README.md +44 -0
  4. package/create-kimchi-app/index.js +214 -0
  5. package/create-kimchi-app/package.json +22 -0
  6. package/editors/README.md +121 -0
  7. package/editors/sublime/KimchiLang.sublime-syntax +138 -0
  8. package/editors/vscode/README.md +90 -0
  9. package/editors/vscode/kimchilang-1.1.0.vsix +0 -0
  10. package/editors/vscode/language-configuration.json +37 -0
  11. package/editors/vscode/package.json +55 -0
  12. package/editors/vscode/src/extension.js +354 -0
  13. package/editors/vscode/syntaxes/kimchi.tmLanguage.json +215 -0
  14. package/examples/api/client.km +36 -0
  15. package/examples/async_pipe.km +58 -0
  16. package/examples/basic.kimchi +109 -0
  17. package/examples/cli_framework/README.md +92 -0
  18. package/examples/cli_framework/calculator.km +61 -0
  19. package/examples/cli_framework/deploy.km +126 -0
  20. package/examples/cli_framework/greeter.km +26 -0
  21. package/examples/config.static +27 -0
  22. package/examples/config.static.js +10 -0
  23. package/examples/env_test.km +37 -0
  24. package/examples/fibonacci.kimchi +17 -0
  25. package/examples/greeter.km +15 -0
  26. package/examples/hello.js +1 -0
  27. package/examples/hello.kimchi +3 -0
  28. package/examples/js_interop.km +42 -0
  29. package/examples/logger_example.km +34 -0
  30. package/examples/memo_fibonacci.km +17 -0
  31. package/examples/myapp/lib/http.js +14 -0
  32. package/examples/myapp/lib/http.km +16 -0
  33. package/examples/myapp/main.km +16 -0
  34. package/examples/myapp/main_with_mock.km +42 -0
  35. package/examples/myapp/services/api.js +18 -0
  36. package/examples/myapp/services/api.km +18 -0
  37. package/examples/new_features.kimchi +52 -0
  38. package/examples/project_example.static +20 -0
  39. package/examples/readme_examples.km +240 -0
  40. package/examples/reduce_pattern_match.km +85 -0
  41. package/examples/regex_match.km +46 -0
  42. package/examples/sample.js +45 -0
  43. package/examples/sample.km +39 -0
  44. package/examples/secrets.static +35 -0
  45. package/examples/secrets.static.js +30 -0
  46. package/examples/shell-example.mjs +144 -0
  47. package/examples/shell_example.km +19 -0
  48. package/examples/stdlib_test.km +22 -0
  49. package/examples/test_example.km +69 -0
  50. package/examples/testing/README.md +88 -0
  51. package/examples/testing/http_client.km +18 -0
  52. package/examples/testing/math.km +48 -0
  53. package/examples/testing/math.test.km +93 -0
  54. package/examples/testing/user_service.km +29 -0
  55. package/examples/testing/user_service.test.km +72 -0
  56. package/examples/use-config.mjs +141 -0
  57. package/examples/use_config.km +13 -0
  58. package/install.sh +59 -0
  59. package/package.json +29 -0
  60. package/pantry/acorn/index.km +1 -0
  61. package/pantry/is_number/index.km +1 -0
  62. package/pantry/is_odd/index.km +2 -0
  63. package/project.static +6 -0
  64. package/src/cli.js +1245 -0
  65. package/src/generator.js +1241 -0
  66. package/src/index.js +141 -0
  67. package/src/js2km.js +568 -0
  68. package/src/lexer.js +822 -0
  69. package/src/linter.js +810 -0
  70. package/src/package-manager.js +307 -0
  71. package/src/parser.js +1876 -0
  72. package/src/static-parser.js +500 -0
  73. package/src/typechecker.js +950 -0
  74. package/stdlib/array.km +0 -0
  75. package/stdlib/bitwise.km +38 -0
  76. package/stdlib/console.km +49 -0
  77. package/stdlib/date.km +97 -0
  78. package/stdlib/function.km +44 -0
  79. package/stdlib/http.km +197 -0
  80. package/stdlib/http.md +333 -0
  81. package/stdlib/index.km +26 -0
  82. package/stdlib/json.km +17 -0
  83. package/stdlib/logger.js +114 -0
  84. package/stdlib/logger.km +104 -0
  85. package/stdlib/math.km +120 -0
  86. package/stdlib/object.km +41 -0
  87. package/stdlib/promise.km +33 -0
  88. package/stdlib/string.km +93 -0
  89. package/stdlib/testing.md +265 -0
  90. package/test/test.js +599 -0
@@ -0,0 +1,307 @@
1
+ // KimchiLang Package Manager
2
+ // Handles fetching and managing external dependencies from GitHub
3
+
4
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'fs';
5
+ import { resolve, join, dirname, basename } from 'path';
6
+ import { execSync } from 'child_process';
7
+ import { parseStaticFile } from './static-parser.js';
8
+
9
+ const DEPS_DIR = '.km_modules';
10
+ const LOCK_FILE = '.km_modules/.lock.json';
11
+
12
+ /**
13
+ * Parse project.static and extract dependencies
14
+ */
15
+ export function parseProjectFile(projectPath = '.') {
16
+ const projectFile = resolve(projectPath, 'project.static');
17
+
18
+ if (!existsSync(projectFile)) {
19
+ return null;
20
+ }
21
+
22
+ const source = readFileSync(projectFile, 'utf-8');
23
+ const declarations = parseStaticFile(source, 'project');
24
+
25
+ return declarations;
26
+ }
27
+
28
+ /**
29
+ * Extract dependency URLs from project declarations
30
+ */
31
+ export function getDependencies(declarations) {
32
+ if (!declarations || !declarations.depend) {
33
+ return [];
34
+ }
35
+
36
+ const depend = declarations.depend;
37
+
38
+ if (depend.type !== 'array') {
39
+ throw new Error('depend must be an array of GitHub URLs');
40
+ }
41
+
42
+ return depend.values.map(v => {
43
+ if (v.type !== 'literal' || typeof v.value !== 'string') {
44
+ throw new Error('Each dependency must be a string URL');
45
+ }
46
+ return v.value;
47
+ });
48
+ }
49
+
50
+ /**
51
+ * Parse a GitHub URL into owner, repo, and optional path/ref
52
+ * Supports formats:
53
+ * - github.com/owner/repo
54
+ * - github.com/owner/repo/path/to/module
55
+ * - github.com/owner/repo@tag
56
+ * - github.com/owner/repo/path@tag
57
+ */
58
+ export function parseGitHubUrl(url) {
59
+ // Remove protocol if present
60
+ let cleanUrl = url.replace(/^https?:\/\//, '');
61
+
62
+ // Must start with github.com
63
+ if (!cleanUrl.startsWith('github.com/')) {
64
+ throw new Error(`Invalid GitHub URL: ${url}. Must start with github.com/`);
65
+ }
66
+
67
+ cleanUrl = cleanUrl.replace('github.com/', '');
68
+
69
+ // Check for @ref
70
+ let ref = 'main';
71
+ if (cleanUrl.includes('@')) {
72
+ const [pathPart, refPart] = cleanUrl.split('@');
73
+ cleanUrl = pathPart;
74
+ ref = refPart;
75
+ }
76
+
77
+ const parts = cleanUrl.split('/');
78
+ if (parts.length < 2) {
79
+ throw new Error(`Invalid GitHub URL: ${url}. Must include owner/repo`);
80
+ }
81
+
82
+ const owner = parts[0];
83
+ const repo = parts[1];
84
+ const subpath = parts.slice(2).join('/') || '';
85
+
86
+ return { owner, repo, subpath, ref };
87
+ }
88
+
89
+ /**
90
+ * Generate a unique directory name for a dependency
91
+ */
92
+ export function getDependencyDirName(owner, repo, ref) {
93
+ const safeName = `${owner}_${repo}`;
94
+ if (ref && ref !== 'main' && ref !== 'master') {
95
+ return `${safeName}_${ref.replace(/[^a-zA-Z0-9]/g, '_')}`;
96
+ }
97
+ return safeName;
98
+ }
99
+
100
+ /**
101
+ * Fetch a dependency from GitHub
102
+ */
103
+ export function fetchDependency(url, projectPath = '.') {
104
+ const { owner, repo, subpath, ref } = parseGitHubUrl(url);
105
+ const depsDir = resolve(projectPath, DEPS_DIR);
106
+ const dirName = getDependencyDirName(owner, repo, ref);
107
+ const targetDir = join(depsDir, dirName);
108
+
109
+ // Create deps directory if needed
110
+ if (!existsSync(depsDir)) {
111
+ mkdirSync(depsDir, { recursive: true });
112
+ }
113
+
114
+ // Check if already exists
115
+ if (existsSync(targetDir)) {
116
+ console.log(` ✓ ${owner}/${repo} already installed`);
117
+ return { dirName, targetDir, subpath };
118
+ }
119
+
120
+ console.log(` ↓ Fetching ${owner}/${repo}@${ref}...`);
121
+
122
+ // Clone the repository
123
+ const cloneUrl = `https://github.com/${owner}/${repo}.git`;
124
+
125
+ try {
126
+ execSync(`git clone --depth 1 --branch ${ref} ${cloneUrl} ${targetDir}`, {
127
+ stdio: 'pipe',
128
+ cwd: projectPath,
129
+ });
130
+
131
+ // Remove .git directory to save space
132
+ const gitDir = join(targetDir, '.git');
133
+ if (existsSync(gitDir)) {
134
+ rmSync(gitDir, { recursive: true, force: true });
135
+ }
136
+
137
+ console.log(` ✓ Installed ${owner}/${repo}`);
138
+ } catch (error) {
139
+ // Try without branch (might be a tag)
140
+ try {
141
+ execSync(`git clone --depth 1 ${cloneUrl} ${targetDir}`, {
142
+ stdio: 'pipe',
143
+ cwd: projectPath,
144
+ });
145
+
146
+ // Checkout the specific ref
147
+ execSync(`git checkout ${ref}`, {
148
+ stdio: 'pipe',
149
+ cwd: targetDir,
150
+ });
151
+
152
+ // Remove .git directory
153
+ const gitDir = join(targetDir, '.git');
154
+ if (existsSync(gitDir)) {
155
+ rmSync(gitDir, { recursive: true, force: true });
156
+ }
157
+
158
+ console.log(` ✓ Installed ${owner}/${repo}@${ref}`);
159
+ } catch (innerError) {
160
+ throw new Error(`Failed to fetch ${url}: ${innerError.message}`);
161
+ }
162
+ }
163
+
164
+ return { dirName, targetDir, subpath };
165
+ }
166
+
167
+ /**
168
+ * Install all dependencies from project.static
169
+ */
170
+ export function installDependencies(projectPath = '.') {
171
+ const declarations = parseProjectFile(projectPath);
172
+
173
+ if (!declarations) {
174
+ console.log('No project.static found');
175
+ return [];
176
+ }
177
+
178
+ const deps = getDependencies(declarations);
179
+
180
+ if (deps.length === 0) {
181
+ console.log('No dependencies declared');
182
+ return [];
183
+ }
184
+
185
+ console.log(`Installing ${deps.length} dependencies...\n`);
186
+
187
+ const installed = [];
188
+
189
+ for (const url of deps) {
190
+ try {
191
+ const result = fetchDependency(url, projectPath);
192
+ installed.push({ url, ...result });
193
+ } catch (error) {
194
+ console.error(` ✗ Failed to install ${url}: ${error.message}`);
195
+ }
196
+ }
197
+
198
+ // Write lock file
199
+ writeLockFile(projectPath, installed);
200
+
201
+ console.log(`\nInstalled ${installed.length}/${deps.length} dependencies`);
202
+
203
+ return installed;
204
+ }
205
+
206
+ /**
207
+ * Write lock file with installed dependency info
208
+ */
209
+ export function writeLockFile(projectPath, installed) {
210
+ const lockPath = resolve(projectPath, LOCK_FILE);
211
+ const lockDir = dirname(lockPath);
212
+
213
+ if (!existsSync(lockDir)) {
214
+ mkdirSync(lockDir, { recursive: true });
215
+ }
216
+
217
+ const lockData = {
218
+ version: 1,
219
+ installedAt: new Date().toISOString(),
220
+ dependencies: installed.map(dep => ({
221
+ url: dep.url,
222
+ dirName: dep.dirName,
223
+ subpath: dep.subpath,
224
+ })),
225
+ };
226
+
227
+ writeFileSync(lockPath, JSON.stringify(lockData, null, 2));
228
+ }
229
+
230
+ /**
231
+ * Read lock file
232
+ */
233
+ export function readLockFile(projectPath = '.') {
234
+ const lockPath = resolve(projectPath, LOCK_FILE);
235
+
236
+ if (!existsSync(lockPath)) {
237
+ return null;
238
+ }
239
+
240
+ return JSON.parse(readFileSync(lockPath, 'utf-8'));
241
+ }
242
+
243
+ /**
244
+ * Resolve a module path to check if it's an external dependency
245
+ * Returns the file path if found in deps, null otherwise
246
+ */
247
+ export function resolveExternalModule(modulePath, projectPath = '.') {
248
+ const lockData = readLockFile(projectPath);
249
+
250
+ if (!lockData || !lockData.dependencies) {
251
+ return null;
252
+ }
253
+
254
+ const depsDir = resolve(projectPath, DEPS_DIR);
255
+
256
+ // Check each installed dependency
257
+ for (const dep of lockData.dependencies) {
258
+ const depDir = join(depsDir, dep.dirName);
259
+
260
+ // If subpath is specified, check if module path starts with a matching pattern
261
+ if (dep.subpath) {
262
+ // The module might be referenced by the subpath name
263
+ const subpathName = basename(dep.subpath);
264
+ if (modulePath.startsWith(subpathName + '.') || modulePath === subpathName) {
265
+ const relativePath = modulePath.replace(subpathName, '').replace(/^\./, '');
266
+ const fullPath = join(depDir, dep.subpath, relativePath.replace(/\./g, '/'));
267
+
268
+ // Try various extensions
269
+ for (const ext of ['.km', '.kimchi', '.kc', '/index.km', '/index.kimchi', '/index.kc']) {
270
+ const tryPath = fullPath + ext;
271
+ if (existsSync(tryPath)) {
272
+ return tryPath;
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ // Try matching by repo name
279
+ const repoName = dep.dirName.split('_')[1] || dep.dirName;
280
+ if (modulePath.startsWith(repoName + '.') || modulePath === repoName) {
281
+ const relativePath = modulePath.replace(repoName, '').replace(/^\./, '');
282
+ const fullPath = join(depDir, relativePath.replace(/\./g, '/'));
283
+
284
+ // Try various extensions
285
+ for (const ext of ['.km', '.kimchi', '.kc', '/index.km', '/index.kimchi', '/index.kc']) {
286
+ const tryPath = fullPath + ext;
287
+ if (existsSync(tryPath)) {
288
+ return tryPath;
289
+ }
290
+ }
291
+ }
292
+ }
293
+
294
+ return null;
295
+ }
296
+
297
+ /**
298
+ * Clean installed dependencies
299
+ */
300
+ export function cleanDependencies(projectPath = '.') {
301
+ const depsDir = resolve(projectPath, DEPS_DIR);
302
+
303
+ if (existsSync(depsDir)) {
304
+ rmSync(depsDir, { recursive: true, force: true });
305
+ console.log('Removed .km_modules');
306
+ }
307
+ }