New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@lit-labs/analyzer

Package Overview
Dependencies
Maintainers
9
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lit-labs/analyzer - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

2

index.d.ts

@@ -8,5 +8,5 @@ /**

export { createPackageAnalyzer } from './lib/analyze-package.js';
export type { Package, Module, Reference, Type, Declaration, VariableDeclaration, ClassDeclaration, LitElementDeclaration, PackageJson, ModuleWithLitElementDeclarations, } from './lib/model.js';
export type { Package, Module, Reference, Type, Event, Declaration, VariableDeclaration, ClassDeclaration, LitElementDeclaration, PackageJson, ModuleWithLitElementDeclarations, } from './lib/model.js';
export type { AbsolutePath, PackagePath } from './lib/paths.js';
export { getImportsStringForReferences } from './lib/model.js';
//# sourceMappingURL=index.d.ts.map

@@ -7,3 +7,3 @@ /**

import ts from 'typescript';
import { Package, PackageJson, AnalyzerInterface } from './model.js';
import { Package, PackageJson, AnalyzerInterface, Module } from './model.js';
import { AbsolutePath } from './paths.js';

@@ -21,2 +21,3 @@ export { PackageJson };

export declare class Analyzer implements AnalyzerInterface {
readonly moduleCache: Map<AbsolutePath, Module>;
private readonly _getProgram;

@@ -29,3 +30,3 @@ readonly fs: AnalyzerInterface['fs'];

get commandLine(): ts.ParsedCommandLine;
getModule(modulePath: AbsolutePath): import("./model.js").Module;
getModule(modulePath: AbsolutePath): Module;
getPackage(): Package;

@@ -32,0 +33,0 @@ }

@@ -15,2 +15,5 @@ /**

constructor(init) {
// Cache of Module models by path; invalidated when the sourceFile
// or any of its dependencies change
this.moduleCache = new Map();
this._commandLine = undefined;

@@ -28,3 +31,3 @@ this._getProgram = init.getProgram;

getModule(modulePath) {
return getModule(this.program.getSourceFile(this.path.normalize(modulePath)), this);
return getModule(modulePath, this);
}

@@ -42,3 +45,3 @@ getPackage() {

...packageInfo,
modules: rootFileNames.map((fileName) => getModule(this.program.getSourceFile(this.path.normalize(fileName)), this, packageInfo)),
modules: rootFileNames.map((fileName) => getModule(this.path.normalize(fileName), this, packageInfo)),
});

@@ -45,0 +48,0 @@ }

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with classes
* Utilities for analyzing class declarations
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -6,8 +6,8 @@ /**

*/
import ts from 'typescript';
import { Module, AnalyzerInterface, PackageInfo } from '../model.js';
import { AbsolutePath } from '../paths.js';
/**
* Returns an analyzer `Module` model for the given ts.SourceFile.
* Returns an analyzer `Module` model for the given module path.
*/
export declare const getModule: (sourceFile: ts.SourceFile, analyzer: AnalyzerInterface, packageInfo?: PackageInfo) => Module;
export declare const getModule: (modulePath: AbsolutePath, analyzer: AnalyzerInterface, packageInfo?: PackageInfo) => Module;
//# sourceMappingURL=modules.d.ts.map

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

*/
/**
* @fileoverview
*
* Utilities for analyzing ES modules
*/
import ts from 'typescript';
import { Module } from '../model.js';
import { isLitElement, getLitElementDeclaration, } from '../lit-element/lit-element.js';
import * as path from 'path';
import { getClassDeclaration } from './classes.js';

@@ -15,36 +19,30 @@ import { getVariableDeclarations } from './variables.js';

import { getPackageInfo } from './packages.js';
import { DiagnosticsError } from '../errors.js';
/**
* Returns an analyzer `Module` model for the given ts.SourceFile.
* Returns an analyzer `Module` model for the given module path.
*/
export const getModule = (sourceFile, analyzer, packageInfo = getPackageInfo(sourceFile.fileName, analyzer)) => {
// Find and load the package.json associated with this module; this both gives
// us the packageRoot for this module (needed for translating the source file
// path to a package relative path), as well as the packageName (needed for
// generating references to any symbols in this module). This will need
// caching/invalidation.
export const getModule = (modulePath, analyzer, packageInfo = getPackageInfo(modulePath, analyzer)) => {
// Return cached module if we've parsed this sourceFile already and its
// dependencies haven't changed
const cachedModule = getAndValidateModuleFromCache(modulePath, analyzer);
if (cachedModule !== undefined) {
return cachedModule;
}
const sourceFile = analyzer.program.getSourceFile(analyzer.path.normalize(modulePath));
if (sourceFile === undefined) {
throw new Error(`Program did not contain a source file for ${modulePath}`);
}
// The packageRoot for this module is needed for translating the source file
// path to a package relative path, and the packageName is needed for
// generating references to any symbols in this module.
const { rootDir, packageJson } = packageInfo;
const sourcePath = absoluteToPackage(analyzer.path.normalize(sourceFile.fileName), rootDir);
const fullSourcePath = path.join(rootDir, sourcePath);
const jsPath = fullSourcePath.endsWith('.js')
? fullSourcePath
: ts
.getOutputFileNames(analyzer.commandLine, fullSourcePath, false)
.filter((f) => f.endsWith('.js'))[0];
// TODO(kschaaf): this could happen if someone imported only a .d.ts file;
// we might need to handle this differently
if (jsPath === undefined) {
throw new Error(`Could not determine output filename for '${sourcePath}'`);
}
const module = new Module({
sourcePath,
// The jsPath appears to come out of the ts API with unix
// separators; since sourcePath uses OS separators, normalize
// this so that all our model paths are OS-native
jsPath: absoluteToPackage(analyzer.path.normalize(jsPath), rootDir),
sourceFile,
packageJson,
});
const sourcePath = absoluteToPackage(analyzer.path.normalize(modulePath), rootDir);
const jsPath = absoluteToPackage(getJSPathFromSourcePath(modulePath, analyzer), rootDir);
const dependencies = new Set();
const declarations = [];
// Find and add models for declarations in the module
// TODO(kschaaf): Add Variable, Function, and MixinDeclarations
for (const statement of sourceFile.statements) {
if (ts.isClassDeclaration(statement)) {
module.declarations.push(isLitElement(statement, analyzer)
declarations.push(isLitElement(statement, analyzer)
? getLitElementDeclaration(statement, analyzer)

@@ -54,9 +52,103 @@ : getClassDeclaration(statement, analyzer));

else if (ts.isVariableStatement(statement)) {
module.declarations.push(...statement.declarationList.declarations
declarations.push(...statement.declarationList.declarations
.map((dec) => getVariableDeclarations(dec, dec.name, analyzer))
.flat());
}
else if (ts.isImportDeclaration(statement)) {
dependencies.add(getPathForModuleSpecifier(statement.moduleSpecifier, analyzer));
}
}
// Construct module and save in cache
const module = new Module({
sourcePath,
jsPath,
sourceFile,
packageJson,
declarations,
dependencies,
});
analyzer.moduleCache.set(analyzer.path.normalize(sourceFile.fileName), module);
return module;
};
/**
* Returns a cached Module model for the given module path if it and all of its
* dependencies' models are still valid since the model was cached. If the
* cached module is out-of-date and needs to be re-created, this method returns
* undefined.
*/
const getAndValidateModuleFromCache = (modulePath, analyzer) => {
const module = analyzer.moduleCache.get(modulePath);
// A cached module is only valid if the source file that was used has not
// changed in the current program, and if all of its dependencies are still
// valid
if (module !== undefined) {
if (module.sourceFile === analyzer.program.getSourceFile(modulePath) &&
depsAreValid(module, analyzer)) {
return module;
}
analyzer.moduleCache.delete(modulePath);
}
return undefined;
};
/**
* Returns true if all dependencies of the module are still valid.
*/
const depsAreValid = (module, analyzer) => Array.from(module.dependencies).every((path) => depIsValid(path, analyzer));
/**
* Returns true if the given dependency is valid, meaning that if it has a
* cached model, the model is still valid. Dependencies that don't yet have a
* cached model are considered valid.
*/
const depIsValid = (modulePath, analyzer) => {
if (analyzer.moduleCache.has(modulePath)) {
// If a dep has a model, it is valid only if its deps are valid
return Boolean(getAndValidateModuleFromCache(modulePath, analyzer));
}
else {
// Deps that don't have a cached model are considered valid
return true;
}
};
/**
* For a given source file, return its associated JS file.
*
* For a JS source file, these will be the same thing. For a TS file, we use the
* TS API to determine where the associated JS will be output based on tsconfig
* settings.
*/
const getJSPathFromSourcePath = (sourcePath, analyzer) => {
sourcePath = analyzer.path.normalize(sourcePath);
// If the source file was already JS, just return that
if (sourcePath.endsWith('js')) {
return sourcePath;
}
// Use the TS API to determine where the associated JS will be output based
// on tsconfig settings.
const outputPath = ts
.getOutputFileNames(analyzer.commandLine, sourcePath, false)
.filter((f) => f.endsWith('.js'))[0];
// TODO(kschaaf): this could happen if someone imported only a .d.ts file;
// we might need to handle this differently
if (outputPath === undefined) {
throw new Error(`Could not determine output filename for '${sourcePath}'`);
}
// The filename appears to come out of the ts API with
// unix separators; since sourcePath uses OS separators, normalize this so
// that all our model paths are OS-native
return analyzer.path.normalize(outputPath);
};
/**
* Resolve a module specifier to an absolute path on disk.
*/
const getPathForModuleSpecifier = (moduleSpecifier, analyzer) => {
const specifier = moduleSpecifier.getText().slice(1, -1);
let resolvedPath = ts.resolveModuleName(specifier, moduleSpecifier.getSourceFile().fileName, analyzer.commandLine.options, analyzer.fs).resolvedModule?.resolvedFileName;
if (resolvedPath === undefined) {
throw new DiagnosticsError(moduleSpecifier, `Could not resolve specifier to filesystem path.`);
}
if (!analyzer.fs.useCaseSensitiveFileNames) {
resolvedPath = resolvedPath.toLowerCase();
}
return analyzer.path.normalize(resolvedPath);
};
//# sourceMappingURL=modules.js.map

@@ -16,9 +16,9 @@ /**

const root = path.parse(searchDir).root;
while (searchDir !== root) {
if (fs.fileExists(path.join(searchDir, 'package.json'))) {
return searchDir;
while (!fs.fileExists(path.join(searchDir, 'package.json'))) {
if (searchDir === root) {
throw new Error(`No package.json found for module path ${modulePath}`);
}
searchDir = path.dirname(searchDir);
}
throw new Error(`No package.json found for module path ${modulePath}`);
return searchDir;
};

@@ -25,0 +25,0 @@ /**

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with classes
* Utilities for analyzing variable declarations
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with classes
* Utilities for analyzing variable declarations
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with ReactiveElement decorators.
* Utilities for analyzing ReactiveElement decorators.
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with ReactiveElement decorators.
* Utilities for analyzing ReactiveElement decorators.
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with events
* Utilities for analyzing with events
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with LitElement (and ReactiveElement) declarations.
* Utilities for analyzing LitElement (and ReactiveElement) declarations.
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with LitElement (and ReactiveElement) declarations.
* Utilities for analyzing LitElement (and ReactiveElement) declarations.
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with reactive property declarations
* Utilities for analyzing reactive property declarations
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -9,3 +9,3 @@ /**

*
* Utilities for working with reactive property declarations
* Utilities for analyzing reactive property declarations
*/

@@ -12,0 +12,0 @@ import ts from 'typescript';

@@ -49,2 +49,4 @@ /**

packageJson: PackageJson;
declarations: Declaration[];
dependencies: Set<AbsolutePath>;
}

@@ -67,3 +69,13 @@ export declare class Module {

readonly jsPath: PackagePath;
/**
* A list of all Declaration models in this module.
*/
readonly declarations: Array<Declaration>;
/**
* A set of all dependencies of this module
*/
readonly dependencies: Set<AbsolutePath>;
/**
* The package.json contents for the package containing this module.
*/
readonly packageJson: PackageJson;

@@ -182,2 +194,3 @@ constructor(init: ModuleInit);

export interface AnalyzerInterface {
moduleCache: Map<AbsolutePath, Module>;
program: ts.Program;

@@ -184,0 +197,0 @@ commandLine: ts.ParsedCommandLine;

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

constructor(init) {
this.declarations = [];
this.sourceFile = init.sourceFile;

@@ -44,2 +43,4 @@ this.sourcePath = init.sourcePath;

this.packageJson = init.packageJson;
this.declarations = init.declarations;
this.dependencies = init.dependencies;
}

@@ -46,0 +47,0 @@ }

@@ -15,3 +15,3 @@ /**

*/
const getImportModuleSpecifier = (declaration) => {
const getImportNameAndModuleSpecifier = (declaration) => {
// TODO(kschaaf) support the various import syntaxes, e.g. `import {foo as bar} from 'baz'`

@@ -26,3 +26,6 @@ if (ts.isImportSpecifier(declaration) &&

.slice(1, -1);
return module;
return {
module,
name: declaration.propertyName?.text ?? declaration.name.text,
};
}

@@ -40,3 +43,3 @@ return undefined;

const { path } = analyzer;
const { name } = symbol;
const { name: symbolName } = symbol;
// TODO(kschaaf): Do we need to check other declarations? The assumption is

@@ -49,3 +52,3 @@ // that even with multiple declarations (e.g. because of class interface +

if (declaration === undefined) {
throw new DiagnosticsError(location, `Could not find declaration for symbol '${name}'`);
throw new DiagnosticsError(location, `Could not find declaration for symbol '${symbolName}'`);
}

@@ -78,3 +81,3 @@ const declarationSourceFile = declaration.getSourceFile();

return new Reference({
name,
name: symbolName,
isGlobal: true,

@@ -84,4 +87,5 @@ });

else {
const moduleSpecifier = getImportModuleSpecifier(declaration);
if (moduleSpecifier !== undefined) {
const importInfo = getImportNameAndModuleSpecifier(declaration);
if (importInfo !== undefined) {
const { module: moduleSpecifier, name: importName } = importInfo;
let refPackage;

@@ -100,3 +104,3 @@ let refModule;

// module path relative to this module
const module = getModule(location.getSourceFile(), analyzer);
const module = getModule(location.getSourceFile().fileName, analyzer);
refPackage = module.packageJson.name;

@@ -123,3 +127,3 @@ refModule = path.join(path.dirname(module.jsPath), moduleSpecifier);

return new Reference({
name,
name: importName,
package: refPackage,

@@ -131,5 +135,5 @@ module: refModule,

// Declared in this file: use the current package and module
const module = getModule(location.getSourceFile(), analyzer);
const module = getModule(location.getSourceFile().fileName, analyzer);
return new Reference({
name,
name: symbolName,
package: module.packageJson.name,

@@ -136,0 +140,0 @@ module: module.jsPath,

{
"name": "@lit-labs/analyzer",
"version": "0.3.0",
"version": "0.4.0",
"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

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
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc