Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Use tooling lint repository for linting and integrate eslint #92

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
"targets": {
"node": "14.16"
}

}
],
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-nullish-coalescing-operator",
[
"module-resolver",
{
Expand Down
72 changes: 0 additions & 72 deletions .eslintrc

This file was deleted.

9 changes: 9 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
root: true,
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.eslint.json'],
},
extends: '@exercism/eslint-config-tooling',
ignorePatterns: ['.eslintrc.js'],
}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.14.0

- Use `@exercism/eslint-config-tooling` for linting the files in this repository
- Use `@exercism/eslint-config-javascript` to lint (and report) on student code

## 0.13.1

- Update dependencies
Expand Down
15 changes: 0 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,6 @@ It will also format the output JSON with 2 space indentation, both in the output
If you wish to _preview_ the actual messages, pass in `--noTemplates` to use the analyzer `Comment`Factories to generate actual messages.
If the comment factories are kept in-sync with `website-copy`, it will be the exact same output as on the site.

### `batch` (.sh, .bat)

```shell
./bin/batch.sh two-fer -cp
```

Runs all the fixtures in `~/test/fixtures/two-fer` through the analyzer, giving a summary at the end with all results.
This places an `analysis.json` in the source fixture folder.

You'll most likely want `-cp` (`--console` and `--pretty`) during development, which enables console output (instead of `stdout`/`stderr`) and formats the
output JSON with 2 space indentation.

If you wish to _preview_ the actual messages, pass in `--noTemplates` to use the analyzer `Comment`Factories to generate actual messages.
If the comment factories are kept in-sync with `website-copy`, it will be the exact same output as on the site.

### `remote` (.sh, .bat)

```shell
Expand Down
23 changes: 13 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@exercism/javascript-analyzer",
"version": "0.13.2",
"version": "0.14.0",
"description": "Exercism analyzer for javascript",
"repository": "https://github.com/exercism/javascript-analyzer",
"author": "Derk-Jan Karrenbeld <[email protected]>",
Expand Down Expand Up @@ -38,27 +38,30 @@
"@babel/plugin-proposal-optional-chaining": "^7.13.12",
"@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0",
"@exercism/eslint-config-tooling": "^0.1.0",
"@tsconfig/recommended": "^1.0.1",
"@types/eslint": "^7.2.10",
"@types/jest": "^26.0.22",
"@types/node": "^14.14.37",
"@types/node": "^14.14.41",
"@types/yargs": "^16.0.1",
"@typescript-eslint/eslint-plugin": "^4.21.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"babel-jest": "^26.6.3",
"babel-plugin-module-resolver": "^4.1.0",
"core-js": "^3.10.1",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.1.0",
"eslint-config-prettier": "^8.2.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.3.4",
"eslint-plugin-jest": "^24.3.5",
"jest": "^26.6.3",
"rimraf": "^3.0.2",
"shelljs": "^0.8.4"
},
"dependencies": {
"@exercism/static-analysis": "^0.8.1",
"@typescript-eslint/parser": "^4.21.0",
"@typescript-eslint/typescript-estree": "^4.21.0",
"@typescript-eslint/visitor-keys": "^4.21.0",
"@exercism/eslint-config-javascript": "^0.3.1",
"@exercism/static-analysis": "^0.9.0",
"@typescript-eslint/parser": "^4.22.0",
"@typescript-eslint/typescript-estree": "^4.22.0",
"@typescript-eslint/visitor-keys": "^4.22.0",
"eslint": "^7.24.0",
"typescript": "^4.2.4",
"yargs": "^16.2.0"
}
Expand Down
3 changes: 2 additions & 1 deletion src/analyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ logger.log(
// so it can be instantiated here. This allows us to add new analyzers without
// needing to update a bookkeeping construct
//
// eslint-disable-next-line @typescript-eslint/naming-convention
const AnalyzerClass = find(exercise)
const analyzer = new AnalyzerClass()

Expand All @@ -39,4 +40,4 @@ const analyzer = new AnalyzerClass()
//
run(analyzer, input, options)
.then(() => process.exit(0))
.catch((err) => logger.fatal(err.toString()))
.catch((err: unknown) => logger.fatal(`${err}`))
10 changes: 7 additions & 3 deletions src/analyzers/AnalyzerImpl.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Input } from '@exercism/static-analysis'
import { getProcessLogger, Logger } from '@exercism/static-analysis'
import type { Input, Logger } from '@exercism/static-analysis'
import { getProcessLogger } from '@exercism/static-analysis'
import type { Analyzer, Comment, Output } from '~src/interface'
import { AnalyzerOutput } from '~src/output/AnalyzerOutput'

Expand Down Expand Up @@ -47,7 +47,11 @@ export abstract class AnalyzerImpl implements Analyzer {

await this.execute(input).catch((err): void | never => {
if (err instanceof EarlyFinalization) {
this.logger.log(`=> early finialization (${this.output.status})`)
this.logger.log(
`=> early finalization (${
this.output.summary ?? this.output.comments.length
})`
)
} else {
throw err
}
Expand Down
36 changes: 24 additions & 12 deletions src/analyzers/Autoload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ type AnalyzerConstructor = new () => Analyzer
export function find(exercise: Readonly<Exercise>): AnalyzerConstructor {
const file = autoload(exercise)
const key = Object.keys(file).find(
(key): boolean => file[key] instanceof Function
(fileKey): boolean => file[fileKey] instanceof Function
)

if (key === undefined) {
throw new Error(`No Analyzer found in './${exercise.slug}`)
}

const analyzer = file[key]
const analyzer = file[key] as AnalyzerConstructor
getProcessLogger().log(`=> analyzer: ${analyzer.name}`)
return analyzer
}
Expand All @@ -35,22 +35,28 @@ class RequireError extends Error {
}
}

function autoload(exercise: Readonly<Exercise>): ReturnType<NodeRequire> {
function autoload(exercise: Readonly<Exercise>): Record<string, unknown> {
// explicit path (no extension)
const modulePaths = [
path.join(__dirname, 'practice', exercise.slug, 'index'),
path.join(__dirname, 'concept', exercise.slug, 'index'),
path.join(__dirname, 'generic', 'index'),
]

const results = modulePaths.map((modulePath) => {
try {
return require(modulePath)
} catch (err) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require(modulePath) as unknown
} catch (err: unknown) {
return new RequireError(modulePath, err)
}
})

if (results.every((result) => result instanceof RequireError)) {
if (
results.every(
(result): result is RequireError => result instanceof RequireError
)
) {
const slug = exercise.slug
const logger = getProcessLogger()

Expand All @@ -72,11 +78,14 @@ function autoload(exercise: Readonly<Exercise>): ReturnType<NodeRequire> {
JSON.stringify(
results.map((error) => ({
name: error.name,
cause: {
name: error.inner.name,
message: error.inner.message,
stack: error.inner.stack,
},
cause:
error.inner instanceof Error
? {
name: error.inner.name,
message: error.inner.message,
stack: error.inner.stack,
}
: error.inner,
})),
undefined,
2
Expand All @@ -85,5 +94,8 @@ function autoload(exercise: Readonly<Exercise>): ReturnType<NodeRequire> {
)
}

return results.find((result) => !(result instanceof RequireError))
return results.find((result) => !(result instanceof RequireError)) as Record<
string,
unknown
>
}
5 changes: 2 additions & 3 deletions src/analyzers/IsolatedAnalyzerImpl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Input } from '@exercism/static-analysis'
import type { Input, Logger } from '@exercism/static-analysis'
import {
getProcessLogger,
Logger,
NoSourceError,
ParserError,
} from '@exercism/static-analysis'
Expand Down Expand Up @@ -61,7 +60,7 @@ export abstract class IsolatedAnalyzerImpl implements Analyzer {
// The isolated analyzer output can use exceptions as control flow.
// This block here explicitly accepts this.
if (err instanceof EarlyFinalization) {
this.logger.log(`=> early finalization (${output.summary || '-'})`)
this.logger.log(`=> early finalization (${output.summary ?? '-'})`)
} else {
throw err
}
Expand Down
15 changes: 5 additions & 10 deletions src/analyzers/SourceImpl.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { extractSource } from '@exercism/static-analysis'
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/typescript-estree'
import type { TSESTree } from '@typescript-eslint/typescript-estree'
import { AST_NODE_TYPES } from '@typescript-eslint/typescript-estree'

type NodeWithLocation = TSESTree.Node & {
range?: TSESTree.Range
loc?: TSESTree.SourceLocation
}

interface Source {
get(node: NodeWithLocation): string
get: (node: NodeWithLocation) => string
}

class SourceImpl implements Source {
Expand All @@ -27,16 +28,10 @@ class SourceImpl implements Source {
return this.get(node).replace(this.get(node.body), '...')
}
case AST_NODE_TYPES.FunctionDeclaration: {
return this.get(node).replace(
(node.body && this.get(node.body)) || '...',
'...'
)
return this.get(node).replace(this.get(node.body) || '...', '...')
}
case AST_NODE_TYPES.FunctionExpression: {
return this.get(node).replace(
(node.body && this.get(node.body)) || '...',
'...'
)
return this.get(node).replace(this.get(node.body) || '...', '...')
}
case AST_NODE_TYPES.VariableDeclaration: {
const first = node.declarations[0].init
Expand Down
9 changes: 6 additions & 3 deletions src/analyzers/concept/__exemplar/ExemplarSolution.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { MetaConfiguration } from '@exercism/static-analysis'
import {
AstParser,
extractExports,
extractFunctions,
} from '@exercism/static-analysis'
import { TSESTree } from '@typescript-eslint/typescript-estree'
import type { TSESTree } from '@typescript-eslint/typescript-estree'
import { readFileSync } from 'fs'
import path from 'path'
import { Source } from '../../SourceImpl'
Expand All @@ -22,9 +23,11 @@ export class ExemplarSolution {

public readExemplar(directory: string): void {
const configPath = path.join(directory, '.meta', 'config.json')
const config = JSON.parse(readFileSync(configPath).toString())
const config = JSON.parse(
readFileSync(configPath).toString()
) as MetaConfiguration

const exemplarPath = path.join(directory, config.files.exemplar[0])
const exemplarPath = path.join(directory, (config.files.exemplar ?? [])[0])
this.exemplar = new Source(readFileSync(exemplarPath).toString())
}

Expand Down
8 changes: 4 additions & 4 deletions src/analyzers/concept/__exemplar/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { Input } from '@exercism/static-analysis'
import {
AstParser,
Input,
NoExportError,
NoMethodError,
} from '@exercism/static-analysis'
import { TSESTree } from '@typescript-eslint/typescript-estree'
import { ExecutionOptions, WritableOutput } from '~src/interface'
import type { TSESTree } from '@typescript-eslint/typescript-estree'
import type { ExecutionOptions, WritableOutput } from '~src/interface'
import {
EXEMPLAR_SOLUTION,
NO_METHOD,
Expand Down Expand Up @@ -44,7 +44,7 @@ export class ExemplarAnalyzer extends IsolatedAnalyzerImpl {
): ExemplarSolution | never {
try {
return new ExemplarSolution(program, source)
} catch (error) {
} catch (error: unknown) {
if (error instanceof NoMethodError) {
output.add(NO_METHOD({ 'method.name': error.method }))
output.finish()
Expand Down
Loading