UNPKG

@html-validate/plugin-utils

Version:

Plugin utilities and helpers for writing plugins to HTML-Validate

183 lines (180 loc) 5.15 kB
// src/position-from-offset.ts function positionFromOffset(text, offset) { let line = 1; let prev = 0; let pos = text.indexOf("\n"); while (pos !== -1) { if (pos >= offset) { return [line, offset - prev + 1]; } line++; prev = pos + 1; pos = text.indexOf("\n", pos + 1); } return [line, offset - prev + 1]; } // src/position-to-offset.ts function positionToOffset(position, data) { let line = position.line; let column = position.column + 1; for (let i = 0; i < data.length; i++) { if (line > 1) { if (data[i] === "\n") { line--; } } else if (column > 1) { column--; } else { return i; } } throw new Error("Failed to compute location offset from position"); } // src/template-extractor.ts import * as walk from "acorn-walk"; import * as espree from "espree"; function joinTemplateLiteral(nodes) { let offset = nodes[0].start + 1; let output = ""; for (const node of nodes) { output += " ".repeat(node.start + 1 - offset); output += node.value.raw; offset = node.end - 2; } return output; } function extractLiteral(node, filename, data) { switch (node.type) { /* ignored nodes */ case "FunctionExpression": case "Identifier": return null; case "Literal": if (typeof node.value !== "string") { return null; } return { data: node.value, filename, line: node.loc.start.line, column: node.loc.start.column + 1, offset: positionToOffset(node.loc.start, data) + 1 }; case "TemplateLiteral": return { data: joinTemplateLiteral(node.quasis), filename, line: node.loc.start.line, column: node.loc.start.column + 1, offset: positionToOffset(node.loc.start, data) + 1 }; case "TaggedTemplateExpression": return { data: joinTemplateLiteral(node.quasi.quasis), filename, line: node.quasi.loc.start.line, column: node.quasi.loc.start.column + 1, offset: positionToOffset(node.quasi.loc.start, data) + 1 }; case "ArrowFunctionExpression": { const whitelist = ["Literal", "TemplateLiteral"]; if (whitelist.includes(node.body.type)) { return extractLiteral(node.body, filename, data); } else { return null; } } /* istanbul ignore next: this only provides a better error, all currently known nodes are tested */ default: { const loc = node.loc.start; const line = String(loc.line); const column = String(loc.column); const context = `${filename}:${line}:${column}`; throw new Error(`Unhandled node type "${node.type}" at "${context}" in extractLiteral`); } } } function compareKey(node, key, filename) { switch (node.type) { case "Identifier": return node.name === key; case "Literal": return node.value === key; /* istanbul ignore next: this only provides a better error, all currently known nodes are tested */ default: { const loc = node.loc.start; const line = String(loc.line); const column = String(loc.column); const context = `${filename}:${line}:${column}`; throw new Error(`Unhandled node type "${node.type}" at "${context}" in compareKey`); } } } var TemplateExtractor = class _TemplateExtractor { ast; filename; data; constructor(ast, filename, data) { this.ast = ast; this.filename = filename; this.data = data; } /** * Create a new [[TemplateExtractor]] from javascript source code. * * `Source` offsets will be relative to the string, i.e. offset 0 is the first * character of the string. If the string is only a subset of a larger string * the offsets must be adjusted manually. * * @param source - Source code. * @param filename - Optional filename to set in the resulting * `Source`. Defauls to `"inline"`. */ static fromString(source, filename) { const ast = espree.parse(source, { ecmaVersion: "latest", sourceType: "module", loc: true }); return new _TemplateExtractor(ast, filename ?? "inline", source); } /** * Extract object properties. * * Given a key `"template"` this method finds all objects literals with a * `"template"` property and creates a [[Source]] instance with proper offsets * with the value of the property. For instance: * * ``` * const myObj = { * foo: 'bar', * }; * ``` * * The above snippet would yield a `Source` with the content `bar`. * */ extractObjectProperty(key) { const result = []; const { filename, data } = this; const node = this.ast; walk.simple(node, { Property(node2) { if (compareKey(node2.key, key, filename)) { const source = extractLiteral(node2.value, filename, data); if (source) { source.filename = filename; result.push(source); } } } }); return result; } }; export { TemplateExtractor, positionFromOffset, positionToOffset }; //# sourceMappingURL=index.mjs.map