tjs-lang 0.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 (91) hide show
  1. package/CONTEXT.md +594 -0
  2. package/LICENSE +190 -0
  3. package/README.md +220 -0
  4. package/bin/benchmarks.ts +351 -0
  5. package/bin/dev.ts +205 -0
  6. package/bin/docs.js +170 -0
  7. package/bin/install-cursor.sh +71 -0
  8. package/bin/install-vscode.sh +71 -0
  9. package/bin/select-local-models.d.ts +1 -0
  10. package/bin/select-local-models.js +28 -0
  11. package/bin/select-local-models.ts +31 -0
  12. package/demo/autocomplete.test.ts +232 -0
  13. package/demo/docs.json +186 -0
  14. package/demo/examples.test.ts +598 -0
  15. package/demo/index.html +91 -0
  16. package/demo/src/autocomplete.ts +482 -0
  17. package/demo/src/capabilities.ts +859 -0
  18. package/demo/src/demo-nav.ts +2097 -0
  19. package/demo/src/examples.test.ts +161 -0
  20. package/demo/src/examples.ts +476 -0
  21. package/demo/src/imports.test.ts +196 -0
  22. package/demo/src/imports.ts +421 -0
  23. package/demo/src/index.ts +639 -0
  24. package/demo/src/module-store.ts +635 -0
  25. package/demo/src/module-sw.ts +132 -0
  26. package/demo/src/playground.ts +949 -0
  27. package/demo/src/service-host.ts +389 -0
  28. package/demo/src/settings.ts +440 -0
  29. package/demo/src/style.ts +280 -0
  30. package/demo/src/tjs-playground.ts +1605 -0
  31. package/demo/src/ts-examples.ts +478 -0
  32. package/demo/src/ts-playground.ts +1092 -0
  33. package/demo/static/favicon.svg +30 -0
  34. package/demo/static/photo-1.jpg +0 -0
  35. package/demo/static/photo-2.jpg +0 -0
  36. package/demo/static/texts/ai-history.txt +9 -0
  37. package/demo/static/texts/coffee-origins.txt +9 -0
  38. package/demo/static/texts/renewable-energy.txt +9 -0
  39. package/dist/index.js +256 -0
  40. package/dist/index.js.map +37 -0
  41. package/dist/tjs-batteries.js +4 -0
  42. package/dist/tjs-batteries.js.map +15 -0
  43. package/dist/tjs-full.js +256 -0
  44. package/dist/tjs-full.js.map +37 -0
  45. package/dist/tjs-transpiler.js +220 -0
  46. package/dist/tjs-transpiler.js.map +21 -0
  47. package/dist/tjs-vm.js +4 -0
  48. package/dist/tjs-vm.js.map +14 -0
  49. package/docs/CNAME +1 -0
  50. package/docs/favicon.svg +30 -0
  51. package/docs/index.html +91 -0
  52. package/docs/index.js +10468 -0
  53. package/docs/index.js.map +92 -0
  54. package/docs/photo-1.jpg +0 -0
  55. package/docs/photo-1.webp +0 -0
  56. package/docs/photo-2.jpg +0 -0
  57. package/docs/photo-2.webp +0 -0
  58. package/docs/texts/ai-history.txt +9 -0
  59. package/docs/texts/coffee-origins.txt +9 -0
  60. package/docs/texts/renewable-energy.txt +9 -0
  61. package/docs/tjs-lang.svg +31 -0
  62. package/docs/tosijs-agent.svg +31 -0
  63. package/editors/README.md +325 -0
  64. package/editors/ace/ajs-mode.js +328 -0
  65. package/editors/ace/ajs-mode.ts +269 -0
  66. package/editors/ajs-syntax.ts +212 -0
  67. package/editors/build-grammars.ts +510 -0
  68. package/editors/codemirror/ajs-language.js +287 -0
  69. package/editors/codemirror/ajs-language.ts +1447 -0
  70. package/editors/codemirror/autocomplete.test.ts +531 -0
  71. package/editors/codemirror/component.ts +404 -0
  72. package/editors/monaco/ajs-monarch.js +243 -0
  73. package/editors/monaco/ajs-monarch.ts +225 -0
  74. package/editors/tjs-syntax.ts +115 -0
  75. package/editors/vscode/language-configuration.json +37 -0
  76. package/editors/vscode/package.json +65 -0
  77. package/editors/vscode/syntaxes/ajs-injection.tmLanguage.json +107 -0
  78. package/editors/vscode/syntaxes/ajs.tmLanguage.json +252 -0
  79. package/editors/vscode/syntaxes/tjs.tmLanguage.json +333 -0
  80. package/package.json +83 -0
  81. package/src/cli/commands/check.ts +41 -0
  82. package/src/cli/commands/convert.ts +133 -0
  83. package/src/cli/commands/emit.ts +260 -0
  84. package/src/cli/commands/run.ts +68 -0
  85. package/src/cli/commands/test.ts +194 -0
  86. package/src/cli/commands/types.ts +20 -0
  87. package/src/cli/create-app.ts +236 -0
  88. package/src/cli/playground.ts +250 -0
  89. package/src/cli/tjs.ts +166 -0
  90. package/src/cli/tjsx.ts +160 -0
  91. package/tjs-lang.svg +31 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * tjs types - Output type metadata as JSON
3
+ */
4
+
5
+ import { readFileSync } from 'fs'
6
+ import { tjs } from '../../lang'
7
+
8
+ export async function types(file: string): Promise<void> {
9
+ const source = readFileSync(file, 'utf-8')
10
+
11
+ const result = tjs(source)
12
+
13
+ // Output the type information as JSON
14
+ const typeInfo = {
15
+ file,
16
+ ...result.types,
17
+ }
18
+
19
+ console.log(JSON.stringify(typeInfo, null, 2))
20
+ }
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * create-tjs-app - Scaffold a new TJS project
4
+ *
5
+ * Usage:
6
+ * bun create tjs-app my-app
7
+ * bunx create-tjs-app my-app
8
+ */
9
+
10
+ import { existsSync, mkdirSync, writeFileSync } from 'fs'
11
+ import { join, resolve } from 'path'
12
+
13
+ const HELP = `
14
+ create-tjs-app - Scaffold a new TJS project
15
+
16
+ Usage:
17
+ bun create tjs-app <project-name>
18
+ bunx create-tjs-app <project-name>
19
+
20
+ Options:
21
+ -h, --help Show this help message
22
+ --minimal Create minimal project (no examples)
23
+
24
+ Examples:
25
+ bun create tjs-app my-app
26
+ bun create tjs-app ./projects/new-app
27
+ bun create tjs-app my-app --minimal
28
+ `
29
+
30
+ const PACKAGE_JSON = (name: string) => `{
31
+ "name": "${name}",
32
+ "version": "0.1.0",
33
+ "type": "module",
34
+ "scripts": {
35
+ "dev": "tjs-playground",
36
+ "check": "tjs check src/",
37
+ "build": "tjs emit src/ -o dist/",
38
+ "test": "tjs test"
39
+ },
40
+ "dependencies": {
41
+ "tjs-lang": "^0.1.0"
42
+ }
43
+ }
44
+ `
45
+
46
+ const MAIN_TJS = `/**
47
+ * Main entry point
48
+ */
49
+
50
+ // A simple function with type validation
51
+ function greet(name: 'World') -> '' {
52
+ return \`Hello, \${name}!\`
53
+ }
54
+
55
+ // Run it
56
+ console.log(greet())
57
+ console.log(greet('TJS'))
58
+ `
59
+
60
+ const UTILS_TJS = `/**
61
+ * Utility functions with runtime type checking
62
+ */
63
+
64
+ // Math utilities
65
+ function add(a: 0, b: 0) -> 0 {
66
+ return a + b
67
+ }
68
+
69
+ function multiply(a: 0, b: 0) -> 0 {
70
+ return a * b
71
+ }
72
+
73
+ // String utilities
74
+ function capitalize(str: '') -> '' {
75
+ if (str.length === 0) return ''
76
+ return str[0].toUpperCase() + str.slice(1)
77
+ }
78
+
79
+ // Array utilities
80
+ function sum(numbers: [0]) -> 0 {
81
+ return numbers.reduce((acc, n) => acc + n, 0)
82
+ }
83
+
84
+ export { add, multiply, capitalize, sum }
85
+ `
86
+
87
+ const UTILS_TEST_TJS = `/**
88
+ * Tests for utility functions
89
+ */
90
+
91
+ import { add, multiply, capitalize, sum } from './utils.tjs'
92
+
93
+ test('add works with numbers', () => {
94
+ expect(add(1, 2)).toBe(3)
95
+ expect(add(-1, 1)).toBe(0)
96
+ expect(add(0.1, 0.2)).toBeCloseTo(0.3)
97
+ })
98
+
99
+ test('multiply works with numbers', () => {
100
+ expect(multiply(2, 3)).toBe(6)
101
+ expect(multiply(-2, 3)).toBe(-6)
102
+ })
103
+
104
+ test('capitalize handles strings', () => {
105
+ expect(capitalize('hello')).toBe('Hello')
106
+ expect(capitalize('')).toBe('')
107
+ expect(capitalize('a')).toBe('A')
108
+ })
109
+
110
+ test('sum adds array elements', () => {
111
+ expect(sum([1, 2, 3])).toBe(6)
112
+ expect(sum([])).toBe(0)
113
+ expect(sum([5])).toBe(5)
114
+ })
115
+
116
+ test('type validation rejects bad inputs', () => {
117
+ // These should return monadic errors, not throw
118
+ const result = add('not', 'numbers')
119
+ expect(result.error).toBeDefined()
120
+ })
121
+ `
122
+
123
+ const README = (name: string) => `# ${name}
124
+
125
+ A TJS (Typed JavaScript) project.
126
+
127
+ ## Getting Started
128
+
129
+ \`\`\`bash
130
+ # Install dependencies
131
+ bun install
132
+
133
+ # Run the playground
134
+ bun run dev
135
+
136
+ # Type check all files
137
+ bun run check
138
+
139
+ # Run tests
140
+ bun run test
141
+
142
+ # Build for production
143
+ bun run build
144
+ \`\`\`
145
+
146
+ ## Project Structure
147
+
148
+ \`\`\`
149
+ ${name}/
150
+ ├── src/
151
+ │ ├── main.tjs # Entry point
152
+ │ ├── utils.tjs # Utility functions
153
+ │ └── utils.test.tjs # Tests
154
+ ├── dist/ # Built output (after build)
155
+ └── package.json
156
+ \`\`\`
157
+
158
+ ## TJS Features
159
+
160
+ TJS adds runtime type validation to JavaScript:
161
+
162
+ \`\`\`javascript
163
+ // Required parameter with type inference from example
164
+ function greet(name: 'Alice') -> '' {
165
+ return \`Hello, \${name}!\`
166
+ }
167
+
168
+ greet('Bob') // Returns "Hello, Bob!"
169
+ greet(123) // Returns { error: 'type mismatch', ... }
170
+ \`\`\`
171
+
172
+ Learn more at the [TJS documentation](https://github.com/tonioloewald/tjs-lang).
173
+ `
174
+
175
+ const GITIGNORE = `node_modules/
176
+ dist/
177
+ .DS_Store
178
+ *.log
179
+ `
180
+
181
+ async function main() {
182
+ const args = process.argv.slice(2)
183
+
184
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
185
+ console.log(HELP)
186
+ process.exit(0)
187
+ }
188
+
189
+ const minimal = args.includes('--minimal')
190
+ const projectArg = args.find((a) => !a.startsWith('-'))
191
+
192
+ if (!projectArg) {
193
+ console.error('Error: Please specify a project name')
194
+ console.error('Usage: bun create tjs-app <project-name>')
195
+ process.exit(1)
196
+ }
197
+
198
+ const projectDir = resolve(projectArg)
199
+ const projectName = projectArg.split('/').pop() || 'tjs-app'
200
+
201
+ // Check if directory exists
202
+ if (existsSync(projectDir)) {
203
+ console.error(`Error: Directory '${projectDir}' already exists`)
204
+ process.exit(1)
205
+ }
206
+
207
+ console.log(`Creating TJS project in ${projectDir}...`)
208
+
209
+ // Create directories
210
+ mkdirSync(projectDir, { recursive: true })
211
+ mkdirSync(join(projectDir, 'src'), { recursive: true })
212
+
213
+ // Write files
214
+ writeFileSync(join(projectDir, 'package.json'), PACKAGE_JSON(projectName))
215
+ writeFileSync(join(projectDir, 'README.md'), README(projectName))
216
+ writeFileSync(join(projectDir, '.gitignore'), GITIGNORE)
217
+ writeFileSync(join(projectDir, 'src', 'main.tjs'), MAIN_TJS)
218
+
219
+ if (!minimal) {
220
+ writeFileSync(join(projectDir, 'src', 'utils.tjs'), UTILS_TJS)
221
+ writeFileSync(join(projectDir, 'src', 'utils.test.tjs'), UTILS_TEST_TJS)
222
+ }
223
+
224
+ console.log(`
225
+ Created ${projectName}!
226
+
227
+ Next steps:
228
+ cd ${projectArg}
229
+ bun install
230
+ bun run dev
231
+
232
+ Happy coding!
233
+ `)
234
+ }
235
+
236
+ main()
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * tjs-playground - Serve the TJS playground locally
4
+ *
5
+ * Usage:
6
+ * tjs-playground [--port 8699]
7
+ * bunx tjs-playground
8
+ */
9
+
10
+ import { watch } from 'fs'
11
+ import { join } from 'path'
12
+ import { $ } from 'bun'
13
+
14
+ const DEFAULT_PORT = 8699 // Homage to Agent-99
15
+
16
+ const HELP = `
17
+ tjs-playground - Serve the TJS playground locally
18
+
19
+ Usage:
20
+ tjs-playground [options]
21
+
22
+ Options:
23
+ -p, --port <port> Port to serve on (default: ${DEFAULT_PORT})
24
+ -h, --help Show this help message
25
+ --no-watch Don't watch for file changes
26
+
27
+ Examples:
28
+ tjs-playground
29
+ tjs-playground --port 3000
30
+ `
31
+
32
+ async function main() {
33
+ const args = process.argv.slice(2)
34
+
35
+ if (args.includes('--help') || args.includes('-h')) {
36
+ console.log(HELP)
37
+ process.exit(0)
38
+ }
39
+
40
+ // Parse port
41
+ const portIdx = args.findIndex((a) => a === '-p' || a === '--port')
42
+ const port = portIdx !== -1 ? parseInt(args[portIdx + 1], 10) : DEFAULT_PORT
43
+
44
+ const noWatch = args.includes('--no-watch')
45
+
46
+ // Find the package root (where docs/ lives)
47
+ // When installed via npm, this will be in node_modules/tjs-lang
48
+ // When running locally, it's the repo root
49
+ let rootDir: string
50
+
51
+ // Check if we're in node_modules
52
+ const scriptPath = import.meta.path
53
+ if (scriptPath.includes('node_modules')) {
54
+ // Installed as a package - go up to find package root
55
+ rootDir = join(import.meta.dir, '..', '..')
56
+ } else {
57
+ // Running from repo
58
+ rootDir = join(import.meta.dir, '..', '..')
59
+ }
60
+
61
+ const docsDir = join(rootDir, 'docs')
62
+ const srcDir = join(rootDir, 'src')
63
+ const demoDir = join(rootDir, 'demo')
64
+ const editorsDir = join(rootDir, 'editors')
65
+
66
+ // Check if docs directory exists
67
+ const docsExists = await Bun.file(join(docsDir, 'index.html')).exists()
68
+
69
+ if (!docsExists) {
70
+ console.log('Docs not built yet. Building...')
71
+ await buildDocs(rootDir)
72
+ }
73
+
74
+ // Kill any existing process on our port
75
+ try {
76
+ const result = await $`lsof -ti:${port}`.quiet()
77
+ const pids = result.text().trim().split('\n').filter(Boolean)
78
+ for (const pid of pids) {
79
+ console.log(`Killing existing process on port ${port} (PID: ${pid})`)
80
+ await $`kill -9 ${pid}`.quiet()
81
+ }
82
+ } catch {
83
+ // No process on port
84
+ }
85
+
86
+ // Build function
87
+ async function buildDocs(root: string) {
88
+ console.log('Building playground...')
89
+ try {
90
+ // Generate docs.json
91
+ const docsScript = join(root, 'bin', 'docs.js')
92
+ if (await Bun.file(docsScript).exists()) {
93
+ await $`node ${docsScript}`.cwd(root)
94
+ }
95
+
96
+ // Build the demo app
97
+ const result = await Bun.build({
98
+ entrypoints: [join(root, 'demo', 'src', 'index.ts')],
99
+ outdir: join(root, 'docs'),
100
+ minify: false,
101
+ sourcemap: 'external',
102
+ target: 'browser',
103
+ })
104
+
105
+ if (!result.success) {
106
+ console.error('Build failed:')
107
+ for (const log of result.logs) {
108
+ console.error(log)
109
+ }
110
+ return false
111
+ }
112
+
113
+ // Copy static files
114
+ const demoStatic = join(root, 'demo', 'static')
115
+ const demoIndex = join(root, 'demo', 'index.html')
116
+ const logoSvg = join(root, 'tjs-lang.svg')
117
+ const targetDocs = join(root, 'docs')
118
+
119
+ await $`cp ${demoIndex} ${logoSvg} ${targetDocs}/`.quiet()
120
+ await $`cp ${join(demoStatic, 'favicon.svg')} ${targetDocs}/`.quiet()
121
+ await $`cp ${join(demoStatic, 'photo-*.jpg')} ${targetDocs}/`.quiet()
122
+ await $`cp -r ${join(demoStatic, 'texts')} ${targetDocs}/`.quiet()
123
+
124
+ console.log('Build complete!')
125
+ return true
126
+ } catch (error) {
127
+ console.error(
128
+ 'Build error:',
129
+ error instanceof Error ? error.message : error
130
+ )
131
+ return false
132
+ }
133
+ }
134
+
135
+ // Debounced rebuild
136
+ let rebuildTimeout: ReturnType<typeof setTimeout> | null = null
137
+ let isBuilding = false
138
+
139
+ async function debouncedBuild(source: string) {
140
+ if (rebuildTimeout) clearTimeout(rebuildTimeout)
141
+ rebuildTimeout = setTimeout(async () => {
142
+ if (isBuilding) {
143
+ debouncedBuild(source)
144
+ return
145
+ }
146
+ isBuilding = true
147
+ console.log(`\n${source}`)
148
+ await buildDocs(rootDir)
149
+ isBuilding = false
150
+ }, 100)
151
+ }
152
+
153
+ // Watch for changes if not disabled
154
+ const watchers: ReturnType<typeof watch>[] = []
155
+
156
+ if (!noWatch) {
157
+ try {
158
+ watchers.push(
159
+ watch(srcDir, { recursive: true }, (event, filename) => {
160
+ debouncedBuild(`File changed: ${filename}`)
161
+ })
162
+ )
163
+ watchers.push(
164
+ watch(join(demoDir, 'src'), { recursive: true }, (event, filename) => {
165
+ debouncedBuild(`Demo file changed: ${filename}`)
166
+ })
167
+ )
168
+ watchers.push(
169
+ watch(editorsDir, { recursive: true }, (event, filename) => {
170
+ debouncedBuild(`Editor file changed: ${filename}`)
171
+ })
172
+ )
173
+ } catch {
174
+ // Watching may fail if directories don't exist (installed package)
175
+ }
176
+ }
177
+
178
+ // Serve the docs directory
179
+ const server = Bun.serve({
180
+ port,
181
+ async fetch(req) {
182
+ const url = new URL(req.url)
183
+ let pathname = url.pathname
184
+
185
+ if (pathname === '/') {
186
+ pathname = '/index.html'
187
+ }
188
+
189
+ const filePath = join(docsDir, pathname)
190
+ const file = Bun.file(filePath)
191
+
192
+ if (await file.exists()) {
193
+ const ext = pathname.split('.').pop()
194
+ const contentTypes: Record<string, string> = {
195
+ html: 'text/html',
196
+ js: 'application/javascript',
197
+ css: 'text/css',
198
+ json: 'application/json',
199
+ svg: 'image/svg+xml',
200
+ png: 'image/png',
201
+ jpg: 'image/jpeg',
202
+ jpeg: 'image/jpeg',
203
+ webp: 'image/webp',
204
+ gif: 'image/gif',
205
+ ico: 'image/x-icon',
206
+ map: 'application/json',
207
+ }
208
+
209
+ return new Response(file, {
210
+ headers: {
211
+ 'Content-Type': contentTypes[ext || 'html'] || 'text/plain',
212
+ 'Access-Control-Allow-Origin': '*',
213
+ },
214
+ })
215
+ }
216
+
217
+ // SPA fallback
218
+ const indexFile = Bun.file(join(docsDir, 'index.html'))
219
+ if (await indexFile.exists()) {
220
+ return new Response(indexFile, {
221
+ headers: { 'Content-Type': 'text/html' },
222
+ })
223
+ }
224
+
225
+ return new Response('Not Found', { status: 404 })
226
+ },
227
+ })
228
+
229
+ console.log(`
230
+ TJS Playground running at http://localhost:${port}
231
+ ${
232
+ !noWatch
233
+ ? `
234
+ Watching for changes...`
235
+ : ''
236
+ }
237
+
238
+ Press Ctrl+C to stop
239
+ `)
240
+
241
+ // Graceful shutdown
242
+ process.on('SIGINT', () => {
243
+ console.log('\nShutting down...')
244
+ watchers.forEach((w) => w.close())
245
+ server.stop()
246
+ process.exit(0)
247
+ })
248
+ }
249
+
250
+ main()
package/src/cli/tjs.ts ADDED
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * TJS CLI - Command line interface for Typed JavaScript
4
+ *
5
+ * Commands:
6
+ * tjs check <file> - Parse and type check, report errors
7
+ * tjs run <file> - Transpile to JS and execute
8
+ * tjs types <file> - Output type metadata as JSON
9
+ * tjs emit <file> - Output transpiled JavaScript
10
+ *
11
+ * Options:
12
+ * --help, -h - Show help
13
+ * --version, -v - Show version
14
+ */
15
+
16
+ import { check } from './commands/check'
17
+ import { run } from './commands/run'
18
+ import { types } from './commands/types'
19
+ import { emit } from './commands/emit'
20
+ import { convert } from './commands/convert'
21
+ import { test } from './commands/test'
22
+
23
+ const VERSION = '0.1.0'
24
+
25
+ const HELP = `
26
+ tjs - Typed JavaScript CLI
27
+
28
+ Usage:
29
+ tjs <command> [options] <file>
30
+
31
+ Commands:
32
+ check <file> Parse and type check a TJS file
33
+ run <file> Transpile and execute a TJS file
34
+ types <file> Output type metadata as JSON
35
+ emit <file> Output transpiled JavaScript (+ docs)
36
+ test [file] Run .test.tjs test files
37
+ convert <src> Convert TypeScript files to TJS
38
+
39
+ Options:
40
+ -h, --help Show this help message
41
+ -v, --version Show version
42
+ --debug Include source locations in __tjs metadata (emit command)
43
+ --unsafe Strip __tjs metadata for production builds (emit command)
44
+ --no-docs Suppress documentation generation (emit command)
45
+ --docs-dir <d> Output docs to separate directory (emit command)
46
+ --jfdi Emit even if tests fail (just fucking do it)
47
+ -o <path> Output path (for emit, convert commands)
48
+ -t <pattern> Test name pattern (for test command)
49
+ --verbose, -V Verbose output
50
+
51
+ Examples:
52
+ tjs check src/utils.tjs
53
+ tjs run examples/hello.tjs
54
+ tjs types lib/api.tjs > api-types.json
55
+ tjs emit src/utils.tjs > dist/utils.js
56
+ tjs emit --debug src/utils.tjs > dist/utils.debug.js
57
+ tjs emit src/ -o dist/ # Emit all .tjs files in directory
58
+ tjs emit --unsafe src/ -o dist/ # Emit without validation (production)
59
+ tjs test # Run all .test.tjs files
60
+ tjs test src/lib/ # Run tests in directory
61
+ tjs test -t "validation" # Run tests matching pattern
62
+ tjs convert src/ -o src-tjs/ # Convert TS files to TJS
63
+ `
64
+
65
+ async function main() {
66
+ const args = process.argv.slice(2)
67
+
68
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
69
+ console.log(HELP)
70
+ process.exit(0)
71
+ }
72
+
73
+ if (args.includes('--version') || args.includes('-v')) {
74
+ console.log(`tjs v${VERSION}`)
75
+ process.exit(0)
76
+ }
77
+
78
+ // Parse flags
79
+ const debug = args.includes('--debug')
80
+ const verbose = args.includes('--verbose') || args.includes('-V')
81
+ const unsafe = args.includes('--unsafe')
82
+ const noDocs = args.includes('--no-docs')
83
+ const jfdi = args.includes('--jfdi')
84
+
85
+ // Parse -o <output> option
86
+ const outputIdx = args.findIndex((a) => a === '-o' || a === '--output')
87
+ const output = outputIdx !== -1 ? args[outputIdx + 1] : undefined
88
+
89
+ // Parse --docs-dir <dir> option
90
+ const docsDirIdx = args.findIndex((a) => a === '--docs-dir')
91
+ const docsDir = docsDirIdx !== -1 ? args[docsDirIdx + 1] : undefined
92
+
93
+ // Parse -t <pattern> option for test command
94
+ const patternIdx = args.findIndex(
95
+ (a) => a === '-t' || a === '--test-name-pattern'
96
+ )
97
+ const testPattern = patternIdx !== -1 ? args[patternIdx + 1] : undefined
98
+
99
+ // Filter out flag arguments
100
+ const filteredArgs = args.filter((a, i) => {
101
+ if (a.startsWith('--') || a.startsWith('-')) return false
102
+ // Skip values that follow -o, -t, or --docs-dir
103
+ if (
104
+ i > 0 &&
105
+ (args[i - 1] === '-o' ||
106
+ args[i - 1] === '--output' ||
107
+ args[i - 1] === '-t' ||
108
+ args[i - 1] === '--test-name-pattern' ||
109
+ args[i - 1] === '--docs-dir')
110
+ )
111
+ return false
112
+ return true
113
+ })
114
+
115
+ const command = filteredArgs[0]
116
+ const file = filteredArgs[1]
117
+
118
+ // Some commands don't require a file argument
119
+ const noFileRequired = ['test']
120
+
121
+ if (!file && !noFileRequired.includes(command)) {
122
+ console.error(`Error: No file specified for command '${command}'`)
123
+ console.error(`Usage: tjs ${command} <file>`)
124
+ process.exit(1)
125
+ }
126
+
127
+ try {
128
+ switch (command) {
129
+ case 'check':
130
+ await check(file)
131
+ break
132
+ case 'run':
133
+ await run(file)
134
+ break
135
+ case 'types':
136
+ await types(file)
137
+ break
138
+ case 'emit':
139
+ await emit(file, {
140
+ debug,
141
+ unsafe,
142
+ output,
143
+ verbose,
144
+ noDocs,
145
+ docsDir,
146
+ jfdi,
147
+ })
148
+ break
149
+ case 'test':
150
+ await test(file, { pattern: testPattern })
151
+ break
152
+ case 'convert':
153
+ await convert(file, { output, verbose })
154
+ break
155
+ default:
156
+ console.error(`Error: Unknown command '${command}'`)
157
+ console.error(`Run 'tjs --help' for usage`)
158
+ process.exit(1)
159
+ }
160
+ } catch (error: any) {
161
+ console.error(`Error: ${error.message}`)
162
+ process.exit(1)
163
+ }
164
+ }
165
+
166
+ main()