tjs-lang 0.6.39 → 0.6.42
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/dist/index.js +87 -82
- package/dist/index.js.map +4 -4
- package/dist/tjs-full.js +87 -82
- package/dist/tjs-full.js.map +4 -4
- package/package.json +1 -1
- package/src/cli/tjs.ts +1 -1
- package/src/lang/emitters/dts.ts +123 -20
- package/src/lang/emitters/from-ts.ts +50 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tjs-lang",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.42",
|
|
4
4
|
"description": "Type-safe JavaScript dialect with runtime validation, sandboxed VM execution, and AI agent orchestration. Transpiles TypeScript to validated JS with fuel-metered execution for untrusted code.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
package/src/cli/tjs.ts
CHANGED
package/src/lang/emitters/dts.ts
CHANGED
|
@@ -453,6 +453,14 @@ function detectTypeDeclarations(source: string): Map<string, string> {
|
|
|
453
453
|
result.set(m[1], m[2].trim())
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
// Empty block: Type Name {} (no example — degraded type, emit as any)
|
|
457
|
+
const emptyBlockRe = /^[ \t]*(?:export\s+)?Type\s+(\w+)\s*\{\s*\}/gm
|
|
458
|
+
while ((m = emptyBlockRe.exec(source)) !== null) {
|
|
459
|
+
if (!result.has(m[1])) {
|
|
460
|
+
result.set(m[1], '') // empty string signals "any"
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
456
464
|
return result
|
|
457
465
|
}
|
|
458
466
|
|
|
@@ -505,6 +513,57 @@ function detectGenerics(source: string): Map<string, GenericInfo> {
|
|
|
505
513
|
return result
|
|
506
514
|
}
|
|
507
515
|
|
|
516
|
+
/** Info about a const/let/var declaration */
|
|
517
|
+
interface VarDeclInfo {
|
|
518
|
+
name: string
|
|
519
|
+
value: string
|
|
520
|
+
kind: 'const' | 'let' | 'var'
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** Detect exported const/let/var declarations and their initializer values */
|
|
524
|
+
function detectVarDeclarations(source: string): VarDeclInfo[] {
|
|
525
|
+
const result: VarDeclInfo[] = []
|
|
526
|
+
const re =
|
|
527
|
+
/^[ \t]*export\s+(?:default\s+)?(const|let|var)\s+(\w+)\s*(?::\s*\w+\s*)?=\s*(.+)/gm
|
|
528
|
+
let m
|
|
529
|
+
while ((m = re.exec(source)) !== null) {
|
|
530
|
+
// Get the value — may span multiple lines for objects/arrays
|
|
531
|
+
let value = m[3].trim()
|
|
532
|
+
// Strip trailing semicolons
|
|
533
|
+
if (value.endsWith(';')) value = value.slice(0, -1).trim()
|
|
534
|
+
result.push({ name: m[2], value, kind: m[1] as 'const' | 'let' | 'var' })
|
|
535
|
+
}
|
|
536
|
+
return result
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** Infer a TS type from a const initializer value */
|
|
540
|
+
function inferConstType(value: string): string {
|
|
541
|
+
// String literal
|
|
542
|
+
if (/^['"]/.test(value)) return 'string'
|
|
543
|
+
// Template literal
|
|
544
|
+
if (value.startsWith('`')) return 'string'
|
|
545
|
+
// Boolean
|
|
546
|
+
if (value === 'true' || value === 'false') return 'boolean'
|
|
547
|
+
// Number
|
|
548
|
+
if (/^[+-]?\d+(\.\d+)?$/.test(value)) return 'number'
|
|
549
|
+
// Symbol
|
|
550
|
+
if (value.startsWith('Symbol(') || value.startsWith('Symbol.'))
|
|
551
|
+
return 'symbol'
|
|
552
|
+
// Array
|
|
553
|
+
if (value.startsWith('[')) return 'any[]'
|
|
554
|
+
// new Map/Set/WeakMap etc.
|
|
555
|
+
if (value.startsWith('new WeakMap')) return 'WeakMap<any, any>'
|
|
556
|
+
if (value.startsWith('new Map')) return 'Map<any, any>'
|
|
557
|
+
if (value.startsWith('new Set')) return 'Set<any>'
|
|
558
|
+
if (value.startsWith('new ')) return 'any'
|
|
559
|
+
// Object/Record
|
|
560
|
+
if (value.startsWith('{')) return 'Record<string, any>'
|
|
561
|
+
// null/undefined
|
|
562
|
+
if (value === 'null') return 'null'
|
|
563
|
+
if (value === 'undefined') return 'undefined'
|
|
564
|
+
return 'any'
|
|
565
|
+
}
|
|
566
|
+
|
|
508
567
|
/**
|
|
509
568
|
* Generate a .d.ts string from TJS transpilation output.
|
|
510
569
|
*
|
|
@@ -594,14 +653,19 @@ export function generateDTS(
|
|
|
594
653
|
const isExported = hasAnyExport ? !!exportInfo?.exported : true
|
|
595
654
|
if (!isExported) continue
|
|
596
655
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
`export
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
` }
|
|
604
|
-
|
|
656
|
+
if (exampleStr === '') {
|
|
657
|
+
// Empty Type {} — degraded from TS type alias, emit as type = any
|
|
658
|
+
lines.push(`export type ${name} = any;`)
|
|
659
|
+
} else {
|
|
660
|
+
const tsType = inferTSTypeFromExample(exampleStr)
|
|
661
|
+
lines.push(
|
|
662
|
+
`export declare const ${name}: {` +
|
|
663
|
+
` check(value: any): boolean;` +
|
|
664
|
+
` default: ${tsType};` +
|
|
665
|
+
` (value: any): boolean;` +
|
|
666
|
+
` };`
|
|
667
|
+
)
|
|
668
|
+
}
|
|
605
669
|
emitted.add(name)
|
|
606
670
|
}
|
|
607
671
|
|
|
@@ -619,14 +683,23 @@ export function generateDTS(
|
|
|
619
683
|
info.typeParams.length > 0 ? `<${info.typeParams.join(', ')}>` : ''
|
|
620
684
|
|
|
621
685
|
if (info.declaration) {
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
.
|
|
629
|
-
|
|
686
|
+
const declContent = info.declaration.trim()
|
|
687
|
+
|
|
688
|
+
// Check if this is a verbatim TS type (conditional, mapped, etc.)
|
|
689
|
+
// These start with "// TS:" and should be emitted as `export type`
|
|
690
|
+
const tsMatch = declContent.match(/^\/\/\s*TS:\s*(.+)$/s)
|
|
691
|
+
if (tsMatch) {
|
|
692
|
+
lines.push(`export type ${name}${typeParamStr} = ${tsMatch[1].trim()};`)
|
|
693
|
+
} else {
|
|
694
|
+
// Structured declaration — emit as interface
|
|
695
|
+
const declLines = declContent
|
|
696
|
+
.split('\n')
|
|
697
|
+
.map((l) => l.trim())
|
|
698
|
+
.filter((l) => l.length > 0)
|
|
699
|
+
.map((l) => ` ${l}`)
|
|
700
|
+
.join('\n')
|
|
701
|
+
lines.push(`export interface ${name}${typeParamStr} {\n${declLines}\n}`)
|
|
702
|
+
}
|
|
630
703
|
} else {
|
|
631
704
|
// No declaration block — emit any-based factory stub
|
|
632
705
|
lines.push(
|
|
@@ -659,15 +732,31 @@ export function generateDTS(
|
|
|
659
732
|
|
|
660
733
|
const tsParams = fpInfo.params
|
|
661
734
|
.map((p) => {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
735
|
+
// Array example [X] → rest param ...name: X[]
|
|
736
|
+
if (p.example.startsWith('[') && p.example.endsWith(']')) {
|
|
737
|
+
const inner = p.example.slice(1, -1).trim()
|
|
738
|
+
const innerType = inner
|
|
739
|
+
? tpNames.has(inner)
|
|
740
|
+
? inner
|
|
741
|
+
: inferTSTypeFromExample(inner)
|
|
742
|
+
: 'any'
|
|
743
|
+
return `...${p.name}: ${innerType}[]`
|
|
744
|
+
}
|
|
745
|
+
// In FunctionPredicate params, null means "any" (not literal null)
|
|
746
|
+
const tsType =
|
|
747
|
+
p.example === 'null'
|
|
748
|
+
? 'any'
|
|
749
|
+
: tpNames.has(p.example)
|
|
750
|
+
? p.example
|
|
751
|
+
: inferTSTypeFromExample(p.example)
|
|
665
752
|
return `${p.name}: ${tsType}`
|
|
666
753
|
})
|
|
667
754
|
.join(', ')
|
|
668
755
|
const tsReturn =
|
|
669
756
|
fpInfo.returns !== undefined
|
|
670
|
-
?
|
|
757
|
+
? fpInfo.returns === 'null'
|
|
758
|
+
? 'any'
|
|
759
|
+
: tpNames.has(fpInfo.returns)
|
|
671
760
|
? fpInfo.returns
|
|
672
761
|
: inferTSTypeFromExample(fpInfo.returns)
|
|
673
762
|
: 'void'
|
|
@@ -677,6 +766,20 @@ export function generateDTS(
|
|
|
677
766
|
emitted.add(name)
|
|
678
767
|
}
|
|
679
768
|
|
|
769
|
+
// Emit exported const/let/var declarations
|
|
770
|
+
const varDecls = detectVarDeclarations(source)
|
|
771
|
+
for (const decl of varDecls) {
|
|
772
|
+
if (emitted.has(decl.name)) continue
|
|
773
|
+
|
|
774
|
+
const exportInfo = exports.get(decl.name)
|
|
775
|
+
const isExported = hasAnyExport ? !!exportInfo?.exported : true
|
|
776
|
+
if (!isExported) continue
|
|
777
|
+
|
|
778
|
+
const tsType = inferConstType(decl.value)
|
|
779
|
+
lines.push(`export declare const ${decl.name}: ${tsType};`)
|
|
780
|
+
emitted.add(decl.name)
|
|
781
|
+
}
|
|
782
|
+
|
|
680
783
|
if (options.moduleName) {
|
|
681
784
|
const indented = lines.map((l) => ` ${l}`).join('\n')
|
|
682
785
|
return `declare module '${options.moduleName}' {\n${indented}\n}\n`
|
|
@@ -1065,8 +1065,28 @@ function transformGenericInterfaceToGeneric(
|
|
|
1065
1065
|
}
|
|
1066
1066
|
|
|
1067
1067
|
const parts = [`description: '${typeName}'`, predicateLine]
|
|
1068
|
+
|
|
1068
1069
|
if (declarationAnnotation?.text) {
|
|
1069
1070
|
parts.push(`declaration ${declarationAnnotation.text}`)
|
|
1071
|
+
} else {
|
|
1072
|
+
// Auto-generate declaration block from interface members
|
|
1073
|
+
const declMembers: string[] = []
|
|
1074
|
+
for (const member of node.members) {
|
|
1075
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
1076
|
+
const propName = member.name.getText(sourceFile)
|
|
1077
|
+
const optional = member.questionToken ? '?' : ''
|
|
1078
|
+
const typeText = member.type ? member.type.getText(sourceFile) : 'any'
|
|
1079
|
+
declMembers.push(`${propName}${optional}: ${typeText}`)
|
|
1080
|
+
} else if (ts.isMethodSignature(member) && member.name) {
|
|
1081
|
+
// Method: name(params): returnType
|
|
1082
|
+
const methodText = member.getText(sourceFile).trim()
|
|
1083
|
+
// Remove trailing semicolon if present
|
|
1084
|
+
declMembers.push(methodText.replace(/;$/, ''))
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (declMembers.length > 0) {
|
|
1088
|
+
parts.push(`declaration {\n ${declMembers.join('\n ')}\n }`)
|
|
1089
|
+
}
|
|
1070
1090
|
}
|
|
1071
1091
|
|
|
1072
1092
|
return `Generic ${typeName}<${typeParams.join(', ')}> {\n ${parts.join(
|
|
@@ -1366,18 +1386,41 @@ function transformGenericTypeAliasToGeneric(
|
|
|
1366
1386
|
predicateLine = `predicate(${predicateParams}) { return true }`
|
|
1367
1387
|
}
|
|
1368
1388
|
|
|
1369
|
-
// Include original TS source as a block comment for manual enhancement
|
|
1370
|
-
const originalSource = node.getText(sourceFile).trim()
|
|
1371
|
-
const comment = `/* Original TS:\n${originalSource}\n*/`
|
|
1372
|
-
|
|
1373
1389
|
const parts = [`description: '${typeName}'`, predicateLine]
|
|
1390
|
+
|
|
1374
1391
|
if (declarationAnnotation?.text) {
|
|
1375
1392
|
parts.push(`declaration ${declarationAnnotation.text}`)
|
|
1393
|
+
} else {
|
|
1394
|
+
// Auto-generate declaration block from the type body
|
|
1395
|
+
const typeBody = node.type
|
|
1396
|
+
|
|
1397
|
+
if (typeBody && ts.isTypeLiteralNode(typeBody)) {
|
|
1398
|
+
// Object type literal: { item: T; count: number }
|
|
1399
|
+
const declMembers: string[] = []
|
|
1400
|
+
for (const member of typeBody.members) {
|
|
1401
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
1402
|
+
const propName = member.name.getText(sourceFile)
|
|
1403
|
+
const optional = member.questionToken ? '?' : ''
|
|
1404
|
+
const typeText = member.type ? member.type.getText(sourceFile) : 'any'
|
|
1405
|
+
declMembers.push(`${propName}${optional}: ${typeText}`)
|
|
1406
|
+
} else if (ts.isMethodSignature(member) && member.name) {
|
|
1407
|
+
declMembers.push(member.getText(sourceFile).trim().replace(/;$/, ''))
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (declMembers.length > 0) {
|
|
1411
|
+
parts.push(`declaration {\n ${declMembers.join('\n ')}\n }`)
|
|
1412
|
+
}
|
|
1413
|
+
} else if (typeBody) {
|
|
1414
|
+
// Complex type (conditional, mapped, intersection, etc.)
|
|
1415
|
+
// Pass through the TS type body verbatim
|
|
1416
|
+
const typeText = typeBody.getText(sourceFile).trim()
|
|
1417
|
+
parts.push(`declaration {\n // TS: ${typeText}\n }`)
|
|
1418
|
+
}
|
|
1376
1419
|
}
|
|
1377
1420
|
|
|
1378
|
-
return
|
|
1379
|
-
'
|
|
1380
|
-
)}
|
|
1421
|
+
return `Generic ${typeName}<${typeParams.join(', ')}> {\n ${parts.join(
|
|
1422
|
+
'\n '
|
|
1423
|
+
)}\n}`
|
|
1381
1424
|
}
|
|
1382
1425
|
|
|
1383
1426
|
function transformFunctionToTJS(
|