Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TypeScript #553

Merged
merged 51 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
3f008f7
already added dependencies to flak and copied files over from js.
BrentBlanckaert Oct 10, 2024
208f589
Have an implementation that might work, but test seem to do something…
BrentBlanckaert Oct 12, 2024
d047237
Made an implementation that mostly works for typescript
BrentBlanckaert Oct 18, 2024
53886db
More consistent use of ESM. Things still broken when running tests
BrentBlanckaert Oct 23, 2024
3d6c7c9
Tried fixing the error regarding ts-node being unknown. Still no solu…
BrentBlanckaert Oct 23, 2024
7943b6d
Merge branch 'master' into feat/add-typescript-with-loader
BrentBlanckaert Oct 23, 2024
896b000
Add tsx in flake
niknetniko Oct 23, 2024
20da6b0
Tsx works for the most part. The same error with "fs" still persists …
BrentBlanckaert Oct 24, 2024
f040248
Fixed accidental change to java test
BrentBlanckaert Oct 24, 2024
0d1c9c6
rewrote some tests
BrentBlanckaert Oct 24, 2024
55bbfa1
reformatted code
BrentBlanckaert Oct 24, 2024
01ad38a
cleaned up code some more and turned off throwing of errors for now.
BrentBlanckaert Oct 24, 2024
3e1b568
Fixed typescript error in terminal. When running tested errors still …
BrentBlanckaert Oct 26, 2024
9480ac3
fixed all tests
BrentBlanckaert Oct 26, 2024
56dcc3c
Changed test such that tsconfig file isn't parsed
BrentBlanckaert Oct 26, 2024
373b0c3
Changing compilation command to tsc instead of tsx because of noEmit
BrentBlanckaert Oct 27, 2024
1b80f28
fixed linting issue
BrentBlanckaert Oct 27, 2024
c22eeb6
cleanup
BrentBlanckaert Oct 29, 2024
20b58c2
Fix merge conflict
BrentBlanckaert Nov 12, 2024
3b70092
Added changes to devcontainer.json
BrentBlanckaert Nov 13, 2024
3214aa5
Fixed problem with tsc
BrentBlanckaert Nov 13, 2024
7bd5b43
Fix linting issue and add actual tsconfig to commit
BrentBlanckaert Nov 13, 2024
f685b7c
Added ESlint for typescript
BrentBlanckaert Nov 14, 2024
0c5fb18
add typescript to more functionality tests
BrentBlanckaert Nov 14, 2024
07c0709
add test files
BrentBlanckaert Nov 14, 2024
3602bb8
add test files
BrentBlanckaert Nov 14, 2024
e28fdd1
updated language quircks
BrentBlanckaert Nov 15, 2024
82ca93e
updated oracle and problem statements tests
BrentBlanckaert Nov 15, 2024
0b78aba
updated stacktrace cleaner and ast tests
BrentBlanckaert Nov 15, 2024
a792223
fixed linting issues
BrentBlanckaert Nov 15, 2024
f083c60
Tried using command-line arguments
BrentBlanckaert Nov 20, 2024
6f4ee94
Cleaned up code.
BrentBlanckaert Nov 20, 2024
59a3f11
Revert "Cleaned up code."
BrentBlanckaert Nov 20, 2024
cca868b
Revert "Tried using command-line arguments"
BrentBlanckaert Nov 20, 2024
dd50af7
switched content position in docker
BrentBlanckaert Nov 21, 2024
4f3fd77
tried adding path
BrentBlanckaert Nov 21, 2024
b6d8f4e
Changed usage of tsc. Keep csharp error for what it is for now
BrentBlanckaert Nov 21, 2024
8cd5a86
Cleaned up my code some more
BrentBlanckaert Nov 21, 2024
cdd3281
fixed typing, linting and removed old print
BrentBlanckaert Nov 21, 2024
aaaa29a
Made some changes to the code as suggested by copilot
BrentBlanckaert Nov 21, 2024
a3897fd
testing dotnet availability.
BrentBlanckaert Nov 21, 2024
487f9ae
Potential fix?
BrentBlanckaert Nov 21, 2024
af62b3f
fixed test
BrentBlanckaert Nov 21, 2024
319ed02
Check what's wrong with linter on github
BrentBlanckaert Nov 22, 2024
0bf135e
potential fix for linter
BrentBlanckaert Nov 22, 2024
a7acf7d
potential fix for linter (again)
BrentBlanckaert Nov 22, 2024
60146bb
Removed redundant code lines
BrentBlanckaert Nov 22, 2024
23c3e4e
reverting changes for js
BrentBlanckaert Nov 22, 2024
b1e6043
removed test for ts
BrentBlanckaert Nov 23, 2024
ba671b9
removed old check in docker
BrentBlanckaert Nov 25, 2024
f507efc
removed save-dev
BrentBlanckaert Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .devcontainer/dodona-tested.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ RUN <<EOF
cabal update
cabal v1-install --global aeson

# TypeScript dependencies
npm install -g typescript@5.6.3 tsx@4.19.2
npm install -g @types/node @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev

# JavaScript dependencies
bash -c 'set -o pipefail && curl -fsSL https://deb.nodesource.com/setup_22.x | bash -'
apt-get install -y --no-install-recommends nodejs
Expand Down
1 change: 1 addition & 0 deletions tested/dsl/schema-strict.json
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@
"haskell",
"java",
"javascript",
"typescript",
"kotlin",
"python",
"runhaskell",
Expand Down
1 change: 1 addition & 0 deletions tested/dsl/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,7 @@
"haskell",
"java",
"javascript",
"typescript",
"kotlin",
"python",
"runhaskell",
Expand Down
8 changes: 8 additions & 0 deletions tested/internationalization/nl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ nl:
linter:
crashed: "Pylint gecrasht"
output: "Pylint produceerde slechte uitvoer."
typescript:
runtime:
invalid:
exception: "%{actual_type} is geen instantie van Error (of een subklasse)."
linter:
timeout: "ESLint overschreed tijdslimiet."
memory: "ESLint overschreed geheugenlimiet."
output: "ESLint produceerde slechte uitvoer."
javascript:
runtime:
invalid:
Expand Down
2 changes: 2 additions & 0 deletions tested/languages/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from tested.languages.language import Language
from tested.languages.python.config import Python
from tested.languages.runhaskell.config import RunHaskell
from tested.languages.typescript.config import TypeScript

if TYPE_CHECKING:
from tested.configs import GlobalConfig
Expand All @@ -30,6 +31,7 @@
"haskell": Haskell,
"java": Java,
"javascript": JavaScript,
"typescript": TypeScript,
"kotlin": Kotlin,
"python": Python,
"runhaskell": RunHaskell,
Expand Down
28 changes: 14 additions & 14 deletions tested/languages/csharp/templates/dotnet.csproj
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<CSFile Include="*.cs" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<CSFile Include="*.cs" />
</ItemGroup>

</Project>
2 changes: 2 additions & 0 deletions tested/languages/language.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def compilation(self, files: list[str]) -> CallbackResult:
be useful to compile. By convention, the last file in the list
is the file containing the "main" function.

:param directory: The directory in which these files can be found.
BrentBlanckaert marked this conversation as resolved.
Show resolved Hide resolved

:return: The compilation command and either the resulting files or a filter
for the resulting files.
"""
Expand Down
256 changes: 256 additions & 0 deletions tested/languages/typescript/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import json
import logging
import os
import re
from pathlib import Path
from typing import TYPE_CHECKING

from tested.datatypes import (
AdvancedObjectTypes,
AllTypes,
BasicStringTypes,
ExpressionTypes,
)
from tested.dodona import AnnotateCode, Message, Status
from tested.features import Construct, TypeSupport
from tested.languages.conventionalize import (
EXECUTION_PREFIX,
Conventionable,
NamingConventions,
submission_file,
submission_name,
)
from tested.languages.language import (
CallbackResult,
Command,
Language,
TypeDeclarationMetadata,
)
from tested.languages.utils import cleanup_description
from tested.serialisation import Statement, Value

if TYPE_CHECKING:
from tested.languages.generation import PreparedExecutionUnit

logger = logging.getLogger(__name__)


class TypeScript(Language):

def initial_dependencies(self) -> list[str]:
return ["values.ts"]

def needs_selector(self) -> bool:
return False

def file_extension(self) -> str:
return "ts"

def naming_conventions(self) -> dict[Conventionable, NamingConventions]:
return {
"namespace": "camel_case",
"function": "camel_case",
"identifier": "camel_case",
"global_identifier": "macro_case",
"property": "camel_case",
"class": "pascal_case",
}

def supported_constructs(self) -> set[Construct]:
return {
Construct.OBJECTS,
Construct.EXCEPTIONS,
Construct.FUNCTION_CALLS,
Construct.ASSIGNMENTS,
Construct.HETEROGENEOUS_COLLECTIONS,
Construct.HETEROGENEOUS_ARGUMENTS,
Construct.EVALUATION,
Construct.DEFAULT_PARAMETERS,
Construct.GLOBAL_VARIABLES,
Construct.NAMED_ARGUMENTS,
}

def datatype_support(self) -> dict[AllTypes, TypeSupport]:
return { # type: ignore
"integer": "supported",
"real": "supported",
"char": "reduced",
"text": "supported",
"string": "supported",
"boolean": "supported",
"sequence": "supported",
"set": "supported",
"map": "supported",
"dictionary": "supported",
"object": "supported",
"nothing": "supported",
"undefined": "supported",
"null": "supported",
"int8": "reduced",
"uint8": "reduced",
"int16": "reduced",
"uint16": "reduced",
"int32": "reduced",
"uint32": "reduced",
"int64": "reduced",
"uint64": "reduced",
"bigint": "supported",
"single_precision": "reduced",
"double_precision": "reduced",
"double_extended": "reduced",
"array": "reduced",
"list": "reduced",
"tuple": "reduced",
}

def collection_restrictions(self) -> dict[AllTypes, set[ExpressionTypes]]:
return {AdvancedObjectTypes.OBJECT: {BasicStringTypes.TEXT}}

def compilation(self, files: list[str]) -> CallbackResult:
submission = submission_file(self)
main_file = self.find_main_file(list(map(lambda name: Path(name), files)), submission)

if main_file != Status.COMPILATION_ERROR:
path_to_modules = os.environ['NODE_PATH']
return (
[
"tsc",
"--target",
"esnext",
"--module",
"nodenext",
"--allowJs",
"--allowImportingTsExtensions",
"--noEmit",
"--esModuleInterop",
"--typeRoots",
f"{path_to_modules}/@types",
main_file,
],
files,
)
else:
return [], files

def execution(self, cwd: Path, file: str, arguments: list[str]) -> Command:
return ["tsx", file, *arguments]
BrentBlanckaert marked this conversation as resolved.
Show resolved Hide resolved

def modify_solution(self, solution: Path):
# import local to prevent errors
from tested.judge.utils import run_command

assert self.config

parse_file = str(Path(__file__).parent / "parseAst.ts")
output = run_command(
solution.parent,
timeout=None,
command=["tsx", parse_file, str(solution.absolute())],
check=True,
)
assert output, "Missing output from TypesScript's modify_solution"
namings = output.stdout.strip()
with open(solution, "a") as file:
print(f"\nexport {{{namings}}};", file=file)

# Add strict mode to the script.
with open(solution, "r") as file:
non_strict = file.read()

# TODO: This may be deleted in the future.
with open(solution, "w") as file:
file.write('"use strict";\n' + non_strict)
self.config.dodona.source_offset -= 2

def linter(self, remaining: float) -> tuple[list[Message], list[AnnotateCode]]:
# Import locally to prevent errors.
from tested.languages.typescript import linter

assert self.config
return linter.run_eslint(self.config.dodona, remaining)

def cleanup_stacktrace(self, stacktrace: str) -> str:
assert self.config
# What this does:
# 1a. While inside the submission code, replace all references to the location with <code>
# 1b. Remove any "submission.SOMETHING" -> "SOMETHING"
# 2. Once we encounter a line with the execution location, skip all lines.
execution_submission_location_regex = f"{self.config.dodona.workdir}/{EXECUTION_PREFIX}[_0-9]+/{submission_file(self)}"
submission_location = (
self.config.dodona.workdir / "common" / submission_file(self)
)
compilation_submission_location = str(submission_location.resolve())
execution_location_regex = f"{self.config.dodona.workdir}/{EXECUTION_PREFIX}[_0-9]+/{EXECUTION_PREFIX}[_0-9]+.ts"
submission_namespace = f"{submission_name(self)}."

resulting_lines = ""
for line in stacktrace.splitlines(keepends=True):
# If we encounter an execution location, we are done.
if re.search(execution_location_regex, line):
break

# Replace any reference to the submission.
line = re.sub(execution_submission_location_regex, "<code>", line)
line = line.replace(compilation_submission_location, "<code>")
# Remove any references of the form "submission.SOMETHING"
line = line.replace(submission_namespace, "")

resulting_lines += line

return resulting_lines

def cleanup_description(self, statement: str) -> str:
statement = cleanup_description(self, statement)
await_regex = re.compile(r"await\s+")
return await_regex.sub("", statement)

def generate_statement(self, statement: Statement) -> str:
from tested.languages.typescript import generators

return generators.convert_statement(statement, full=True)

def generate_execution_unit(self, execution_unit: "PreparedExecutionUnit") -> str:
from tested.languages.typescript import generators

return generators.convert_execution_unit(execution_unit)

def generate_encoder(self, values: list[Value]) -> str:
from tested.languages.typescript import generators

return generators.convert_encoder(values)

def get_declaration_metadata(self) -> TypeDeclarationMetadata:
return {
"names": { # type: ignore
"integer": "number",
"real": "number",
"char": "string",
"text": "string",
"string": "string",
"boolean": "boolean",
"sequence": "array",
"set": "set",
"map": "object",
"nothing": "null",
"undefined": "undefined",
"int8": "number",
"uint8": "number",
"int16": "number",
"uint16": "number",
"int32": "number",
"uint32": "number",
"int64": "number",
"uint64": "number",
"bigint": "number",
"single_precision": "number",
"double_precision": "number",
"double_extended": "number",
"fixed_precision": "number",
"array": "array",
"list": "array",
"tuple": "array",
"any": "object",
},
"nested": ("<", ">"),
"exception": "Error",
}
19 changes: 19 additions & 0 deletions tested/languages/typescript/eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: "latest"
sourceType: "module"
ecmaFeatures: {}
plugins:
- "@typescript-eslint"
extends: "plugin:@typescript-eslint/recommended"
env:
node: yes
es2020: yes
rules:
no-var: "warn"
semi: "warn"
# Disable unused vars, otherwise written classes, functions and variables will be detected as unused,
# even when the student is expected to write these classes, functions and variables
# TODO: Add module.export before linting, to allow detecting unused vars in classes and functions
no-unused-vars: ["off", { "vars": "local", "args": "after-used", "ignoreRestSiblings": false }]

Loading
Loading