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

@rehearsal/plugins

Package Overview
Dependencies
Maintainers
3
Versions
65
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@rehearsal/plugins - npm Package Compare versions

Comparing version 2.0.2-beta to 2.1.0

3

dist/src/helpers.d.ts

@@ -1,5 +0,6 @@

import { type SourceFile } from 'typescript';
import { type FormatCodeSettings, type SourceFile } from 'typescript';
import { Location } from '@rehearsal/reporter';
export declare function getLocation(sourceFile: SourceFile, start: number, length: number): Location;
export declare function setProcessTTYto(setting: boolean): void;
export declare function getFormatCodeSettingsForFile(filePath: string): Promise<FormatCodeSettings>;
//# sourceMappingURL=helpers.d.ts.map

@@ -0,1 +1,9 @@

import ts from 'typescript';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import Module from 'node:module';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const require = Module.createRequire(import.meta.url);
const { SemicolonPreference, getDefaultFormatCodeSettings } = ts;
const INDEX_BUMP = 1; //bump line and column numbers from 0 to 1 for sarif reader

@@ -17,2 +25,45 @@ export function getLocation(sourceFile, start, length) {

}
export async function getFormatCodeSettingsForFile(filePath) {
let prettierConfig = null;
try {
const prettier = importPrettier(filePath);
prettierConfig = await prettier.resolveConfig(filePath, {
editorconfig: true,
});
}
catch (e) {
// swallow the error. Prettier is not installed
}
const tsFormatCodeOptions = getDefaultFormatCodeSettings();
let useSemicolons = true;
let indentSize = tsFormatCodeOptions.tabSize ?? 2;
let convertTabsToSpaces = true;
if (prettierConfig) {
useSemicolons = prettierConfig.semi !== false;
indentSize = prettierConfig.tabWidth ?? indentSize;
convertTabsToSpaces = prettierConfig.useTabs !== true;
}
return {
...tsFormatCodeOptions,
baseIndentSize: indentSize,
convertTabsToSpaces,
indentSize,
semicolons: useSemicolons ? SemicolonPreference.Insert : SemicolonPreference.Remove,
};
}
function importPrettier(fromPath) {
const pkg = getPackageInfo('prettier', fromPath);
const main = resolve(pkg.path);
return require(main);
}
function getPackageInfo(packageName, fromPath) {
const paths = [__dirname];
paths.unshift(fromPath);
const packageJSONPath = require.resolve(`${packageName}/package.json`, {
paths,
});
return {
path: dirname(packageJSONPath),
};
}
//# sourceMappingURL=helpers.js.map

@@ -1,2 +0,2 @@

import ts from 'typescript';
import ts, { type FormatCodeSettings } from 'typescript';
import { type DiagnosticWithContext } from '@rehearsal/codefixes';

@@ -8,2 +8,3 @@ import { PluginOptions, PluginsRunnerContext, Service, Plugin } from '@rehearsal/service';

strictTyping?: boolean;
mode: 'single-pass' | 'drain';
}

@@ -21,7 +22,9 @@ /**

run(): Promise<string[]>;
applyFixes(context: PluginsRunnerContext, fileName: string, diagnosticCategory: ts.DiagnosticCategory, options: DiagnosticFixPluginOptions): Promise<void>;
private singlePassMode;
private drainMode;
applyFixes(context: PluginsRunnerContext, fileName: string, diagnosticCategory: ts.DiagnosticCategory, options: DiagnosticFixPluginOptions, formatCodeSettings: FormatCodeSettings): Promise<void>;
/**
* Returns the list of diagnostics with location and additional context of the application
*/
getDiagnostics(service: Service, fileName: string, diagnosticFilterCategory: ts.DiagnosticCategory): DiagnosticWithContext[];
getDiagnostics(service: Service, fileName: string, diagnosticFilterCategories: ts.DiagnosticCategory[]): DiagnosticWithContext[];
private applyCommandAction;

@@ -31,5 +34,5 @@ /**

*/
getCodeFix(diagnostic: DiagnosticWithContext, options: DiagnosticFixPluginOptions): ts.CodeFixAction | undefined;
getCodeFix(diagnostic: DiagnosticWithContext, options: DiagnosticFixPluginOptions, formatCodeSettings: FormatCodeSettings): ts.CodeFixAction | undefined;
private wasAttemptedToFix;
}
//# sourceMappingURL=diagnostic-fix.plugin.d.ts.map

@@ -5,5 +5,6 @@ import { createRequire } from 'node:module';

import ts from 'typescript';
import { codefixes, getDiagnosticOrder, isInstallPackageCommand, } from '@rehearsal/codefixes';
import { codefixes, getDiagnosticOrder, isInstallPackageCommand, applyCodeFix, } from '@rehearsal/codefixes';
import { Plugin } from '@rehearsal/service';
import { findNodeAtPosition } from '@rehearsal/ts-utils';
import { getFormatCodeSettingsForFile } from '../helpers.js';
const require = createRequire(import.meta.url);

@@ -13,2 +14,3 @@ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

const DEBUG_CALLBACK = debug('rehearsal:plugins:diagnostic-fix');
const { DiagnosticCategory } = ts;
/**

@@ -26,21 +28,71 @@ * Diagnose issues in the file and applied transforms to fix them

async run() {
const { options } = this;
const { mode } = options;
switch (mode) {
case 'drain':
await this.drainMode();
break;
case 'single-pass':
default:
await this.singlePassMode();
}
return Array.from(this.allFixedFiles);
}
async singlePassMode() {
const { fileName, context, options } = this;
DEBUG_CALLBACK(`Plugin 'DiagnosticFix' run on %O:`, fileName);
const formatCodeSettings = await getFormatCodeSettingsForFile(fileName);
// First attempt to fix all the errors
await this.applyFixes(context, fileName, ts.DiagnosticCategory.Error, options);
await this.applyFixes(context, fileName, ts.DiagnosticCategory.Error, options, formatCodeSettings);
// Then attempt to run the suggestions
await this.applyFixes(context, fileName, ts.DiagnosticCategory.Suggestion, options);
await this.applyFixes(context, fileName, ts.DiagnosticCategory.Suggestion, options, formatCodeSettings);
this.changeTrackers.forEach((tracker, file) => {
context.service.setFileText(file, tracker.toString());
});
return Array.from(this.allFixedFiles);
}
async applyFixes(context, fileName, diagnosticCategory, options) {
const diagnostics = this.getDiagnostics(context.service, fileName, diagnosticCategory);
for (const diagnostic of diagnostics) {
if (!diagnostic.node) {
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.start}:\t node not found`);
async drainMode() {
const { fileName, context, options } = this;
const formatCodeSettings = await getFormatCodeSettingsForFile(fileName);
let diagnostics = this.getDiagnostics(context.service, fileName, [
DiagnosticCategory.Error,
DiagnosticCategory.Suggestion,
]);
// In the drain mode diagnostics list is getting refreshed in every cycle which might have end up
// with more error need to be fixed then was originally. The limit based on original amount of diagnostics
// helps to avoid an infinitive loop in some edge cases when new errors keep coming when previous fixed.
let limit = diagnostics.length * 10;
while (limit-- && diagnostics.length) {
const diagnostic = diagnostics.shift();
const fix = this.getCodeFix(diagnostic, options, formatCodeSettings);
if (!fix) {
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.start}:\t didn't fix`);
continue;
}
const fix = this.getCodeFix(diagnostic, options);
if (isInstallPackageCommand(fix)) {
await this.applyCommandAction(fix.commands, context);
}
applyCodeFix(fix, {
getText(filename) {
return context.service.getFileText(filename);
},
applyText(newText) {
DEBUG_CALLBACK(`- TS${diagnostic.code} at ${diagnostic.start}:\t ${newText}`);
},
setText: (filename, text) => {
context.service.setFileText(filename, text);
context.reporter.incrementRunFixedItemCount();
this.allFixedFiles.add(filename);
DEBUG_CALLBACK(`- TS${diagnostic.code} at ${diagnostic.start}:\t codefix applied`);
},
});
diagnostics = this.getDiagnostics(context.service, fileName, [
DiagnosticCategory.Error,
DiagnosticCategory.Suggestion,
]);
}
}
async applyFixes(context, fileName, diagnosticCategory, options, formatCodeSettings) {
const diagnostics = this.getDiagnostics(context.service, fileName, [diagnosticCategory]);
for (const diagnostic of diagnostics) {
const fix = this.getCodeFix(diagnostic, options, formatCodeSettings);
if (!fix) {

@@ -67,2 +119,3 @@ DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.start}:\t didn't fix`);

changeTracker.remove(change.span.start, change.span.start + change.span.length);
this.allFixedFiles.add(fileTextChange.fileName);
}

@@ -88,3 +141,3 @@ if (!this.appliedAtOffset[fileTextChange.fileName]) {

*/
getDiagnostics(service, fileName, diagnosticFilterCategory) {
getDiagnostics(service, fileName, diagnosticFilterCategories) {
const languageService = service.getLanguageService();

@@ -96,3 +149,3 @@ const program = languageService.getProgram();

.filter((diagnostic) => {
return diagnostic.category === diagnosticFilterCategory;
return diagnosticFilterCategories.some((category) => category === diagnostic.category);
})

@@ -124,7 +177,7 @@ // Convert DiagnosticWithLocation to DiagnosticWithContext

*/
getCodeFix(diagnostic, options) {
getCodeFix(diagnostic, options, formatCodeSettings) {
const fixes = codefixes.getCodeFixes(diagnostic, {
safeFixes: options.safeFixes,
strictTyping: options.strictTyping,
});
}, formatCodeSettings);
if (fixes.length === 0) {

@@ -131,0 +184,0 @@ return undefined;

@@ -9,6 +9,2 @@ import { DiagnosticWithContext } from '@rehearsal/codefixes';

run(): Promise<string[]>;
getBoundaryOfCommentBlock(start: number, length: number, text: string): {
start: number;
end: number;
};
getDiagnostics(service: Service, fileName: string): DiagnosticWithContext[];

@@ -15,0 +11,0 @@ isValidDiagnostic(diagnostic: DiagnosticWithContext): boolean;

@@ -7,3 +7,3 @@ import { hints } from '@rehearsal/codefixes';

import { getLocation } from '../helpers.js';
const { isLineBreak } = ts;
import { getBoundaryOfCommentBlock } from './utils.js';
const DEBUG_CALLBACK = debug('rehearsal:plugins:diagnostic-report');

@@ -14,5 +14,5 @@ export class DiagnosticReportPlugin extends Plugin {

DEBUG_CALLBACK(`Plugin 'DiagnosticReport' run on %O:`, fileName);
const originalConentWithErrorsSupressed = context.service.getFileText(fileName);
const lineHasSupression = {};
let contentWithErrors = originalConentWithErrorsSupressed;
const originalContentWithErrorsSuppressed = context.service.getFileText(fileName);
const lineHasSuppression = {};
let contentWithErrors = originalContentWithErrorsSuppressed;
const sourceFile = context.service.getSourceFile(fileName);

@@ -30,7 +30,8 @@ const tagStarts = [...contentWithErrors.matchAll(new RegExp(options.commentTag, 'g'))].map((m) => m.index);

// Remove comment, together with the {} that wraps around comments in React
const boundary = this.getBoundaryOfCommentBlock(commentSpan.start, commentSpan.length, contentWithErrors);
const boundary = getBoundaryOfCommentBlock(commentSpan.start, commentSpan.length, contentWithErrors);
contentWithErrors =
contentWithErrors.substring(0, boundary.start) + contentWithErrors.substring(boundary.end);
contentWithErrors.substring(0, boundary.start) +
contentWithErrors.substring(boundary.end + 1);
}
// Our document now has unsupressed errors in it. Set that content into the langauge server so we can type check it
// Our document now has unsuppressed errors in it. Set that content into the language server, so we can type check it
context.service.setFileText(fileName, contentWithErrors);

@@ -43,21 +44,11 @@ const diagnostics = this.getDiagnostics(context.service, fileName);

// We only allow for a single entry per line
if (!lineHasSupression[location.startLine]) {
if (!lineHasSuppression[location.startLine]) {
context.reporter.addTSItemToRun(diagnostic, diagnostic.node, location, hint, helpUrl, options.addHints);
lineHasSupression[location.startLine] = true;
lineHasSuppression[location.startLine] = true;
}
}
// We have now collected the correct line / cols of the errors. We can now set the document back to one without errors.
context.service.setFileText(fileName, originalConentWithErrorsSupressed);
context.service.setFileText(fileName, originalContentWithErrorsSuppressed);
return Promise.resolve([]);
}
getBoundaryOfCommentBlock(start, length, text) {
const newStart = start - 1 >= 0 && text[start - 1] === '{' ? start - 1 : start;
let end = start + length - 1;
end = end + 1 < text.length && text[end + 1] === '}' ? end + 1 : end;
end = isLineBreak(text.charCodeAt(end + 1)) ? end + 1 : end;
return {
start: newStart,
end,
};
}
getDiagnostics(service, fileName) {

@@ -64,0 +55,0 @@ const languageService = service.getLanguageService();

@@ -1,5 +0,5 @@

import { type TransformManager } from '@glint/core';
import { DiagnosticWithContext } from '@rehearsal/codefixes';
import { GlintService, PluginOptions, Service, PathUtils, Plugin } from '@rehearsal/service';
import { type Location } from '@rehearsal/reporter';
import type { TransformManager } from '@glint/core';
import type MS from 'magic-string';

@@ -6,0 +6,0 @@ export interface GlintCommentPluginOptions extends PluginOptions {

@@ -78,2 +78,7 @@ import { extname } from 'node:path';

const isInHbsContext = this.shouldUseHbsComment(service.pathUtils, info, diagnostic.file.fileName, changeTracker.original, diagnostic, index);
if (isInHbsContext) {
// Abort trying to comment an hbs context until https://github.com/rehearsal-js/rehearsal-js/issues/1119 is resolved
// For now, we DO NOT WANT add any {{! @glint-expect-errors }} directives in hbs contexts.
return;
}
const message = `${commentTag} TODO TS${diagnostic.code}: ${hint}`;

@@ -113,3 +118,14 @@ const tsIgnoreCommentText = isInHbsContext

if (module) {
const template = module.findTemplateAtOriginalOffset(filePath, diagnostic.start);
let template;
// We must wrap this with a try catch because the findTemplateAtOriginalOffset will throw
// on a template only conmponent with only a string and no logic or args.
try {
template = module.findTemplateAtOriginalOffset(filePath, diagnostic.start);
}
catch (e) {
DEBUG_CALLBACK(`Unable to findTemplateAtOriginalOffset for ${diagnostic.code} with ${filePath}`);
if (extname(filePath) === '.hbs') {
return true;
}
}
// If we're able to find a template associated with the diagnostic, then we know the error

@@ -116,0 +132,0 @@ // is pointing to the body of a template, and we likely to use HBS comments

@@ -1,8 +0,14 @@

import ts from 'typescript';
import { Diagnostic } from 'vscode-languageserver';
import { DiagnosticWithContext } from '@rehearsal/codefixes';
import ts, { FormatCodeSettings } from 'typescript';
import { Plugin, GlintService, PluginsRunnerContext } from '@rehearsal/service';
import type { Diagnostic } from 'vscode-languageserver';
import type MS from 'magic-string';
export declare class GlintFixPlugin extends Plugin {
appliedAtOffset: {
[file: string]: number[];
import type { TextChange } from 'typescript';
export interface GlintFixPluginOptions {
mode: 'single-pass' | 'drain';
}
export declare class GlintFixPlugin extends Plugin<GlintFixPluginOptions> {
attemptedToFix: string[];
appliedTextChanges: {
[file: string]: TextChange[];
};

@@ -12,6 +18,11 @@ changeTrackers: Map<string, MS.default>;

run(): Promise<string[]>;
applyFix(fileName: string, context: PluginsRunnerContext, diagnosticCategory: ts.DiagnosticCategory): void;
getDiagnostics(service: GlintService, fileName: string, diagnosticCategory: ts.DiagnosticCategory): Diagnostic[];
getCodeFix(fileName: string, diagnostic: Diagnostic, service: GlintService): ts.CodeFixAction | undefined;
private drainMode;
private singlePassMode;
applyFix(fileName: string, context: PluginsRunnerContext, diagnosticCategory: ts.DiagnosticCategory, formatCodeSettings: FormatCodeSettings): void;
hasAppliedChange(fileName: string, change: TextChange): boolean;
getDiagnostics(service: GlintService, fileName: string, diagnosticCategories: ts.DiagnosticCategory[]): Diagnostic[];
getCodeFix(fileName: string, diagnostic: Diagnostic, service: GlintService, formatCodeSettings: FormatCodeSettings): ts.CodeFixAction | undefined;
private wasAttemptedToFix;
getDiagnosticsWithContext(fileName: string, diagnostic: Diagnostic, service: GlintService): DiagnosticWithContext;
}
//# sourceMappingURL=glint-fix.plugin.d.ts.map
import { createRequire } from 'node:module';
import { makeCodeFixStrict } from '@rehearsal/codefixes';
import { glintCodeFixes, applyCodeFix, getDiagnosticOrder, } from '@rehearsal/codefixes';
import debug from 'debug';
import ts from 'typescript';
import { CodeActionKind } from 'vscode-languageserver';
import { Plugin } from '@rehearsal/service';
import hash from 'object-hash';
import { findNodeAtPosition, isSameChange, normalizeTextChanges } from '@rehearsal/ts-utils';
import { getFormatCodeSettingsForFile } from '../helpers.js';
const require = createRequire(import.meta.url);

@@ -11,6 +13,8 @@ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

const DEBUG_CALLBACK = debug('rehearsal:plugins:glint-fix');
const { DiagnosticCategory } = ts;
export class GlintFixPlugin extends Plugin {
constructor() {
super(...arguments);
this.appliedAtOffset = {};
this.attemptedToFix = [];
this.appliedTextChanges = {};
this.changeTrackers = new Map();

@@ -20,17 +24,71 @@ this.allFixedFiles = new Set();

async run() {
const { options } = this;
const { mode } = options;
switch (mode) {
case 'drain':
await this.drainMode();
break;
case 'single-pass':
default:
await this.singlePassMode();
}
return Promise.resolve(Array.from(this.allFixedFiles));
}
async drainMode() {
const { fileName, context } = this;
this.applyFix(fileName, context, ts.DiagnosticCategory.Error);
this.applyFix(fileName, context, ts.DiagnosticCategory.Suggestion);
const service = context.service;
const formatCodeSettings = await getFormatCodeSettingsForFile(fileName);
let diagnostics = this.getDiagnostics(service, fileName, [
DiagnosticCategory.Error,
DiagnosticCategory.Suggestion,
]);
// In the drain mode diagnostics list is getting refreshed in every cycle which might have end up
// with more error need to be fixed then was originally. The limit based on original amount of diagnostics
// helps to avoid an infinitive loop in some edge cases when new errors keep coming when previous fixed.
let limit = diagnostics.length * 10;
while (limit-- && diagnostics.length) {
const diagnostic = diagnostics.shift();
const fix = this.getCodeFix(fileName, diagnostic, service, formatCodeSettings);
if (fix === undefined) {
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.range.start.line}:${diagnostic.range.start.character}:\t fix not found`);
continue;
}
applyCodeFix(fix, {
getText(filename) {
return context.service.getFileText(filename);
},
applyText(newText) {
DEBUG_CALLBACK(`- TS${diagnostic.code} at ${diagnostic.range.start.line}:${diagnostic.range.start.character}:\t ${newText}`);
},
setText: (filename, text) => {
context.service.setFileText(filename, text);
context.reporter.incrementRunFixedItemCount();
this.allFixedFiles.add(filename);
DEBUG_CALLBACK(`- TS${diagnostic.code} at ${diagnostic.range.start.line}:${diagnostic.range.start.character}:\t codefix applied`);
},
});
context.reporter.incrementRunFixedItemCount();
diagnostics = this.getDiagnostics(service, fileName, [
DiagnosticCategory.Error,
DiagnosticCategory.Suggestion,
]);
}
}
async singlePassMode() {
const { fileName, context } = this;
const formatCodeSettings = await getFormatCodeSettingsForFile(fileName);
this.applyFix(fileName, context, ts.DiagnosticCategory.Error, formatCodeSettings);
this.applyFix(fileName, context, ts.DiagnosticCategory.Suggestion, formatCodeSettings);
this.changeTrackers.forEach((tracker, fileName) => {
context.service.setFileText(fileName, tracker.toString());
});
return Promise.resolve(Array.from(this.allFixedFiles));
}
applyFix(fileName, context, diagnosticCategory) {
applyFix(fileName, context, diagnosticCategory, formatCodeSettings) {
var _a;
const service = context.service;
const diagnostics = this.getDiagnostics(service, fileName, diagnosticCategory);
const diagnostics = this.getDiagnostics(service, fileName, [diagnosticCategory]);
for (const diagnostic of diagnostics) {
const fix = this.getCodeFix(fileName, diagnostic, service);
const fix = this.getCodeFix(fileName, diagnostic, service, formatCodeSettings);
if (fix === undefined) {
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.range.start}:\t node not found`);
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnostic.range.start.line}:${diagnostic.range.start.character}:\t fix not found`);
continue;

@@ -53,13 +111,17 @@ }

}
if (!this.appliedAtOffset[fileTextChange.fileName]) {
this.appliedAtOffset[fileTextChange.fileName] = [];
const targetFileName = fileTextChange.fileName;
// Track the applied changes so we can dedupe as we go
if (this.hasAppliedChange(targetFileName, change)) {
continue; // Skip if a duplicate change
}
if (this.appliedAtOffset[fileTextChange.fileName].includes(change.span.start)) {
continue;
}
else {
// this.appliedAtOffset[fileTextChange.fileName].push(change.span.start);
// Init if undefined
(_a = this.appliedTextChanges)[targetFileName] ?? (_a[targetFileName] = []);
// Append and normalize
this.appliedTextChanges[targetFileName].push(change);
this.appliedTextChanges[targetFileName] = normalizeTextChanges(this.appliedTextChanges[targetFileName]);
}
DEBUG_CALLBACK(`- TS${diagnostic.code} at ${diagnostic.range.start}:\t ${change.newText}`);
changeTracker.appendLeft(change.span.start, change.newText);
context.reporter.incrementRunFixedItemCount();
this.allFixedFiles.add(fileTextChange.fileName);

@@ -70,23 +132,65 @@ }

}
getDiagnostics(service, fileName, diagnosticCategory) {
return service
.getDiagnostics(fileName)
.filter((d) => d.category === diagnosticCategory)
hasAppliedChange(fileName, change) {
if (!this.appliedTextChanges[fileName]) {
return false;
}
return !!this.appliedTextChanges[fileName].find((existing) => isSameChange(existing, change));
}
getDiagnostics(service, fileName, diagnosticCategories) {
const unordered = service.getDiagnostics(fileName);
const diagnostics = getDiagnosticOrder(unordered);
return diagnostics
.filter((d) => diagnosticCategories.some((category) => category === d.category))
.map((d) => service.convertTsDiagnosticToLSP(d));
}
getCodeFix(fileName, diagnostic, service) {
const glintService = service.getGlintService();
const rawActions = glintService.getCodeActions(fileName, CodeActionKind.QuickFix, diagnostic.range, [diagnostic]);
const transformedActions = service
.transformCodeActionToCodeFixAction(rawActions)
.reduce((acc, fix) => {
const strictFix = makeCodeFixStrict(fix);
if (strictFix) {
acc.push(strictFix);
}
return acc;
}, []);
return transformedActions[0];
getCodeFix(fileName, diagnostic, service, formatCodeSettings) {
const diagnosticWithContext = this.getDiagnosticsWithContext(fileName, diagnostic, service);
const fixes = glintCodeFixes.getCodeFixes(diagnosticWithContext, {
safeFixes: true,
strictTyping: true,
}, formatCodeSettings);
if (fixes.length === 0) {
return undefined;
}
// Use the first available codefix in automatic mode
let fix = fixes.shift();
while (fix && this.wasAttemptedToFix(diagnostic, fix)) {
// Try the next fix if we already tried the first one
fix = fixes.shift();
}
if (fix === undefined) {
DEBUG_CALLBACK(` - TS${diagnostic.code} at ${diagnosticWithContext.start}:\t fixes didn't work`);
}
return fix;
}
wasAttemptedToFix(diagnostic, fix) {
const diagnosticFixHash = hash([
diagnostic.code,
diagnostic.range.start.line,
diagnostic.range.start.character,
fix,
]);
if (!this.attemptedToFix.includes(diagnosticFixHash)) {
this.attemptedToFix.push(diagnosticFixHash);
return false;
}
return true;
}
getDiagnosticsWithContext(fileName, diagnostic, service) {
const program = service.getLanguageService().getProgram();
const checker = program.getTypeChecker();
const diagnosticWithLocation = service.convertLSPDiagnosticToTs(fileName, diagnostic);
return {
...diagnosticWithLocation,
...{
glintService: service,
glintDiagnostic: diagnostic,
service: service.getLanguageService(),
program,
checker,
node: findNodeAtPosition(diagnosticWithLocation.file, diagnosticWithLocation.start, diagnosticWithLocation.length),
},
};
}
}
//# sourceMappingURL=glint-fix.plugin.js.map

@@ -1,2 +0,2 @@

import { hints, getDiagnosticOrder } from '@rehearsal/codefixes';
import { hints } from '@rehearsal/codefixes';
import { Plugin } from '@rehearsal/service';

@@ -31,3 +31,3 @@ import debug from 'debug';

}
// Our document now has unsupressed errors in it. Set that content into the langauge server so we can type check it
// Our document now has unsuppressed errors in it. Set that content into the langauge server so we can type check it
service.setFileText(fileName, contentWithErrors);

@@ -41,7 +41,12 @@ const diagnostics = this.getDiagnostics(service, fileName);

if (!lineHasSupression[location.startLine]) {
context.reporter.addTSItemToRun(diagnostic, diagnostic.node, location, hint, helpUrl);
if (diagnostic.source === 'glint') {
context.reporter.addGlintItemToRun(diagnostic, diagnostic.node, location, hint, helpUrl);
}
else {
context.reporter.addTSItemToRun(diagnostic, diagnostic.node, location, hint, helpUrl);
}
lineHasSupression[location.startLine] = true;
}
}
// Set the document back to the content with the errors supressed
// Set the document back to the content with the errors suppressed
service.setFileText(fileName, originalContentWithErrorsSuppressed);

@@ -54,3 +59,3 @@ return Promise.resolve([]);

const checker = program.getTypeChecker();
const diagnostics = getDiagnosticOrder(service.getDiagnostics(fileName)).filter((d) => this.isErrorDiagnostic(d));
const diagnostics = service.getDiagnostics(fileName).filter((d) => this.isErrorDiagnostic(d));
return diagnostics.reduce((acc, diagnostic) => {

@@ -57,0 +62,0 @@ const location = getLocation(diagnostic.file, diagnostic.start, diagnostic.length);

@@ -11,3 +11,3 @@ import { Plugin } from '@rehearsal/service';

*/
export declare function isPrettierUsedForFormatting(fileName: string): boolean;
export declare function isPrettierUsedForFormatting(fileName: string): Promise<boolean>;
//# sourceMappingURL=prettier.plugin.d.ts.map

@@ -13,5 +13,5 @@ import { Plugin } from '@rehearsal/service';

try {
const prettierOptions = prettier.resolveConfig.sync(fileName) || {};
const prettierOptions = (await prettier.resolveConfig(fileName)) || {};
prettierOptions.filepath = fileName;
const result = prettier.format(text, prettierOptions);
const result = await prettier.format(text, prettierOptions);
DEBUG_CALLBACK(`Plugin 'Prettier' run on %O:`, fileName);

@@ -30,6 +30,6 @@ context.service.setFileText(fileName, result);

*/
export function isPrettierUsedForFormatting(fileName) {
export async function isPrettierUsedForFormatting(fileName) {
// TODO: Better validation can be implemented
return prettier.resolveConfigFile.sync(fileName) !== null;
return (await prettier.resolveConfigFile(fileName)) !== null;
}
//# sourceMappingURL=prettier.plugin.js.map

@@ -10,7 +10,3 @@ import { PluginOptions, Plugin } from '@rehearsal/service';

run(): Promise<string[]>;
getBoundaryOfCommentBlock(start: number, length: number, text: string): {
start: number;
end: number;
};
}
//# sourceMappingURL=re-rehearse.plugin.d.ts.map

@@ -1,6 +0,5 @@

import ts from 'typescript';
import { Plugin } from '@rehearsal/service';
import debug from 'debug';
import { getBoundaryOfCommentBlock } from './utils.js';
const DEBUG_CALLBACK = debug('rehearsal:plugins:rerehearse');
const { isLineBreak } = ts;
/**

@@ -12,2 +11,3 @@ * Removes all comments with `@rehearsal` tag inside

const { fileName, context, options } = this;
DEBUG_CALLBACK(`this: %O`, this);
let text = context.service.getFileText(fileName);

@@ -25,4 +25,4 @@ const sourceFile = context.service.getSourceFile(fileName);

// Remove comment, together with the {} that wraps around comments in React
const boundary = this.getBoundaryOfCommentBlock(commentSpan.start, commentSpan.length, text);
text = text.substring(0, boundary.start) + text.substring(boundary.end);
const boundary = getBoundaryOfCommentBlock(commentSpan.start, commentSpan.length, text);
text = text.substring(0, boundary.start) + text.substring(boundary.end + 1);
}

@@ -33,13 +33,3 @@ context.service.setFileText(fileName, text);

}
getBoundaryOfCommentBlock(start, length, text) {
const newStart = start - 1 >= 0 && text[start - 1] === '{' ? start - 1 : start;
let end = start + length - 1;
end = end + 1 < text.length && text[end + 1] === '}' ? end + 1 : end;
end = isLineBreak(text.charCodeAt(end + 1)) ? end + 1 : end;
return {
start: newStart,
end,
};
}
}
//# sourceMappingURL=re-rehearse.plugin.js.map

@@ -34,2 +34,3 @@ import { Plugin, PluginOptions } from '@rehearsal/service';

toClassName(str: string): string;
hasServiceInMap(serviceName: string): boolean;
/**

@@ -36,0 +37,0 @@ * Search for service in the services map using camelCase and kebab-case as a key

@@ -61,15 +61,15 @@ import { extname } from 'node:path';

let serviceModule;
if (this.isFullyQualifiedService(qualifiedService)) {
if (this.hasServiceInMap(qualifiedService)) {
// Strategy: Use services map
DEBUG_CALLBACK(`Found service in map ${qualifiedService}`);
serviceModule = this.findServiceInMap(qualifiedService);
const [, serviceName] = this.parseFullyQualifiedService(qualifiedService);
serviceClass = this.toClassName(serviceName);
}
else if (this.isFullyQualifiedService(qualifiedService)) {
// Strategy: Ember fully qualified service names: `addon@service`
const [addon, serviceName] = this.parseFullyQualifiedService(qualifiedService);
const [packageName, serviceName] = this.parseFullyQualifiedService(qualifiedService);
serviceClass = this.toClassName(serviceName);
serviceModule = `${addon}/services/${this.toKebabCase(serviceName)}`;
serviceModule = `${packageName}/services/${this.toKebabCase(serviceName)}`;
}
else {
// Strategy: Use services map
serviceModule = this.findServiceInMap(qualifiedService);
if (serviceModule) {
serviceClass = this.toClassName(qualifiedService);
}
}
if (!serviceClass || !serviceModule) {

@@ -161,16 +161,11 @@ continue;

getQualifiedServiceName(decorator, prop) {
if (ts.isCallExpression(decorator.expression)) {
// Covers `@service('service-name')` and `@service('addon@service-name')`
if (decorator.expression.arguments.length) {
const arg = decorator.expression.arguments[0];
return ts.isStringLiteral(arg) ? arg.text : undefined;
}
// Covers `@service('service-name')` and `@service('addon@service-name')`
if (ts.isCallExpression(decorator.expression) && decorator.expression.arguments.length) {
const arg = decorator.expression.arguments[0];
return ts.isStringLiteral(arg) ? arg.text : undefined;
}
else {
// Covers `@service serviceName`
if (ts.isPropertyDeclaration(prop) && ts.isIdentifier(prop.name)) {
return this.toKebabCase(prop.name.escapedText.toString());
}
// Covers `@service serviceName` and `@service() serviceName`
if (ts.isPropertyDeclaration(prop) && ts.isIdentifier(prop.name)) {
return this.toKebabCase(prop.name.escapedText.toString());
}
// Covers @service() propName
return undefined;

@@ -185,3 +180,7 @@ }

parseFullyQualifiedService(service) {
return service.split('@');
if (!this.isFullyQualifiedService(service)) {
return ['', service];
}
const idx = service.lastIndexOf('@');
return [service.substring(0, idx), service.substring(idx + 1)];
}

@@ -207,2 +206,5 @@ /**

}
hasServiceInMap(serviceName) {
return this.findServiceInMap(serviceName) !== undefined;
}
/**

@@ -215,2 +217,5 @@ * Search for service in the services map using camelCase and kebab-case as a key

}
if (this.options.servicesMap.has(serviceName)) {
return this.options.servicesMap.get(serviceName);
}
const serviceNameKebab = this.toKebabCase(serviceName);

@@ -217,0 +222,0 @@ if (this.options.servicesMap.has(serviceNameKebab)) {

@@ -17,2 +17,6 @@ import ts from 'typescript';

export declare function inJsxText(sourceFile: ts.SourceFile, pos: number): boolean;
export declare function getBoundaryOfCommentBlock(start: number, length: number, text: string): {
start: number;
end: number;
};
//# sourceMappingURL=utils.d.ts.map

@@ -90,2 +90,12 @@ import ts from 'typescript';

}
export function getBoundaryOfCommentBlock(start, length, text) {
const newStart = start - 1 >= 0 && text[start - 1] === '{' ? start - 1 : start;
let end = start + length - 1;
end = end + 1 < text.length && text[end + 1] === '}' ? end + 1 : end;
end = ts.isLineBreak(text.charCodeAt(end + 1)) ? end + 1 : end;
return {
start: newStart,
end,
};
}
//# sourceMappingURL=utils.js.map
{
"name": "@rehearsal/plugins",
"version": "2.0.2-beta",
"version": "2.1.0",
"description": "Rehearsal JavaScript to TypeScript Shared Libraries",

@@ -36,13 +36,13 @@ "keywords": [

"object-hash": "^3.0.0",
"prettier": "^2.8.7",
"vscode-languageserver": "^8.1.0",
"@rehearsal/codefixes": "2.0.2-beta",
"@rehearsal/reporter": "2.0.2-beta",
"@rehearsal/service": "2.0.2-beta",
"@rehearsal/ts-utils": "2.0.2-beta"
"@rehearsal/reporter": "2.1.0",
"@rehearsal/codefixes": "2.1.0",
"@rehearsal/ts-utils": "2.1.0",
"@rehearsal/service": "2.1.0"
},
"devDependencies": {
"@types/eslint": "^8.37.0",
"@vitest/coverage-c8": "^0.30.1",
"@vitest/coverage-c8": "^0.33.0",
"fixturify-project": "^5.2.0",
"prettier": "^3.0.2",
"vitest": "^0.30.0"

@@ -52,5 +52,5 @@ },

"@glint/core": "^1.0.2",
"typescript": "^5.0"
"typescript": "^5.1"
},
"packageManager": "pnpm@8.2.0",
"packageManager": "pnpm@8.6.7",
"engines": {

@@ -68,3 +68,2 @@ "node": ">=14.16.0"

"test": "vitest --run --config ./vitest.config.ts --coverage",
"test:slow": "vitest --run",
"test:watch": "vitest --coverage --watch",

@@ -71,0 +70,0 @@ "version": "pnpm version"

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

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