"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; } }