Socket
Socket
Sign inDemoInstall

@lit-labs/analyzer

Package Overview
Dependencies
2
Maintainers
11
Versions
21
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.9.1 to 0.9.2

9

lib/custom-elements/custom-elements.js

@@ -16,3 +16,3 @@ /**

import { addEventsToMap } from './events.js';
import { parseNodeJSDocInfo, parseNamedJSDocInfo } from '../javascript/jsdoc.js';
import { parseNodeJSDocInfo, parseNamedTypedJSDocInfo, } from '../javascript/jsdoc.js';
const _isCustomElementClassDeclaration = (t, analyzer) => {

@@ -76,3 +76,3 @@ const declarations = t.getSymbol()?.getDeclarations();

const addNamedJSDocInfoToMap = (map, tag, analyzer) => {
const info = parseNamedJSDocInfo(tag, analyzer);
const info = parseNamedTypedJSDocInfo(tag, analyzer);
if (info !== undefined) {

@@ -102,8 +102,9 @@ map.set(info.name, info);

case 'cssProp':
addNamedJSDocInfoToMap(cssProperties, tag, analyzer);
break;
case 'cssprop':
case 'cssProperty':
case 'cssproperty':
addNamedJSDocInfoToMap(cssProperties, tag, analyzer);
break;
case 'cssPart':
case 'csspart':
addNamedJSDocInfoToMap(cssParts, tag, analyzer);

@@ -110,0 +111,0 @@ break;

@@ -39,2 +39,17 @@ /**

};
const getIsReadonlyForNode = (node, analyzer) => {
const { typescript } = analyzer;
if (typescript.isPropertyDeclaration(node)) {
return (node.modifiers?.some((mod) => typescript.isReadonlyKeywordOrPlusOrMinusToken(mod)) ||
typescript
.getJSDocTags(node)
.some((tag) => tag.tagName.text === 'readonly'));
}
else if (typescript.isStatement(node)) {
return typescript
.getJSDocTags(node)
.some((tag) => tag.tagName.text === 'readonly');
}
return false;
};
/**

@@ -49,6 +64,34 @@ * Returns the `fields` and `methods` of a class.

const staticMethodMap = new Map();
const accessors = new Map();
declaration.members.forEach((node) => {
// Ignore non-implementation signatures of overloaded methods by checking
// for `node.body`.
if (typescript.isMethodDeclaration(node) && node.body) {
if (typescript.isConstructorDeclaration(node) && node.body) {
// TODO(bennypowers): We probably want to see if this matches what TypeScript considers a field initialization.
// Maybe instead of iterating through the constructor statements, we walk the body looking for any
// assignment expression so that we get ones inside of if statements, in parenthesized expressions, etc.
//
// Also, this doesn't cover destructuring assignment.
//
// This is ok for now because these are rare ways to "declare" a field,
// especially in web components where you shouldn't have constructor parameters.
node.body.statements.forEach((node) => {
if (typescript.isExpressionStatement(node) &&
typescript.isBinaryExpression(node.expression) &&
node.expression.operatorToken.kind ===
typescript.SyntaxKind.EqualsToken &&
typescript.isPropertyAccessExpression(node.expression.left) &&
node.expression.left.expression.kind ===
typescript.SyntaxKind.ThisKeyword) {
const name = node.expression.left.name.getText();
fieldMap.set(name, new ClassField({
name,
type: getTypeForNode(node.expression.right, analyzer),
privacy: getPrivacy(typescript, node),
readonly: getIsReadonlyForNode(node, analyzer),
}));
}
});
}
else if (typescript.isMethodDeclaration(node) && node.body) {
const info = getMemberInfo(typescript, node);

@@ -63,3 +106,4 @@ const name = node.name.getText();

else if (typescript.isPropertyDeclaration(node)) {
if (!typescript.isIdentifier(node.name)) {
if (!typescript.isIdentifier(node.name) &&
!typescript.isPrivateIdentifier(node.name)) {
analyzer.addDiagnostic(createDiagnostic({

@@ -81,5 +125,28 @@ typescript,

...parseNodeJSDocInfo(node, analyzer),
readonly: getIsReadonlyForNode(node, analyzer),
}));
}
else if (typescript.isAccessor(node)) {
const name = node.name.getText();
const _accessors = accessors.get(name) ?? {};
if (typescript.isGetAccessor(node))
_accessors.get = node;
else if (typescript.isSetAccessor(node))
_accessors.set = node;
accessors.set(name, _accessors);
}
});
for (const [name, { get, set }] of accessors) {
if (get ?? set) {
fieldMap.set(name, new ClassField({
name,
type: getTypeForNode((get ?? set), analyzer),
privacy: getPrivacy(typescript, (get ?? set)),
readonly: !!get && !set,
// TODO(bennypowers): derive from getter?
// default: ???
// TODO(bennypowers): reflect, etc?
}));
}
}
return {

@@ -86,0 +153,0 @@ fieldMap,

@@ -12,3 +12,3 @@ /**

import type ts from 'typescript';
import { Described, TypedNamedDescribed, DeprecatableDescribed, AnalyzerInterface, NamedDescribed } from '../model.js';
import { Described, TypedNamedDescribed, DeprecatableDescribed, AnalyzerInterface } from '../model.js';
export type TypeScript = typeof ts;

@@ -36,9 +36,6 @@ /**

* * @fires event-name {Type}: description
*/
export declare const parseNamedTypedJSDocInfo: (tag: ts.JSDocTag, analyzer: AnalyzerInterface) => TypedNamedDescribed | undefined;
/**
* Parses name and description from JSDoc tag for things like `@slot`,
* `@cssPart`, and `@cssProp`.
*
* Supports the following patterns following the tag (TS parses the tag for us):
* * @fires {Type} event-name
* * @fires {Type} event-name description
* * @fires {Type} event-name - description
* * @fires {Type} event-name: description
* * @slot name

@@ -52,4 +49,8 @@ * * @slot name description

* * @cssProp [--name=default]: description
* * @cssprop {<color>} [--name=default]
* * @cssprop {<color>} [--name=default] description
* * @cssprop {<color>} [--name=default] - description
* * @cssprop {<color>} [--name=default]: description
*/
export declare const parseNamedJSDocInfo: (tag: ts.JSDocTag, analyzer: AnalyzerInterface, requireDash?: boolean) => NamedDescribed | undefined;
export declare const parseNamedTypedJSDocInfo: (tag: ts.JSDocTag, analyzer: AnalyzerInterface) => TypedNamedDescribed | undefined;
/**

@@ -56,0 +57,0 @@ * Parses the description from JSDoc tag for things like `@return`.

@@ -24,9 +24,5 @@ /**

// Regex for parsing name, type, and description from JSDoc comments
const parseNameTypeDescRE = /^(?<name>\S+)(?:\s+{(?<type>.*)})?(?:\s+-\s+)?(?<description>[\s\S]*)$/m;
// Regex for parsing optional name and description from JSDoc comments, where
// the dash is required before the description (syntax for `@slot` tag, whose
// default slot has no name)
const parseNameDashDescRE = /^\[?(?<name>[^[\]\s=]+)(?:=(?<defaultValue>[^\]]+))?\]?\s+-\s+(?<description>[\s\S]*)$/;
// Regex for parsing optional name, default, and description from JSDoc comments
const parseNameDescRE = /^\[?(?<name>[^[\]\s=]+)(?:=(?<defaultValue>[^\]]+))?\]?(?:\s+-\s+)?(?<description>[\s\S]*)$/;
const parseNameTypeDescRE = /^\[?(?<name>[^{}[\]\s=]+)(?:=(?<defaultValue>[^\]]+))?\]?(?:\s+{(?<type>.*)})?(?:\s+-\s+)?(?<description>[\s\S]*)$/m;
// Regex for parsing type, name, and description from JSDoc comments
const parseTypeNameDescRE = /^\{(?<type>.+)\}\s+\[?(?<name>[^{}[\]\s=]+)(?:=(?<defaultValue>[^\]]+))?\]?(?:\s+-\s+)?(?<description>[\s\S]*)$/m;
const getJSDocTagComment = (tag, analyzer) => {

@@ -65,39 +61,6 @@ let { comment } = tag;

* * @fires event-name {Type}: description
*/
export const parseNamedTypedJSDocInfo = (tag, analyzer) => {
const comment = getJSDocTagComment(tag, analyzer);
if (comment == undefined) {
return undefined;
}
const nameTypeDesc = comment.match(parseNameTypeDescRE);
if (nameTypeDesc === null) {
analyzer.addDiagnostic(createDiagnostic({
typescript: analyzer.typescript,
node: tag,
message: `JSDoc error: unexpected JSDoc format`,
}));
return undefined;
}
const { name, type, description } = nameTypeDesc.groups;
const info = { name, type };
if (description.length > 0) {
info.description = normalizeLineEndings(description);
}
return info;
};
function makeDashParseDiagnostic(typescript, tag, requireDash) {
return createDiagnostic({
typescript,
node: tag,
message: `Unexpected JSDoc format.${requireDash
? ' Tag must contain a whitespace-separated dash between the name and description, ' +
"i.e. '@slot header - This is the description'"
: ''}`,
});
}
/**
* Parses name and description from JSDoc tag for things like `@slot`,
* `@cssPart`, and `@cssProp`.
*
* Supports the following patterns following the tag (TS parses the tag for us):
* * @fires {Type} event-name
* * @fires {Type} event-name description
* * @fires {Type} event-name - description
* * @fires {Type} event-name: description
* * @slot name

@@ -111,4 +74,8 @@ * * @slot name description

* * @cssProp [--name=default]: description
* * @cssprop {<color>} [--name=default]
* * @cssprop {<color>} [--name=default] description
* * @cssprop {<color>} [--name=default] - description
* * @cssprop {<color>} [--name=default]: description
*/
export const parseNamedJSDocInfo = (tag, analyzer, requireDash = false) => {
export const parseNamedTypedJSDocInfo = (tag, analyzer) => {
const comment = getJSDocTagComment(tag, analyzer);

@@ -118,11 +85,15 @@ if (comment == undefined) {

}
const regex = requireDash ? parseNameDashDescRE : parseNameDescRE;
const nameDesc = comment.match(regex);
if (nameDesc === null) {
analyzer.addDiagnostic(makeDashParseDiagnostic(analyzer.typescript, tag, requireDash));
const regex = comment.charAt(0) === '{' ? parseTypeNameDescRE : parseNameTypeDescRE;
const nameTypeDesc = comment.match(regex);
if (nameTypeDesc === null) {
analyzer.addDiagnostic(createDiagnostic({
typescript: analyzer.typescript,
node: tag,
message: `JSDoc error: unexpected JSDoc format`,
}));
return undefined;
}
const { name, description, defaultValue } = nameDesc.groups;
const { name, type, defaultValue, description } = nameTypeDesc.groups;
const info = { name };
if (description?.length > 0) {
if (description.length > 0) {
info.description = normalizeLineEndings(description);

@@ -133,2 +104,8 @@ }

}
if (tag.tagName.text.toLowerCase().startsWith('cssprop')) {
info.syntax = type;
}
else {
info.type = type;
}
return info;

@@ -135,0 +112,0 @@ };

@@ -230,2 +230,3 @@ /**

source?: SourceReference | undefined;
readonly?: boolean | undefined;
}

@@ -237,2 +238,3 @@ export declare class ClassField extends Declaration {

source?: SourceReference | undefined;
readonly?: boolean | undefined;
type?: Type | undefined;

@@ -327,2 +329,5 @@ default?: string | undefined;

}
export interface CSSPropertyInfo extends NamedDescribed {
syntax?: string;
}
export interface TypedNamedDescribed extends NamedDescribed {

@@ -338,3 +343,3 @@ type?: string;

slots: Map<string, NamedDescribed>;
cssProperties: Map<string, NamedDescribed>;
cssProperties: Map<string, CSSPropertyInfo>;
cssParts: Map<string, NamedDescribed>;

@@ -358,3 +363,3 @@ }

readonly slots: Map<string, NamedDescribed>;
readonly cssProperties: Map<string, NamedDescribed>;
readonly cssProperties: Map<string, CSSPropertyInfo>;
readonly cssParts: Map<string, NamedDescribed>;

@@ -361,0 +366,0 @@ constructor(init: CustomElementDeclarationInit);

@@ -215,2 +215,3 @@ /**

this.default = init.default;
this.readonly = init.readonly;
}

@@ -217,0 +218,0 @@ }

@@ -20,3 +20,3 @@ /**

export declare const hasProtectedModifier: (ts: TypeScript, node: ts.HasModifiers) => boolean;
export declare const getPrivacy: (ts: TypeScript, node: ts.HasModifiers) => Privacy;
export declare const getPrivacy: (ts: TypeScript, node: ts.Node) => Privacy;
//# sourceMappingURL=utils.d.ts.map

@@ -26,6 +26,10 @@ /**

const isPrivate = (ts, node) => {
return hasPrivateModifier(ts, node) || hasJSDocTag(ts, node, 'private');
return ((ts.canHaveModifiers(node) && hasPrivateModifier(ts, node)) ||
((ts.isPropertyDeclaration(node) || ts.isMethodDeclaration(node)) &&
ts.isPrivateIdentifier(node.name)) ||
hasJSDocTag(ts, node, 'private'));
};
const isProtected = (ts, node) => {
return hasProtectedModifier(ts, node) || hasJSDocTag(ts, node, 'protected');
return ((ts.canHaveModifiers(node) && hasProtectedModifier(ts, node)) ||
hasJSDocTag(ts, node, 'protected'));
};

@@ -32,0 +36,0 @@ export const getPrivacy = (ts, node) => {

{
"name": "@lit-labs/analyzer",
"version": "0.9.1",
"version": "0.9.2",
"publishConfig": {

@@ -5,0 +5,0 @@ "access": "public"

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc