You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
507 lines
19 KiB
JavaScript
507 lines
19 KiB
JavaScript
10 months ago
|
"use strict";
|
||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||
|
if (k2 === undefined) k2 = k;
|
||
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
||
|
}
|
||
|
Object.defineProperty(o, k2, desc);
|
||
|
}) : (function(o, m, k, k2) {
|
||
|
if (k2 === undefined) k2 = k;
|
||
|
o[k2] = m[k];
|
||
|
}));
|
||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||
|
}) : function(o, v) {
|
||
|
o["default"] = v;
|
||
|
});
|
||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||
|
if (mod && mod.__esModule) return mod;
|
||
|
var result = {};
|
||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||
|
__setModuleDefault(result, mod);
|
||
|
return result;
|
||
|
};
|
||
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||
|
};
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.getSvelteCompileWarnings = void 0;
|
||
|
const compiler = __importStar(require("svelte/compiler"));
|
||
|
const sourcemap_codec_1 = require("@jridgewell/sourcemap-codec");
|
||
|
const lines_and_columns_1 = require("../../utils/lines-and-columns");
|
||
|
const typescript_1 = require("./transform/typescript");
|
||
|
const babel_1 = require("./transform/babel");
|
||
|
const postcss_1 = require("./transform/postcss");
|
||
|
const sass_1 = require("./transform/sass");
|
||
|
const less_1 = require("./transform/less");
|
||
|
const stylus_1 = require("./transform/stylus");
|
||
|
const ignore_comment_1 = require("./ignore-comment");
|
||
|
const extract_leading_comments_1 = require("./extract-leading-comments");
|
||
|
const ast_utils_1 = require("../../utils/ast-utils");
|
||
|
const path_1 = __importDefault(require("path"));
|
||
|
const fs_1 = __importDefault(require("fs"));
|
||
|
const semver_1 = __importDefault(require("semver"));
|
||
|
const compat_1 = require("../../utils/compat");
|
||
|
const STYLE_TRANSFORMS = {
|
||
|
postcss: postcss_1.transform,
|
||
|
pcss: postcss_1.transform,
|
||
|
scss: (node, text, context) => (0, sass_1.transform)(node, text, context, 'scss'),
|
||
|
sass: (node, text, context) => (0, sass_1.transform)(node, text, context, 'sass'),
|
||
|
less: less_1.transform,
|
||
|
stylus: stylus_1.transform,
|
||
|
styl: stylus_1.transform
|
||
|
};
|
||
|
const CSS_WARN_CODES = new Set([
|
||
|
'css-unused-selector',
|
||
|
'css-invalid-global',
|
||
|
'css-invalid-global-selector'
|
||
|
]);
|
||
|
const cacheAll = new WeakMap();
|
||
|
function getSvelteCompileWarnings(context) {
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const cache = cacheAll.get(sourceCode.ast);
|
||
|
if (cache) {
|
||
|
return cache;
|
||
|
}
|
||
|
const result = getSvelteCompileWarningsWithoutCache(context);
|
||
|
cacheAll.set(sourceCode.ast, result);
|
||
|
return result;
|
||
|
}
|
||
|
exports.getSvelteCompileWarnings = getSvelteCompileWarnings;
|
||
|
function getSvelteCompileWarningsWithoutCache(context) {
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const styleElementsWithNotCSS = [...extractStyleElementsWithLangOtherThanCSS(context)];
|
||
|
const stripStyleElements = [];
|
||
|
const transformResults = [];
|
||
|
for (const style of styleElementsWithNotCSS) {
|
||
|
const transform = STYLE_TRANSFORMS[style.lang];
|
||
|
if (transform) {
|
||
|
const result = transform(style.node, (0, compat_1.getSourceCode)(context).text, context);
|
||
|
if (result) {
|
||
|
transformResults.push(result);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
stripStyleElements.push(style.node);
|
||
|
}
|
||
|
const stripStyleTokens = stripStyleElements.flatMap((e) => e.children);
|
||
|
const ignoreComments = (0, ignore_comment_1.getSvelteIgnoreItems)(context).filter((item) => item.code != null);
|
||
|
const text = buildStrippedText(context, ignoreComments, stripStyleTokens);
|
||
|
transformResults.push(...transformScripts(context, text));
|
||
|
if (!transformResults.length) {
|
||
|
const warnings = getWarningsFromCode(text, context);
|
||
|
return {
|
||
|
...processIgnore(warnings.warnings, warnings.kind, stripStyleElements, ignoreComments, context),
|
||
|
kind: warnings.kind,
|
||
|
stripStyleElements
|
||
|
};
|
||
|
}
|
||
|
class RemapContext {
|
||
|
constructor() {
|
||
|
this.originalStart = 0;
|
||
|
this.code = '';
|
||
|
this.locs = null;
|
||
|
this.mapIndexes = [];
|
||
|
}
|
||
|
appendOriginal(endIndex) {
|
||
|
const codeStart = this.code.length;
|
||
|
const start = this.originalStart;
|
||
|
this.code += text.slice(start, endIndex);
|
||
|
this.originalStart = endIndex;
|
||
|
const offset = start - codeStart;
|
||
|
this.mapIndexes.push({
|
||
|
range: [codeStart, this.code.length],
|
||
|
remap(index) {
|
||
|
return index + offset;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
postprocess() {
|
||
|
this.appendOriginal(text.length);
|
||
|
return this.code;
|
||
|
}
|
||
|
appendTranspile(output) {
|
||
|
const endIndex = output.inputRange[1];
|
||
|
const codeStart = this.code.length;
|
||
|
const start = this.originalStart;
|
||
|
const inputText = text.slice(start, endIndex);
|
||
|
const outputText = `${output.output}\n`;
|
||
|
this.code += outputText;
|
||
|
this.originalStart = endIndex;
|
||
|
let outputLocs = null;
|
||
|
let inputLocs = null;
|
||
|
let decoded = null;
|
||
|
this.mapIndexes.push({
|
||
|
range: [codeStart, this.code.length],
|
||
|
remap: (index) => {
|
||
|
outputLocs = outputLocs ?? new lines_and_columns_1.LinesAndColumns(outputText);
|
||
|
inputLocs = inputLocs ?? new lines_and_columns_1.LinesAndColumns(inputText);
|
||
|
const outputCodePos = outputLocs.getLocFromIndex(index - codeStart);
|
||
|
const inputCodePos = remapPosition(outputCodePos);
|
||
|
return inputLocs.getIndexFromLoc(inputCodePos) + start;
|
||
|
}
|
||
|
});
|
||
|
function remapPosition(pos) {
|
||
|
decoded = decoded ?? (0, sourcemap_codec_1.decode)(output.mappings);
|
||
|
const lineMaps = decoded[pos.line - 1];
|
||
|
if (!lineMaps?.length) {
|
||
|
for (let line = pos.line - 1; line >= 0; line--) {
|
||
|
const prevLineMaps = decoded[line];
|
||
|
if (prevLineMaps?.length) {
|
||
|
const [, , sourceCodeLine, sourceCodeColumn] = prevLineMaps[prevLineMaps.length - 1];
|
||
|
return {
|
||
|
line: sourceCodeLine + 1,
|
||
|
column: sourceCodeColumn
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
return { line: -1, column: -1 };
|
||
|
}
|
||
|
for (let index = 0; index < lineMaps.length - 1; index++) {
|
||
|
const [generateCodeColumn, , sourceCodeLine, sourceCodeColumn] = lineMaps[index];
|
||
|
if (generateCodeColumn <= pos.column && pos.column < lineMaps[index + 1][0]) {
|
||
|
return {
|
||
|
line: sourceCodeLine + 1,
|
||
|
column: sourceCodeColumn + (pos.column - generateCodeColumn)
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
const [generateCodeColumn, , sourceCodeLine, sourceCodeColumn] = lineMaps[lineMaps.length - 1];
|
||
|
return {
|
||
|
line: sourceCodeLine + 1,
|
||
|
column: sourceCodeColumn + (pos.column - generateCodeColumn)
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
remapLocs(points) {
|
||
|
const mapIndexes = this.mapIndexes;
|
||
|
const locs = (this.locs = this.locs ?? new lines_and_columns_1.LinesAndColumns(this.code));
|
||
|
let start = undefined;
|
||
|
let end = undefined;
|
||
|
if (points.start) {
|
||
|
const index = locs.getIndexFromLoc(points.start);
|
||
|
const remapped = remapIndex(index);
|
||
|
if (remapped) {
|
||
|
start = sourceCode.getLocFromIndex(remapped);
|
||
|
}
|
||
|
}
|
||
|
if (points.end) {
|
||
|
const index = locs.getIndexFromLoc(points.end);
|
||
|
const remapped = remapIndex(index - 1);
|
||
|
if (remapped) {
|
||
|
end = sourceCode.getLocFromIndex(remapped + 1);
|
||
|
}
|
||
|
}
|
||
|
return { start, end };
|
||
|
function remapIndex(index) {
|
||
|
for (const mapIndex of mapIndexes) {
|
||
|
if (mapIndex.range[0] <= index && index < mapIndex.range[1]) {
|
||
|
return mapIndex.remap(index);
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const remapContext = new RemapContext();
|
||
|
for (const result of transformResults.sort((a, b) => a.inputRange[0] - b.inputRange[0])) {
|
||
|
remapContext.appendOriginal(result.inputRange[0]);
|
||
|
remapContext.appendTranspile(result);
|
||
|
}
|
||
|
const code = remapContext.postprocess();
|
||
|
const baseWarnings = getWarningsFromCode(code, context);
|
||
|
const warnings = [];
|
||
|
for (const warn of baseWarnings.warnings) {
|
||
|
let loc = null;
|
||
|
const getLoc = function getLoc() {
|
||
|
if (loc) {
|
||
|
return loc;
|
||
|
}
|
||
|
return (loc = remapContext.remapLocs(warn));
|
||
|
};
|
||
|
warnings.push({
|
||
|
code: warn.code,
|
||
|
message: warn.message,
|
||
|
get start() {
|
||
|
return getLoc().start;
|
||
|
},
|
||
|
get end() {
|
||
|
return getLoc().end;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
return {
|
||
|
...processIgnore(warnings, baseWarnings.kind, stripStyleElements, ignoreComments, context),
|
||
|
kind: baseWarnings.kind,
|
||
|
stripStyleElements
|
||
|
};
|
||
|
}
|
||
|
function* extractStyleElementsWithLangOtherThanCSS(context) {
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const root = sourceCode.ast;
|
||
|
for (const node of root.body) {
|
||
|
if (node.type === 'SvelteStyleElement') {
|
||
|
const lang = (0, ast_utils_1.getLangValue)(node);
|
||
|
if (lang != null && lang.toLowerCase() !== 'css') {
|
||
|
yield { node, lang: lang.toLowerCase() };
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function buildStrippedText(context, ignoreComments, stripStyleTokens) {
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const baseText = sourceCode.text;
|
||
|
const stripTokens = new Set([...ignoreComments.map((item) => item.token), ...stripStyleTokens]);
|
||
|
if (!stripTokens.size) {
|
||
|
return baseText;
|
||
|
}
|
||
|
let code = '';
|
||
|
let start = 0;
|
||
|
for (const token of [...stripTokens].sort((a, b) => a.range[0] - b.range[0])) {
|
||
|
code +=
|
||
|
baseText.slice(start, token.range[0]) +
|
||
|
baseText.slice(...token.range).replace(/[^\t\n\r ]/g, ' ');
|
||
|
start = token.range[1];
|
||
|
}
|
||
|
code += baseText.slice(start);
|
||
|
return code;
|
||
|
}
|
||
|
function* transformScripts(context, text) {
|
||
|
const transform = isUseTypeScript(context)
|
||
|
? (0, typescript_1.hasTypeScript)(context)
|
||
|
? typescript_1.transform
|
||
|
: babel_1.transform
|
||
|
: isUseBabel(context)
|
||
|
? babel_1.transform
|
||
|
: null;
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
if (transform) {
|
||
|
const root = sourceCode.ast;
|
||
|
for (const node of root.body) {
|
||
|
if (node.type === 'SvelteScriptElement') {
|
||
|
const result = transform(node, text, context);
|
||
|
if (result) {
|
||
|
yield result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function hasTagOption(program) {
|
||
|
return program.body.some((body) => {
|
||
|
if (body.type !== 'SvelteElement' || body.kind !== 'special') {
|
||
|
return false;
|
||
|
}
|
||
|
if (body.name.name !== 'svelte:options') {
|
||
|
return false;
|
||
|
}
|
||
|
return Boolean((0, ast_utils_1.findAttribute)(body, 'tag'));
|
||
|
});
|
||
|
}
|
||
|
function getWarningsFromCode(code, context) {
|
||
|
try {
|
||
|
const result = compiler.compile(code, {
|
||
|
generate: false,
|
||
|
...(semver_1.default.satisfies(compiler.VERSION, '>=4.0.0-0')
|
||
|
? { customElement: true }
|
||
|
: hasTagOption((0, compat_1.getSourceCode)(context).ast)
|
||
|
? { customElement: true }
|
||
|
: {})
|
||
|
});
|
||
|
return { warnings: result.warnings, kind: 'warn' };
|
||
|
}
|
||
|
catch (e) {
|
||
|
return {
|
||
|
warnings: [
|
||
|
{
|
||
|
code: e.code,
|
||
|
message: e.message,
|
||
|
start: e.start,
|
||
|
end: e.end
|
||
|
}
|
||
|
],
|
||
|
kind: 'error'
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
function processIgnore(warnings, kind, stripStyleElements, ignoreComments, context) {
|
||
|
if (kind === 'error') {
|
||
|
return {
|
||
|
warnings,
|
||
|
unusedIgnores: ignoreComments
|
||
|
};
|
||
|
}
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const unusedIgnores = new Set(ignoreComments);
|
||
|
const remainingWarning = new Set(warnings);
|
||
|
for (const warning of warnings) {
|
||
|
if (!warning.code) {
|
||
|
continue;
|
||
|
}
|
||
|
let node = getWarningNode(warning);
|
||
|
while (node) {
|
||
|
for (const comment of (0, extract_leading_comments_1.extractLeadingComments)(context, node).reverse()) {
|
||
|
const ignoreItem = ignoreComments.find((item) => item.token === comment && item.code === warning.code);
|
||
|
if (ignoreItem) {
|
||
|
unusedIgnores.delete(ignoreItem);
|
||
|
remainingWarning.delete(warning);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
node = getIgnoreParent(node);
|
||
|
}
|
||
|
}
|
||
|
for (const node of stripStyleElements) {
|
||
|
for (const comment of (0, extract_leading_comments_1.extractLeadingComments)(context, node).reverse()) {
|
||
|
const ignoreItem = ignoreComments.find((item) => item.token === comment && CSS_WARN_CODES.has(item.code));
|
||
|
if (ignoreItem) {
|
||
|
unusedIgnores.delete(ignoreItem);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return {
|
||
|
warnings: [...remainingWarning],
|
||
|
unusedIgnores: [...unusedIgnores]
|
||
|
};
|
||
|
function getIgnoreParent(node) {
|
||
|
if (node.type !== 'SvelteElement' &&
|
||
|
node.type !== 'SvelteIfBlock' &&
|
||
|
node.type !== 'SvelteKeyBlock' &&
|
||
|
node.type !== 'SvelteEachBlock' &&
|
||
|
node.type !== 'SvelteAwaitBlock') {
|
||
|
return null;
|
||
|
}
|
||
|
const parent = node.parent;
|
||
|
if (parent.type === 'SvelteElseBlock') {
|
||
|
return parent.parent;
|
||
|
}
|
||
|
if (parent.type === 'SvelteAwaitPendingBlock' ||
|
||
|
parent.type === 'SvelteAwaitThenBlock' ||
|
||
|
parent.type === 'SvelteAwaitCatchBlock') {
|
||
|
return parent.parent;
|
||
|
}
|
||
|
if (parent.type !== 'SvelteElement' &&
|
||
|
parent.type !== 'SvelteIfBlock' &&
|
||
|
parent.type !== 'SvelteKeyBlock' &&
|
||
|
parent.type !== 'SvelteEachBlock') {
|
||
|
return null;
|
||
|
}
|
||
|
return parent;
|
||
|
}
|
||
|
function getWarningNode(warning) {
|
||
|
const indexes = getWarningIndexes(warning);
|
||
|
if (indexes.start != null) {
|
||
|
const node = getWarningTargetNodeFromIndex(indexes.start);
|
||
|
if (node) {
|
||
|
return node;
|
||
|
}
|
||
|
if (indexes.end != null) {
|
||
|
const center = Math.floor(indexes.start + (indexes.end - indexes.start) / 2);
|
||
|
return getWarningTargetNodeFromIndex(center);
|
||
|
}
|
||
|
}
|
||
|
if (indexes.end != null) {
|
||
|
return getWarningTargetNodeFromIndex(indexes.end);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
function getWarningTargetNodeFromIndex(index) {
|
||
|
let targetNode = sourceCode.getNodeByRangeIndex(index);
|
||
|
while (targetNode) {
|
||
|
if (targetNode.type === 'SvelteElement' || targetNode.type === 'SvelteStyleElement') {
|
||
|
return targetNode;
|
||
|
}
|
||
|
if (targetNode.parent) {
|
||
|
if (targetNode.parent.type === 'Program' ||
|
||
|
targetNode.parent.type === 'SvelteScriptElement') {
|
||
|
return targetNode;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
return null;
|
||
|
}
|
||
|
targetNode = targetNode.parent || null;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
function getWarningIndexes(warning) {
|
||
|
const start = warning.start && sourceCode.getIndexFromLoc(warning.start);
|
||
|
const end = warning.end && sourceCode.getIndexFromLoc(warning.end);
|
||
|
return { start, end };
|
||
|
}
|
||
|
}
|
||
|
function isUseTypeScript(context) {
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
if (sourceCode.parserServices.esTreeNodeToTSNodeMap)
|
||
|
return true;
|
||
|
const root = sourceCode.ast;
|
||
|
for (const node of root.body) {
|
||
|
if (node.type === 'SvelteScriptElement') {
|
||
|
const lang = (0, ast_utils_1.getLangValue)(node)?.toLowerCase();
|
||
|
if (lang === 'ts' || lang === 'typescript') {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
function isUseBabel(context) {
|
||
|
const parser = context.parserOptions?.parser;
|
||
|
if (!parser) {
|
||
|
return false;
|
||
|
}
|
||
|
const sourceCode = (0, compat_1.getSourceCode)(context);
|
||
|
const root = sourceCode.ast;
|
||
|
let scriptLang = 'js';
|
||
|
for (const node of root.body) {
|
||
|
if (node.type === 'SvelteScriptElement') {
|
||
|
const lang = (0, ast_utils_1.getLangValue)(node)?.toLowerCase();
|
||
|
if (lang === 'ts' || lang === 'typescript') {
|
||
|
scriptLang = lang;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const parserName = getParserName(scriptLang, parser);
|
||
|
if (!parserName) {
|
||
|
return false;
|
||
|
}
|
||
|
if (parserName === '@babel/eslint-parser') {
|
||
|
return true;
|
||
|
}
|
||
|
if (parserName.includes('@babel/eslint-parser')) {
|
||
|
let targetPath = parserName;
|
||
|
while (targetPath) {
|
||
|
const pkgPath = path_1.default.join(targetPath, 'package.json');
|
||
|
if (fs_1.default.existsSync(pkgPath)) {
|
||
|
try {
|
||
|
return JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'))?.name === '@babel/eslint-parser';
|
||
|
}
|
||
|
catch {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
const parent = path_1.default.dirname(targetPath);
|
||
|
if (targetPath === parent) {
|
||
|
break;
|
||
|
}
|
||
|
targetPath = parent;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
function getParserName(lang, parser) {
|
||
|
if (typeof parser === 'string') {
|
||
|
return parser;
|
||
|
}
|
||
|
else if (typeof parser === 'object') {
|
||
|
const name = parser[lang];
|
||
|
if (typeof name === 'string') {
|
||
|
return name;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|