diff --git a/package-lock.json b/package-lock.json index d6c1563f..494915d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "web-component-analyzer", - "version": "2.0.0-next.5", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "web-component-analyzer", - "version": "2.0.0-next.5", + "version": "2.0.0", "license": "MIT", "dependencies": { "fast-glob": "^3.2.2", @@ -30,7 +30,7 @@ "cross-env": "^7.0.2", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", - "husky": "^8.0.3", + "husky": "^8.0.0", "lint-staged": "^14.0.1", "prettier": "^3.0.3", "rimraf": "^3.0.2", diff --git a/src/analyze/flavors/lit-element/discover-members.ts b/src/analyze/flavors/lit-element/discover-members.ts index bb9ad642..ccd79670 100644 --- a/src/analyze/flavors/lit-element/discover-members.ts +++ b/src/analyze/flavors/lit-element/discover-members.ts @@ -1,4 +1,12 @@ -import { GetAccessorDeclaration, Node, PropertyDeclaration, PropertySignature, ReturnStatement, SetAccessorDeclaration } from "typescript"; +import { + Expression, + GetAccessorDeclaration, + Node, + PropertyDeclaration, + PropertySignature, + ReturnStatement, + SetAccessorDeclaration +} from "typescript"; import { ComponentMember } from "../../types/features/component-member"; import { LitElementPropertyConfig } from "../../types/features/lit-element-property-config"; import { getMemberVisibilityFromNode, getModifiersFromNode, getNodeSourceFileLang, hasModifier } from "../../util/ast-util"; @@ -23,13 +31,21 @@ export function discoverMembers(node: Node, context: AnalyzerDeclarationVisitCon return undefined; } + // static properties = { myProp: {type: String} } + if (ts.isPropertyDeclaration(node) && hasModifier(node, ts.SyntaxKind.StaticKeyword, ts)) { + const name = node.name.getText(); + if (name === "properties" && node.initializer != null) { + return parseStaticProperties(node.initializer, context); + } + } + // static get properties() { return { myProp: {type: String} } } if (ts.isGetAccessor(node) && hasModifier(node, ts.SyntaxKind.StaticKeyword, ts)) { const name = node.name.getText(); if (name === "properties" && node.body != null) { const returnStatement = node.body.statements.find(ts.isReturnStatement.bind(ts)); - if (returnStatement != null) { - return parseStaticProperties(returnStatement, context); + if (returnStatement != null && returnStatement.expression != null) { + return parseStaticProperties(returnStatement.expression, context); } } } @@ -155,17 +171,17 @@ function getLitAttributeName(propName: string, litConfig: LitElementPropertyConf /** * Visits static properties * static get properties() { return { myProp: {type: String, attribute: "my-attr"} } } - * @param returnStatement + * @param expression * @param context */ -function parseStaticProperties(returnStatement: ReturnStatement, context: AnalyzerDeclarationVisitContext): ComponentMember[] { +function parseStaticProperties(expression: Expression, context: AnalyzerDeclarationVisitContext): ComponentMember[] { const { ts } = context; const memberResults: ComponentMember[] = []; - if (returnStatement.expression != null && ts.isObjectLiteralExpression(returnStatement.expression)) { + if (expression != null && ts.isObjectLiteralExpression(expression)) { // Each property in the object literal expression corresponds to a class field. - for (const propNode of returnStatement.expression.properties) { + for (const propNode of expression.properties) { // Get propName const propName = propNode.name != null && ts.isIdentifier(propNode.name) ? propNode.name.text : undefined; if (propName == null) { diff --git a/test/flavors/lit-element/member-test.ts b/test/flavors/lit-element/member-test.ts index 989c1e01..ef78b466 100644 --- a/test/flavors/lit-element/member-test.ts +++ b/test/flavors/lit-element/member-test.ts @@ -50,6 +50,56 @@ tsTest("LitElement: Discovers properties from 'static get properties'", t => { ); }); +tsTest("LitElement: Discovers properties from 'static properties = {}'", t => { + const { + results: [result], + checker + } = analyzeTextWithCurrentTsModule(` + /** + * @element + */ + class MyElement extends HTMLElement { + static properties = { + /** + * This is a comment + * @default hello 123 + * @type {String} + */ + myProp: {type: String} + } + } + `); + + const { members = [] } = result.componentDefinitions[0]?.declaration || {}; + + assertHasMembers( + members, + [ + { + kind: "property", + propName: "myProp", + attrName: "myProp", + jsDoc: { + description: "This is a comment" + }, + default: "hello 123", + typeHint: "String", + type: () => ({ kind: "STRING" }), + visibility: undefined, + reflect: "to-property", + deprecated: undefined, + required: undefined + }, + { + kind: "property", + propName: "properties" + } + ], + t, + checker + ); +}); + tsTest("LitElement: Discovers properties from '@property'", t => { const { results: [result],