-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #553 from dodona-edu/feat/add-typescript-with-loader
Add TypeScript
- Loading branch information
Showing
68 changed files
with
1,795 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,6 +65,15 @@ RUN <<EOF | |
xxd \ | ||
shellcheck | ||
|
||
# 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 | ||
npm install -g [email protected] [email protected] | ||
|
||
# TypeScript dependencies | ||
npm install -g [email protected] [email protected] | ||
npm install -g @types/node @typescript-eslint/parser @typescript-eslint/eslint-plugin | ||
|
||
# Haskell dependencies | ||
apt-get install -y --no-install-recommends \ | ||
hlint \ | ||
|
@@ -78,11 +87,6 @@ RUN <<EOF | |
cabal update | ||
cabal v1-install --global aeson | ||
|
||
# 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 | ||
npm install -g [email protected] [email protected] | ||
|
||
# C# dependencies | ||
curl https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb --output packages-microsoft-prod.deb | ||
dpkg -i packages-microsoft-prod.deb | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -774,6 +774,7 @@ | |
"haskell", | ||
"java", | ||
"javascript", | ||
"typescript", | ||
"kotlin", | ||
"python", | ||
"runhaskell", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -774,6 +774,7 @@ | |
"haskell", | ||
"java", | ||
"javascript", | ||
"typescript", | ||
"kotlin", | ||
"python", | ||
"runhaskell", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,258 @@ | ||
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", | ||
str(main_file.name), | ||
], | ||
files, | ||
) | ||
else: | ||
return [], files | ||
|
||
def execution(self, cwd: Path, file: str, arguments: list[str]) -> Command: | ||
return ["tsx", file, *arguments] | ||
|
||
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", | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
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" | ||
'@typescript-eslint/no-unused-expressions': "off" | ||
|
Oops, something went wrong.