Skip to content

[labs/analyzer] More analysis coverage for gen-manifest#3529

Merged
kevinpschaaf merged 26 commits intomainfrom
cem-moar
Jan 20, 2023
Merged

[labs/analyzer] More analysis coverage for gen-manifest#3529
kevinpschaaf merged 26 commits intomainfrom
cem-moar

Conversation

@kevinpschaaf
Copy link
Copy Markdown
Member

@kevinpschaaf kevinpschaaf commented Dec 16, 2022

This PR includes the following:

  • CLI improvements:
    • Print generate command diagnostic errors more gracefully to CLI - 8d7b0e9
    • Add support for --exclude options (e.g. exclude tests): 7fac64c) - important for excluding test files from e.g. manifest or wrapper generation
  • Add more analysis support and CEM emit:
    • TS enum type variables: bb8ba31
    • description, summary, and deprecated for all models: de76b2e
    • module-level description & summary: 32145ba
    • ClassField and ClassMethod: 3fd322c
  • Also introduces ...IfNotEmpty() helpers in gen-manifest to trim down the JSON size by only including optional fields when they have with meaningful values (e.g. non-empty string, non-empty array, true boolean, etc.)

This PR successfully builds the following manifest for MWC: custom-elements.json, after making the following minor MWC source changes:

  • menu/lib/menu.ts
    • export ActionDetail interface
    • remove unused? {SelectedDetail} type from @fires annotation

Addresses much of remaining items in #2993, with exception of:

  • FunctionDeclaration
  • MixinDeclaration / CustomElementMixinDeclaration
  • Attributes (of LitElementDeclaration)
  • ClassFields from accessors

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Dec 16, 2022

🦋 Changeset detected

Latest commit: 416e1f4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@lit-labs/analyzer Minor
@lit-labs/cli Minor
@lit-labs/gen-manifest Minor
@lit-labs/gen-utils Patch
@lit-labs/gen-wrapper-angular Patch
@lit-labs/gen-wrapper-react Patch
@lit-labs/gen-wrapper-vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Dec 16, 2022

📊 Tachometer Benchmark Results

Summary

⏳ Benchmarks are currently running. Results below are out of date.

nop-update

  • lit-html-kitchen-sink: unsure 🔍 -5% - +2% (-1.07ms - +0.54ms)
    this-change vs tip-of-tree

render

  • lit-element-list: 82.89ms - 88.09ms
  • lit-html-kitchen-sink: unsure 🔍 -6% - +3% (-2.05ms - +0.88ms)
    this-change vs tip-of-tree
  • lit-html-repeat: unsure 🔍 -13% - +10% (-1.70ms - +1.22ms)
    this-change vs tip-of-tree
  • lit-html-template-heavy: unsure 🔍 -2% - +2% (-0.87ms - +1.00ms)
    this-change vs tip-of-tree
  • reactive-element-list: unsure 🔍 -4% - +2% (-2.31ms - +1.04ms)
    this-change vs tip-of-tree

update

  • lit-element-list: 754.16ms - 769.85ms
  • lit-html-kitchen-sink: unsure 🔍 -3% - +3% (-2.37ms - +2.64ms)
    this-change vs tip-of-tree
  • lit-html-repeat: unsure 🔍 -3% - +1% (-10.41ms - +3.04ms)
    this-change vs tip-of-tree
  • lit-html-template-heavy: unsure 🔍 -1% - +1% (-1.00ms - +0.85ms)
    this-change vs tip-of-tree
  • reactive-element-list: unsure 🔍 -0% - +2% (-0.01ms - +11.55ms)
    this-change vs tip-of-tree

update-reflect

  • lit-element-list: 757.50ms - 767.72ms
  • reactive-element-list: unsure 🔍 -0% - +1% (-0.97ms - +9.38ms)
    this-change vs tip-of-tree

Results

⏳ Benchmarks are currently running. Results below are out of date.
lit-element-list

render

VersionAvg timevs
82.89ms - 88.09ms-

update

VersionAvg timevs
754.16ms - 769.85ms-

update-reflect

VersionAvg timevs
757.50ms - 767.72ms-
lit-html-kitchen-sink

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
30.50ms - 32.47ms-unsure 🔍
-6% - +3%
-2.05ms - +0.88ms
unsure 🔍
-6% - +2%
-1.90ms - +0.76ms
tip-of-tree
tip-of-tree
30.99ms - 33.15msunsure 🔍
-3% - +7%
-0.88ms - +2.05ms
-unsure 🔍
-4% - +4%
-1.39ms - +1.42ms
previous-release
previous-release
31.16ms - 32.95msunsure 🔍
-2% - +6%
-0.76ms - +1.90ms
unsure 🔍
-4% - +4%
-1.42ms - +1.39ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
81.20ms - 84.70ms-unsure 🔍
-3% - +3%
-2.37ms - +2.64ms
unsure 🔍
-2% - +5%
-1.52ms - +3.83ms
tip-of-tree
tip-of-tree
81.02ms - 84.61msunsure 🔍
-3% - +3%
-2.64ms - +2.37ms
-unsure 🔍
-2% - +5%
-1.68ms - +3.72ms
previous-release
previous-release
79.77ms - 83.82msunsure 🔍
-5% - +2%
-3.83ms - +1.52ms
unsure 🔍
-4% - +2%
-3.72ms - +1.68ms
-

nop-update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
21.90ms - 22.95ms-unsure 🔍
-5% - +2%
-1.07ms - +0.54ms
unsure 🔍
-4% - +3%
-0.86ms - +0.59ms
tip-of-tree
tip-of-tree
22.08ms - 23.30msunsure 🔍
-2% - +5%
-0.54ms - +1.07ms
-unsure 🔍
-3% - +4%
-0.66ms - +0.92ms
previous-release
previous-release
22.06ms - 23.06msunsure 🔍
-3% - +4%
-0.59ms - +0.86ms
unsure 🔍
-4% - +3%
-0.92ms - +0.66ms
-
lit-html-repeat

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
11.94ms - 12.57ms-unsure 🔍
-13% - +10%
-1.70ms - +1.22ms
unsure 🔍
-2% - +6%
-0.29ms - +0.74ms
tip-of-tree
tip-of-tree
11.07ms - 13.92msunsure 🔍
-10% - +14%
-1.22ms - +1.70ms
-unsure 🔍
-8% - +16%
-1.02ms - +1.94ms
previous-release
previous-release
11.62ms - 12.44msunsure 🔍
-6% - +2%
-0.74ms - +0.29ms
unsure 🔍
-15% - +8%
-1.94ms - +1.02ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
293.26ms - 299.38ms-unsure 🔍
-3% - +1%
-10.41ms - +3.04ms
unsure 🔍
-3% - +1%
-10.29ms - +1.92ms
tip-of-tree
tip-of-tree
294.02ms - 305.99msunsure 🔍
-1% - +4%
-3.04ms - +10.41ms
-unsure 🔍
-3% - +2%
-8.49ms - +7.48ms
previous-release
previous-release
295.22ms - 305.79msunsure 🔍
-1% - +3%
-1.92ms - +10.29ms
unsure 🔍
-2% - +3%
-7.48ms - +8.49ms
-
lit-html-template-heavy

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
52.72ms - 53.90ms-unsure 🔍
-2% - +2%
-0.87ms - +1.00ms
unsure 🔍
-2% - +1%
-0.99ms - +0.75ms
tip-of-tree
tip-of-tree
52.52ms - 53.97msunsure 🔍
-2% - +2%
-1.00ms - +0.87ms
-unsure 🔍
-2% - +1%
-1.14ms - +0.78ms
previous-release
previous-release
52.80ms - 54.06msunsure 🔍
-1% - +2%
-0.75ms - +0.99ms
unsure 🔍
-1% - +2%
-0.78ms - +1.14ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
106.79ms - 108.11ms-unsure 🔍
-1% - +1%
-1.00ms - +0.85ms
unsure 🔍
-0% - +1%
-0.43ms - +1.18ms
tip-of-tree
tip-of-tree
106.88ms - 108.17msunsure 🔍
-1% - +1%
-0.85ms - +1.00ms
-unsure 🔍
-0% - +1%
-0.34ms - +1.24ms
previous-release
previous-release
106.62ms - 107.53msunsure 🔍
-1% - +0%
-1.18ms - +0.43ms
unsure 🔍
-1% - +0%
-1.24ms - +0.34ms
-
reactive-element-list

render

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
53.04ms - 55.15ms-unsure 🔍
-4% - +2%
-2.31ms - +1.04ms
unsure 🔍
-4% - +2%
-1.99ms - +0.99ms
tip-of-tree
tip-of-tree
53.43ms - 56.04msunsure 🔍
-2% - +4%
-1.04ms - +2.31ms
-unsure 🔍
-3% - +3%
-1.54ms - +1.81ms
previous-release
previous-release
53.54ms - 55.65msunsure 🔍
-2% - +4%
-0.99ms - +1.99ms
unsure 🔍
-3% - +3%
-1.81ms - +1.54ms
-

update

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
759.00ms - 767.98ms-unsure 🔍
-0% - +2%
-0.01ms - +11.55ms
unsure 🔍
-0% - +1%
-2.20ms - +9.52ms
tip-of-tree
tip-of-tree
754.08ms - 761.36msunsure 🔍
-2% - -0%
-11.55ms - +0.01ms
-unsure 🔍
-1% - +0%
-7.35ms - +3.13ms
previous-release
previous-release
756.06ms - 763.60msunsure 🔍
-1% - +0%
-9.52ms - +2.20ms
unsure 🔍
-0% - +1%
-3.13ms - +7.35ms
-

update-reflect

VersionAvg timevs this-change
vs tip-of-tree
tip-of-tree
vs previous-release
previous-release
this-change
779.13ms - 787.56ms-unsure 🔍
-0% - +1%
-0.97ms - +9.38ms
unsure 🔍
-0% - +1%
-3.73ms - +7.12ms
tip-of-tree
tip-of-tree
776.13ms - 782.15msunsure 🔍
-1% - +0%
-9.38ms - +0.97ms
-unsure 🔍
-1% - +0%
-7.06ms - +2.04ms
previous-release
previous-release
778.24ms - 785.06msunsure 🔍
-1% - +0%
-7.12ms - +3.73ms
unsure 🔍
-0% - +1%
-2.04ms - +7.06ms
-

tachometer-reporter-action v2 for Benchmarks

@kevinpschaaf kevinpschaaf requested a review from sorvell December 16, 2022 02:31
@sorvell
Copy link
Copy Markdown
Member

sorvell commented Jan 6, 2023

I noticed that some of the privacy labels in custom-elements.json are incorrect. For example firstUpdated is noted as public. Is that because the source is not properly including override or does privacy need to be brought forward from the base class?

}
const {name, type, description, summary} = nameDescSummary.groups!;
const info: NamedTypedJSDocInfo = {name, type};
if (summary !== undefined) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can type be undefined as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if the regex does't find a type string in the {...}, it just goes in as undefined

* * @slot name: summary
* * @slot name - summary
* * @slot name description
* * @slot name: description
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a : separator supported here but not in parseNamedTypedJSDocInfo?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After consulting with Justin, we removed supporting : as a separator altogether. My best guess is I misinterpreted a regex originally implemented by Justin as intending to include : and started supporting that everywhere.

@kevinpschaaf kevinpschaaf requested a review from sorvell January 13, 2023 23:52
Copy link
Copy Markdown
Collaborator

@justinfagnani justinfagnani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm about halfway through

import {Analyzer} from './analyzer.js';

export interface AnalyzerOptions {
exclude?: string[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment. Are these globs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Yeah, globs. Useful for excluding things source like test folders that might otherwise be included in a project's tsconfig.

It might be interesting (later on, not now) to analyze a package.json's files or exports and filter down the input source to analyze based on that.

However ^^ highlights an unsolved problem in the analyzer right now (will likely need to be resolved via config) which is that just because we can determine that src/foo.ts gets output by tsc to build/foo.js doesn't mean that's where the file is ultimately importable from, since additional transforms outside of ts can (and often do) further process that into another location that is ultimately published (i.e. we have tsc output to ./development/*.js and process that further with rollup to ./*.js).

This is something we're going to hit pretty quickly when we start testing against real-world packages. It's been on my radar, but went ahead and added #3578 to track it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a few things we can do here that won't cover every case, but will cover a lot of them... I'll comment on the issue.

The biggest thing that clicked for me here is that these augment the tsconfig exclude setting.

moduleResolution: 'node',
},
include: ['**/*.js'],
exclude: options.exclude ? [...options.exclude] : [],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need to make a copy? Does parseJsonConfigFileContent modify the array?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to options.exclude ?? []. Was probably just being paranoid I guess?

* - If the first statement has more than one JSDoc block, we collect all
* but the last and use those, regardless of whether they contain one
* of the above module-designating tags (the last one is assumed to belong
* to the first statement).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice comment

}

export interface ReactiveProperty {
export interface PropertyLike extends NodeJSDocInfo {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this heritage seems a little weird to me at first read. Is JSDocInfo the right name? It seems to imply something specifically analyzed from JSDoc, but properties won't always have JSDoc comments and the main thing is that they're described - we just happened to get that info from JSDoc. So maybe name them Described, Named, etc?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, I mean those things can't be inferred from source other than from JSDoc comments, which is how I got to that naming, but I renamed them to be more generic as suggested.

this.inheritedFrom = init.inheritedFrom;
this.source = init.source;
this.type = init.type;
this.default = init.default;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏌️‍♂️ I once went looking for a more concise pattern for this, and this is the best I could come up with:

({
  static: this.static,
  privacy: this.privacy,
  inheritedFrom: this.inheritedFrom,
  source: this.source,
  type: this.type,
  default: this.default,
} = init);

It removes the repeated init., but I'm not sure it's shorter enough to matter, and some people might find it weird.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clever

Comment on lines +28 to +31
v !== undefined &&
(typeof v !== 'boolean' || v === true) &&
(typeof v !== 'string' || v.length > 0) &&
(!Array.isArray(v) || v.length);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic was a little hard for me to parse. What about writing the positive cases?

Suggested change
v !== undefined &&
(typeof v !== 'boolean' || v === true) &&
(typeof v !== 'string' || v.length > 0) &&
(!Array.isArray(v) || v.length);
v === true || ((typeof v === 'string' || Array.isArray(v)) && v.length > 0);

and add a comment on why true is considered not empty? I don't see that case in the expressions you replaced with the function call.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +117 to +121
return {
...pickIfNotEmpty(info, 'description'),
...pickIfNotEmpty(info, 'summary'),
...pickIfNotEmpty(info, 'deprecated'),
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined properties aren't written out with JSON.stringify(), so might it be nicer to not use spread to write a single value, and do something like:

Suggested change
return {
...pickIfNotEmpty(info, 'description'),
...pickIfNotEmpty(info, 'summary'),
...pickIfNotEmpty(info, 'deprecated'),
};
return {
description: removeEmpty(info.description),
summary: removeEmpty(info.summary),
deprecated: removeEmpty(info.deprecated),
};

Or, are the all the properties in NodeJSDocInfo always going to be used? Could you transform them all?

Suggested change
return {
...pickIfNotEmpty(info, 'description'),
...pickIfNotEmpty(info, 'summary'),
...pickIfNotEmpty(info, 'deprecated'),
};
return removeEmpties(info);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undefined properties aren't written out with JSON.stringify()

OMG! 🤦 Yeah I think I knew this but totally forgot about it... definitely better to leave the key and avoid the spread. Will refactor.

Copy link
Copy Markdown
Member Author

@kevinpschaaf kevinpschaaf Jan 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored into

  • ifNotEmpty(v)
  • transformIfNotEmpty(v, transform)
    which always return a meaningful value or undefined, and are assigned directly to keys (not spread)

// // TODO: ClassField
// // TODO: ClassMethod
// ],
...useIfNotEmpty('members', [
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar comment about undefined being ok here, so we don't need spread.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

return {};
}
const jsDocs = firstChild.getChildren().filter(ts.isJSDoc);
const moduleJSDocs = jsDocs.slice(0, jsDocs.length > 1 ? -1 : 1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth some comments in here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +36 to +39
v === undefined ||
(typeof v === 'boolean' && v === false) ||
(typeof v === 'string' && v.length === 0) ||
(Array.isArray(v) && v.length === 0)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can still be simplified, ie with === the typeof v === 'boolean' isn't necessary, and the undefined check isn't needed:

Suggested change
v === undefined ||
(typeof v === 'boolean' && v === false) ||
(typeof v === 'string' && v.length === 0) ||
(Array.isArray(v) && v.length === 0)
v === false || ((typeof v === 'string' || Array.isArray(v)) && v.length === 0)

@kevinpschaaf kevinpschaaf merged commit 389d0c5 into main Jan 20, 2023
@kevinpschaaf kevinpschaaf deleted the cem-moar branch January 20, 2023 00:51
@lit-robot lit-robot mentioned this pull request Mar 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants