Skip to content

Commit

Permalink
feat: typescript language support
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitryger committed Apr 10, 2024
1 parent f98d053 commit 54734e6
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,6 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*


.idea
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { Ruby } from "./languages/ruby";
import { Rust } from "./languages/rust";
import { SQL } from "./languages/sql";
import { YAML } from "./languages/yaml";
import { Typescript } from "./languages/typescript";
import { nearTop, getPoints } from "./points";
import { convert } from "./shiki";
import { shebangMap } from "./shebang";
Expand Down Expand Up @@ -51,7 +52,8 @@ const languages: Record<string, LanguagePattern[]> = {
Ruby,
Rust,
SQL,
YAML
YAML,
Typescript
};

/**
Expand Down
63 changes: 63 additions & 0 deletions src/languages/typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { LanguagePattern } from "../types";


export const Typescript: LanguagePattern[] = [{
pattern: /(Readonly<|ReadonlyArray<|Array<|Record<|Pick<|Omit<|Exclude<|Extract<)/,
type: "constant.dictionary"
},
{
pattern: /\w+\[]/,
type: "keyword.other"
},
{
pattern: /:\s*\w+\s*</,
type: "keyword.control"
},
{
pattern: /\??:\s*(any|string|boolean|undefined|null|number|void|never|symbol|bigint)\s*/,
type: "constant.type"
},
{
pattern: /(let|const|var)*\s*\w*:\s*(any|string|boolean|undefined|null|number|void|never|symbol|bigint)\s*=/,
type: "constant.type"
},
{ pattern: /console\.log\s*\(/, type: "keyword.print" },
{
pattern: /interface\s*(\w+)\s*{/,
type: "constant.type"
},
{
pattern: /enum\s*\w+\s*=/,
type: "constant.type"
},
{
pattern: /type\s*(\w+)\s*=/,
type: "constant.type"
},
{
pattern: /function\s+\w*\s*(<(\w*\s*)*>)?\((\w+\s*\??:\s*(\w+|({(\s*\w+\s*\??:\s*\w+\s*(,)?\s*)*})?)\s*(,)?\s*)*\)\s*:\s*\w+\s*{/,

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with 'function <' and containing many repetitions of ' '.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with 'function (0:' and containing many repetitions of '000:'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with 'function (0:{0:' and containing many repetitions of '000:'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with 'function (0:{0:0' and containing many repetitions of ' 0:0'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with 'function (0:' and containing many repetitions of ' 0:'.
type: "keyword.function"
},
{
pattern: /(\s*\w*\s*)(=)?\s+\w*\s*(<(\w*\s*)*>)?\((\w+\s*\??:\s*(\w+|({(\s*\w+\s*\??:\s*\w+\s*(,)?\s*)*})?)\s*(,)?\s*)*\)\s*:\s*\w+\s*(=>)\s*{/,

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with ' <' and containing many repetitions of ' '.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with ' (0:' and containing many repetitions of '000:'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with ' (0:{0:' and containing many repetitions of '000:'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with ' (0:{0:0' and containing many repetitions of ' 0:0'.

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with ' (0:' and containing many repetitions of ' 0:'.
type: "keyword.function"
},
{
pattern: /(typeof|declare)\s+/,
type: "keyword"
},
{
pattern: /\s+as\s+/,
type: "keyword"
},
// Rust types
{
pattern: /usize/,
type: "not"
},
// Kotlin
{
pattern: /Array<String>/,
type: "not"
}
];
3 changes: 2 additions & 1 deletion tests/cpp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ test("hello world", () => {
Rust: 0,
SQL: 0,
Unknown: 1,
YAML: 0
YAML: 0,
Typescript: 0,

Check failure on line 37 in tests/cpp.test.ts

View workflow job for this annotation

GitHub Actions / CI

Unexpected trailing comma
});
assert.equal(code.linesOfCode, 1);
});
Expand Down
3 changes: 2 additions & 1 deletion tests/cs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ test("hello world", () => {
Rust: -40,
SQL: 0,
Unknown: 1,
YAML: 0
YAML: 0,
Typescript: 0
});
assert.equal(code.linesOfCode, 2);
});
Expand Down
3 changes: 2 additions & 1 deletion tests/large.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,8 @@ test("large input", () => {
Rust: 4,
SQL: 22,
Unknown: 1,
YAML: 4
YAML: 4,
Typescript: 2,

Check failure on line 686 in tests/large.test.ts

View workflow job for this annotation

GitHub Actions / CI

Unexpected trailing comma
});
assert.equal(code.linesOfCode, 299);
});
Expand Down
182 changes: 182 additions & 0 deletions tests/typescript.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import { test } from "uvu";
import * as assert from "uvu/assert";
import detectLang from "../src/index";

test("hello world", () => {
const code = detectLang("console.log(\"Hello world!\" as string);");
assert.equal(code.language, "Typescript");
});

test("fizz buzz", () => {
const code = detectLang(`
const length: number = 100
const numberArrays: number[] = Array.from({length: length}, (_, i: number) => i)
numberArrays.forEach((item: number): void => {
if (item % 15 === 0) console.log('FizzBuzz' as string)
else if(item % 3 === 0) console.log('Fizz' as string)
else if (item % 5 === 0) console.log('Buzz' as string)
})
`);
assert.equal(code.language, "Typescript");
});

test("quick sort", () => {
const code = detectLang(`
function heapSort(arr: Array<number>): void {
heapify(arr)
let end: number = arr.length - 1
while (end > 0) {
[arr[end], arr[0]] = [arr[0], arr[end]]
end--
siftDown(arr, 0, end)
}
}
function heapify(arr: Array<number>): void {
let start: number = Math.floor(arr.length/2) - 1
while (start >= 0) {
siftDown(arr, start, arr.length - 1)
start--
}
}
function siftDown(arr: Array<number>, startPos: number, endPos: number): void {
let rootPos: number = startPos
while (rootPos * 2 + 1 <= endPos) {
let childPos: number = rootPos * 2 + 1
if (childPos + 1 <= endPos && arr[childPos] < arr[childPos + 1]) {
childPos++
}
if (arr[rootPos] < arr[childPos]) {
[arr[rootPos], arr[childPos]] = [arr[childPos], arr[rootPos]]
rootPos = childPos
} else {
return
}
}
}
test('test code', (): void => {
let arr: Array<number> = [12, 11, 15, 10, 9, 1, 2, 3, 13, 14, 4, 5, 6, 7, 8,]
const result: Array<number> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
heapSort(arr)
expect(arr).toStrictEqual(result)
})
`);
assert.equal(code.language, "Typescript");
});

test("http server", () => {
const code = detectLang(`
const http = require('http');
interface Error {
message: string
}
http.get('http://rosettacode.org', (resp: any): void => {
let data: string = '';
resp.on('data', (chunk: any): void => {
data += chunk;
});
resp.on('end', (): void => {
console.log("Data:", data);
});
}).on("error", (err: Error): void => {
console.log("Error: " + err.message);
});
`);

assert.equal(code.language, "Typescript");
});

test("longest palindrome", () => {
const code = detectLang(`
function isPalindrome(s: string): boolean {
return s === s.split('').reverse().join('')
}
function longestPalindrome(s: string): string {
if (s.length < 2)
return s
const unique: Set<string> = new Set(s)
if (unique.size === 1) {
return s
}
let startPos: number = 0
let nextPos: number = 1
let startCharSubstring: string = s[0]
let longestSubstring: string = s[0]
let curSubString: string = s[0] + s[1]
while (startPos !== s.length - 1) {
if (startCharSubstring === s[nextPos]) {
if (isPalindrome(curSubString) && curSubString.length > longestSubstring.length) {
longestSubstring = curSubString
}
}
if (nextPos === s.length - 1) {
startPos += 1
nextPos = startPos + 1
curSubString = s[startPos] + s[nextPos]
startCharSubstring = s[startPos]
} else {
nextPos += 1
curSubString += s[nextPos]
}
}
return longestSubstring
}
`);
assert.equal(code.language, "Typescript");
});

test("vue 3 component code", () => {
const code = detectLang(`
enum VARIANT {
'big'= 'BIG TITLE',
'small'= 'SMALL TITLE'
}
type Variants = 'big' | 'small'
interface Props {
title?: string,
isView: string,
variant: Variants
}
interface Emits {
(e: 'click', item: string): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emits>()
const newTitle = ref<string>('')
const changeTitle = (): void => {
if (props.variant === 'big') {
newTitle.value = VARIANT.big
} else if (props.variant === 'small') {
newTitle.value = VARIANT.small
}
}
`);
assert.equal(code.language, "Typescript");
});

test.run();

0 comments on commit 54734e6

Please sign in to comment.