saepequia
Version:
A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps
186 lines (152 loc) • 5.28 kB
text/typescript
import stripAnsi from 'strip-ansi';
import transform from '../transform';
import type { Replacement } from '../types';
type Errors = {
[key: string]:
| {
name?: string;
code?: string;
message: string;
pos?: number;
loc?: {
line: number;
column: number;
};
}
| null
| undefined;
};
type Cache = {
[key: string]: Replacement[] | null | undefined;
};
type Warning = {
rule?: string;
text: string;
severity: 'error' | 'warning';
line: number;
column: number;
};
type LintResult = {
errored: boolean;
warnings: Warning[];
};
function preprocessor() {
const errors: Errors = {};
const cache: Cache = {};
return {
code(input: string, filename: string) {
let result;
try {
result = transform(input, {
filename,
});
cache[filename] = undefined;
errors[filename] = undefined;
} catch (e) {
cache[filename] = undefined;
errors[filename] = e;
// Ignore parse errors here
// We handle it separately
return '';
}
const { rules, replacements } = result;
if (!rules) {
return '';
}
// Construct a CSS-ish file from the unprocessed style rules
let cssText = '';
Object.keys(rules).forEach((selector) => {
const rule = rules[selector];
// Append new lines until we get to the start line number
let line = cssText.split('\n').length;
while (rule.start && line < rule.start.line) {
cssText += '\n';
line++;
}
cssText += `.${rule.displayName} {`;
// Append blank spaces until we get to the start column number
const last = cssText.split('\n').pop();
let column = last ? last.length : 0;
while (rule.start && column < rule.start.column) {
cssText += ' ';
column++;
}
cssText += `${rule.cssText} }`;
});
cache[filename] = replacements;
return cssText;
},
result(result: LintResult, filename: string) {
const error = errors[filename];
const replacements = cache[filename];
if (error) {
// Babel adds this to the error message
const prefix = `${filename}: `;
let message = stripAnsi(
error.message.startsWith(prefix)
? error.message.replace(prefix, '')
: error.message
);
let { loc } = error;
if (!loc) {
// If the error doesn't have location info, try to find it from the code frame
const line = message.split('\n').find((l) => l.startsWith('>'));
const column = message.split('\n').find((l) => l.includes('^'));
if (line && column) {
loc = {
line: Number(line.replace(/^> /, '').split('|')[0].trim()),
column: column.replace(/[^|]+\|\s/, '').length,
};
}
}
if (loc) {
// Strip the codeframe text if we have location of the error
// It's formatted badly by stylelint, so not very helpful
message = message.replace(/^>?\s+\d?\s\|.*$/gm, '').trim();
}
// eslint-disable-next-line no-param-reassign
result.errored = true;
result.warnings.push({
rule: error.code || error.name,
text: message,
line: loc ? loc.line : 0,
column: loc ? loc.column : 0,
severity: 'error',
});
}
if (replacements) {
replacements.forEach(({ original, length }) => {
// If the warnings contain stuff that's been replaced,
// Correct the line and column numbers to what's replaced
result.warnings.forEach((w) => {
/* eslint-disable no-param-reassign */
if (w.line === original.start.line) {
// If the error is on the same line where an interpolation started, we need to adjust the line and column numbers
// Because a replacement would have increased or decreased the column numbers
// If it's in the same line where interpolation ended, it would have been adjusted during replacement
if (w.column > original.start.column + length) {
// The error is from an item after the replacements
// So we need to adjust the column
w.column +=
original.end.column - original.start.column + 1 - length;
} else if (
w.column >= original.start.column &&
w.column < original.start.column + length
) {
// The linter will underline the whole word in the editor if column is in inside a word
// Set the column to the end, so it will underline the word inside the interpolation
// e.g. in `${colors.primary}`, `primary` will be underlined
w.column =
original.start.line === original.end.line
? original.end.column - 1
: original.start.column;
}
}
});
});
}
return result;
},
};
}
module.exports = preprocessor;