-
Notifications
You must be signed in to change notification settings - Fork 362
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
feat: Eslint JavaScript config (try 2) #3454
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## main #3454 +/- ##
==========================================
- Coverage 96.34% 96.29% -0.05%
==========================================
Files 192 195 +3
Lines 37696 38944 +1248
Branches 3524 3711 +187
==========================================
+ Hits 36320 37503 +1183
- Misses 1376 1441 +65 ☔ View full report in Codecov by Sentry. |
0242628
to
d24e583
Compare
eb5dd99
to
ca9bea9
Compare
package.json
Outdated
"typesVersions": { | ||
"<=3.9": { | ||
"lib/*": [ | ||
"lib/.types-compat/ts3.9/*", | ||
"lib/.types-compat/ts3.9/*/index.d.ts" | ||
] | ||
} | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That means some new code is too complex for ts3.9
I'd prefer to not have that. Can you check the file and see what is incompatible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This came in after a rebase onto new main. I am unsure where it came from. I’ll look and see what’s up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wrong. I was able to diff the two output folders in lib, and determined it was the imports in code-token-map.ts
- I thought I'd use type
imports, but apparently that triggered this, so I remored the type
part, and now jsii
has stopped adding that to the file.
src/code-resolvable.ts
Outdated
// let count = 0; | ||
// while (!CodeToken.isResolved(value) && count++ < 10) { | ||
// value = CodeToken.resolve(value, 0, idt); | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ll clean this up.
In a previous version of stringify it was practical for nested tokens to not get processed, and needed reprocessed. This is no longer the case.
* | ||
* @see https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
*/ | ||
JAVASCRIPT_OLD_CJS = "old-cjs", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OLD
is always a bad name. It just get's outdated. Eslint seems to call this "eslintrc format".
This should also be deprecated like the others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe I saw them calling this the legacy format. I’m good either way. I agree “old” is bad naming.
And that answers my unasked question about deprecation of the others. 😄
* @deprecated ESLINT project is transitioning away from this format, use `JAVASCRIPT_FLAT` instead | ||
* @see https://eslint.org/docs/latest/use/configure/configuration-files | ||
*/ | ||
JSON = "json", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefix with ESLINTRC_JSON
* @deprecated ESLINT project is transitioning away from this format, use `JAVASCRIPT_FLAT` instead | ||
* @see https://eslint.org/docs/latest/use/configure/configuration-files | ||
*/ | ||
YAML = "yaml", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefix with ESLINTRC_YAML
* | ||
* @see https://eslint.org/docs/latest/use/configure/configuration-files-new | ||
*/ | ||
JAVASCRIPT_FLAT_ESM = "flat-esm", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if the format type is more important:
Enums should have the same string value as the enum:
JAVASCRIPT_FLAT_ESM = "flat-esm", | |
FLAT_JAVASCRIPT_ESM = "FLAT_JAVASCRIPT_ESM", |
* This is actually **required**, but marked as optional so upstream projects can accept this interface | ||
* and provide this value. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upstream projects should solve this themselves, sorry.
*/ | ||
readonly dirs: string[]; | ||
readonly dirs?: string[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
readonly dirs?: string[]; | |
readonly dirs: string[]; |
@@ -359,7 +432,7 @@ export class Eslint extends Component { | |||
"*.d.ts", | |||
"node_modules/", | |||
"*.generated.ts", | |||
"coverage", | |||
"./coverage", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not this?
"./coverage", | |
"coverage/", |
format === EslintConfigFileFormat.JAVASCRIPT_FLAT_ESM ? "mjs" : "cjs"; | ||
configFileName = `eslint.config.${ext}`; | ||
|
||
this._javascript = new JavascriptFile(project, configFileName, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we encapsulate this into a new EslintConfigFile Component?
/** | ||
* Update the task with the current list of lint patterns and file extensions | ||
*/ | ||
private updateTask() { | ||
const taskExecCommand = "eslint"; | ||
const argsSet = new Set<string>(); | ||
if (this._fileExtensions.size > 0) { | ||
argsSet.add(`--ext ${[...this._fileExtensions].join(",")}`); | ||
if ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this feels like it's in the wrong place
test/javascript/eslint.test.ts
Outdated
// EslintConfigFileFormat.JSON, | ||
// EslintConfigFileFormat.YAML, | ||
// EslintConfigFileFormat.JAVASCRIPT_OLD_CJS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. That was me using the testing for development, and I forgot to uncomment them. These tests are terribly slow, since they build a complete project to test actual eslint running properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is missing docblocks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, I'll add that.
import { JsonFile, JsonFileOptions } from "./json"; | ||
import { Project } from "./project"; | ||
|
||
export class JavascriptFunction extends CodeResolvableBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add visibility to methods please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/javascript-file.ts
Outdated
private readonly properties: Array<unknown>, | ||
private readonly body: unknown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are these unknown in type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unknown
will give errors if we try to use the value without inspecting the contents first. If we use any
then is disabled typescript checks altogether. In this case, the value is passed to javascriptStringify
which also take an unknown
value, which forced us to validate any time we use it as before using it.
I only use any
as a last resort, and usually will fail a PR I'm reviewing if I see one without a lengthy explanation of why it's necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was terribly unclear, sorry.
I'll re-evaluate all of the unknowns
, but some of them are actually able to be nearly any stringable type. Some of them should only be ICodeResolvable
so I'll make sure I wasn't just being sloppy.
src/javascript-file.ts
Outdated
throw new Error( | ||
`Default import already exists for ${from}: ${this.defaultImports.get( | ||
from | ||
)} !== ${imports}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of hard error, could we warn and re-write the code so that it still works? Not sure if that's feasible though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the user only uses the retuened token and doesn't form their own, it'll work. I'll add a warning to that affect.
src/javascript-file.ts
Outdated
const defaultImport = this.defaultImports.get(from); | ||
const value = imports.map((i) => i.toString()).join(", "); | ||
if (defaultImport && imports.length > 0) { | ||
if (this.cjs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if this class should have two flavours: JavascriptESMDependencies
and JavascriptCJSDependencies
instead of inlining a lot of if statements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll split it to three classes, JavascriptDependenciesBase
(abstract), CJSJavascriptDependencies
and ESMJavascriptDependencies
. All the common logic will be in the base.
options: JavascriptFileOptions | ||
) { | ||
super(project, filePath, { ...options, allowComments: false }); | ||
this.cjs = options.cjs ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, instead of a boolean flag would it be nicer to have two classes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be a lot of duplicated code for very little gain. All the logic remains the same, just eh little bits of syntax output change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is amazing! Looking forward to getting it merged.
Fixes #2405
Closes #2414
Fixes maybe #3240
Fixes maybe #3126
Building on the great work of @andrestone in #2405, which appears to be abandoned (it happens), I've been using techniques I've used in other projects and inspired by Tokens in CDK to make the code work.
The goal of this PR is two-fold:
ESLint File Format
The
yaml
option has been deprecated and replaced withfileFormat
valued as an enum with the options:JSON
,YAML
,JAVASCRIPT_FLAT_ESM
,JAVASCRIPT_OLD_CJS
,JAVASCRIPT_FLAT_ESM
, andJAVASCRIPT_FLAT_CJS
.All of these are tested and verified with new tests. The internals still use the old file format initially, and accept commands like
addOverride()
that are in reference to the old file format (overrides are replaced with the settings simply being a "flat" array, where each value naturally overrides the config before it).Each of the options are converted to a flat-format equivalent. Particularly tricky parts were plugins and parsers, where they are now imported (or required) and used as javascript. The logic used in eslint for the legacy configs was emulated to determined the module names to use, and the imports are added then used. Note that imports are NOT added to the project, but that could be added later.
Also of note what that
ignorePatterns
is noignores
and the pattern no longer assumes**/
, so we use a heuristic: if we don't find/
,**
, or!
(as the first character) in the patterns, we simple prepend**/
.There's more work I'd like to do here, like making auto-fixable options
warn
instead oferror
(so they don't break auto-save with format and auto-fix in VSCode, among others), and switching rules to use the newtypescript-eslint
module and ESLint Stylistic where many deprecated eslint rules have moved to. These will be in other PRs and issues though.JavascriptFile class
The new interface is highlighted by this code from the tests:
Here's the contents of
value
it creates, extracted so it'll get properly syntax highlighted:Escape-hatches and late binding
Also, late-binding along with
addOverride
and patching escape hatches still work. A brief demonstration:At this point the output would simply be:
Now lets patch that to include the
fs
and instead read the value from the file name the is inreadFileName
:Since we used
JavascriptDataStructure.value(() => readFileName)
it will (1) insert the value as data instead of raw code, which in this case is a quoted string, and (2) readreadFileName
at the last possible moment.So, we can still change it:
Now the output will look like:
JavascriptFile features
resolve()
is called for late-binding dataJavascriptDependencies
import tracking feature for example is added in just such a wayJavascriptDependencies
can make CJSrequire
or ESMimport
statementsJavascriptDependencies
addImport
call returns tokens that can be used in the code as strings for the imported values (js
andjsDoc
in the above example)It is notably missing the ability to arbitrarily insert comments.
PS: JavascriptFile could be easily expanded to make TypeScript code as well, BTW.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.