📄 createLinter¶
📊 Analysis Summary¶
| Metric | Count |
|---|---|
| 🔧 Functions | 8 |
| 📦 Imports | 19 |
| 📐 Interfaces | 1 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/website/src/components/linter/createLinter.ts
📦 Imports¶
| Name | Source |
|---|---|
JSONSchema |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
FlatConfig |
@typescript-eslint/utils/ts-eslint |
Linter |
@typescript-eslint/utils/ts-eslint |
SourceType |
@typescript-eslint/utils/ts-eslint |
ErrorGroup |
../../../../website/src/components/types |
TabType |
../../../../website/src/components/types |
LinterOnLint |
./types |
LinterOnParse |
./types |
PlaygroundSystem |
./types |
RegisterFile |
./types |
WebLinterModule |
./types |
createCompilerOptions |
../lib/createCompilerOptions |
createEventsBinder |
../lib/createEventsBinder |
parseESLintRC |
../lib/parseConfig |
parseTSConfig |
../lib/parseConfig |
fileTypes |
../options |
defaultEslintLanguageConfig |
./config |
createParser |
./createParser |
Functions¶
createLinter(system: PlaygroundSystem, webLinterModule: WebLinterModule, vfs: typeof tsvfs, onMarkersChange: React.Dispatch< React.SetStateAction<Re…): CreateLinter¶
Parameters:
systemPlaygroundSystemwebLinterModuleWebLinterModulevfstypeof tsvfsonMarkersChange`React.Dispatch< React.SetStateAction> `
Returns: CreateLinter
Calls:
Object.entriescreateParser (from ./createParser)onParse.triggerfileTypes.mapcreateEventsBinder (from ../lib/createEventsBinder)webLinterModule.createLinterObject.entries(webLinterModule.plugin.rules).forEachrules.set[...webLinterModule.builtinRules.entries()].forEachwebLinterModule.builtinRules.entriesconsole.infosystem.readFilelinter.verifyonLint.triggerStringlinter.verifyAndFixparseESLintRC (from ../lib/parseConfig)configs.getArray.isArrayeslintExtendedConfig.pushconsole.logconsole.errorparseTSConfig (from ../lib/parseConfig)createCompilerOptions (from ../lib/createCompilerOptions)parser.updateConfige.message .trim() .split('\n') .maponMarkersChangeObject.fromEntriesprev.tsconfig.mapObject.valuesparser.registerFiletriggerLintAllsystem.searchFiles('/input.*').forEachsystem.watchFileapplyEslintConfigapplyTSConfigconfigs.keys
Internal Comments:
Code
export function createLinter(
system: PlaygroundSystem,
webLinterModule: WebLinterModule,
vfs: typeof tsvfs,
onMarkersChange: React.Dispatch<
React.SetStateAction<Record<TabType, ErrorGroup[]>>
>,
): CreateLinter {
const rules: CreateLinter['rules'] = new Map();
const configs = new Map(Object.entries(webLinterModule.configs));
let compilerOptions: ts.CompilerOptions = {};
const parser = createParser(
system,
compilerOptions,
(filename, model): void => {
onParse.trigger(filename, model);
},
webLinterModule,
vfs,
);
const eslintLanguageConfig: FlatConfig.Config = {
files: fileTypes.map(extension => `**/*${extension}`),
languageOptions: {
...defaultEslintLanguageConfig,
parser,
parserOptions: { ...defaultEslintLanguageConfig.parserOptions },
},
plugins: {
'@typescript-eslint': webLinterModule.plugin,
},
};
const eslintExtendedConfig: FlatConfig.ConfigArray = [];
const eslintRulesConfig: FlatConfig.Config = {
rules: {},
};
const onLint = createEventsBinder<LinterOnLint>();
const onParse = createEventsBinder<LinterOnParse>();
const linter = webLinterModule.createLinter();
Object.entries(webLinterModule.plugin.rules).forEach(([name, item]) => {
rules.set(`@typescript-eslint/${name}`, {
description: item.meta.docs?.description,
name: `@typescript-eslint/${name}`,
schema: item.meta.schema,
url: item.meta.docs?.url,
});
});
[...webLinterModule.builtinRules.entries()].forEach(([name, item]) => {
rules.set(name, {
description: item.meta.docs?.description,
name,
schema: item.meta.schema,
url: item.meta.docs?.url,
});
});
const triggerLint = (filename: string): void => {
console.info('[Editor] linting triggered for file', filename);
const code = system.readFile(filename) ?? '\n';
try {
const messages = linter.verify(
code,
[eslintLanguageConfig, ...eslintExtendedConfig, eslintRulesConfig],
filename,
);
onLint.trigger(filename, messages);
} catch (e) {
const lintMessage: Linter.LintMessage = {
column: 1,
line: 1,
message: String(e instanceof Error ? `${e.message}\n\n${e.stack}` : e),
ruleId: '',
severity: 2,
source: 'eslint',
};
if (typeof e === 'object' && e && 'currentNode' in e) {
const node = e.currentNode as TSESTree.Node;
lintMessage.column = node.loc.start.column + 1;
lintMessage.line = node.loc.start.line;
lintMessage.endColumn = node.loc.end.column + 1;
lintMessage.endLine = node.loc.end.line;
}
onLint.trigger(filename, [lintMessage]);
}
};
const triggerFix = (filename: string): Linter.FixReport | undefined => {
const code = system.readFile(filename);
if (code) {
return linter.verifyAndFix(
code,
[eslintLanguageConfig, ...eslintExtendedConfig, eslintRulesConfig],
{
filename,
fix: true,
},
);
}
return undefined;
};
const updateParserOptions = (sourceType?: SourceType): void => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
eslintLanguageConfig.languageOptions!.parserOptions!.sourceType =
sourceType ?? 'module';
};
const applyEslintConfig = (fileName: string): void => {
try {
const file = system.readFile(fileName) ?? '{}';
const parsed = parseESLintRC(file);
eslintRulesConfig.rules = parsed.rules;
for (const extendsName of parsed.extends) {
const maybeConfig = configs.get(extendsName);
if (maybeConfig) {
if (Array.isArray(maybeConfig)) {
eslintExtendedConfig.push(...maybeConfig);
} else {
eslintExtendedConfig.push(maybeConfig);
}
}
}
console.log('[Editor] Updating', fileName, [
eslintLanguageConfig,
...eslintExtendedConfig,
eslintRulesConfig,
]);
} catch (e) {
console.error(e);
}
};
const applyTSConfig = (fileName: string): void => {
let error: ErrorGroup | null = null;
try {
const file = system.readFile(fileName) ?? '{}';
const parsed = parseTSConfig(file).compilerOptions;
compilerOptions = createCompilerOptions(parsed);
console.log('[Editor] Updating', fileName, compilerOptions);
parser.updateConfig(compilerOptions);
} catch (e) {
if (e instanceof Error) {
error = {
group: 'TypeScript',
items: e.message
.trim()
.split('\n')
.map((message: string) => {
return {
message,
severity: 8, // MarkerSeverity.Error
};
}),
uri: undefined,
};
}
} finally {
onMarkersChange(prev => {
const activeTabErrors = Object.fromEntries(
prev.tsconfig.map(error => [error.group, error]),
);
if (error) {
activeTabErrors.TypeScript = error;
} else {
delete activeTabErrors.TypeScript;
}
return {
...prev,
tsconfig: Object.values(activeTabErrors),
};
});
}
};
const registerFile = (fileName: string, code: string) => {
parser.registerFile(fileName, code);
triggerLintAll();
};
const triggerLintAll = (): void => {
system.searchFiles('/input.*').forEach(triggerLint);
};
system.watchFile('/input.*', triggerLint);
system.watchFile('/.eslintrc', filename => {
applyEslintConfig(filename);
triggerLintAll();
});
system.watchFile('/tsconfig.json', filename => {
applyTSConfig(filename);
triggerLintAll();
});
applyEslintConfig('/.eslintrc');
applyTSConfig('/tsconfig.json');
return {
configs: [...configs.keys()],
onLint: onLint.register,
onParse: onParse.register,
registerFile,
rules,
triggerFix,
triggerLint,
updateParserOptions,
};
}
Internal helpers¶
Declared inside another function in this file.
triggerLint(filename: string): void¶
Parameters:
filenamestring
Returns: void
Calls:
console.infosystem.readFilelinter.verifyonLint.triggerString
Code
(filename: string): void => {
console.info('[Editor] linting triggered for file', filename);
const code = system.readFile(filename) ?? '\n';
try {
const messages = linter.verify(
code,
[eslintLanguageConfig, ...eslintExtendedConfig, eslintRulesConfig],
filename,
);
onLint.trigger(filename, messages);
} catch (e) {
const lintMessage: Linter.LintMessage = {
column: 1,
line: 1,
message: String(e instanceof Error ? `${e.message}\n\n${e.stack}` : e),
ruleId: '',
severity: 2,
source: 'eslint',
};
if (typeof e === 'object' && e && 'currentNode' in e) {
const node = e.currentNode as TSESTree.Node;
lintMessage.column = node.loc.start.column + 1;
lintMessage.line = node.loc.start.line;
lintMessage.endColumn = node.loc.end.column + 1;
lintMessage.endLine = node.loc.end.line;
}
onLint.trigger(filename, [lintMessage]);
}
}
triggerFix(filename: string): Linter.FixReport | undefined¶
Parameters:
filenamestring
Returns: Linter.FixReport | undefined
Calls:
system.readFilelinter.verifyAndFix
Code
updateParserOptions(sourceType: SourceType): void¶
Parameters:
sourceTypeSourceType
Returns: void
Internal Comments:
Code
applyEslintConfig(fileName: string): void¶
Parameters:
fileNamestring
Returns: void
Calls:
system.readFileparseESLintRC (from ../lib/parseConfig)configs.getArray.isArrayeslintExtendedConfig.pushconsole.logconsole.error
Code
(fileName: string): void => {
try {
const file = system.readFile(fileName) ?? '{}';
const parsed = parseESLintRC(file);
eslintRulesConfig.rules = parsed.rules;
for (const extendsName of parsed.extends) {
const maybeConfig = configs.get(extendsName);
if (maybeConfig) {
if (Array.isArray(maybeConfig)) {
eslintExtendedConfig.push(...maybeConfig);
} else {
eslintExtendedConfig.push(maybeConfig);
}
}
}
console.log('[Editor] Updating', fileName, [
eslintLanguageConfig,
...eslintExtendedConfig,
eslintRulesConfig,
]);
} catch (e) {
console.error(e);
}
}
applyTSConfig(fileName: string): void¶
Parameters:
fileNamestring
Returns: void
Calls:
system.readFileparseTSConfig (from ../lib/parseConfig)createCompilerOptions (from ../lib/createCompilerOptions)console.logparser.updateConfige.message .trim() .split('\n') .maponMarkersChangeObject.fromEntriesprev.tsconfig.mapObject.values
Code
(fileName: string): void => {
let error: ErrorGroup | null = null;
try {
const file = system.readFile(fileName) ?? '{}';
const parsed = parseTSConfig(file).compilerOptions;
compilerOptions = createCompilerOptions(parsed);
console.log('[Editor] Updating', fileName, compilerOptions);
parser.updateConfig(compilerOptions);
} catch (e) {
if (e instanceof Error) {
error = {
group: 'TypeScript',
items: e.message
.trim()
.split('\n')
.map((message: string) => {
return {
message,
severity: 8, // MarkerSeverity.Error
};
}),
uri: undefined,
};
}
} finally {
onMarkersChange(prev => {
const activeTabErrors = Object.fromEntries(
prev.tsconfig.map(error => [error.group, error]),
);
if (error) {
activeTabErrors.TypeScript = error;
} else {
delete activeTabErrors.TypeScript;
}
return {
...prev,
tsconfig: Object.values(activeTabErrors),
};
});
}
}
registerFile(fileName: string, code: string): void¶
Parameters:
fileNamestringcodestring
Returns: void
Calls:
parser.registerFiletriggerLintAll
triggerLintAll(): void¶
Returns: void
Calls:
system.searchFiles('/input.*').forEach
Interfaces¶
CreateLinter¶
Interface Code
export interface CreateLinter {
configs: string[];
onLint(cb: LinterOnLint): () => void;
onParse(cb: LinterOnParse): () => void;
rules: Map<
string,
{
description?: string;
name: string;
schema: JSONSchema.JSONSchema4 | readonly JSONSchema.JSONSchema4[];
url?: string;
}
>;
triggerFix(filename: string): Linter.FixReport | undefined;
triggerLint(filename: string): void;
updateParserOptions(sourceType?: SourceType): void;
registerFile: RegisterFile;
}
Properties¶
| Name | Type | Optional | Description |
|---|---|---|---|
configs |
string[] |
✗ | not shown |
rules |
Map< string, { description?: string; name: string; schema: JSONSchema.JSONSch... |
✗ | not shown |
registerFile |
RegisterFile |
✗ | not shown |
Generated by Syntax Scribe