From 3a9bef9a499523af0a1190bae27a080eff8f5939 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Fri, 17 Jan 2025 09:03:35 +0100 Subject: [PATCH 01/13] deps: Bump sax-wasm from 2.2.4 to 3.0.0 --- npm-shrinkwrap.json | 12 ++++++++---- package.json | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index da5a5e033..499a78d68 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "2.2.4", + "sax-wasm": "^3.0.0", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" @@ -11166,9 +11166,13 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/sax-wasm": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-2.2.4.tgz", - "integrity": "sha512-KreOdNF02zEEDUyqLZjWQTMf+ev/6gEzp2ZT8w2xvqfHO/DRPL4RBYYeiXQpYEGD+1oZ81pjc3fPG5NLDh5ICg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.0.tgz", + "integrity": "sha512-8Dn91vhGZ0LxSosVyPv4zcgZDOX0qyFxpTLlW8rb1L9kP3l0yXk5fXv3X86MPgk9hgJbajAGKuOXkhUQSTetWQ==", + "license": "MIT", + "engines": { + "node": ">=18.20.5" + } }, "node_modules/semver": { "version": "7.6.3", diff --git a/package.json b/package.json index 9598bcbe8..5895625a2 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "2.2.4", + "sax-wasm": "^3.0.0", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" From fd929b239acefa628ede90723c3bc9a947bb0ffa Mon Sep 17 00:00:00 2001 From: Merlin Beutlberger Date: Fri, 3 Jan 2025 13:33:18 +0100 Subject: [PATCH 02/13] fix(sax-wasm): Remove obsolete 'options' parameter --- src/linter/xmlTemplate/transpiler.ts | 5 +---- src/utils/xmlParser.ts | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/linter/xmlTemplate/transpiler.ts b/src/linter/xmlTemplate/transpiler.ts index 9d36a52db..37bb31e8b 100644 --- a/src/linter/xmlTemplate/transpiler.ts +++ b/src/linter/xmlTemplate/transpiler.ts @@ -62,10 +62,7 @@ async function transpileXmlToJs( const parser = new Parser(resourcePath, apiExtract, context, controllerByIdInfo); // Initialize parser - const options = {highWaterMark: 32 * 1024}; // 32k chunks - const saxParser = new SAXParser( - SaxEventType.OpenTag | SaxEventType.CloseTag, - options); + const saxParser = new SAXParser(SaxEventType.OpenTag | SaxEventType.CloseTag); saxParser.eventHandler = (event, tag) => { if (tag instanceof SaxTag) { diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 4e1f142ca..d3ded9376 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -16,9 +16,8 @@ async function initSaxWasm() { } export async function parseXML(contentStream: ReadStream, parseHandler: (type: SaxEventType, tag: Detail) => void) { - const options = {highWaterMark: 32 * 1024}; // 32k chunks const saxWasmBuffer = await initSaxWasm(); - const saxParser = new SAXParser(SaxEventType.CloseTag + SaxEventType.OpenTag, options); + const saxParser = new SAXParser(SaxEventType.CloseTag + SaxEventType.OpenTag); saxParser.eventHandler = parseHandler; From 85969b3baa2f3e0f389d194891707991eabc73a2 Mon Sep 17 00:00:00 2001 From: Florian Vogt Date: Fri, 17 Jan 2025 09:10:03 +0100 Subject: [PATCH 03/13] fix(sax-wasm): Adjust type to Reader --- src/utils/xmlParser.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index d3ded9376..067fd7311 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -1,5 +1,5 @@ import type {ReadStream} from "node:fs"; -import {Detail, SaxEventType, SAXParser} from "sax-wasm"; +import {Detail, Reader, SaxEventType, SAXParser} from "sax-wasm"; import {finished} from "node:stream/promises"; import fs from "node:fs/promises"; import {createRequire} from "node:module"; @@ -15,7 +15,8 @@ async function initSaxWasm() { return saxWasmBuffer; } -export async function parseXML(contentStream: ReadStream, parseHandler: (type: SaxEventType, tag: Detail) => void) { +export async function parseXML( + contentStream: ReadStream, parseHandler: (type: SaxEventType, tag: Reader) => void) { const saxWasmBuffer = await initSaxWasm(); const saxParser = new SAXParser(SaxEventType.CloseTag + SaxEventType.OpenTag); From ae46121deb952c6175d9e2cc6abb0591e9363825 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 20 Jan 2025 13:09:47 +0200 Subject: [PATCH 04/13] fix: Migrate SaxParser to serialized tag --- src/utils/xmlParser.ts | 33 ++++++++++++++++++++++++++++++++- test/lib/utils/xmlParser.ts | 6 +++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 067fd7311..54b36ef2d 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -1,10 +1,41 @@ import type {ReadStream} from "node:fs"; -import {Detail, Reader, SaxEventType, SAXParser} from "sax-wasm"; +import {AttributeType, Detail, PositionDetail, Reader, SaxEventType, SAXParser} from "sax-wasm"; import {finished} from "node:stream/promises"; import fs from "node:fs/promises"; import {createRequire} from "node:module"; const require = createRequire(import.meta.url); +export interface SaxParserToJSON { + openStart: PositionDetail; + openEnd: PositionDetail; + closeStart: PositionDetail; + closeEnd: PositionDetail; + name: string; + attributes: { + name: { + start: PositionDetail; + end: PositionDetail; + value: string; + }; + value: { + start: PositionDetail; + end: PositionDetail; + value: string; + }; + type: AttributeType; + }[]; + textNodes: { + start: PositionDetail; + end: PositionDetail; + value: string; + }[]; + selfClosing: boolean; +}; + +export function isSaxParserToJSON(tag: any): tag is SaxParserToJSON { + return tag && typeof tag === "object" && !!tag.name && !!tag.attributes && !!tag.textNodes; +} + let saxWasmBuffer: Buffer; async function initSaxWasm() { if (!saxWasmBuffer) { diff --git a/test/lib/utils/xmlParser.ts b/test/lib/utils/xmlParser.ts index 10cb160e9..34f020d1e 100644 --- a/test/lib/utils/xmlParser.ts +++ b/test/lib/utils/xmlParser.ts @@ -1,5 +1,5 @@ import test from "ava"; -import {parseXML} from "../../../src/utils/xmlParser.js"; +import {parseXML, SaxParserToJSON} from "../../../src/utils/xmlParser.js"; import {ReadStream} from "node:fs"; import {Readable} from "node:stream"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; @@ -35,12 +35,12 @@ test("Test xmlParser with .library", async (t) => { contentStream.push(null); // Call SAXParser with the contentStream - const libs: SaxTag[] = []; + const libs: SaxParserToJSON[] = []; await parseXML(contentStream, (event, tag) => { if (tag instanceof SaxTag && event === SaxEventType.CloseTag && tag.value === "libraryName") { - libs.push(tag); + libs.push(tag.toJSON()); } }); From f6d671d7f0d35c56e2b3f9f984a9d13a7c298d86 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 20 Jan 2025 13:28:01 +0200 Subject: [PATCH 05/13] fix: Migrate the HTML Parser --- src/linter/html/HtmlReporter.ts | 5 +++-- src/linter/html/parser.ts | 19 ++++++++++--------- src/utils/xmlParser.ts | 11 +++++++++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/linter/html/HtmlReporter.ts b/src/linter/html/HtmlReporter.ts index d2a564b5a..91639c8de 100644 --- a/src/linter/html/HtmlReporter.ts +++ b/src/linter/html/HtmlReporter.ts @@ -2,6 +2,7 @@ import {Tag as SaxTag, Text as SaxText, Position as SaxPosition} from "sax-wasm" import LinterContext, {CoverageInfo, ResourcePath} from "../LinterContext.js"; import {MESSAGE} from "../messages.js"; import {MessageArgs} from "../MessageArgs.js"; +import {isSaxParserToJSON} from "../../utils/xmlParser.js"; interface ReporterCoverageInfo extends CoverageInfo { node: SaxTag; @@ -25,7 +26,7 @@ export default class HtmlReporter { throw new Error("Invalid arguments: Missing second argument"); } let args: MessageArgs[M]; - if (argsOrNode instanceof SaxTag) { + if (isSaxParserToJSON(argsOrNode)) { node = argsOrNode; args = null as unknown as MessageArgs[M]; } else if (argsOrNode instanceof SaxText) { @@ -38,7 +39,7 @@ export default class HtmlReporter { } let startPos: SaxPosition; - if (node instanceof SaxTag) { + if (isSaxParserToJSON(node)) { startPos = node.openStart; } else { startPos = node.start; diff --git a/src/linter/html/parser.ts b/src/linter/html/parser.ts index b64f940f3..c19777967 100644 --- a/src/linter/html/parser.ts +++ b/src/linter/html/parser.ts @@ -1,10 +1,10 @@ import type {ReadStream} from "node:fs"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; -import {parseXML} from "../../utils/xmlParser.js"; +import {parseXML, SaxParserToJSON} from "../../utils/xmlParser.js"; interface ExtractedTags { - scriptTags: SaxTag[]; - stylesheetLinkTags: SaxTag[]; + scriptTags: SaxParserToJSON[]; + stylesheetLinkTags: SaxParserToJSON[]; } export async function extractHTMLTags(contentStream: ReadStream) { @@ -16,17 +16,18 @@ export async function extractHTMLTags(contentStream: ReadStream) { if (!(tag instanceof SaxTag)) { return; } + const serializedTag = tag.toJSON() as SaxParserToJSON; if (event === SaxEventType.OpenTag && - tag.value === "link") { - if (tag.attributes.some((attr) => { + serializedTag.name === "link") { + if (serializedTag.attributes.some((attr) => { return (attr.name.value === "rel" && attr.value.value === "stylesheet"); })) { - extractedTags.stylesheetLinkTags.push(tag); + extractedTags.stylesheetLinkTags.push(serializedTag); }; } else if (event === SaxEventType.CloseTag && - tag.value === "script") { - const isJSScriptTag = tag.attributes.every((attr) => { + serializedTag.name === "script") { + const isJSScriptTag = serializedTag.attributes.every((attr) => { // The "type" attribute of the script tag should be // 1. not set (default), // 2. an empty string, @@ -41,7 +42,7 @@ export async function extractHTMLTags(contentStream: ReadStream) { ].includes(attr.value.value.toLowerCase())); }); if (isJSScriptTag) { - extractedTags.scriptTags.push(tag); + extractedTags.scriptTags.push(serializedTag); } } }); diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 54b36ef2d..597ade4e9 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -32,8 +32,15 @@ export interface SaxParserToJSON { selfClosing: boolean; }; -export function isSaxParserToJSON(tag: any): tag is SaxParserToJSON { - return tag && typeof tag === "object" && !!tag.name && !!tag.attributes && !!tag.textNodes; +export function isSaxParserToJSON(tag: unknown): tag is SaxParserToJSON { + const tagAsSaxParserToJSON = tag as SaxParserToJSON; + return !!tag && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "openStart") && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "openEnd") && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "closeStart") && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "closeEnd") && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "attributes") && + Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "textNodes"); } let saxWasmBuffer: Buffer; From f65a92db193911345805e427aef46894912ea02f Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 20 Jan 2025 14:04:01 +0200 Subject: [PATCH 06/13] refactor: DotLibrary migration --- src/linter/dotLibrary/DotLibraryLinter.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/linter/dotLibrary/DotLibraryLinter.ts b/src/linter/dotLibrary/DotLibraryLinter.ts index 0b1026555..f859b0943 100644 --- a/src/linter/dotLibrary/DotLibraryLinter.ts +++ b/src/linter/dotLibrary/DotLibraryLinter.ts @@ -1,7 +1,7 @@ import LinterContext from "../LinterContext.js"; import {deprecatedLibraries} from "../../utils/deprecations.js"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; -import {parseXML} from "../../utils/xmlParser.js"; +import {parseXML, SaxParserToJSON} from "../../utils/xmlParser.js"; import {ReadStream} from "node:fs"; import {MESSAGE} from "../messages.js"; @@ -26,7 +26,7 @@ export default class DotLibraryLinter { } } - async #parseDotLibrary(contentStream: ReadStream): Promise { + async #parseDotLibrary(contentStream: ReadStream): Promise { const libs = new Set(); const tagsStack: string[] = []; const libNamePath = ["library", "dependencies", "dependency"]; @@ -35,27 +35,28 @@ export default class DotLibraryLinter { return; } - if (event === SaxEventType.OpenTag && !tag.selfClosing) { - tagsStack.push(tag.value); - } else if (event === SaxEventType.CloseTag && !tag.selfClosing) { + const serializedTag = tag.toJSON(); + if (event === SaxEventType.OpenTag && !serializedTag.selfClosing) { + tagsStack.push(serializedTag.name); + } else if (event === SaxEventType.CloseTag && !serializedTag.selfClosing) { tagsStack.pop(); } if (event === SaxEventType.CloseTag && - tag.value === "libraryName") { + serializedTag.name === "libraryName") { const isMatchingPath = libNamePath.length === tagsStack.length && libNamePath.every((lib, index) => lib === tagsStack[index]); if (isMatchingPath) { - libs.add(tag); + libs.add(serializedTag); } } }); - return Array.from(libs) as SaxTag[]; + return Array.from(libs) as SaxParserToJSON[]; } - #analyzeDeprecatedLibs(libs: SaxTag[]) { + #analyzeDeprecatedLibs(libs: SaxParserToJSON[]) { // Check for deprecated libraries libs.forEach((lib) => { // textNodes is always an array, but it might be empty From ff99ed46b23bf66de6f8fb3eeefadadc373f8d95 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Tue, 21 Jan 2025 11:13:52 +0200 Subject: [PATCH 07/13] refactor: Migrate xml parser to the new version --- src/linter/xmlTemplate/Parser.ts | 13 +++++++------ src/linter/xmlTemplate/transpiler.ts | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/linter/xmlTemplate/Parser.ts b/src/linter/xmlTemplate/Parser.ts index c868c3714..ec5ce0a24 100644 --- a/src/linter/xmlTemplate/Parser.ts +++ b/src/linter/xmlTemplate/Parser.ts @@ -1,4 +1,4 @@ -import {Attribute as SaxAttribute, Tag as SaxTag, Position as SaxPosition} from "sax-wasm"; +import {PositionDetail as SaxPosition} from "sax-wasm"; import he from "he"; import ViewGenerator from "./generator/ViewGenerator.js"; import FragmentGenerator from "./generator/FragmentGenerator.js"; @@ -11,6 +11,7 @@ import {MESSAGE} from "../messages.js"; import {ApiExtract} from "../../utils/ApiExtract.js"; import ControllerByIdInfo from "./ControllerByIdInfo.js"; import BindingLinter from "../binding/BindingLinter.js"; +import {SaxParserToJSON} from "../../utils/xmlParser.js"; const log = getLogger("linter:xmlTemplate:Parser"); export type Namespace = string; @@ -160,11 +161,11 @@ export default class Parser { this.#bindingLinter = new BindingLinter(resourcePath, context); } - pushTag(tag: SaxTag) { + pushTag(tag: SaxParserToJSON) { this.#nodeStack.push(this._createNode(tag)); } - popTag(_tag: SaxTag) { // No need to use the parsed tag, we rely on our nodeStack + popTag(_tag: SaxParserToJSON) { // No need to use the parsed tag, we rely on our nodeStack const level = this.#nodeStack.length; const closingNode = this.#nodeStack.pop(); @@ -271,7 +272,7 @@ export default class Parser { } } - _createNode(tag: SaxTag): NodeDeclaration { + _createNode(tag: SaxParserToJSON): NodeDeclaration { let tagName = tag.name; let tagNamespace = null; // default namespace @@ -281,7 +282,7 @@ export default class Parser { } const attributes = new Set(); - tag.attributes.forEach((attr: SaxAttribute) => { + tag.attributes.forEach((attr) => { const attrName = attr.name.value; const attrValue = he.decode(attr.value.value); // Extract namespaces immediately so we can resolve namespaced attributes in the next go @@ -392,7 +393,7 @@ export default class Parser { _handleUi5LibraryNamespace( moduleName: string, namespace: Namespace, attributes: Set, - tag: SaxTag + tag: SaxParserToJSON ): ControlDeclaration | AggregationDeclaration | FragmentDefinitionDeclaration { const controlProperties = new Set(); const customDataElements: ControlDeclaration[] = []; diff --git a/src/linter/xmlTemplate/transpiler.ts b/src/linter/xmlTemplate/transpiler.ts index 37bb31e8b..a827a22d5 100644 --- a/src/linter/xmlTemplate/transpiler.ts +++ b/src/linter/xmlTemplate/transpiler.ts @@ -67,9 +67,9 @@ async function transpileXmlToJs( saxParser.eventHandler = (event, tag) => { if (tag instanceof SaxTag) { if (event === SaxEventType.OpenTag) { - parser.pushTag(tag); + parser.pushTag(tag.toJSON()); } else if (event === SaxEventType.CloseTag) { - parser.popTag(tag); + parser.popTag(tag.toJSON()); } } }; From 0e0920682a1fa319639f0541702213840adfc4fd Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Thu, 23 Jan 2025 08:55:26 +0200 Subject: [PATCH 08/13] fix: Update sax-wasm to 3.0.1 --- npm-shrinkwrap.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 499a78d68..4f9d00993 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.0", + "sax-wasm": "^3.0.1", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" @@ -11166,9 +11166,9 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/sax-wasm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.0.tgz", - "integrity": "sha512-8Dn91vhGZ0LxSosVyPv4zcgZDOX0qyFxpTLlW8rb1L9kP3l0yXk5fXv3X86MPgk9hgJbajAGKuOXkhUQSTetWQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.1.tgz", + "integrity": "sha512-kONzxrAqvm134J2XYRe+tqz3frcm0W1Bl67OTn8PGNMOEyam+Hy9WFfMIw2k6sm07Yb3IrLZkcmaQMZtNnkx7w==", "license": "MIT", "engines": { "node": ">=18.20.5" diff --git a/package.json b/package.json index 5895625a2..af2dd35e0 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.0", + "sax-wasm": "^3.0.1", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" From 63e58e5ab19ffb8c86bf3cf923f0f1948490a973 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Thu, 23 Jan 2025 11:16:06 +0200 Subject: [PATCH 09/13] fix: Messaging --- src/linter/html/HtmlReporter.ts | 10 +++++----- src/linter/html/transpiler.ts | 9 +++++---- src/utils/xmlParser.ts | 21 ++++++++++----------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/linter/html/HtmlReporter.ts b/src/linter/html/HtmlReporter.ts index 91639c8de..31d2624d7 100644 --- a/src/linter/html/HtmlReporter.ts +++ b/src/linter/html/HtmlReporter.ts @@ -2,7 +2,7 @@ import {Tag as SaxTag, Text as SaxText, Position as SaxPosition} from "sax-wasm" import LinterContext, {CoverageInfo, ResourcePath} from "../LinterContext.js"; import {MESSAGE} from "../messages.js"; import {MessageArgs} from "../MessageArgs.js"; -import {isSaxParserToJSON} from "../../utils/xmlParser.js"; +import {isSaxParserToJSON, SaxParserToJSON, isSaxText} from "../../utils/xmlParser.js"; interface ReporterCoverageInfo extends CoverageInfo { node: SaxTag; @@ -17,10 +17,10 @@ export default class HtmlReporter { this.#context = context; } - addMessage(id: M, args: MessageArgs[M], node: SaxTag | SaxText): void; - addMessage(id: M, node: SaxTag | SaxText): void; + addMessage(id: M, args: MessageArgs[M], node: SaxParserToJSON | SaxText): void; + addMessage(id: M, node: SaxParserToJSON | SaxText): void; addMessage( - id: M, argsOrNode?: MessageArgs[M] | SaxTag | SaxText, node?: SaxTag | SaxText + id: M, argsOrNode?: MessageArgs[M] | SaxParserToJSON | SaxText, node?: SaxParserToJSON | SaxText ) { if (!argsOrNode) { throw new Error("Invalid arguments: Missing second argument"); @@ -29,7 +29,7 @@ export default class HtmlReporter { if (isSaxParserToJSON(argsOrNode)) { node = argsOrNode; args = null as unknown as MessageArgs[M]; - } else if (argsOrNode instanceof SaxText) { + } else if (isSaxText(argsOrNode)) { node = argsOrNode; args = null as unknown as MessageArgs[M]; } else if (!node) { diff --git a/src/linter/html/transpiler.ts b/src/linter/html/transpiler.ts index d88772290..3c6e6d791 100644 --- a/src/linter/html/transpiler.ts +++ b/src/linter/html/transpiler.ts @@ -4,8 +4,9 @@ import HtmlReporter from "./HtmlReporter.js"; import LinterContext, {ResourcePath, TranspileResult} from "../LinterContext.js"; import {taskStart} from "../../utils/perf.js"; import {MESSAGE} from "../messages.js"; -import {Tag, Attribute} from "sax-wasm"; +import {Attribute} from "sax-wasm"; import {deprecatedLibraries, deprecatedThemes} from "../../utils/deprecations.js"; +import type {SaxParserToJSON} from "../../utils/xmlParser.js"; export default async function transpileHtml( resourcePath: ResourcePath, contentStream: ReadStream, context: LinterContext @@ -54,7 +55,7 @@ export default async function transpileHtml( } } -function detectTestStarter(resourcePath: ResourcePath, scriptTags: Tag[], context: LinterContext) { +function detectTestStarter(resourcePath: ResourcePath, scriptTags: SaxParserToJSON[], context: LinterContext) { const shouldBeMigrated = scriptTags.some((tag) => { const isTestsuiteQunitFile = /testsuite(?:\.[a-z][a-z0-9-]*)*\.qunit\.html$/.test(resourcePath); return (isTestsuiteQunitFile && !tag.attributes.some((attr) => { @@ -74,7 +75,7 @@ function detectTestStarter(resourcePath: ResourcePath, scriptTags: Tag[], contex } } -function findBootstrapTag(tags: Tag[]): Tag | undefined { +function findBootstrapTag(tags: SaxParserToJSON[]): SaxParserToJSON | undefined { // First search for script tag with id "sap-ui-bootstrap" for (const tag of tags) { for (const attr of tag.attributes) { @@ -119,7 +120,7 @@ const aliasToAttr = new Map([ ["data-sap-ui-xx-noless", "data-sap-ui-xx-no-less"], ]); -function lintBootstrapAttributes(tag: Tag, report: HtmlReporter) { +function lintBootstrapAttributes(tag: SaxParserToJSON, report: HtmlReporter) { const attributes = new Set(); for (const attr of tag.attributes) { let attributeName = attr.name.value.toLowerCase(); diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 597ade4e9..1d57bb41e 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -1,5 +1,5 @@ import type {ReadStream} from "node:fs"; -import {AttributeType, Detail, PositionDetail, Reader, SaxEventType, SAXParser} from "sax-wasm"; +import {AttributeType, Detail, PositionDetail, Reader, SaxEventType, SAXParser, Text} from "sax-wasm"; import {finished} from "node:stream/promises"; import fs from "node:fs/promises"; import {createRequire} from "node:module"; @@ -12,16 +12,8 @@ export interface SaxParserToJSON { closeEnd: PositionDetail; name: string; attributes: { - name: { - start: PositionDetail; - end: PositionDetail; - value: string; - }; - value: { - start: PositionDetail; - end: PositionDetail; - value: string; - }; + name: Text; + value: Text; type: AttributeType; }[]; textNodes: { @@ -43,6 +35,13 @@ export function isSaxParserToJSON(tag: unknown): tag is SaxParserToJSON { Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "textNodes"); } +export function isSaxText(tag: unknown): tag is Text { + return !!tag && + Object.prototype.hasOwnProperty.call(tag, "start") && + Object.prototype.hasOwnProperty.call(tag, "end") && + Object.prototype.hasOwnProperty.call(tag, "value"); +} + let saxWasmBuffer: Buffer; async function initSaxWasm() { if (!saxWasmBuffer) { From 878eb8cc186d2f47c48ed457982e9d2eba9d0c68 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Thu, 23 Jan 2025 12:14:24 +0200 Subject: [PATCH 10/13] fix: Type definitions --- src/linter/dotLibrary/DotLibraryLinter.ts | 10 ++++----- src/linter/html/HtmlReporter.ts | 12 +++++------ src/linter/html/parser.ts | 8 ++++---- src/linter/html/transpiler.ts | 12 +++++------ src/linter/xmlTemplate/Parser.ts | 10 ++++----- src/utils/xmlParser.ts | 25 +++-------------------- test/lib/utils/xmlParser.ts | 6 +++--- 7 files changed, 31 insertions(+), 52 deletions(-) diff --git a/src/linter/dotLibrary/DotLibraryLinter.ts b/src/linter/dotLibrary/DotLibraryLinter.ts index f859b0943..b7cd0cc56 100644 --- a/src/linter/dotLibrary/DotLibraryLinter.ts +++ b/src/linter/dotLibrary/DotLibraryLinter.ts @@ -1,7 +1,7 @@ import LinterContext from "../LinterContext.js"; import {deprecatedLibraries} from "../../utils/deprecations.js"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; -import {parseXML, SaxParserToJSON} from "../../utils/xmlParser.js"; +import {parseXML} from "../../utils/xmlParser.js"; import {ReadStream} from "node:fs"; import {MESSAGE} from "../messages.js"; @@ -26,7 +26,7 @@ export default class DotLibraryLinter { } } - async #parseDotLibrary(contentStream: ReadStream): Promise { + async #parseDotLibrary(contentStream: ReadStream): Promise { const libs = new Set(); const tagsStack: string[] = []; const libNamePath = ["library", "dependencies", "dependency"]; @@ -35,7 +35,7 @@ export default class DotLibraryLinter { return; } - const serializedTag = tag.toJSON(); + const serializedTag = tag.toJSON() as SaxTag; if (event === SaxEventType.OpenTag && !serializedTag.selfClosing) { tagsStack.push(serializedTag.name); } else if (event === SaxEventType.CloseTag && !serializedTag.selfClosing) { @@ -53,10 +53,10 @@ export default class DotLibraryLinter { } }); - return Array.from(libs) as SaxParserToJSON[]; + return Array.from(libs) as SaxTag[]; } - #analyzeDeprecatedLibs(libs: SaxParserToJSON[]) { + #analyzeDeprecatedLibs(libs: SaxTag[]) { // Check for deprecated libraries libs.forEach((lib) => { // textNodes is always an array, but it might be empty diff --git a/src/linter/html/HtmlReporter.ts b/src/linter/html/HtmlReporter.ts index 31d2624d7..14b19cf2c 100644 --- a/src/linter/html/HtmlReporter.ts +++ b/src/linter/html/HtmlReporter.ts @@ -1,8 +1,8 @@ -import {Tag as SaxTag, Text as SaxText, Position as SaxPosition} from "sax-wasm"; +import {Position as SaxPosition, Tag as SaxTag, Text as SaxText} from "sax-wasm"; import LinterContext, {CoverageInfo, ResourcePath} from "../LinterContext.js"; import {MESSAGE} from "../messages.js"; import {MessageArgs} from "../MessageArgs.js"; -import {isSaxParserToJSON, SaxParserToJSON, isSaxText} from "../../utils/xmlParser.js"; +import {isSaxParserToJSON, isSaxText} from "../../utils/xmlParser.js"; interface ReporterCoverageInfo extends CoverageInfo { node: SaxTag; @@ -17,10 +17,10 @@ export default class HtmlReporter { this.#context = context; } - addMessage(id: M, args: MessageArgs[M], node: SaxParserToJSON | SaxText): void; - addMessage(id: M, node: SaxParserToJSON | SaxText): void; + addMessage(id: M, args: MessageArgs[M], node: SaxTag | SaxText): void; + addMessage(id: M, node: SaxTag | SaxText): void; addMessage( - id: M, argsOrNode?: MessageArgs[M] | SaxParserToJSON | SaxText, node?: SaxParserToJSON | SaxText + id: M, argsOrNode?: MessageArgs[M] | SaxTag | SaxText, node?: SaxTag | SaxText ) { if (!argsOrNode) { throw new Error("Invalid arguments: Missing second argument"); @@ -52,7 +52,7 @@ export default class HtmlReporter { addCoverageInfo({node, message, category}: ReporterCoverageInfo) { let line = 0, column = 0, endLine = 0, endColumn = 0; - if (node instanceof SaxTag) { + if (isSaxParserToJSON(node)) { ({line, character: column} = node.openStart); ({line: endLine, character: endColumn} = node.closeEnd); } diff --git a/src/linter/html/parser.ts b/src/linter/html/parser.ts index c19777967..ea9de3125 100644 --- a/src/linter/html/parser.ts +++ b/src/linter/html/parser.ts @@ -1,10 +1,10 @@ import type {ReadStream} from "node:fs"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; -import {parseXML, SaxParserToJSON} from "../../utils/xmlParser.js"; +import {parseXML} from "../../utils/xmlParser.js"; interface ExtractedTags { - scriptTags: SaxParserToJSON[]; - stylesheetLinkTags: SaxParserToJSON[]; + scriptTags: SaxTag[]; + stylesheetLinkTags: SaxTag[]; } export async function extractHTMLTags(contentStream: ReadStream) { @@ -16,7 +16,7 @@ export async function extractHTMLTags(contentStream: ReadStream) { if (!(tag instanceof SaxTag)) { return; } - const serializedTag = tag.toJSON() as SaxParserToJSON; + const serializedTag = tag.toJSON() as SaxTag; if (event === SaxEventType.OpenTag && serializedTag.name === "link") { if (serializedTag.attributes.some((attr) => { diff --git a/src/linter/html/transpiler.ts b/src/linter/html/transpiler.ts index 3c6e6d791..cdd751b3c 100644 --- a/src/linter/html/transpiler.ts +++ b/src/linter/html/transpiler.ts @@ -4,9 +4,8 @@ import HtmlReporter from "./HtmlReporter.js"; import LinterContext, {ResourcePath, TranspileResult} from "../LinterContext.js"; import {taskStart} from "../../utils/perf.js"; import {MESSAGE} from "../messages.js"; -import {Attribute} from "sax-wasm"; +import {Attribute, Tag as SaxTag} from "sax-wasm"; import {deprecatedLibraries, deprecatedThemes} from "../../utils/deprecations.js"; -import type {SaxParserToJSON} from "../../utils/xmlParser.js"; export default async function transpileHtml( resourcePath: ResourcePath, contentStream: ReadStream, context: LinterContext @@ -35,8 +34,7 @@ export default async function transpileHtml( }); stylesheetLinkTags.forEach((tag) => { - const href = tag.attributes.find((attr) => - attr.name.value.toLowerCase() === "href"); + const href = tag.attributes.find((attr) => attr.name.value.toLowerCase() === "href"); if (href) { deprecatedThemes.forEach((themeName) => { if (href.value.value.includes(`/themes/${themeName}/`)) { @@ -55,7 +53,7 @@ export default async function transpileHtml( } } -function detectTestStarter(resourcePath: ResourcePath, scriptTags: SaxParserToJSON[], context: LinterContext) { +function detectTestStarter(resourcePath: ResourcePath, scriptTags: SaxTag[], context: LinterContext) { const shouldBeMigrated = scriptTags.some((tag) => { const isTestsuiteQunitFile = /testsuite(?:\.[a-z][a-z0-9-]*)*\.qunit\.html$/.test(resourcePath); return (isTestsuiteQunitFile && !tag.attributes.some((attr) => { @@ -75,7 +73,7 @@ function detectTestStarter(resourcePath: ResourcePath, scriptTags: SaxParserToJS } } -function findBootstrapTag(tags: SaxParserToJSON[]): SaxParserToJSON | undefined { +function findBootstrapTag(tags: SaxTag[]): SaxTag | undefined { // First search for script tag with id "sap-ui-bootstrap" for (const tag of tags) { for (const attr of tag.attributes) { @@ -120,7 +118,7 @@ const aliasToAttr = new Map([ ["data-sap-ui-xx-noless", "data-sap-ui-xx-no-less"], ]); -function lintBootstrapAttributes(tag: SaxParserToJSON, report: HtmlReporter) { +function lintBootstrapAttributes(tag: SaxTag, report: HtmlReporter) { const attributes = new Set(); for (const attr of tag.attributes) { let attributeName = attr.name.value.toLowerCase(); diff --git a/src/linter/xmlTemplate/Parser.ts b/src/linter/xmlTemplate/Parser.ts index ec5ce0a24..c02d5f1e0 100644 --- a/src/linter/xmlTemplate/Parser.ts +++ b/src/linter/xmlTemplate/Parser.ts @@ -11,7 +11,7 @@ import {MESSAGE} from "../messages.js"; import {ApiExtract} from "../../utils/ApiExtract.js"; import ControllerByIdInfo from "./ControllerByIdInfo.js"; import BindingLinter from "../binding/BindingLinter.js"; -import {SaxParserToJSON} from "../../utils/xmlParser.js"; +import {Tag as SaxTag} from "sax-wasm"; const log = getLogger("linter:xmlTemplate:Parser"); export type Namespace = string; @@ -161,11 +161,11 @@ export default class Parser { this.#bindingLinter = new BindingLinter(resourcePath, context); } - pushTag(tag: SaxParserToJSON) { + pushTag(tag: SaxTag) { this.#nodeStack.push(this._createNode(tag)); } - popTag(_tag: SaxParserToJSON) { // No need to use the parsed tag, we rely on our nodeStack + popTag(_tag: SaxTag) { // No need to use the parsed tag, we rely on our nodeStack const level = this.#nodeStack.length; const closingNode = this.#nodeStack.pop(); @@ -272,7 +272,7 @@ export default class Parser { } } - _createNode(tag: SaxParserToJSON): NodeDeclaration { + _createNode(tag: SaxTag): NodeDeclaration { let tagName = tag.name; let tagNamespace = null; // default namespace @@ -393,7 +393,7 @@ export default class Parser { _handleUi5LibraryNamespace( moduleName: string, namespace: Namespace, attributes: Set, - tag: SaxParserToJSON + tag: SaxTag ): ControlDeclaration | AggregationDeclaration | FragmentDefinitionDeclaration { const controlProperties = new Set(); const customDataElements: ControlDeclaration[] = []; diff --git a/src/utils/xmlParser.ts b/src/utils/xmlParser.ts index 1d57bb41e..10fc1a0cb 100644 --- a/src/utils/xmlParser.ts +++ b/src/utils/xmlParser.ts @@ -1,31 +1,12 @@ import type {ReadStream} from "node:fs"; -import {AttributeType, Detail, PositionDetail, Reader, SaxEventType, SAXParser, Text} from "sax-wasm"; +import {Detail, Reader, SaxEventType, SAXParser, Tag, Text} from "sax-wasm"; import {finished} from "node:stream/promises"; import fs from "node:fs/promises"; import {createRequire} from "node:module"; const require = createRequire(import.meta.url); -export interface SaxParserToJSON { - openStart: PositionDetail; - openEnd: PositionDetail; - closeStart: PositionDetail; - closeEnd: PositionDetail; - name: string; - attributes: { - name: Text; - value: Text; - type: AttributeType; - }[]; - textNodes: { - start: PositionDetail; - end: PositionDetail; - value: string; - }[]; - selfClosing: boolean; -}; - -export function isSaxParserToJSON(tag: unknown): tag is SaxParserToJSON { - const tagAsSaxParserToJSON = tag as SaxParserToJSON; +export function isSaxParserToJSON(tag: unknown): tag is Tag { + const tagAsSaxParserToJSON = tag as Tag; return !!tag && Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "openStart") && Object.prototype.hasOwnProperty.call(tagAsSaxParserToJSON, "openEnd") && diff --git a/test/lib/utils/xmlParser.ts b/test/lib/utils/xmlParser.ts index 34f020d1e..49366c814 100644 --- a/test/lib/utils/xmlParser.ts +++ b/test/lib/utils/xmlParser.ts @@ -1,5 +1,5 @@ import test from "ava"; -import {parseXML, SaxParserToJSON} from "../../../src/utils/xmlParser.js"; +import {parseXML} from "../../../src/utils/xmlParser.js"; import {ReadStream} from "node:fs"; import {Readable} from "node:stream"; import {SaxEventType, Tag as SaxTag} from "sax-wasm"; @@ -35,12 +35,12 @@ test("Test xmlParser with .library", async (t) => { contentStream.push(null); // Call SAXParser with the contentStream - const libs: SaxParserToJSON[] = []; + const libs: SaxTag[] = []; await parseXML(contentStream, (event, tag) => { if (tag instanceof SaxTag && event === SaxEventType.CloseTag && tag.value === "libraryName") { - libs.push(tag.toJSON()); + libs.push(tag.toJSON() as SaxTag); } }); From ded3433dc7134928676d65e5ee270c1080f736e6 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Thu, 23 Jan 2025 12:51:09 +0200 Subject: [PATCH 11/13] fix: Types --- src/linter/xmlTemplate/transpiler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/linter/xmlTemplate/transpiler.ts b/src/linter/xmlTemplate/transpiler.ts index a827a22d5..955eb7064 100644 --- a/src/linter/xmlTemplate/transpiler.ts +++ b/src/linter/xmlTemplate/transpiler.ts @@ -67,9 +67,9 @@ async function transpileXmlToJs( saxParser.eventHandler = (event, tag) => { if (tag instanceof SaxTag) { if (event === SaxEventType.OpenTag) { - parser.pushTag(tag.toJSON()); + parser.pushTag(tag.toJSON() as SaxTag); } else if (event === SaxEventType.CloseTag) { - parser.popTag(tag.toJSON()); + parser.popTag(tag.toJSON() as SaxTag); } } }; From be20739cc3f5a830f5732e6b0cdaa1c27dd36f86 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Mon, 27 Jan 2025 09:17:01 +0200 Subject: [PATCH 12/13] fix: Update sax-wasm to 3.0.2 --- npm-shrinkwrap.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4f9d00993..790cd6aa5 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.1", + "sax-wasm": "^3.0.2", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" @@ -11166,9 +11166,9 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/sax-wasm": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.1.tgz", - "integrity": "sha512-kONzxrAqvm134J2XYRe+tqz3frcm0W1Bl67OTn8PGNMOEyam+Hy9WFfMIw2k6sm07Yb3IrLZkcmaQMZtNnkx7w==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.2.tgz", + "integrity": "sha512-TworLX5TKyHfSq3Pys8PJM+XzxmR4JEWcTyFjJJXs/vKxAm5CH6xSuBu9ngSM9ROXsvIsSWRVtLbtRDd7O/k1w==", "license": "MIT", "engines": { "node": ">=18.20.5" diff --git a/package.json b/package.json index af2dd35e0..617276e45 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.1", + "sax-wasm": "^3.0.2", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" From 264718a694984e7c019c454936459b7b937a1899 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Tue, 28 Jan 2025 09:43:49 +0200 Subject: [PATCH 13/13] fix: Update sax-wasm to 3.0.3 --- npm-shrinkwrap.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 790cd6aa5..e1784a5a4 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.2", + "sax-wasm": "^3.0.3", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2" @@ -11166,9 +11166,9 @@ "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==" }, "node_modules/sax-wasm": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.2.tgz", - "integrity": "sha512-TworLX5TKyHfSq3Pys8PJM+XzxmR4JEWcTyFjJJXs/vKxAm5CH6xSuBu9ngSM9ROXsvIsSWRVtLbtRDd7O/k1w==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sax-wasm/-/sax-wasm-3.0.3.tgz", + "integrity": "sha512-v8aa7S1XC+ocg+w5ShX/+hvDPQNJ7AFMfRYx552ZAZPHKlfoSbGA80xdwdI7ObNGibs/0sS/4QdxhQMKK4iRsg==", "license": "MIT", "engines": { "node": ">=18.20.5" diff --git a/package.json b/package.json index 617276e45..6c506d2d1 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "he": "^1.2.0", "json-source-map": "^0.6.1", "minimatch": "^10.0.1", - "sax-wasm": "^3.0.2", + "sax-wasm": "^3.0.3", "typescript": "^5.7.3", "update-notifier": "^7.3.1", "yargs": "^17.7.2"