feat(jsii): Re-implemented jsii to support --watch and produce better error reporting#188
feat(jsii): Re-implemented jsii to support --watch and produce better error reporting#188RomainMuller merged 6 commits intomasterfrom
Conversation
Re-wrote JSII using the TypeScript compiler services API, integrating with the `ts.ProgramBuilder` classes so that incremental build is possible. Additonally, error messages leverage `TypeScript`'s built-in diagnostic formatter, and enable the rendering of code excerpts with error messages.
ff6f7ff to
7dca3ba
Compare
|
--watch support
eladb
left a comment
There was a problem hiding this comment.
Generally this looks great and I wouldn't want it to get stale, so let's merge away! I haven't performed a full deep dive, but since I can see that you haven't needed to change any of the (positive) tests, and I know we have pretty good coverage, I think we can merge this change, and I will provide feedback/propose changes as we work with this new code base.
Great job!
packages/jsii/lib/compiler.ts
Outdated
| export interface CompilerOptions { | ||
| /** The information about the project to be built */ | ||
| projectInfo: ProjectInfo; | ||
| /** Whether the compiler should watch for changes or juts compile once */ |
| // JavaDoc is very particular about it being @return. | ||
| case 'returns': return 'return'; | ||
| default: return tagName; | ||
| public async emit(...files: string[]): Promise<EmitResult | never> { |
There was a problem hiding this comment.
What's the use case for files?
There was a problem hiding this comment.
It's used when running the tests against the negatives (the standard behaviour otherwise globs all the negatives, which isn't what we want). There was an other way around (basically, copy each file to a temporary work-dir with a package.json file to run the tests against), but it was a lot more work.
Adding a comment on @param to reflect that.
| if (jsii.types) { | ||
| for (const fqn of Object.keys(jsii.types)) { | ||
| lookup.set(fqn, jsii.types[fqn]); | ||
| const SOURCE_DIRS = new Set(['bin', 'lib', 'test']); |
There was a problem hiding this comment.
this should probably be configurable (at some point). issue?
| */ | ||
| public async emit(): Promise<EmitResult> { | ||
| this._diagnostics = []; | ||
| if (!this.projectInfo.description) { |
There was a problem hiding this comment.
Wouldn't it make more sense to perform these validations in ProjectInfo?
There was a problem hiding this comment.
I decided to have ProjectInfo remain rather un-opinionated, especially w/r/t optionals. Could make it also emit diagnostics, I suppose. I'm going to hold off on this one for now though.
| const mainFile = path.resolve(this.projectInfo.projectRoot, this.projectInfo.types.replace(/\.d\.ts(x?)$/, '.ts$1')); | ||
| for (const sourceFile of this.program.getSourceFiles().filter(f => !f.isDeclarationFile)) { | ||
| if (sourceFile.fileName !== mainFile) { continue; } | ||
| if (LOG.isTraceEnabled()) { |
There was a problem hiding this comment.
Why do you need to check if trace is enabled? Isn't that just a log level?
There was a problem hiding this comment.
It's a log level. The check is to avoid taking on possibly expensive side-effects (this string interpolates with color & all).
There was a problem hiding this comment.
Indeed sounds very expensive. Sometimes those loggers allow you to pass in a function which is lazy evaluated only if the level is enabled...
There was a problem hiding this comment.
Yeah but that actually makes the syntax of logging heavier. That's a bit annoying.
There was a problem hiding this comment.
wrapping every trace call with an "if" statement is also heavy...
| }; | ||
|
|
||
| const validator = new Validator(this.projectInfo, assembly); | ||
| const validationResult = await validator.emit(); |
There was a problem hiding this comment.
What's the value in making the validator an emitter? What does it emit really? What's the polymorphic benefit?
There was a problem hiding this comment.
The validator actually further annotates the type specification (for example, it tags theoverridden members).
There was a problem hiding this comment.
Yah I think ultimately I want to merge those into the Assembler class entirely. Separating made it easier for me to wrap my head around the two classes of concerns I had to deal with here:
- Understanding the type model that I obtain from the
tscAPI - Make sure we generate valid type definitions for JSII standards
But we can "fail faster" if we merge...
|
|
||
| const validator = new Validator(this.projectInfo, assembly); | ||
| const validationResult = await validator.emit(); | ||
| if (!validationResult.emitSkipped) { |
There was a problem hiding this comment.
I usually prefer the special-case not to be the success path
There was a problem hiding this comment.
Yeah, but it actually forces me to write more code (that is possibly less readable).
packages/jsii/lib/assembler.ts
Outdated
| } | ||
|
|
||
| // Clearing ``this._types`` to allow contents to be garbage-collected. | ||
| delete this._types; |
There was a problem hiding this comment.
Maybe put in finally to avoid "remembering" to do this
There was a problem hiding this comment.
Yeah - Actually why wasn't it there already? o_O
| for (const prop of moduleType.getProperties()) { | ||
| allTypes.push(...await this._visitNode(prop.valueDeclaration, namePrefix.concat(node.name.getText()))); | ||
| } | ||
| if (LOG.isTraceEnabled()) { |
There was a problem hiding this comment.
Why do you need to check if trace is enabled?
There was a problem hiding this comment.
Again, its a way to make compilation faster by not building large interpolated strings if we're not going to use them.
[0.7.2](v0.7.1...v0.7.2) (2018-09-06) Bug Fixes * Missing types in JSII assembly, invalid Java code, confusing docs ([#208](#208)) ([b37101f](b37101f)), closes [#175](#175) Features * **jsii:** Re-implemented jsii to support --watch and produce better error reporting ([#188](#188)) ([76472be](76472be))
[0.7.2](v0.7.1...v0.7.2) (2018-09-06) Bug Fixes * Missing types in JSII assembly, invalid Java code, confusing docs ([#208](#208)) ([b37101f](b37101f)), closes [#175](#175) Features * **jsii:** Re-implemented jsii to support --watch and produce better error reporting ([#188](#188)) ([76472be](76472be))
* v0.7.2 [0.7.2](v0.7.1...v0.7.2) (2018-09-06) Bug Fixes * Missing types in JSII assembly, invalid Java code, confusing docs ([#208](#208)) ([b37101f](b37101f)), closes [#175](#175) Features * **jsii:** Re-implemented jsii to support --watch and produce better error reporting ([#188](#188)) ([76472be](76472be)) * Check in changes to tarball expectations.
Re-wrote JSII using the TypeScript compiler services API, integrating with the
ts.ProgramBuilderclasses so that incremental build is possible. Additonally, error messages leverageTypeScript's built-in diagnostic formatter, and enable the rendering of code excerpts with error messages.The error message generation can be further improved by maintaining a map of entity (type, method, property, parameter, ...) to node, which could then be used by the
Validatorclass to emit error messages that are tied to the source code. This was omitted for now as the change is already substantial enough.I have successfully built the AWS CDK using this enhanced compiler. Also, it produces almost no changes in the regression tests' output (only change is that it is now able to carry
enummember documentation).