diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1b4a7f0f7..f58d40ef0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,10 +14,10 @@ jobs: docs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3.5.0 + - uses: actions/checkout@v4 with: token: ${{ secrets.SDQ_DEV_DEPLOY_TOKEN }} - - uses: actions/checkout@v3.5.0 + - uses: actions/checkout@v4 with: repository: ${{ github.repository }}.wiki path: wiki diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index e688c18c0..9958ac0f4 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ca224e041..7f21a6818 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,7 +7,7 @@ jobs: publish-maven-central: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '17' @@ -17,7 +17,7 @@ jobs: with: servers: '[{ "id": "ossrh", "username": "jplag", "password": "${{ secrets.OSSRH_TOKEN }}" }]' - name: Import GPG key - uses: crazy-max/ghaction-import-gpg@v5 + uses: crazy-max/ghaction-import-gpg@v6 with: gpg_private_key: ${{ secrets.PGP_SECRET }} passphrase: ${{ secrets.PGP_PW }} @@ -29,7 +29,7 @@ jobs: publish-release-artifact: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: '17' diff --git a/.github/workflows/report-viewer-build-test.yml b/.github/workflows/report-viewer-build-test.yml index 050c87511..1e51bfefb 100644 --- a/.github/workflows/report-viewer-build-test.yml +++ b/.github/workflows/report-viewer-build-test.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/report-viewer-dev.yml b/.github/workflows/report-viewer-dev.yml index 4302772ab..e6bf77f37 100644 --- a/.github/workflows/report-viewer-dev.yml +++ b/.github/workflows/report-viewer-dev.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/report-viewer-lint.yml b/.github/workflows/report-viewer-lint.yml index d1a055d9e..768cdd6c5 100644 --- a/.github/workflows/report-viewer-lint.yml +++ b/.github/workflows/report-viewer-lint.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/report-viewer-prettier.yml b/.github/workflows/report-viewer-prettier.yml index 4505b87a0..2da72eec3 100644 --- a/.github/workflows/report-viewer-prettier.yml +++ b/.github/workflows/report-viewer-prettier.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/report-viewer-sonarcloud.yml b/.github/workflows/report-viewer-sonarcloud.yml index c823e8d69..4f7231759 100644 --- a/.github/workflows/report-viewer-sonarcloud.yml +++ b/.github/workflows/report-viewer-sonarcloud.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.actor != 'dependabot[bot]' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: SonarCloud Scan diff --git a/.github/workflows/report-viewer-unit.yml b/.github/workflows/report-viewer-unit.yml index 199e81583..1d3012e4e 100644 --- a/.github/workflows/report-viewer-unit.yml +++ b/.github/workflows/report-viewer-unit.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/report-viewer.yml b/.github/workflows/report-viewer.yml index 8eaea136d..eb3642828 100644 --- a/.github/workflows/report-viewer.yml +++ b/.github/workflows/report-viewer.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛎️ - uses: actions/checkout@v3 + uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: diff --git a/.github/workflows/sonarcloud-branch.yml b/.github/workflows/sonarcloud-branch.yml index ce89117e8..b257a8101 100644 --- a/.github/workflows/sonarcloud-branch.yml +++ b/.github/workflows/sonarcloud-branch.yml @@ -21,7 +21,7 @@ jobs: if: ${{ github.actor != 'dependabot[bot]' }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Set up JDK diff --git a/.github/workflows/sonarcloud-pr.yml b/.github/workflows/sonarcloud-pr.yml index 360b37dd7..a4274b34f 100644 --- a/.github/workflows/sonarcloud-pr.yml +++ b/.github/workflows/sonarcloud-pr.yml @@ -42,7 +42,7 @@ jobs: needs: get-info steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: ${{ github.event.workflow_run.head_repository.full_name }} ref: ${{ github.event.workflow_run.head_branch }} diff --git a/.github/workflows/spotless.yml b/.github/workflows/spotless.yml index 4fe4ce8c7..25de0f0a0 100644 --- a/.github/workflows/spotless.yml +++ b/.github/workflows/spotless.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/README.md b/README.md index 1e7496b57..38f581b8c 100644 --- a/README.md +++ b/README.md @@ -19,24 +19,25 @@ JPlag is a system that finds similarities among multiple sets of source code fil In the following, a list of all supported languages with their supported language version is provided. A language can be selected from the command line using subcommands (jplag [jplag options] [language options]). Alternatively you can use the legacy "-l" argument. -| Language | Version | CLI Argument Name | [state](https://github.com/jplag/JPlag/wiki/2.-Supported-Languages) | parser | -|--------------------------------------------------------|--------:|-------------------|:-------------------------------------------------------------------:|:---------:| -| [Java](https://www.java.com) | 17 | java | mature | JavaC | -| [C/C++](https://isocpp.org) | 11 | cpp | legacy | JavaCC | -| [C/C++](https://isocpp.org) | 14 | cpp2 | beta | ANTLR 4 | -| [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) | 6 | csharp | beta | ANTLR 4 | -| [Go](https://go.dev) | 1.17 | golang | beta | ANTLR 4 | -| [Kotlin](https://kotlinlang.org) | 1.3 | kotlin | beta | ANTLR 4 | -| [Python](https://www.python.org) | 3.6 | python3 | legacy | ANTLR 4 | -| [R](https://www.r-project.org/) | 3.5.0 | rlang | beta | ANTLR 4 | -| [Rust](https://www.rust-lang.org/) | 1.60.0 | rust | beta | ANTLR 4 | -| [Scala](https://www.scala-lang.org) | 2.13.8 | scala | beta | Scalameta | -| [Scheme](http://www.scheme-reports.org) | ? | scheme | unknown | JavaCC | -| [Swift](https://www.swift.org) | 5.4 | swift | beta | ANTLR 4 | -| [EMF Metamodel](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf | beta | EMF | -| [EMF Model](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf-model | alpha | EMF | -| [LLVM IR](https://llvm.org) | 15 | llvmir | beta | ANTLR 4 | -| Text (naive) | - | text | legacy | CoreNLP | +| Language | Version | CLI Argument Name | [state](https://github.com/jplag/JPlag/wiki/2.-Supported-Languages) | parser | +|------------------------------------------------------------|---------------------------------------------------------------------------------------:|-------------------|:-------------------------------------------------------------------:|:---------:| +| [Java](https://www.java.com) | 17 | java | mature | JavaC | +| [C/C++](https://isocpp.org) | 11 | cpp | legacy | JavaCC | +| [C/C++](https://isocpp.org) | 14 | cpp2 | beta | ANTLR 4 | +| [C#](https://docs.microsoft.com/en-us/dotnet/csharp/) | 6 | csharp | beta | ANTLR 4 | +| [Go](https://go.dev) | 1.17 | golang | beta | ANTLR 4 | +| [Kotlin](https://kotlinlang.org) | 1.3 | kotlin | beta | ANTLR 4 | +| [Python](https://www.python.org) | 3.6 | python3 | legacy | ANTLR 4 | +| [R](https://www.r-project.org/) | 3.5.0 | rlang | beta | ANTLR 4 | +| [Rust](https://www.rust-lang.org/) | 1.60.0 | rust | beta | ANTLR 4 | +| [Scala](https://www.scala-lang.org) | 2.13.8 | scala | beta | Scalameta | +| [Scheme](http://www.scheme-reports.org) | ? | scheme | unknown | JavaCC | +| [Swift](https://www.swift.org) | 5.4 | swift | beta | ANTLR 4 | +| [EMF Metamodel](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf | beta | EMF | +| [EMF Model](https://www.eclipse.org/modeling/emf/) | 2.25.0 | emf-model | alpha | EMF | +| [LLVM IR](https://llvm.org) | 15 | llvmir | beta | ANTLR 4 | +| [TypeScript](https://www.typescriptlang.org/) / JavaScript | [~5](https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/README.md) | typescript | beta | ANTLR 4 | +| Text (naive) | - | text | legacy | CoreNLP | ## Download and Installation You need Java SE 17 to run or build JPlag. @@ -161,6 +162,7 @@ Commands: scxml swift text + typescript ``` ### Java API diff --git a/cli/pom.xml b/cli/pom.xml index 2c542543e..b6812a3eb 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -102,6 +102,11 @@ emf-model ${revision} + + de.jplag + typescript + ${revision} + de.jplag llvmir diff --git a/cli/src/test/java/de/jplag/cli/LanguageTest.java b/cli/src/test/java/de/jplag/cli/LanguageTest.java index 1ed766329..ed59a1a13 100644 --- a/cli/src/test/java/de/jplag/cli/LanguageTest.java +++ b/cli/src/test/java/de/jplag/cli/LanguageTest.java @@ -26,7 +26,7 @@ void testInvalidLanguage() { @Test void testLoading() { var languages = LanguageLoader.getAllAvailableLanguages(); - assertEquals(17, languages.size(), "Loaded Languages: " + languages.keySet()); + assertEquals(18, languages.size(), "Loaded Languages: " + languages.keySet()); } @Test diff --git a/coverage-report/pom.xml b/coverage-report/pom.xml index 57b22a10b..3d918dd10 100644 --- a/coverage-report/pom.xml +++ b/coverage-report/pom.xml @@ -126,6 +126,11 @@ emf-model ${revision} + + de.jplag + typescript + ${revision} + de.jplag llvmir diff --git a/language-antlr-utils/pom.xml b/language-antlr-utils/pom.xml index 96b547f14..c5542fbfc 100644 --- a/language-antlr-utils/pom.xml +++ b/language-antlr-utils/pom.xml @@ -14,7 +14,7 @@ org.antlr antlr4-runtime - 4.13.0 + 4.13.1 de.jplag diff --git a/language-api/src/main/java/de/jplag/options/OptionType.java b/language-api/src/main/java/de/jplag/options/OptionType.java index 04cc24487..aaf677370 100644 --- a/language-api/src/main/java/de/jplag/options/OptionType.java +++ b/language-api/src/main/java/de/jplag/options/OptionType.java @@ -21,6 +21,14 @@ private IntegerType() { } } + static final class BooleanType extends OptionType { + public static final BooleanType INSTANCE = new BooleanType(); + + private BooleanType() { + super(Boolean.class); + } + } + public static StringType string() { return StringType.INSTANCE; } @@ -29,6 +37,10 @@ public static IntegerType integer() { return IntegerType.INSTANCE; } + public static BooleanType bool() { + return BooleanType.INSTANCE; + } + private final Class javaType; private OptionType(Class javaType) { diff --git a/languages/pom.xml b/languages/pom.xml index 2de4e639e..203275f10 100644 --- a/languages/pom.xml +++ b/languages/pom.xml @@ -26,6 +26,7 @@ scxml swift text + typescript llvmir diff --git a/languages/scala/pom.xml b/languages/scala/pom.xml index 67138fb22..dcedea621 100644 --- a/languages/scala/pom.xml +++ b/languages/scala/pom.xml @@ -10,7 +10,7 @@ scala - 2.13.11 + 2.13.12 2.13 @@ -25,7 +25,7 @@ org.scalameta scalameta_${scala.compat.version} - 4.8.9 + 4.8.10 diff --git a/languages/typescript/README.md b/languages/typescript/README.md new file mode 100644 index 000000000..351bc2da2 --- /dev/null +++ b/languages/typescript/README.md @@ -0,0 +1,24 @@ +# JPlag TypeScript language module +Due to TypeScript being a superset of JavaScript this frontend can also parse JavaScript files. +
+The JPlag TypeScript module allows the use of JPlag with submissions in TypeScript.
+It is based on the [TypeScript ANTLR4 grammar](https://github.com/antlr/grammars-v4/tree/master/javascript/typescript), licensed under the Apache 2.0. + + +### TypeScript specification compatibility +> This TypeScript grammar does not exactly correspond to the TypeScript standard. The main goal during developing was practical usage, performance, and clarity (getting rid of duplicates). + +Since the grammar has no support for decorators the version can be estimated < 5.0. The grammar can still parse files with decorators, but can not extract a tokens for them. +
The grammar can parse multiple language features from version 4.x. +
Because of this the version is still given as an estimated v5. + +If there are any major updates or fixes to the grammar1, they should surely be applied to this module as well. + +### Token Extraction +The choice of tokens is intended to be similar to the Java or Python modules. It includes a range of nesting structures (class, method, control flow expressions) as well as variable declaration, object creation and assignment. + +### Usage +To use the TypeScript module, use the `typescript` subcommand in the CLI, or use a `JPlagOption` object with `new de.jplag.typescript.TypeScriptLanguage()` as `language` in the Java API as described in the usage information in the [readme of the main project](https://github.com/jplag/JPlag#usage) and [in the wiki](https://github.com/jplag/JPlag/wiki/1.-How-to-Use-JPlag). + +#### Footnotes +
1 The grammar files are taken from grammar-v4, with the most recent modification in commit 768b12e from March 2023.
\ No newline at end of file diff --git a/languages/typescript/pom.xml b/languages/typescript/pom.xml new file mode 100644 index 000000000..f866687aa --- /dev/null +++ b/languages/typescript/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + + de.jplag + languages + ${revision} + + typescript + + + + org.antlr + antlr4-runtime + + + de.jplag + language-antlr-utils + ${revision} + compile + + + + + + + org.antlr + antlr4-maven-plugin + + + + antlr4 + + + + + + + diff --git a/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 new file mode 100644 index 000000000..3f2d34d80 --- /dev/null +++ b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptLexer.g4 @@ -0,0 +1,309 @@ +/// This grammer was slightly modified, so the generated code fit the JPlag code style +lexer grammar TypeScriptLexer; + +channels { ERROR } + +options { + superClass=TypeScriptLexerBase; +} + + +MultiLineComment: '/*' .*? '*/' -> channel(HIDDEN); +SingleLineComment: '//' ~[\r\n\u2028\u2029]* -> channel(HIDDEN); +RegularExpressionLiteral: '/' RegularExpressionFirstChar RegularExpressionChar* {this.isRegexPossible()}? '/' IdentifierPart*; + +OpenBracket: '['; +CloseBracket: ']'; +OpenParen: '('; +CloseParen: ')'; +OpenBrace: '{' {this.processOpenBrace();}; +TemplateCloseBrace: {this.isInTemplateString()}? '}' -> popMode; +CloseBrace: '}' {this.processCloseBrace();}; +SemiColon: ';'; +Comma: ','; +Assign: '='; +QuestionMark: '?'; +Colon: ':'; +Ellipsis: '...'; +Dot: '.'; +PlusPlus: '++'; +MinusMinus: '--'; +Plus: '+'; +Minus: '-'; +BitNot: '~'; +Not: '!'; +Multiply: '*'; +Divide: '/'; +Modulus: '%'; +RightShiftArithmetic: '>>'; +LeftShiftArithmetic: '<<'; +RightShiftLogical: '>>>'; +LessThan: '<'; +MoreThan: '>'; +LessThanEquals: '<='; +GreaterThanEquals: '>='; +Equals_: '=='; +NotEquals: '!='; +IdentityEquals: '==='; +IdentityNotEquals: '!=='; +BitAnd: '&'; +BitXOr: '^'; +BitOr: '|'; +And: '&&'; +Or: '||'; +MultiplyAssign: '*='; +DivideAssign: '/='; +ModulusAssign: '%='; +PlusAssign: '+='; +MinusAssign: '-='; +LeftShiftArithmeticAssign: '<<='; +RightShiftArithmeticAssign: '>>='; +RightShiftLogicalAssign: '>>>='; +BitAndAssign: '&='; +BitXorAssign: '^='; +BitOrAssign: '|='; +ARROW: '=>'; + +/// Null Literals + +NullLiteral: 'null'; + +/// Boolean Literals + +BooleanLiteral: 'true' + | 'false'; + +/// Numeric Literals + +DecimalLiteral: DecimalIntegerLiteral '.' [0-9]* ExponentPart? + | '.' [0-9]+ ExponentPart? + | DecimalIntegerLiteral ExponentPart? + ; + +/// Numeric Literals + +HexIntegerLiteral: '0' [xX] HexDigit+; +OctalIntegerLiteral: '0' [0-7]+ {!this.isStrictMode()}?; +OctalIntegerLiteral2: '0' [oO] [0-7]+; +BinaryIntegerLiteral: '0' [bB] [01]+; + +/// Keywords + +Break: 'break'; +Do: 'do'; +Instanceof: 'instanceof'; +Typeof: 'typeof'; +Case: 'case'; +Else: 'else'; +New: 'new'; +Var: 'var'; +Catch: 'catch'; +Finally: 'finally'; +Return: 'return'; +Void: 'void'; +Continue: 'continue'; +For: 'for'; +Switch: 'switch'; +While: 'while'; +Debugger: 'debugger'; +Function_: 'function'; +This: 'this'; +With: 'with'; +Default: 'default'; +If: 'if'; +Throw: 'throw'; +Delete: 'delete'; +In: 'in'; +Try: 'try'; +As: 'as'; +From: 'from'; +ReadOnly: 'readonly'; +Async: 'async'; + +/// Future Reserved Words + +Class: 'class'; +Enum: 'enum'; +Extends: 'extends'; +Super: 'super'; +Const: 'const'; +Export: 'export'; +Import: 'import'; + +/// The following tokens are also considered to be FutureReservedWords +/// when parsing strict mode + +Implements: 'implements' ; +Let: 'let' ; +Private: 'private' ; +Public: 'public' ; +Interface: 'interface' ; +Package: 'package' ; +Protected: 'protected' ; +Static: 'static' ; +Yield: 'yield' ; + +//keywords: + +Any : 'any'; +Number: 'number'; +Boolean: 'boolean'; +String: 'string'; +Symbol: 'symbol'; + + +TypeAlias : 'type'; + +Get: 'get'; +Set: 'set'; + +Constructor: 'constructor'; +Namespace: 'namespace'; +Require: 'require'; +Module: 'module'; +Declare: 'declare'; + +Abstract: 'abstract'; + +Is: 'is'; + +// +// Ext.2 Additions to 1.8: Decorators +// +At: '@'; + +/// Identifier Names and Identifiers + +Identifier: IdentifierStart IdentifierPart*; + +/// String Literals +StringLiteral: ('"' DoubleStringCharacter* '"' + | '\'' SingleStringCharacter* '\'') {this.processStringLiteral();} + ; + +BackTick: '`' {this.increaseTemplateDepth();} -> pushMode(TEMPLATE); + +WhiteSpaces: [\t\u000B\u000C\u0020\u00A0]+ -> channel(HIDDEN); + +LineTerminator: [\r\n\u2028\u2029] -> channel(HIDDEN); + +/// Comments + + +HtmlComment: '' -> channel(HIDDEN); +CDataComment: '' -> channel(HIDDEN); +UnexpectedCharacter: . -> channel(ERROR); + +mode TEMPLATE; + +TemplateStringEscapeAtom: '\\' .; +BackTickInside: '`' {this.decreaseTemplateDepth();} -> type(BackTick), popMode; +TemplateStringStartExpression: '${' {this.startTemplateString();} -> pushMode(DEFAULT_MODE); +TemplateStringAtom: ~[`\\]; + +// Fragment rules + +fragment DoubleStringCharacter + : ~["\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment SingleStringCharacter + : ~['\\\r\n] + | '\\' EscapeSequence + | LineContinuation + ; + +fragment EscapeSequence + : CharacterEscapeSequence + | '0' // no digit ahead! TODO + | HexEscapeSequence + | UnicodeEscapeSequence + | ExtendedUnicodeEscapeSequence + ; + +fragment CharacterEscapeSequence + : SingleEscapeCharacter + | NonEscapeCharacter + ; + +fragment HexEscapeSequence + : 'x' HexDigit HexDigit + ; + +fragment UnicodeEscapeSequence + : 'u' HexDigit HexDigit HexDigit HexDigit + ; + +fragment ExtendedUnicodeEscapeSequence + : 'u' '{' HexDigit+ '}' + ; + +fragment SingleEscapeCharacter + : ['"\\bfnrtv] + ; + +fragment NonEscapeCharacter + : ~['"\\bfnrtv0-9xu\r\n] + ; + +fragment EscapeCharacter + : SingleEscapeCharacter + | [0-9] + | [xu] + ; + +fragment LineContinuation + : '\\' [\r\n\u2028\u2029] + ; + +fragment HexDigit + : [0-9a-fA-F] + ; + +fragment DecimalIntegerLiteral + : '0' + | [1-9] [0-9]* + ; + +fragment ExponentPart + : [eE] [+-]? [0-9]+ + ; + +fragment IdentifierPart + : IdentifierStart + | [\p{Mn}] + | [\p{Nd}] + | [\p{Pc}] + | '\u200C' + | '\u200D' + ; + +fragment IdentifierStart + : [\p{L}] + | [$_] + | '\\' UnicodeEscapeSequence + ; + +fragment RegularExpressionFirstChar + : ~[*\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionChar + : ~[\r\n\u2028\u2029\\/[] + | RegularExpressionBackslashSequence + | '[' RegularExpressionClassChar* ']' + ; + +fragment RegularExpressionClassChar + : ~[\r\n\u2028\u2029\]\\] + | RegularExpressionBackslashSequence + ; + +fragment RegularExpressionBackslashSequence + : '\\' ~[\r\n\u2028\u2029] + ; + diff --git a/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 new file mode 100644 index 000000000..6e81b63ea --- /dev/null +++ b/languages/typescript/src/main/antlr4/de/jplag/typescript/grammar/TypeScriptParser.g4 @@ -0,0 +1,854 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp) + * Copyright (c) 2017 by Ivan Kochurkin (Positive Technologies): + added ECMAScript 6 support, cleared and transformed to the universal grammar. + * Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go) + * Copyright (c) 2019 by Andrii Artiushok (contributor -> added TypeScript support) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +parser grammar TypeScriptParser; + +options { + tokenVocab=TypeScriptLexer; + superClass=TypeScriptParserBase; +} + +// SupportSyntax + +initializer + : '=' singleExpression + ; + +bindingPattern + : (arrayLiteral | objectLiteral) + ; + +// TypeScript SPart +// A.1 Types + +typeParameters + : '<' typeParameterList? '>' + ; + +typeParameterList + : typeParameter (',' typeParameter)* + ; + +typeParameter + : Identifier constraint? + | typeParameters + ; + +constraint + : 'extends' type_ + ; + +typeArguments + : '<' typeArgumentList? '>' + ; + +typeArgumentList + : typeArgument (',' typeArgument)* + ; + +typeArgument + : type_ + ; + +type_ + : unionOrIntersectionOrPrimaryType + | functionType + | constructorType + | typeGeneric + | StringLiteral + ; + +unionOrIntersectionOrPrimaryType + : unionOrIntersectionOrPrimaryType '|' unionOrIntersectionOrPrimaryType #Union + | unionOrIntersectionOrPrimaryType '&' unionOrIntersectionOrPrimaryType #Intersection + | primaryType #Primary + ; + +primaryType + : '(' type_ ')' #ParenthesizedPrimType + | predefinedType #PredefinedPrimType + | typeReference #ReferencePrimType + | objectType #ObjectPrimType + | primaryType {notLineTerminator()}? '[' ']' #ArrayPrimType + | '[' tupleElementTypes ']' #TuplePrimType + | typeQuery #QueryPrimType + | This #ThisPrimType + | typeReference Is primaryType #RedefinitionOfType + ; + +predefinedType + : Any + | Number + | Boolean + | String + | Symbol + | Void + ; + +typeReference + : typeName nestedTypeGeneric? + ; + +nestedTypeGeneric + : typeIncludeGeneric + | typeGeneric + ; + +// I tried recursive include, but it's not working. +// typeGeneric +// : '<' typeArgumentList typeGeneric?'>' +// ; +// +// TODO: Fix recursive +// +typeGeneric + : '<' typeArgumentList '>' + ; + +typeIncludeGeneric + :'<' typeArgumentList '<' typeArgumentList ('>' bindingPattern '>' | '>>') + ; + +typeName + : Identifier + | namespaceName + ; + +objectType + : '{' typeBody? '}' + ; + +typeBody + : typeMemberList (SemiColon | ',')? + ; + +typeMemberList + : typeMember ((SemiColon | ',') typeMember)* + ; + +typeMember + : propertySignatur + | callSignature + | constructSignature + | indexSignature + | methodSignature ('=>' type_)? + ; + +arrayType + : primaryType {notLineTerminator()}? '[' ']' + ; + +tupleType + : '[' tupleElementTypes ']' + ; + +tupleElementTypes + : type_ (',' type_)* + ; + +functionType + : typeParameters? '(' parameterList? ')' '=>' type_ + ; + +constructorType + : 'new' typeParameters? '(' parameterList? ')' '=>' type_ + ; + +typeQuery + : 'typeof' typeQueryExpression + ; + +typeQueryExpression + : Identifier + | (identifierName '.')+ identifierName + ; + +propertySignatur + : ReadOnly? propertyName '?'? typeAnnotation? ('=>' type_)? + ; + +typeAnnotation + : ':' type_ + ; + +callSignature + : typeParameters? '(' parameterList? ')' typeAnnotation? + ; + +parameterList + : restParameter + | parameter (',' parameter)* (',' restParameter)? + ; + +requiredParameterList + : requiredParameter (',' requiredParameter)* + ; + +parameter + : requiredParameter + | optionalParameter + ; + +optionalParameter + : decoratorList? ( accessibilityModifier? identifierOrPattern ('?' typeAnnotation? | typeAnnotation? initializer)) + ; + +restParameter + : '...' singleExpression typeAnnotation? + ; + +requiredParameter + : decoratorList? accessibilityModifier? identifierOrPattern typeAnnotation? + ; + +accessibilityModifier + : Public + | Private + | Protected + ; + +identifierOrPattern + : identifierName + | bindingPattern + ; + +constructSignature + : 'new' typeParameters? '(' parameterList? ')' typeAnnotation? + ; + +indexSignature + : '[' Identifier ':' (Number|String) ']' typeAnnotation + ; + +methodSignature + : propertyName '?'? callSignature + ; + +typeAliasDeclaration + : 'type' Identifier typeParameters? '=' type_ SemiColon + ; + +constructorDeclaration + : accessibilityModifier? Constructor '(' formalParameterList? ')' ( ('{' functionBody '}') | SemiColon)? + ; + +// A.5 Interface + +interfaceDeclaration + : Export? Declare? Interface Identifier typeParameters? interfaceExtendsClause? objectType SemiColon? + ; + +interfaceExtendsClause + : Extends classOrInterfaceTypeList + ; + +classOrInterfaceTypeList + : typeReference (',' typeReference)* + ; + +// A.7 Interface + +enumDeclaration + : Const? Enum Identifier '{' enumBody? '}' + ; + +enumBody + : enumMemberList ','? + ; + +enumMemberList + : enumMember (',' enumMember)* + ; + +enumMember + : propertyName ('=' singleExpression)? + ; + +// A.8 Namespaces + +namespaceDeclaration + : Namespace namespaceName '{' statementList? '}' + ; + +namespaceName + : Identifier ('.'+ Identifier)* + ; + +importAliasDeclaration + : Identifier '=' namespaceName SemiColon + ; + +// Ext.2 Additions to 1.8: Decorators + +decoratorList + : decorator+ ; + +decorator + : '@' (decoratorMemberExpression | decoratorCallExpression) + ; + +decoratorMemberExpression + : Identifier + | decoratorMemberExpression '.' identifierName + | '(' singleExpression ')' + ; + +decoratorCallExpression + : decoratorMemberExpression arguments; + +// ECMAPart +program + : sourceElements? EOF + ; + +sourceElement + : Export? statement + ; + +statement + : block + | importStatement + | exportStatement + | emptyStatement_ + | abstractDeclaration //ADDED + | classDeclaration + | interfaceDeclaration //ADDED + | namespaceDeclaration //ADDED + | ifStatement + | iterationStatement + | continueStatement + | breakStatement + | returnStatement + | yieldStatement + | withStatement + | labelledStatement + | switchStatement + | throwStatement + | tryStatement + | debuggerStatement + | functionDeclaration + | arrowFunctionDeclaration + | generatorFunctionDeclaration + | variableStatement + | typeAliasDeclaration //ADDED + | enumDeclaration //ADDED + | expressionStatement + | Export statement + ; + +block + : '{' statementList? '}' + ; + +statementList + : statement+ + ; + +abstractDeclaration + : Abstract (Identifier callSignature | variableStatement) eos + ; + +importStatement + : Import (fromBlock | importAliasDeclaration) + ; + +fromBlock + : (Multiply | multipleImportStatement) (As identifierName)? From StringLiteral eos + ; + +multipleImportStatement + : (identifierName ',')? '{' identifierName (',' identifierName)* '}' + ; + +exportStatement + : Export Default? (fromBlock | statement) + ; + +variableStatement + : bindingPattern typeAnnotation? initializer SemiColon? + | accessibilityModifier? varModifier? ReadOnly? variableDeclarationList SemiColon? + | Declare varModifier? variableDeclarationList SemiColon? + ; + +variableDeclarationList + : variableDeclaration (',' variableDeclaration)* + ; + +variableDeclaration + : ( identifierOrKeyWord | arrayLiteral | objectLiteral) typeAnnotation? singleExpression? ('=' typeParameters? singleExpression)? // ECMAScript 6: Array & Object Matching + ; + +emptyStatement_ + : SemiColon + ; + +expressionStatement + : {this.notOpenBraceAndNotFunction()}? expressionSequence SemiColon? + ; + +ifStatement + : If '(' expressionSequence ')' statement (Else statement)? + ; + + +iterationStatement + : Do statement While '(' expressionSequence ')' eos # DoStatement + | While '(' expressionSequence ')' statement # WhileStatement + | For '(' expressionSequence? SemiColon expressionSequence? SemiColon expressionSequence? ')' statement # ForStatement + | For '(' varModifier variableDeclarationList SemiColon expressionSequence? SemiColon expressionSequence? ')' + statement # ForVarStatement + | For '(' singleExpression (In | Identifier{this.p("of")}?) expressionSequence ')' statement # ForInStatement + | For '(' varModifier variableDeclaration (In | Identifier{this.p("of")}?) expressionSequence ')' statement # ForVarInStatement + ; + +varModifier + : Var + | Let + | Const + ; + +continueStatement + : Continue ({this.notLineTerminator()}? Identifier)? eos + ; + +breakStatement + : Break ({this.notLineTerminator()}? Identifier)? eos + ; + +returnStatement + : Return ({this.notLineTerminator()}? expressionSequence)? eos + ; + +yieldStatement + : Yield ({this.notLineTerminator()}? expressionSequence)? eos + ; + +withStatement + : With '(' expressionSequence ')' statement + ; + +switchStatement + : Switch '(' expressionSequence ')' caseBlock + ; + +caseBlock + : '{' caseClauses? (defaultClause caseClauses?)? '}' + ; + +caseClauses + : caseClause+ + ; + +caseClause + : Case expressionSequence ':' statementList? + ; + +defaultClause + : Default ':' statementList? + ; + +labelledStatement + : Identifier ':' statement + ; + +throwStatement + : Throw {this.notLineTerminator()}? expressionSequence eos + ; + +tryStatement + : Try block (catchProduction finallyProduction? | finallyProduction) + ; + +catchProduction + : Catch '(' Identifier ')' block + ; + +finallyProduction + : Finally block + ; + +debuggerStatement + : Debugger eos + ; + +functionDeclaration + : Function_ Identifier callSignature ( ('{' functionBody '}') | SemiColon) + ; + +//Ovveride ECMA +classDeclaration + : decoratorList? (Export Default?)? Abstract? Class Identifier typeParameters? classHeritage classTail + ; + +classHeritage + : classExtendsClause? implementsClause? + ; + +classTail + : '{' classElement* '}' + ; + +classExtendsClause + : Extends typeReference + ; + +implementsClause + : Implements classOrInterfaceTypeList + ; + +// Classes modified +classElement + : constructorDeclaration + | decoratorList? propertyMemberDeclaration + | indexMemberDeclaration + | statement + ; + +propertyMemberDeclaration + : propertyMemberBase propertyName '?'? typeAnnotation? initializer? SemiColon # PropertyDeclarationExpression + | propertyMemberBase propertyName callSignature ( ('{' functionBody '}') | SemiColon) # MethodDeclarationExpression + | propertyMemberBase (getAccessor | setAccessor) # GetterSetterDeclarationExpression + | abstractDeclaration # AbstractMemberDeclaration + ; + +propertyMemberBase + : accessibilityModifier? Async? Static? ReadOnly? + ; + +indexMemberDeclaration + : indexSignature SemiColon + ; + +generatorMethod + : '*'? Identifier '(' formalParameterList? ')' '{' functionBody '}' + ; + +generatorFunctionDeclaration + : Function_ '*' Identifier? '(' formalParameterList? ')' '{' functionBody '}' + ; + +generatorBlock + : '{' generatorDefinition (',' generatorDefinition)* ','? '}' + ; + +generatorDefinition + : '*' iteratorDefinition + ; + +iteratorBlock + : '{' iteratorDefinition (',' iteratorDefinition)* ','? '}' + ; + +iteratorDefinition + : '[' singleExpression ']' '(' formalParameterList? ')' '{' functionBody '}' + ; + +formalParameterList + : formalParameterArg (',' formalParameterArg)* (',' lastFormalParameterArg)? + | lastFormalParameterArg + | arrayLiteral // ECMAScript 6: Parameter Context Matching + | objectLiteral (':' formalParameterList)? // ECMAScript 6: Parameter Context Matching + ; + +formalParameterArg + : decorator? accessibilityModifier? identifierOrKeyWord '?'? typeAnnotation? ('=' singleExpression)? // ECMAScript 6: Initialization + ; + +lastFormalParameterArg // ECMAScript 6: Rest Parameter + : Ellipsis Identifier typeAnnotation? + ; + +functionBody + : sourceElements? + ; + +sourceElements + : sourceElement+ + ; + +arrayLiteral + : ('[' elementList? ']') + ; + +elementList + : arrayElement (','+ arrayElement)* + ; + +arrayElement // ECMAScript 6: Spread Operator + : Ellipsis? (singleExpression | Identifier) ','? + ; + +objectLiteral + : '{' (propertyAssignment (',' propertyAssignment)* ','?)? '}' + ; + +// MODIFIED +propertyAssignment + : propertyName (':' |'=') singleExpression # PropertyExpressionAssignment + | '[' singleExpression ']' ':' singleExpression # ComputedPropertyExpressionAssignment + | getAccessor # PropertyGetter + | setAccessor # PropertySetter + | generatorMethod # MethodProperty + | identifierOrKeyWord # PropertyShorthand + | restParameter # RestParameterInObject + ; + +getAccessor + : getter '(' ')' typeAnnotation? '{' functionBody '}' + ; + +setAccessor + : setter '(' ( Identifier | bindingPattern) typeAnnotation? ')' '{' functionBody '}' + ; + +propertyName + : identifierName + | StringLiteral + | numericLiteral + ; + +arguments + : '(' (argumentList ','?)? ')' + ; + +argumentList + : argument (',' argument)* + ; + +argument // ECMAScript 6: Spread Operator + : Ellipsis? (singleExpression | Identifier) + ; + +expressionSequence + : singleExpression (',' singleExpression)* + ; + +functionExpressionDeclaration + : Function_ Identifier? '(' formalParameterList? ')' typeAnnotation? '{' functionBody '}' + ; + +singleExpression + : functionExpressionDeclaration # FunctionExpression + | arrowFunctionDeclaration # ArrowFunctionExpression // ECMAScript 6 + | singleExpression '[' expressionSequence ']' # MemberIndexExpression + | singleExpression '!'? '.' identifierName nestedTypeGeneric? # MemberDotExpression + // Split to try `new Date()` first, then `new Date`. + | New singleExpression typeArguments? arguments # NewExpression + | New singleExpression typeArguments? # NewExpression + | singleExpression arguments # ArgumentsExpression + | singleExpression {this.notLineTerminator()}? '++' # PostIncrementExpression + | singleExpression {this.notLineTerminator()}? '--' # PostDecreaseExpression + | Delete singleExpression # DeleteExpression + | Void singleExpression # VoidExpression + | Typeof singleExpression # TypeofExpression + | '++' singleExpression # PreIncrementExpression + | '--' singleExpression # PreDecreaseExpression + | '+' singleExpression # UnaryPlusExpression + | '-' singleExpression # UnaryMinusExpression + | '~' singleExpression # BitNotExpression + | '!' singleExpression # NotExpression + | singleExpression ('*' | '/' | '%') singleExpression # MultiplicativeExpression + | singleExpression ('+' | '-') singleExpression # AdditiveExpression + | singleExpression ('<<' | '>>' | '>>>') singleExpression # BitShiftExpression + | singleExpression ('<' | '>' | '<=' | '>=') singleExpression # RelationalExpression + | singleExpression Instanceof singleExpression # InstanceofExpression + | singleExpression In singleExpression # InExpression + | singleExpression ('==' | '!=' | '===' | '!==') singleExpression # EqualityExpression + | singleExpression '&' singleExpression # BitAndExpression + | singleExpression '^' singleExpression # BitXOrExpression + | singleExpression '|' singleExpression # BitOrExpression + | singleExpression '&&' singleExpression # LogicalAndExpression + | singleExpression '||' singleExpression # LogicalOrExpression + | singleExpression '?' singleExpression ':' singleExpression # TernaryExpression + | singleExpression '=' singleExpression # AssignmentExpression + | singleExpression assignmentOperator singleExpression # AssignmentOperatorExpression + | singleExpression templateStringLiteral # TemplateStringExpression // ECMAScript 6 + | iteratorBlock # IteratorsExpression // ECMAScript 6 + | generatorBlock # GeneratorsExpression // ECMAScript 6 + | generatorFunctionDeclaration # GeneratorsFunctionExpression // ECMAScript 6 + | yieldStatement # YieldExpression // ECMAScript 6 + | This # ThisExpression + | identifierName singleExpression? # IdentifierExpression + | Super # SuperExpression + | literal # LiteralExpression + | arrayLiteral # ArrayLiteralExpression + | objectLiteral # ObjectLiteralExpression + | '(' expressionSequence ')' # ParenthesizedExpression + | typeArguments expressionSequence? # GenericTypes + | singleExpression As asExpression # CastAsExpression + ; + +asExpression + : predefinedType ('[' ']')? + | singleExpression + ; + +arrowFunctionDeclaration + : Async? arrowFunctionParameters typeAnnotation? '=>' arrowFunctionBody + ; + +arrowFunctionParameters + : Identifier + | '(' formalParameterList? ')' + ; + +arrowFunctionBody + : singleExpression + | '{' functionBody '}' + ; + +assignmentOperator + : '*=' + | '/=' + | '%=' + | '+=' + | '-=' + | '<<=' + | '>>=' + | '>>>=' + | '&=' + | '^=' + | '|=' + ; + +literal + : NullLiteral + | BooleanLiteral + | StringLiteral + | templateStringLiteral + | RegularExpressionLiteral + | numericLiteral + ; + +templateStringLiteral + : BackTick templateStringAtom* BackTick + ; + +templateStringAtom + : TemplateStringAtom + | TemplateStringStartExpression singleExpression TemplateCloseBrace + | TemplateStringEscapeAtom + ; + +numericLiteral + : DecimalLiteral + | HexIntegerLiteral + | OctalIntegerLiteral + | OctalIntegerLiteral2 + | BinaryIntegerLiteral + ; + +identifierName + : Identifier + | reservedWord + ; + +identifierOrKeyWord + : Identifier + | TypeAlias + | Require + ; + +reservedWord + : keyword + | NullLiteral + | BooleanLiteral + ; + +keyword + : Break + | Do + | Instanceof + | Typeof + | Case + | Else + | New + | Var + | Catch + | Finally + | Return + | Void + | Continue + | For + | Switch + | While + | Debugger + | Function_ + | This + | With + | Default + | If + | Throw + | Delete + | In + | Try + | ReadOnly + | Async + | From + | Class + | Enum + | Extends + | Super + | Const + | Export + | Import + | Implements + | Let + | Private + | Public + | Interface + | Package + | Protected + | Static + | Yield + | Get + | Set + | Require + | TypeAlias + | String + | Boolean + | Number + | Module + ; + +getter + : Get propertyName + ; + +setter + : Set propertyName + ; + +eos + : SemiColon + | EOF + | {this.lineTerminatorAhead()}? + | {this.closeBrace()}? + ; diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java new file mode 100644 index 000000000..d6f1fbb0a --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguage.java @@ -0,0 +1,45 @@ +package de.jplag.typescript; + +import org.kohsuke.MetaInfServices; + +import de.jplag.antlr.AbstractAntlrLanguage; + +/** + * This represents the TypeScript language as a language supported by JPlag. + */ +@MetaInfServices(de.jplag.Language.class) +public class TypeScriptLanguage extends AbstractAntlrLanguage { + + private static final String IDENTIFIER = "typescript"; + private final TypeScriptLanguageOptions options = new TypeScriptLanguageOptions(); + + @Override + public String[] suffixes() { + return new String[] {".ts", ".js"}; + } + + @Override + public String getName() { + return "Typescript Parser"; + } + + @Override + public String getIdentifier() { + return IDENTIFIER; + } + + @Override + public int minimumTokenMatch() { + return 12; + } + + @Override + public TypeScriptLanguageOptions getOptions() { + return options; + } + + @Override + protected TypeScriptParserAdapter initializeParser() { + return new TypeScriptParserAdapter(getOptions().useStrictDefault()); + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java new file mode 100644 index 000000000..8a01976c0 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptLanguageOptions.java @@ -0,0 +1,22 @@ +package de.jplag.typescript; + +import de.jplag.options.LanguageOption; +import de.jplag.options.LanguageOptions; +import de.jplag.options.OptionType; + +/** + * Language Specific options for the TypeScript language + */ +public class TypeScriptLanguageOptions extends LanguageOptions { + + /** + * Whether the Antlr Grammar should parse + */ + private final LanguageOption useStrictDefault = createDefaultOption(OptionType.bool(), "useStrictMode", + "If set JPlag parses files with the JavaScript strict syntax", false); + + public boolean useStrictDefault() { + return this.useStrictDefault.getValue(); + } + +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java new file mode 100644 index 000000000..a71b3dc84 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptListener.java @@ -0,0 +1,151 @@ +package de.jplag.typescript; + +import static de.jplag.typescript.TypeScriptTokenType.ASSIGNMENT; +import static de.jplag.typescript.TypeScriptTokenType.BREAK; +import static de.jplag.typescript.TypeScriptTokenType.CATCH_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CATCH_END; +import static de.jplag.typescript.TypeScriptTokenType.CLASS_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CLASS_END; +import static de.jplag.typescript.TypeScriptTokenType.CONSTRUCTOR_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.CONSTRUCTOR_END; +import static de.jplag.typescript.TypeScriptTokenType.CONTINUE; +import static de.jplag.typescript.TypeScriptTokenType.DECLARATION; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_END; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_MEMBER; +import static de.jplag.typescript.TypeScriptTokenType.EXPORT; +import static de.jplag.typescript.TypeScriptTokenType.FINALLY_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.FINALLY_END; +import static de.jplag.typescript.TypeScriptTokenType.FOR_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.FOR_END; +import static de.jplag.typescript.TypeScriptTokenType.FUNCTION_CALL; +import static de.jplag.typescript.TypeScriptTokenType.IF_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.IF_END; +import static de.jplag.typescript.TypeScriptTokenType.IMPORT; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_END; +import static de.jplag.typescript.TypeScriptTokenType.METHOD_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.METHOD_END; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_END; +import static de.jplag.typescript.TypeScriptTokenType.RETURN; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_CASE; +import static de.jplag.typescript.TypeScriptTokenType.SWITCH_END; +import static de.jplag.typescript.TypeScriptTokenType.THROW; +import static de.jplag.typescript.TypeScriptTokenType.TRY_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.WHILE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.WHILE_END; +import static de.jplag.typescript.grammar.TypeScriptParser.ArgumentsContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ArrowFunctionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.AssignmentExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.BreakStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.CaseClauseContext; +import static de.jplag.typescript.grammar.TypeScriptParser.CatchProductionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ClassDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ConstructorDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ContinueStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.DefaultClauseContext; +import static de.jplag.typescript.grammar.TypeScriptParser.Else; +import static de.jplag.typescript.grammar.TypeScriptParser.EnumDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.EnumMemberContext; +import static de.jplag.typescript.grammar.TypeScriptParser.Export; +import static de.jplag.typescript.grammar.TypeScriptParser.FinallyProductionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForInStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ForVarStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.FunctionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.FunctionExpressionDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.GetterSetterDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.IfStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ImportStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.InterfaceDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.MethodDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.NamespaceDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PostDecreaseExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PostIncrementExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PreDecreaseExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PreIncrementExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertyDeclarationExpressionContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertySetterContext; +import static de.jplag.typescript.grammar.TypeScriptParser.PropertySignaturContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ReturnStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.SwitchStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.ThrowStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.TryStatementContext; +import static de.jplag.typescript.grammar.TypeScriptParser.VariableDeclarationContext; +import static de.jplag.typescript.grammar.TypeScriptParser.WhileStatementContext; + +import java.io.File; + +import de.jplag.antlr.AbstractAntlrListener; +import de.jplag.antlr.TokenCollector; + +/** + * This class is responsible for mapping parsed TypeScript to the internal Token structure + */ +public class TypeScriptListener extends AbstractAntlrListener { + + public TypeScriptListener(TokenCollector collector, File currentFile) { + super(collector, currentFile); + + this.mapRange(ImportStatementContext.class, IMPORT); + this.mapTerminal(Export, EXPORT); + this.mapEnterExit(NamespaceDeclarationContext.class, NAMESPACE_BEGIN, NAMESPACE_END); + + this.mapEnterExit(ClassDeclarationContext.class, CLASS_BEGIN, CLASS_END); + this.mapEnterExit(GetterSetterDeclarationExpressionContext.class, METHOD_BEGIN, METHOD_END); + this.mapRange(PropertyDeclarationExpressionContext.class, DECLARATION); + this.mapRange(PropertyDeclarationExpressionContext.class, ASSIGNMENT, it -> it.initializer() != null); + this.mapRange(PropertySetterContext.class, ASSIGNMENT); + this.mapRange(PropertySignaturContext.class, DECLARATION); + + this.mapEnterExit(InterfaceDeclarationContext.class, INTERFACE_BEGIN, INTERFACE_END); + this.mapEnterExit(ConstructorDeclarationContext.class, CONSTRUCTOR_BEGIN, CONSTRUCTOR_END); + + this.mapEnterExit(EnumDeclarationContext.class, ENUM_BEGIN, ENUM_END); + this.mapRange(EnumMemberContext.class, ENUM_MEMBER); + + this.mapRange(VariableDeclarationContext.class, DECLARATION); + this.mapRange(VariableDeclarationContext.class, ASSIGNMENT, it -> it.Assign() != null); + + this.mapEnterExit(IfStatementContext.class, IF_BEGIN, IF_END); + this.mapTerminal(Else, IF_BEGIN); + + this.mapEnterExit(SwitchStatementContext.class, SWITCH_BEGIN, SWITCH_END); + this.mapRange(CaseClauseContext.class, SWITCH_CASE); + this.mapRange(DefaultClauseContext.class, SWITCH_CASE); + + this.mapEnterExit(MethodDeclarationExpressionContext.class, METHOD_BEGIN, METHOD_END); + + this.mapRange(FunctionDeclarationContext.class, DECLARATION); + this.mapRange(FunctionDeclarationContext.class, ASSIGNMENT); + this.mapEnterExit(FunctionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + + this.mapEnterExit(ArrowFunctionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + this.mapEnterExit(FunctionExpressionDeclarationContext.class, METHOD_BEGIN, METHOD_END); + + this.mapEnterExit(WhileStatementContext.class, WHILE_BEGIN, WHILE_END); + this.mapEnterExit(ForStatementContext.class, FOR_BEGIN, FOR_END); + this.mapEnterExit(ForVarStatementContext.class, FOR_BEGIN, FOR_END); + this.mapEnterExit(ForInStatementContext.class, FOR_BEGIN, FOR_END); + + this.mapRange(TryStatementContext.class, TRY_BEGIN); + this.mapEnterExit(CatchProductionContext.class, CATCH_BEGIN, CATCH_END); + this.mapEnterExit(FinallyProductionContext.class, FINALLY_BEGIN, FINALLY_END); + + this.mapRange(BreakStatementContext.class, BREAK); + this.mapRange(ReturnStatementContext.class, RETURN); + this.mapRange(ContinueStatementContext.class, CONTINUE); + this.mapRange(ThrowStatementContext.class, THROW); + + this.mapRange(AssignmentExpressionContext.class, ASSIGNMENT); + this.mapRange(PostDecreaseExpressionContext.class, ASSIGNMENT); + this.mapRange(PreDecreaseExpressionContext.class, ASSIGNMENT); + this.mapRange(PostIncrementExpressionContext.class, ASSIGNMENT); + this.mapRange(PreIncrementExpressionContext.class, ASSIGNMENT); + + this.mapRange(ArgumentsContext.class, FUNCTION_CALL); + } + +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java new file mode 100644 index 000000000..2f7586d1c --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptParserAdapter.java @@ -0,0 +1,52 @@ +package de.jplag.typescript; + +import java.io.File; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.ParserRuleContext; + +import de.jplag.antlr.AbstractAntlrListener; +import de.jplag.antlr.AbstractAntlrParserAdapter; +import de.jplag.antlr.TokenCollector; +import de.jplag.typescript.grammar.TypeScriptLexer; +import de.jplag.typescript.grammar.TypeScriptParser; + +/** + * The Antlr adapter used for the TypeScript language module + */ +public class TypeScriptParserAdapter extends AbstractAntlrParserAdapter { + + private final boolean useStrictDefault; + + /** + * Creates a new Parser adapter for the Typescript Antlr Grammar + * @param useStrictDefault True if the grammars should parse the files using the JavaScript strict syntax + */ + public TypeScriptParserAdapter(boolean useStrictDefault) { + this.useStrictDefault = useStrictDefault; + } + + @Override + protected Lexer createLexer(CharStream input) { + TypeScriptLexer lexer = new TypeScriptLexer(input); + lexer.setUseStrictDefault(useStrictDefault); + return lexer; + } + + @Override + protected TypeScriptParser createParser(CommonTokenStream tokenStream) { + return new TypeScriptParser(tokenStream); + } + + @Override + protected ParserRuleContext getEntryContext(TypeScriptParser parser) { + return parser.sourceElements(); + } + + @Override + protected AbstractAntlrListener createListener(TokenCollector collector, File currentFile) { + return new TypeScriptListener(collector, currentFile); + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java new file mode 100644 index 000000000..da8b0f8da --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/TypeScriptTokenType.java @@ -0,0 +1,56 @@ +package de.jplag.typescript; + +import de.jplag.TokenType; + +/** + * Tokens extracted by the TypeScript language module + */ +public enum TypeScriptTokenType implements TokenType { + + IMPORT("IMPORT"), + EXPORT("EXPORT"), + NAMESPACE_BEGIN("NAMESPACE{"), + NAMESPACE_END("}NAMESPACE"), + CLASS_BEGIN("CLASS{"), + CLASS_END("}CLASS"), + INTERFACE_BEGIN("INTERFACE{"), + INTERFACE_END("}INTERFACE"), + ENUM_BEGIN("ENUM{"), + ENUM_END("}ENUM"), + METHOD_BEGIN("METHOD{"), + METHOD_END("}METHOD"), + WHILE_BEGIN("WHILE{"), + WHILE_END("}WHILE"), + FOR_BEGIN("FOR{"), + FOR_END("}FOR"), + ASSIGNMENT("ASSIGN"), + IF_BEGIN("IF{"), + IF_END("}IF"), + SWITCH_BEGIN("SWITCH{"), + SWITCH_END("}SWITCH"), + SWITCH_CASE("CASE"), + TRY_BEGIN("TRY{"), + CATCH_BEGIN("}CATCH{"), + CATCH_END("}CATCH"), + FINALLY_BEGIN("FINALLY{"), + FINALLY_END("}FINALLY"), + BREAK("BREAK"), + RETURN("RETURN"), + THROW("THROW"), + CONTINUE("CONTINUE"), + FUNCTION_CALL("CALL"), + ENUM_MEMBER("ENUM_MEMBER"), + CONSTRUCTOR_BEGIN("CONSTRUCT{"), + CONSTRUCTOR_END("}CONSTRUCT"), + DECLARATION("DECLARE"); + + private final String description; + + public String getDescription() { + return this.description; + } + + TypeScriptTokenType(String description) { + this.description = description; + } +} diff --git a/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java new file mode 100644 index 000000000..d08aa4cb6 --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptLexerBase.java @@ -0,0 +1,133 @@ +package de.jplag.typescript.grammar; + +import java.util.ArrayDeque; +import java.util.Deque; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; + +/** + * Copied from https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/Java. Slightly modified to fit + * JPlag code style + */ +abstract class TypeScriptLexerBase extends Lexer { + /** + * Stores values of nested modes. By default mode is strict or defined externally (useStrictDefault) + */ + private final Deque scopeStrictModes = new ArrayDeque<>(); + + private Token lastToken = null; + /** + * Default value of strict mode Can be defined externally by setUseStrictDefault + */ + private boolean useStrictDefault = false; + /** + * Current value of strict mode Can be defined during parsing, see StringFunctions.js and StringGlobal.js samples + */ + private boolean useStrictCurrent = false; + /** + * Keeps track of the current depth of nested template string backticks. E.g. after the X in: `${a ? `${X templateDepth + * will be 2. This variable is needed to determine if a `}` is a plain CloseBrace, or one that closes an expression + * inside a template string. + */ + private int templateDepth = 0; + + private int openBracesCount = 0; + + protected TypeScriptLexerBase(CharStream input) { + super(input); + } + + public boolean getStrictDefault() { + return useStrictDefault; + } + + public void setUseStrictDefault(boolean value) { + useStrictDefault = value; + useStrictCurrent = value; + } + + public boolean isStrictMode() { + return useStrictCurrent; + } + + public void startTemplateString() { + this.openBracesCount = 0; + } + + public boolean isInTemplateString() { + return this.templateDepth > 0 && this.openBracesCount == 0; + } + + /** + * Return the next token from the character stream and records this last token in case it resides on the default + * channel. This recorded token is used to determine when the lexer could possibly match a regex literal. Also changes + * scopeStrictModes stack if tokenize special string 'use strict'; + * @return the next token from the character stream. + */ + @Override + public Token nextToken() { + Token next = super.nextToken(); + + if (next.getChannel() == Token.DEFAULT_CHANNEL) { + // Keep track of the last token on the default channel. + this.lastToken = next; + } + + return next; + } + + protected void processOpenBrace() { + openBracesCount++; + useStrictCurrent = !scopeStrictModes.isEmpty() && scopeStrictModes.peek() || useStrictDefault; + scopeStrictModes.push(useStrictCurrent); + } + + protected void processCloseBrace() { + openBracesCount--; + useStrictCurrent = scopeStrictModes.isEmpty() ? useStrictDefault : scopeStrictModes.pop(); + } + + protected void processStringLiteral() { + if (lastToken == null || lastToken.getType() == TypeScriptLexer.OpenBrace) { + String text = getText(); + if (text.equals("\"use strict\"") || text.equals("'use strict'")) { + if (!scopeStrictModes.isEmpty()) { + scopeStrictModes.pop(); + } + useStrictCurrent = true; + scopeStrictModes.push(true); + } + } + } + + protected void increaseTemplateDepth() { + this.templateDepth++; + } + + protected void decreaseTemplateDepth() { + this.templateDepth--; + } + + /** + * Returns {@code true} if the lexer can match a regex literal. + */ + protected boolean isRegexPossible() { + + if (this.lastToken == null) { + // No token has been produced yet: at the start of the input, + // no division is possible, so a regex literal _is_ possible. + return true; + } + + return switch (this.lastToken.getType()) { + case TypeScriptLexer.Identifier, TypeScriptLexer.NullLiteral, TypeScriptLexer.BooleanLiteral, TypeScriptLexer.This, TypeScriptLexer.CloseBracket, TypeScriptLexer.CloseParen, TypeScriptLexer.OctalIntegerLiteral, TypeScriptLexer.DecimalLiteral, TypeScriptLexer.HexIntegerLiteral, TypeScriptLexer.StringLiteral, TypeScriptLexer.PlusPlus, TypeScriptLexer.MinusMinus -> + // After any of the tokens above, no regex literal can follow. + false; + default -> + // In all other cases, a regex literal _is_ possible. + true; + }; + } +} \ No newline at end of file diff --git a/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java new file mode 100644 index 000000000..cf4c032cc --- /dev/null +++ b/languages/typescript/src/main/java/de/jplag/typescript/grammar/TypeScriptParserBase.java @@ -0,0 +1,113 @@ +package de.jplag.typescript.grammar; + +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; + +/** + * All parser methods that used in grammar (p, prev, notLineTerminator, etc.) should start with lower case char similar + * to parser rules. Copied from https://github.com/antlr/grammars-v4/tree/master/javascript/typescript/Java. Slightly + * modified to fit JPlag code style + */ +public abstract class TypeScriptParserBase extends Parser { + protected TypeScriptParserBase(TokenStream input) { + super(input); + } + + /** + * Short form for prev(String str) + */ + protected boolean p(String str) { + return prev(str); + } + + /** + * Whether the previous token value equals to @param str + */ + protected boolean prev(String str) { + return _input.LT(-1).getText().equals(str); + } + + /** + * Short form for next(String str) + */ + protected boolean n(String str) { + return next(str); + } + + /** + * Whether the next token value equals to @param str + */ + protected boolean next(String str) { + return _input.LT(1).getText().equals(str); + } + + protected boolean notLineTerminator() { + return !here(TypeScriptParser.LineTerminator); + } + + protected boolean notOpenBraceAndNotFunction() { + int nextTokenType = _input.LT(1).getType(); + return nextTokenType != TypeScriptParser.OpenBrace && nextTokenType != TypeScriptParser.Function_; + } + + protected boolean closeBrace() { + return _input.LT(1).getType() == TypeScriptParser.CloseBrace; + } + + /** + * Returns {@code true} iff on the current index of the parser's token stream a token of the given {@code type} exists + * on the {@code HIDDEN} channel. + * @param type the type of the token on the {@code HIDDEN} channel to check. + * @return {@code true} iff on the current index of the parser's token stream a token of the given {@code type} exists + * on the {@code HIDDEN} channel. + */ + private boolean here(final int type) { + + // Get the token ahead of the current index. + int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1; + Token ahead = _input.get(possibleIndexEosToken); + + // Check if the token resides on the HIDDEN channel and if it's of the + // provided type. + return (ahead.getChannel() == Lexer.HIDDEN) && (ahead.getType() == type); + } + + /** + * Returns {@code true} iff on the current index of the parser's token stream a token exists on the {@code HIDDEN} + * channel which either is a line terminator, or is a multi line comment that contains a line terminator. + * @return {@code true} iff on the current index of the parser's token stream a token exists on the {@code HIDDEN} + * channel which either is a line terminator, or is a multi line comment that contains a line terminator. + */ + protected boolean lineTerminatorAhead() { + + // Get the token ahead of the current index. + int possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 1; + Token ahead = _input.get(possibleIndexEosToken); + + if (ahead.getChannel() != Lexer.HIDDEN) { + // We're only interested in tokens on the HIDDEN channel. + return false; + } + + if (ahead.getType() == TypeScriptParser.LineTerminator) { + // There is definitely a line terminator ahead. + return true; + } + + if (ahead.getType() == TypeScriptParser.WhiteSpaces) { + // Get the token ahead of the current whitespaces. + possibleIndexEosToken = this.getCurrentToken().getTokenIndex() - 2; + ahead = _input.get(possibleIndexEosToken); + } + + // Get the token's text and type. + String text = ahead.getText(); + int type = ahead.getType(); + + // Check if the token is, or contains a line terminator. + return (type == TypeScriptParser.MultiLineComment && (text.contains("\r") || text.contains("\n"))) + || (type == TypeScriptParser.LineTerminator); + } +} diff --git a/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java b/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java new file mode 100644 index 000000000..77c696a3f --- /dev/null +++ b/languages/typescript/src/test/java/de/jplag/typescript/JavaScriptLanguageTest.java @@ -0,0 +1,37 @@ +package de.jplag.typescript; + +import static de.jplag.typescript.TypeScriptTokenType.ENUM_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_END; +import static de.jplag.typescript.TypeScriptTokenType.ENUM_MEMBER; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.INTERFACE_END; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_BEGIN; +import static de.jplag.typescript.TypeScriptTokenType.NAMESPACE_END; + +import java.util.ArrayList; +import java.util.List; + +import de.jplag.TokenType; +import de.jplag.testutils.LanguageModuleTest; +import de.jplag.testutils.datacollector.TestDataCollector; +import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector; + +public class JavaScriptLanguageTest extends LanguageModuleTest { + + public JavaScriptLanguageTest() { + super(new TypeScriptLanguage(), TypeScriptTokenType.class); + } + + @Override + protected void collectTestData(TestDataCollector collector) { + List javascriptTokens = new ArrayList<>(List.of(TypeScriptTokenType.values())); + javascriptTokens.removeAll(List.of(NAMESPACE_BEGIN, NAMESPACE_END, INTERFACE_BEGIN, INTERFACE_END, ENUM_BEGIN, ENUM_END, ENUM_MEMBER)); + collector.testFile("allJSTokens.js").testSourceCoverage().testContainedTokens(javascriptTokens.toArray(new TokenType[0])); + } + + @Override + protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) { + collector.ignoreMultipleLines("/*", "*/"); + collector.ignoreLinesByPrefix("//"); + } +} diff --git a/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java b/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java new file mode 100644 index 000000000..338066bc7 --- /dev/null +++ b/languages/typescript/src/test/java/de/jplag/typescript/TypeScriptLanguageTest.java @@ -0,0 +1,46 @@ +package de.jplag.typescript; + +import de.jplag.testutils.LanguageModuleTest; +import de.jplag.testutils.datacollector.TestDataCollector; +import de.jplag.testutils.datacollector.TestSourceIgnoredLinesCollector; + +public class TypeScriptLanguageTest extends LanguageModuleTest { + + public TypeScriptLanguageTest() { + super(new TypeScriptLanguage(), TypeScriptTokenType.class); + } + + @Override + protected void collectTestData(TestDataCollector collector) { + collector.testFile("simpleTest.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.ASSIGNMENT); + collector.testFile("forLoops.ts").testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.FUNCTION_CALL, + TypeScriptTokenType.FOR_END, TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END, + TypeScriptTokenType.FOR_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.FOR_END); + collector.testFile("methods.ts").testTokenSequence(TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, + TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, + TypeScriptTokenType.METHOD_END); + collector.testFile("class.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.CLASS_BEGIN, TypeScriptTokenType.DECLARATION, + TypeScriptTokenType.DECLARATION, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.CONSTRUCTOR_BEGIN, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.CONSTRUCTOR_END, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, + TypeScriptTokenType.METHOD_END, TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_END, + TypeScriptTokenType.METHOD_BEGIN, TypeScriptTokenType.RETURN, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.METHOD_BEGIN, + TypeScriptTokenType.ASSIGNMENT, TypeScriptTokenType.METHOD_END, TypeScriptTokenType.CLASS_END); + collector.testFile("if.ts").testSourceCoverage().testTokenSequence(TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.FUNCTION_CALL, + TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.IF_BEGIN, TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_BEGIN, + TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_END, TypeScriptTokenType.IF_END, TypeScriptTokenType.IF_BEGIN, + TypeScriptTokenType.FUNCTION_CALL, TypeScriptTokenType.IF_END); + collector.testFile("allTokens.ts").testCoverages(); + } + + @Override + protected void configureIgnoredLines(TestSourceIgnoredLinesCollector collector) { + collector.ignoreMultipleLines("/*", "*/"); + collector.ignoreLinesByPrefix("//"); + } +} diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js b/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js new file mode 100644 index 000000000..315b9d965 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/allJSTokens.js @@ -0,0 +1,58 @@ +import {test} from 'test'; +class Class { + #x; + + constructor(x) { + this.x = x; + } + + test(y) { + return this.x + y; + } +} + +let a = 3; +const d = 4; + +function c() { + let b; + b = 8; + return a - b; +} + +switch (c()) { + case 1: + break; + default: + console.log(c()) +} + +try { + throw "Error" +} catch (e) { + console.log('Error', e); +} finally { + console.log() +} + +for (let i = 0; i < a; i++) { + +} + +while(a > 3) { + a--; + if (d > 5) { + continue; + } +} + +export default Class; +export { c }; + +if (d) { + +} else if (d < 10) { + +} else { + +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts b/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts new file mode 100644 index 000000000..86a64b99a --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/allTokens.ts @@ -0,0 +1,70 @@ +import {test} from 'test'; + +namespace Name { + enum Values { + val1 = 3, + val2 + } +} + +interface Inter { + value: number; +} + +class Class { + private x; + + constructor(x) { + this.x = x; + } + + public test(y: number) { + return this.x + y; + } +} + +let a = 3; +const d = 4; + +function c() { + let b; + b = 8; + return a - b; +} + +switch (c()) { + case 1: + break; + default: + console.log(c()) +} + +try { + throw "Error" +} catch (e) { + console.log('Error', e); +} finally { + console.log() +} + +for (let i = 0; i < a; i++) { + +} + +while(a > 3) { + a--; + if (d > 5) { + continue; + } +} + +export default Name; +export { c }; + +if (d) { + +} else if (d < 10) { + +} else { + +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/class.ts b/languages/typescript/src/test/resources/de/jplag/typescript/class.ts new file mode 100644 index 000000000..fca08b0d6 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/class.ts @@ -0,0 +1,25 @@ +class AddTest { + + private _x: number; + protected _y = 5; + + constructor(x) { + this._x = x; + } + + public getX() { + return this._x; + } + + public setX(x: number) { + this._x = x; + } + + get y() { + return this._y; + } + + set y(y: number) { + this._y = y; + } +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts b/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts new file mode 100644 index 000000000..17c9b8a78 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/forLoops.ts @@ -0,0 +1,13 @@ +const are = ["Test", "Test2"]; + +for (let i = 0; i < are.length; i++) { + console.log(are[i]) +} + +for (const a in are) { + console.log(a) +} + +for (const a of are) { + console.log(a) +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/if.ts b/languages/typescript/src/test/resources/de/jplag/typescript/if.ts new file mode 100644 index 000000000..11c474542 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/if.ts @@ -0,0 +1,11 @@ +if (true) { + console.log() +} else if(true) { + console.log() +} else { + console.log() +} + +if (true) { + console.log() +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts b/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts new file mode 100644 index 000000000..69cf57521 --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/methods.ts @@ -0,0 +1,11 @@ +function add1(a: number, b: number) { + return a + b; +} + +const add2 = function (a: number, b: number) { + return a + b; +} + +const add3 = (a: number, b: number) => { + return a + b; +} \ No newline at end of file diff --git a/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts b/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts new file mode 100644 index 000000000..9f57d346d --- /dev/null +++ b/languages/typescript/src/test/resources/de/jplag/typescript/simpleTest.ts @@ -0,0 +1,9 @@ +let counter = 0; +let arr = [] + +for (let i = 1; i <=5; i++) { + arr.push(i); +} + +let arrRev = arr.reverse(); +counter++; \ No newline at end of file diff --git a/pom.xml b/pom.xml index b26b5a3c3..801156d6d 100644 --- a/pom.xml +++ b/pom.xml @@ -75,11 +75,11 @@ 17 17 2.39.0 - 2.0.7 + 2.0.9 5.10.0 2.7.7 - 4.13.0 + 4.13.1 2.34.0 2.28.0 2.18.0 diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index 6f32b9622..0ea377c82 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -12,7 +12,7 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "chart.js": "^4.3.3", + "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", @@ -28,27 +28,27 @@ "@playwright/test": "^1.37.1", "@rushstack/eslint-patch": "^1.3.3", "@types/jsdom": "^21.1.2", - "@types/node": "^20.5.0", + "@types/node": "^20.6.0", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-typescript": "^11.0.3", "@vue/test-utils": "^2.4.1", "@vue/tsconfig": "^0.4.0", "autoprefixer": "^10.4.15", - "eslint": "^8.48.0", + "eslint": "^8.49.0", "eslint-plugin-vue": "^9.17.0", "husky": "^8.0.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "npm-run-all": "^4.1.5", - "postcss": "^8.4.27", - "prettier": "^3.0.2", - "prettier-plugin-tailwindcss": "^0.5.3", + "postcss": "^8.4.29", + "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "^3.3.3", "typescript": "^5.2.2", "vite": "^4.4.9", - "vitest": "^0.34.3", - "vue-tsc": "^1.8.8" + "vitest": "^0.34.4", + "vue-tsc": "^1.8.11" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -483,9 +483,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", - "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", + "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -546,9 +546,9 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -788,9 +788,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.0.tgz", - "integrity": "sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q==", + "version": "20.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", + "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==", "dev": true }, "node_modules/@types/semver": { @@ -1007,13 +1007,13 @@ } }, "node_modules/@vitest/expect": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.3.tgz", - "integrity": "sha512-F8MTXZUYRBVsYL1uoIft1HHWhwDbSzwAU9Zgh8S6WFC3YgVb4AnFV2GXO3P5Em8FjEYaZtTnQYoNwwBrlOMXgg==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.4.tgz", + "integrity": "sha512-XlMKX8HyYUqB8dsY8Xxrc64J2Qs9pKMt2Z8vFTL4mBWXJsg4yoALHzJfDWi8h5nkO4Zua4zjqtapQ/IluVkSnA==", "dev": true, "dependencies": { - "@vitest/spy": "0.34.3", - "@vitest/utils": "0.34.3", + "@vitest/spy": "0.34.4", + "@vitest/utils": "0.34.4", "chai": "^4.3.7" }, "funding": { @@ -1021,12 +1021,12 @@ } }, "node_modules/@vitest/runner": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.3.tgz", - "integrity": "sha512-lYNq7N3vR57VMKMPLVvmJoiN4bqwzZ1euTW+XXYH5kzr3W/+xQG3b41xJn9ChJ3AhYOSoweu974S1V3qDcFESA==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.4.tgz", + "integrity": "sha512-hwwdB1StERqUls8oV8YcpmTIpVeJMe4WgYuDongVzixl5hlYLT2G8afhcdADeDeqCaAmZcSgLTLtqkjPQF7x+w==", "dev": true, "dependencies": { - "@vitest/utils": "0.34.3", + "@vitest/utils": "0.34.4", "p-limit": "^4.0.0", "pathe": "^1.1.1" }, @@ -1062,9 +1062,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.3.tgz", - "integrity": "sha512-QyPaE15DQwbnIBp/yNJ8lbvXTZxS00kRly0kfFgAD5EYmCbYcA+1EEyRalc93M0gosL/xHeg3lKAClIXYpmUiQ==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.4.tgz", + "integrity": "sha512-GCsh4coc3YUSL/o+BPUo7lHQbzpdttTxL6f4q0jRx2qVGoYz/cyTRDJHbnwks6TILi6560bVWoBpYC10PuTLHw==", "dev": true, "dependencies": { "magic-string": "^0.30.1", @@ -1076,9 +1076,9 @@ } }, "node_modules/@vitest/spy": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.3.tgz", - "integrity": "sha512-N1V0RFQ6AI7CPgzBq9kzjRdPIgThC340DGjdKdPSE8r86aUSmeliTUgkTqLSgtEwWWsGfBQ+UetZWhK0BgJmkQ==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.4.tgz", + "integrity": "sha512-PNU+fd7DUPgA3Ya924b1qKuQkonAW6hL7YUjkON3wmBwSTIlhOSpy04SJ0NrRsEbrXgMMj6Morh04BMf8k+w0g==", "dev": true, "dependencies": { "tinyspy": "^2.1.1" @@ -1088,9 +1088,9 @@ } }, "node_modules/@vitest/utils": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.3.tgz", - "integrity": "sha512-kiSnzLG6m/tiT0XEl4U2H8JDBjFtwVlaE8I3QfGiMFR0QvnRDfYfdP3YvTBWM/6iJDAyaPY6yVQiCTUc7ZzTHA==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.4.tgz", + "integrity": "sha512-yR2+5CHhp/K4ySY0Qtd+CAL9f5Yh1aXrKfAT42bq6CtlGPh92jIDDDSg7ydlRow1CP+dys4TrOrbELOyNInHSg==", "dev": true, "dependencies": { "diff-sequences": "^29.4.3", @@ -1102,30 +1102,30 @@ } }, "node_modules/@volar/language-core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.0.tgz", - "integrity": "sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.1.tgz", + "integrity": "sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==", "dev": true, "dependencies": { - "@volar/source-map": "1.10.0" + "@volar/source-map": "1.10.1" } }, "node_modules/@volar/source-map": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.0.tgz", - "integrity": "sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.1.tgz", + "integrity": "sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==", "dev": true, "dependencies": { "muggle-string": "^0.3.1" } }, "node_modules/@volar/typescript": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.0.tgz", - "integrity": "sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.1.tgz", + "integrity": "sha512-+iiO9yUSRHIYjlteT+QcdRq8b44qH19/eiUZtjNtuh6D9ailYM7DVR0zO2sEgJlvCaunw/CF9Ov2KooQBpR4VQ==", "dev": true, "dependencies": { - "@volar/language-core": "1.10.0" + "@volar/language-core": "1.10.1" } }, "node_modules/@vue/compiler-core": { @@ -1218,9 +1218,9 @@ } }, "node_modules/@vue/language-core": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.8.tgz", - "integrity": "sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==", + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.11.tgz", + "integrity": "sha512-+MZOBGqGwfld6hpo0DB47x8eNM0dNqk15ZdfOhj19CpvuYuOWCeVdOEGZunKDyo3QLkTn3kLOSysJzg7FDOQBA==", "dev": true, "dependencies": { "@volar/language-core": "~1.10.0", @@ -1347,13 +1347,13 @@ "dev": true }, "node_modules/@vue/typescript": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.8.tgz", - "integrity": "sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==", + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.11.tgz", + "integrity": "sha512-skUmMDiPUUtu1flPmf2YybF+PX8IzBtMioQOaNn6Ck/RhdrPJGj1AX/7s3Buf9G6ln+/KHR1XQuti/FFxw5XVA==", "dev": true, "dependencies": { "@volar/typescript": "~1.10.0", - "@vue/language-core": "1.8.8" + "@vue/language-core": "1.8.11" } }, "node_modules/abab": { @@ -1800,9 +1800,9 @@ } }, "node_modules/chart.js": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", - "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -2414,16 +2414,16 @@ } }, "node_modules/eslint": { - "version": "8.48.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", - "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", + "version": "8.49.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", + "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.48.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint/js": "8.49.0", + "@humanwhocodes/config-array": "^0.11.11", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.12.4", @@ -4146,9 +4146,9 @@ "integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==" }, "node_modules/mlly": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.1.tgz", - "integrity": "sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", + "integrity": "sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==", "dev": true, "dependencies": { "acorn": "^8.10.0", @@ -4846,9 +4846,9 @@ } }, "node_modules/postcss": { - "version": "8.4.27", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz", - "integrity": "sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==", + "version": "8.4.29", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", + "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", "funding": [ { "type": "opencollective", @@ -4985,9 +4985,9 @@ } }, "node_modules/prettier": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.2.tgz", - "integrity": "sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -5012,9 +5012,9 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.3.tgz", - "integrity": "sha512-M5K80V21yM+CTm/FEFYRv9/9LyInYbCSXpIoPAKMm8zy89IOwdiA2e4JVbcO7tvRtAQWz32zdj7/WKcsmFyAVg==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.4.tgz", + "integrity": "sha512-QZzzB1bID6qPsKHTeA9qPo1APmmxfFrA5DD3LQ+vbTmAnY40eJI7t9Q1ocqel2EKMWNPLJqdTDWZj1hKYgqSgg==", "dev": true, "engines": { "node": ">=14.21.3" @@ -5084,9 +5084,9 @@ } }, "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -6422,9 +6422,9 @@ } }, "node_modules/vite-node": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.3.tgz", - "integrity": "sha512-+0TzJf1g0tYXj6tR2vEyiA42OPq68QkRZCu/ERSo2PtsDJfBpDyEfuKbRvLmZqi/CgC7SCBtyC+WjTGNMRIaig==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.4.tgz", + "integrity": "sha512-ho8HtiLc+nsmbwZMw8SlghESEE3KxJNp04F/jPUCLVvaURwt0d+r9LxEqCX5hvrrOQ0GSyxbYr5ZfRYhQ0yVKQ==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -6445,19 +6445,19 @@ } }, "node_modules/vitest": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.3.tgz", - "integrity": "sha512-7+VA5Iw4S3USYk+qwPxHl8plCMhA5rtfwMjgoQXMT7rO5ldWcdsdo3U1QD289JgglGK4WeOzgoLTsGFu6VISyQ==", + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.4.tgz", + "integrity": "sha512-SE/laOsB6995QlbSE6BtkpXDeVNLJc1u2LHRG/OpnN4RsRzM3GQm4nm3PQCK5OBtrsUqnhzLdnT7se3aeNGdlw==", "dev": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.34.3", - "@vitest/runner": "0.34.3", - "@vitest/snapshot": "0.34.3", - "@vitest/spy": "0.34.3", - "@vitest/utils": "0.34.3", + "@vitest/expect": "0.34.4", + "@vitest/runner": "0.34.4", + "@vitest/snapshot": "0.34.4", + "@vitest/spy": "0.34.4", + "@vitest/utils": "0.34.4", "acorn": "^8.9.0", "acorn-walk": "^8.2.0", "cac": "^6.7.14", @@ -6471,8 +6471,8 @@ "strip-literal": "^1.0.1", "tinybench": "^2.5.0", "tinypool": "^0.7.0", - "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.34.3", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.4", "why-is-node-running": "^2.2.2" }, "bin": { @@ -6644,13 +6644,13 @@ } }, "node_modules/vue-tsc": { - "version": "1.8.8", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.8.tgz", - "integrity": "sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==", + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.11.tgz", + "integrity": "sha512-BzfiMdPqDHBlysx4g26NkfVHSQwGD/lTRausmxN9sFyjXz34OWfsbkh0YsVkX84Hu65In1fFlxHiG39Tr4Vojg==", "dev": true, "dependencies": { - "@vue/language-core": "1.8.8", - "@vue/typescript": "1.8.8", + "@vue/language-core": "1.8.11", + "@vue/typescript": "1.8.11", "semver": "^7.3.8" }, "bin": { diff --git a/report-viewer/package.json b/report-viewer/package.json index d38e30331..3650b11e7 100644 --- a/report-viewer/package.json +++ b/report-viewer/package.json @@ -21,7 +21,7 @@ "@fortawesome/free-regular-svg-icons": "^6.4.2", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/vue-fontawesome": "^3.0.3", - "chart.js": "^4.3.3", + "chart.js": "^4.4.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.8.0", "jszip": "^3.10.0", @@ -37,26 +37,26 @@ "@playwright/test": "^1.37.1", "@rushstack/eslint-patch": "^1.3.3", "@types/jsdom": "^21.1.2", - "@types/node": "^20.5.0", + "@types/node": "^20.6.0", "@vitejs/plugin-vue": "^4.3.4", "@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-typescript": "^11.0.3", "@vue/test-utils": "^2.4.1", "@vue/tsconfig": "^0.4.0", "autoprefixer": "^10.4.15", - "eslint": "^8.48.0", + "eslint": "^8.49.0", "eslint-plugin-vue": "^9.17.0", "husky": "^8.0.0", "jsdom": "^22.1.0", "lint-staged": "^14.0.1", "npm-run-all": "^4.1.5", - "postcss": "^8.4.27", - "prettier": "^3.0.2", - "prettier-plugin-tailwindcss": "^0.5.3", + "postcss": "^8.4.29", + "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "^3.3.3", "typescript": "^5.2.2", "vite": "^4.4.9", - "vitest": "^0.34.3", - "vue-tsc": "^1.8.8" + "vitest": "^0.34.4", + "vue-tsc": "^1.8.11" } }