From a262bd2f3860b55f061f9f691edf508b45be215e Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Thu, 30 Jan 2025 12:04:12 +0200 Subject: [PATCH 01/39] Change the version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 733cfec..5b5bcc4 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "Textwire", "main": "./build/extension.js", "description": "Textwire templating language support for VSCode", - "version": "1.5.2", + "version": "1.5.3", "homepage": "https://github.com/textwire/vscode-textwire/blob/main/README.md", "pricing": "Free", "engines": { From 5b699415f7d6153518e2599ba7f8699a847e4e7c Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Thu, 30 Jan 2025 19:44:38 +0200 Subject: [PATCH 02/39] Add testing with Mocha --- .gitignore | 3 +- .vscode-test.js | 13 +++++++ CHANGELOG.md | 3 ++ package.json | 12 +++++-- src/extension.ts | 4 +-- src/test/runTests.ts | 28 +++++++++++++++ .../completion/loopObjCompletion.test.ts | 26 ++++++++++++++ src/test/suite/index.ts | 36 +++++++++++++++++++ src/test/suite/utils/openTextDocument.ts | 8 +++++ src/test/suite/utils/triggerAutocomplete.ts | 9 +++++ tsconfig.json | 6 ++-- 11 files changed, 140 insertions(+), 8 deletions(-) create mode 100644 .vscode-test.js create mode 100644 src/test/runTests.ts create mode 100644 src/test/suite/completion/loopObjCompletion.test.ts create mode 100644 src/test/suite/index.ts create mode 100644 src/test/suite/utils/openTextDocument.ts create mode 100644 src/test/suite/utils/triggerAutocomplete.ts diff --git a/.gitignore b/.gitignore index 0a75723..22213b0 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules *.vsix yarn.lock package-lock.json -build \ No newline at end of file +build +.vscode-test \ No newline at end of file diff --git a/.vscode-test.js b/.vscode-test.js new file mode 100644 index 0000000..c9c4542 --- /dev/null +++ b/.vscode-test.js @@ -0,0 +1,13 @@ +const { defineConfig } = require('@vscode/test-cli') + +module.exports = defineConfig([ + { + label: 'unitTests', + files: 'build/test/**/*.test.js', + version: 'insiders', + mocha: { + ui: 'tdd', + timeout: 20000, + }, + }, +]) diff --git a/CHANGELOG.md b/CHANGELOG.md index db62d70..7d3ed59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## [1.5.3] - 2025-01-30 +- Added tests coverage to test all the necessary features + ## [1.5.2] - 2025-01-30 - Fixed issue where loop object would only show autocomplete suggestions when it was inside `{{` and `}}` brackets - Refactor code to make it more readable and maintainable diff --git a/package.json b/package.json index 5b5bcc4..a21bc89 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,9 @@ "publisher": "SerhiiCho", "license": "MIT", "contributes": { + "testing": { + "testController": "vscode-test" + }, "languages": [ { "id": "textwire", @@ -56,10 +59,15 @@ "scripts": { "login": "vsce login SerhiiCho", "deploy": "vsce publish", - "build": "tsc -p ." + "build": "tsc -p .", + "test": "npm run build && vscode-test" }, "devDependencies": { - "@types/node": "^22.10.10", + "@types/mocha": "^10.0.10", + "@types/node": "^22.12.0", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.4.1", + "glob": "^11.0.1", "typescript": "^5.7.3", "vscode": "^1.1.37" } diff --git a/src/extension.ts b/src/extension.ts index a60aa09..5ea2263 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2,8 +2,8 @@ import * as vscode from 'vscode' import loopObjCompletion from './completions/loopObjCompletion' import directiveCompletion from './completions/directiveCompletion' -export function activate(context: vscode.ExtensionContext) { - context.subscriptions.push(loopObjCompletion, directiveCompletion) +export function activate(ctx: vscode.ExtensionContext) { + ctx.subscriptions.push(loopObjCompletion, directiveCompletion) } export function deactivate() {} diff --git a/src/test/runTests.ts b/src/test/runTests.ts new file mode 100644 index 0000000..8c67dce --- /dev/null +++ b/src/test/runTests.ts @@ -0,0 +1,28 @@ +import * as path from 'path' + +import { runTests } from '@vscode/test-electron' + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../') + + // The path to the extension test runner script + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './suite/index') + + // Download VS Code, unzip it and run the integration test + await runTests({ + extensionDevelopmentPath, + extensionTestsPath, + launchArgs: ['--disable-gpu', '--no-sandbox', '--disable-extensions'], + }) + } catch (err) { + console.error(err) + console.error('Failed to run tests') + process.exit(1) + } +} + +main() diff --git a/src/test/suite/completion/loopObjCompletion.test.ts b/src/test/suite/completion/loopObjCompletion.test.ts new file mode 100644 index 0000000..3200195 --- /dev/null +++ b/src/test/suite/completion/loopObjCompletion.test.ts @@ -0,0 +1,26 @@ +import * as assert from 'assert' +import * as vscode from 'vscode' +import triggerAutocomplete from '../utils/triggerAutocomplete' +import openTextDocument from '../utils/openTextDocument' + +suite('Autocomplete - loop. inside @each', () => { + test('Should suggest loop properties inside @each', async () => { + const content = `@each(item in items){{ loop. }}@end` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 29) + const completions = await triggerAutocomplete(pos, doc.uri) + + if (!completions) { + assert.fail('No completions found!') + } + + const expectedLabels = ['index', 'first', 'last', 'iter'] + + for (const expectedLabel of expectedLabels) { + assert.ok( + completions.items.some(actualItem => actualItem.label === expectedLabel), + `Expected completion '${expectedLabel}' not found!`, + ) + } + }) +}) diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts new file mode 100644 index 0000000..d029ec8 --- /dev/null +++ b/src/test/suite/index.ts @@ -0,0 +1,36 @@ +import * as path from 'path' +import Mocha from 'mocha' +import { glob } from 'glob' + +export function run(): Promise { + const mocha = new Mocha({ + ui: 'tdd', + color: true, + }) + + const testsRoot = path.resolve(__dirname, '..') + + return new Promise((c, e) => { + glob('**/**.test.js', { cwd: testsRoot }) + .then(files => { + // Add files to the test suite + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))) + + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)) + } else { + c() + } + }) + } catch (err) { + e(err) + } + }) + .catch(err => { + return e(err) + }) + }) +} diff --git a/src/test/suite/utils/openTextDocument.ts b/src/test/suite/utils/openTextDocument.ts new file mode 100644 index 0000000..e4567d7 --- /dev/null +++ b/src/test/suite/utils/openTextDocument.ts @@ -0,0 +1,8 @@ +import * as vscode from 'vscode' + +export default async (content: string): Promise => { + return await vscode.workspace.openTextDocument({ + language: 'textwire', + content, + }) +} diff --git a/src/test/suite/utils/triggerAutocomplete.ts b/src/test/suite/utils/triggerAutocomplete.ts new file mode 100644 index 0000000..6afe632 --- /dev/null +++ b/src/test/suite/utils/triggerAutocomplete.ts @@ -0,0 +1,9 @@ +import * as vscode from 'vscode' + +export default async (pos: vscode.Position, uri: vscode.Uri) => { + return await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + uri, + pos, + ) +} diff --git a/tsconfig.json b/tsconfig.json index ad14ab6..7e3b11d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,8 +7,8 @@ "strict": true, "removeComments": true, "esModuleInterop": true, - "types": ["node", "vscode"] + "types": ["node", "vscode", "mocha"] }, - "include": ["src/**/*"], - "exclude": ["node_modules", ".vscode-test"] + "include": ["src/**/*.ts", "src/test/**/*.ts"], + "exclude": ["node_modules", "build", ".vscode-test"] } From cbdec02b2e1f8ddcc3619089dbff723407c720a1 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 11:20:58 +0200 Subject: [PATCH 03/39] Fix error thanks to Deep Seek --- .vscode/settings.json | 7 +++++-- package.json | 2 +- src/test/runTests.ts | 11 ++++++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 55712c1..435d830 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file + "typescript.tsdk": "node_modules/typescript/lib", + "files.watcherExclude": { + "**/build/win-unpacked/**": true + } +} diff --git a/package.json b/package.json index a21bc89..731418f 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "login": "vsce login SerhiiCho", "deploy": "vsce publish", "build": "tsc -p .", - "test": "npm run build && vscode-test" + "test": "npm run build && node ./build/test/runTests.js" }, "devDependencies": { "@types/mocha": "^10.0.10", diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 8c67dce..6c26599 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -4,6 +4,8 @@ import { runTests } from '@vscode/test-electron' async function main() { try { + process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' + // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, '../../') @@ -16,11 +18,14 @@ async function main() { await runTests({ extensionDevelopmentPath, extensionTestsPath, - launchArgs: ['--disable-gpu', '--no-sandbox', '--disable-extensions'], + version: 'stable', + launchArgs: [], }) + + console.log('✅ All tests passed. Exiting process cleanly.') + process.exit(0) } catch (err) { - console.error(err) - console.error('Failed to run tests') + console.error('❌ Error running tests:', err) process.exit(1) } } From f4198774da7f3511260b1eaccf3245f481500532 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 11:26:21 +0200 Subject: [PATCH 04/39] Update launch arguments to avoid GPU errors on Mac M1 Pro --- src/test/runTests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 6c26599..9d4e8eb 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -19,7 +19,7 @@ async function main() { extensionDevelopmentPath, extensionTestsPath, version: 'stable', - launchArgs: [], + launchArgs: ['--no-sandbox'], // Don't use --disable-gpu, gives error on Mac M1 Pro }) console.log('✅ All tests passed. Exiting process cleanly.') From 770cb1ffc20b6a40e9d718ec11e9bd10c730a2d8 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:43:51 +0200 Subject: [PATCH 05/39] Add more tests --- .../completion/loopObjCompletion.test.ts | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/test/suite/completion/loopObjCompletion.test.ts b/src/test/suite/completion/loopObjCompletion.test.ts index 3200195..938828c 100644 --- a/src/test/suite/completion/loopObjCompletion.test.ts +++ b/src/test/suite/completion/loopObjCompletion.test.ts @@ -3,11 +3,12 @@ import * as vscode from 'vscode' import triggerAutocomplete from '../utils/triggerAutocomplete' import openTextDocument from '../utils/openTextDocument' -suite('Autocomplete - loop. inside @each', () => { +suite('Autocomplete - loop variable', () => { test('Should suggest loop properties inside @each', async () => { const content = `@each(item in items){{ loop. }}@end` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 29) + const completions = await triggerAutocomplete(pos, doc.uri) if (!completions) { @@ -23,4 +24,27 @@ suite('Autocomplete - loop. inside @each', () => { ) } }) + + test('Should not suggest loop properties outside @each', async () => { + const content = `
{{ loop. }}
@each(item in items){{ item }}@end` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 12) + + const completions = await triggerAutocomplete(pos, doc.uri) + + if (!completions) { + assert.fail('No completions found! Should have HTML native completions') + } + + // There are still going to be some HTML completions, we are + // just making sure that suggestions don't include: + const shouldNotHave = ['index', 'first', 'last', 'iter'] + + for (const expectedLabel of shouldNotHave) { + assert.ok( + !completions.items.some(actualItem => actualItem.label === expectedLabel), + `Unexpected completion '${expectedLabel}' found!`, + ) + } + }) }) From a908403c4a17ff45a71f74b08f905b9522a5b440 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:43:55 +0200 Subject: [PATCH 06/39] Remove unnecessary security warning suppression in test runner --- src/test/runTests.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 9d4e8eb..956e648 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -4,8 +4,6 @@ import { runTests } from '@vscode/test-electron' async function main() { try { - process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true' - // The folder containing the Extension Manifest package.json // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, '../../') From f075366609797be8c7ff5fd9abdff7c1bd699725 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:46:48 +0200 Subject: [PATCH 07/39] Rename build to out --- .gitignore | 2 +- .vscode-test.js | 2 +- .vscode/launch.json | 17 +- build/completions/directiveCompletion.js | 85 ++++++++ build/completions/loopObjCompletion.js | 72 +++++++ build/extension.js | 13 ++ build/modules/Cursor.js | 66 ++++++ build/modules/completionItem.js | 46 ++++ build/static/info/directivesInfo.js | 201 ++++++++++++++++++ build/static/info/loopInfo.js | 39 ++++ build/static/snippets/directivesSnippets.js | 68 ++++++ build/test/runTests.js | 67 ++++++ .../completion/loopObjCompletion.test.js | 79 +++++++ build/test/suite/index.js | 71 +++++++ build/test/suite/utils/openTextDocument.js | 51 +++++ build/test/suite/utils/triggerAutocomplete.js | 48 +++++ package.json | 6 +- tsconfig.json | 4 +- 18 files changed, 926 insertions(+), 11 deletions(-) create mode 100644 build/completions/directiveCompletion.js create mode 100644 build/completions/loopObjCompletion.js create mode 100644 build/extension.js create mode 100644 build/modules/Cursor.js create mode 100644 build/modules/completionItem.js create mode 100644 build/static/info/directivesInfo.js create mode 100644 build/static/info/loopInfo.js create mode 100644 build/static/snippets/directivesSnippets.js create mode 100644 build/test/runTests.js create mode 100644 build/test/suite/completion/loopObjCompletion.test.js create mode 100644 build/test/suite/index.js create mode 100644 build/test/suite/utils/openTextDocument.js create mode 100644 build/test/suite/utils/triggerAutocomplete.js diff --git a/.gitignore b/.gitignore index 22213b0..3a9856b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ node_modules *.vsix yarn.lock package-lock.json -build +out .vscode-test \ No newline at end of file diff --git a/.vscode-test.js b/.vscode-test.js index c9c4542..aa4e254 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -3,7 +3,7 @@ const { defineConfig } = require('@vscode/test-cli') module.exports = defineConfig([ { label: 'unitTests', - files: 'build/test/**/*.test.js', + files: 'out/test/**/*.test.js', version: 'insiders', mocha: { ui: 'tdd', diff --git a/.vscode/launch.json b/.vscode/launch.json index 0e191b5..9c05c23 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -3,15 +3,24 @@ // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { - "version": "0.2.0", + "version": "0.2.0", "configurations": [ { "name": "Extension", "type": "extensionHost", "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"] + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ] + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": ["${workspaceFolder}/out/test/**/*.js"] } ] -} \ No newline at end of file +} diff --git a/build/completions/directiveCompletion.js b/build/completions/directiveCompletion.js new file mode 100644 index 0000000..21cadf2 --- /dev/null +++ b/build/completions/directiveCompletion.js @@ -0,0 +1,85 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __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 }); +const vscode = __importStar(require("vscode")); +const completionItem_1 = __importDefault(require("../modules/completionItem")); +const directivesInfo_1 = __importDefault(require("../static/info/directivesInfo")); +const directivesSnippets_1 = __importDefault(require("../static/snippets/directivesSnippets")); +const Cursor_1 = __importDefault(require("../modules/Cursor")); +const DIR_START_REG = /(? d.label.slice(1).startsWith(partialDir)); + }, +}, ...triggerChars); diff --git a/build/completions/loopObjCompletion.js b/build/completions/loopObjCompletion.js new file mode 100644 index 0000000..71844a4 --- /dev/null +++ b/build/completions/loopObjCompletion.js @@ -0,0 +1,72 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __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 }); +const vscode = __importStar(require("vscode")); +const completionItem_1 = __importDefault(require("../modules/completionItem")); +const loopInfo_1 = __importDefault(require("../static/info/loopInfo")); +const Cursor_1 = __importDefault(require("../modules/Cursor")); +const IGNORE_REG = /{{--\s*@(each|for)/g; +const START_REG = /@(each|for)/g; +const END_REG = /@end/g; +const LOOP_VAR_REG = /\bloop\./g; +const triggerChars = ['.', 'l']; +exports.default = vscode.languages.registerCompletionItemProvider({ language: 'textwire' }, { + provideCompletionItems(doc, pos) { + const cursor = new Cursor_1.default(pos, doc); + if (cursor.notBetween(START_REG, END_REG, IGNORE_REG)) { + return []; + } + if (cursor.prevCharIs('l')) { + const field = vscode.CompletionItemKind.Variable; + return [(0, completionItem_1.default)('loop', loopInfo_1.default.loop, field)]; + } + const range = new vscode.Range(pos.with(pos.line, 0), pos); + const textBefore = doc.getText(range); + const match = LOOP_VAR_REG.test(textBefore.trim()); + if (!match) { + return []; + } + const field = vscode.CompletionItemKind.Field; + return [ + (0, completionItem_1.default)('index', loopInfo_1.default.index, field), + (0, completionItem_1.default)('first', loopInfo_1.default.first, field), + (0, completionItem_1.default)('last', loopInfo_1.default.last, field), + (0, completionItem_1.default)('iter', loopInfo_1.default.iter, field), + ]; + }, +}, ...triggerChars); diff --git a/build/extension.js b/build/extension.js new file mode 100644 index 0000000..6c898c9 --- /dev/null +++ b/build/extension.js @@ -0,0 +1,13 @@ +"use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.activate = activate; +exports.deactivate = deactivate; +const loopObjCompletion_1 = __importDefault(require("./completions/loopObjCompletion")); +const directiveCompletion_1 = __importDefault(require("./completions/directiveCompletion")); +function activate(ctx) { + ctx.subscriptions.push(loopObjCompletion_1.default, directiveCompletion_1.default); +} +function deactivate() { } diff --git a/build/modules/Cursor.js b/build/modules/Cursor.js new file mode 100644 index 0000000..b1dec51 --- /dev/null +++ b/build/modules/Cursor.js @@ -0,0 +1,66 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +const vscode = __importStar(require("vscode")); +class Cursor { + constructor(pos, doc) { + this.pos = pos; + this.doc = doc; + } + prevChar() { + return this.doc.getText(new vscode.Range(this.pos.translate(0, -1), this.pos)); + } + prevCharIs(char) { + return this.prevChar() === char; + } + isBetween(startReg, endReg, ignoreReg) { + const textBefore = this.textBefore(); + if (ignoreReg.test(textBefore)) { + return false; + } + const startMatches = Array.from(textBefore.matchAll(startReg)); + const endMatches = Array.from(textBefore.matchAll(endReg)); + const lastStart = startMatches.pop() || false; + const lastEnd = endMatches.pop() || false; + return lastStart && (!lastEnd || lastStart.index > lastEnd.index); + } + notBetween(startReg, endReg, ignoreReg) { + return !this.isBetween(startReg, endReg, ignoreReg); + } + textBefore() { + return this.doc.getText(new vscode.Range(new vscode.Position(0, 0), this.pos)); + } +} +exports.default = Cursor; diff --git a/build/modules/completionItem.js b/build/modules/completionItem.js new file mode 100644 index 0000000..73b7d7f --- /dev/null +++ b/build/modules/completionItem.js @@ -0,0 +1,46 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = completionItem; +const vscode = __importStar(require("vscode")); +function completionItem(label, documentation, kind, insert) { + const item = new vscode.CompletionItem(label, kind); + item.insertText = new vscode.SnippetString(insert || label); + item.documentation = new vscode.MarkdownString(documentation.replace(/\n/g, ' \n')); + if (label.startsWith('@')) { + item.filterText = label.slice(1); + } + return item; +} diff --git a/build/static/info/directivesInfo.js b/build/static/info/directivesInfo.js new file mode 100644 index 0000000..3bd4e7a --- /dev/null +++ b/build/static/info/directivesInfo.js @@ -0,0 +1,201 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const _if = `(directive) +Conditionally render content. + +\`\`\`textwire +@if(condition) +

content

+@end +\`\`\``; +const ifElse = `(directive) +Conditionally render content and provide an alternative that will be rendered when the condition is \`false\`. + +\`\`\`textwire +@if(condition) +

content

+@else +

alternative content

+@end +\`\`\``; +const each = `(directive) +Loop that iterates over arrays. + +\`\`\`textwire +@each(item in items) +

{{ item }}

+@end +\`\`\``; +const _for = `(directive) +For loop that iterates while the condition is \`true\`. + +\`\`\`textwire +@for(i = 0; i < items.len(); i++) +

{{ items[i] }}

+@end +\`\`\``; +const forElse = `(directive) +For loop that iterates while the condition is true, with a fallback for initially falsy conditions. For example, when the array is empty. + +\`\`\`textwire +@for(i = 0; i < items.len(); i++) +

{{ items[i] }}

+@else +

No items available.

+@end +\`\`\``; +const eachElse = `(directive) +Loop that iterates over arrays, with a fallback for empty arrays. + +\`\`\`textwire +@each(item in items) +

{{ item }}

+@else +

No items available.

+@end +\`\`\` + +Use the \`@else\` directive to specify content that will be displayed if the array is empty.`; +const dump = `(directive) +Debugging helper to output the value of variables. + +\`\`\`textwire +@dump(variable) +\`\`\` + +Outputs the value of variables, objects, arrays, strings, and other data types to the screen.`; +const use = `(directive) +Specify the layout file to be used for rendering the current template. + +\`\`\`textwire +@use('layoutName') +\`\`\` + +This directive includes the layout file specified, which defines the overall structure of the page.`; +const insert = `(directive) +Inject content into reserved placeholders defined in the layout file by providing a second argument as content. + +\`\`\`textwire +@insert('reservedName', 'content') +\`\`\` + +Use this directive in to specify content for placeholders in the layout file.`; +const insertEnd = `(directive) +Inject content into reserved placeholders defined in the layout file by providing a block of content. + +\`\`\`textwire +@insert('reservedName') +

content

+@end +\`\`\` + +Use this directive to specify content for placeholders in the layout file.`; +const reserve = `(directive) +Reserve placeholders for dynamic content to be injected by templates using the \`@insert\` directive. + +\`\`\`textwire +@reserve('reservedName') +\`\`\` + +Use this directive to specify a placeholder only in the layout file.`; +const comp = `(directive) +Components help organize and structure templates by encapsulating reusable parts of your UI. + +\`\`\`textwire +@component('path/to', { prop }) +\`\`\` + +Use this directive to include a component in your template.`; +const compSlot = `(directive) +Components help organize and structure templates by encapsulating reusable parts of your UI. + +\`\`\`textwire +@component('path/to', { prop }) + @slot +

content

+ @end +@end +\`\`\` + +Use this directive to include a component in your template with slots.`; +const slot = `(directive) +Define a default slot in a component to provide a placeholder for content. + +\`\`\`textwire +@slot +

content

+@end +\`\`\``; +const slotDef = `(directive) +Define a named slot in a component to provide a placeholder for content. + +\`\`\`textwire +@slot('name') +

content

+@end +\`\`\``; +const ifElseif = `(directive) +Conditionally render content with additional conditions using \`@elseif\`. + +\`\`\`textwire +@if(condition1) +

condition1 is true

+@elseif(condition2) +

condition2 is true

+@end +\`\`\` + +Use the \`@elseif\` directive to handle additional conditional branches. If none of the conditions are met, use \`@else\` to provide fallback content.`; +const end = `(directive) +End a directive block by using the \`@end\` directive. + +\`\`\`textwire +@end +\`\`\``; +const _break = `(directive) +Exits a loop early when used inside a loop block. + +\`\`\`textwire +@break +\`\`\``; +const _continue = `(directive) +Skips the current iteration of a loop when used inside a loop block. + +\`\`\`textwire +@continue +\`\`\``; +const breakIf = `(directive) +Exits a loop early when the specified condition is met. + +\`\`\`textwire +@breakIf(condition) +\`\`\``; +const continueIf = `(directive) +Skips the current iteration of a loop when the specified condition is met. + +\`\`\`textwire +@continueIf(condition) +\`\`\``; +exports.default = { + if: _if, + ifElse, + ifElseif, + each, + eachElse, + for: _for, + forElse, + dump, + use, + insert, + insertEnd, + reserve, + comp, + compSlot, + slot, + slotDef, + end, + break: _break, + breakIf, + continue: _continue, + continueIf, +}; diff --git a/build/static/info/loopInfo.js b/build/static/info/loopInfo.js new file mode 100644 index 0000000..a17f472 --- /dev/null +++ b/build/static/info/loopInfo.js @@ -0,0 +1,39 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const index = `(property) index: int +The current iteration of the loop. Starts with 0. + +\`\`\`textwire +{{ loop.index }} +\`\`\``; +const first = `(property) first: bool +Returns \`true\` if this is the first iteration of the loop. + +\`\`\`textwire +{{ loop.first }} +\`\`\``; +const last = `(property) last: bool +Returns \`true\` if this is the last iteration of the loop. + +\`\`\`textwire +{{ loop.last }} +\`\`\``; +const iter = `(property) iter: int +The current iteration of the loop. Starts with 1. + +\`\`\`textwire +{{ loop.iter }} +\`\`\``; +const loop = `(variable) loop: object +The loop object contains information about the current loop iteration. + +\`\`\`textwire +{{ loop.last }} +\`\`\``; +exports.default = { + index, + first, + last, + iter, + loop, +}; diff --git a/build/static/snippets/directivesSnippets.js b/build/static/snippets/directivesSnippets.js new file mode 100644 index 0000000..2340566 --- /dev/null +++ b/build/static/snippets/directivesSnippets.js @@ -0,0 +1,68 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const _if = `if($1) + $2 +@end`; +const ifElse = `if($1) + $2 +@else + $3 +@end`; +const each = `each($1 in $2) + $3 +@end`; +const eachElse = `each($1 in $2) + $3 +@else + $4 +@end`; +const _for = `for(i = 0; i < $1; i++) + $3 +@end`; +const forElse = `for(i = 0; i < $1; i++) + $3 +@else + $4 +@end`; +const insertEnd = `insert($1) + $2 +@end`; +const compSlot = `component($1) + @slot + $2 + @end +@end`; +const slot = `slot($1) + $1 +@end`; +const slotDef = `slot + $2 +@end`; +const ifElseif = `if($1) + $2 +@elseif($2) + $3 +@end`; +exports.default = { + if: _if, + ifElse, + ifElseif, + each, + eachElse, + for: _for, + forElse, + compSlot, + slot, + slotDef, + insert: 'insert($1, $2)', + insertEnd, + dump: 'dump($1)', + use: 'use($1)', + reserve: 'reserve($1)', + comp: 'component($1)', + end: 'end', + break: 'break', + continue: 'continue', + breakIf: 'breakIf($1)', + continueIf: 'continueIf($1)', +}; diff --git a/build/test/runTests.js b/build/test/runTests.js new file mode 100644 index 0000000..8644a7a --- /dev/null +++ b/build/test/runTests.js @@ -0,0 +1,67 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = __importStar(require("path")); +const test_electron_1 = require("@vscode/test-electron"); +function main() { + return __awaiter(this, void 0, void 0, function* () { + try { + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + const extensionTestsPath = path.resolve(__dirname, './suite/index'); + yield (0, test_electron_1.runTests)({ + extensionDevelopmentPath, + extensionTestsPath, + version: 'stable', + launchArgs: ['--no-sandbox'], + }); + console.log('✅ All tests passed. Exiting process cleanly.'); + process.exit(0); + } + catch (err) { + console.error('❌ Error running tests:', err); + process.exit(1); + } + }); +} +main(); diff --git a/build/test/suite/completion/loopObjCompletion.test.js b/build/test/suite/completion/loopObjCompletion.test.js new file mode 100644 index 0000000..1a6206d --- /dev/null +++ b/build/test/suite/completion/loopObjCompletion.test.js @@ -0,0 +1,79 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const assert = __importStar(require("assert")); +const vscode = __importStar(require("vscode")); +const triggerAutocomplete_1 = __importDefault(require("../utils/triggerAutocomplete")); +const openTextDocument_1 = __importDefault(require("../utils/openTextDocument")); +suite('Autocomplete - loop variable', () => { + test('Should suggest loop properties inside @each', () => __awaiter(void 0, void 0, void 0, function* () { + const content = `@each(item in items){{ loop. }}@end`; + const doc = yield (0, openTextDocument_1.default)(content); + const pos = new vscode.Position(0, 29); + const completions = yield (0, triggerAutocomplete_1.default)(pos, doc.uri); + if (!completions) { + assert.fail('No completions found!'); + } + const expectedLabels = ['index', 'first', 'last', 'iter']; + for (const expectedLabel of expectedLabels) { + assert.ok(completions.items.some(actualItem => actualItem.label === expectedLabel), `Expected completion '${expectedLabel}' not found!`); + } + })); + test('Should not suggest loop properties outside @each', () => __awaiter(void 0, void 0, void 0, function* () { + const content = `
{{ loop. }}
@each(item in items){{ item }}@end`; + const doc = yield (0, openTextDocument_1.default)(content); + const pos = new vscode.Position(0, 12); + const completions = yield (0, triggerAutocomplete_1.default)(pos, doc.uri); + if (!completions) { + assert.fail('No completions found! Should have HTML native completions'); + } + const shouldNotHave = ['index', 'first', 'last', 'iter']; + for (const expectedLabel of shouldNotHave) { + assert.ok(!completions.items.some(actualItem => actualItem.label === expectedLabel), `Unexpected completion '${expectedLabel}' found!`); + } + })); +}); diff --git a/build/test/suite/index.js b/build/test/suite/index.js new file mode 100644 index 0000000..73f3671 --- /dev/null +++ b/build/test/suite/index.js @@ -0,0 +1,71 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __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.run = run; +const path = __importStar(require("path")); +const mocha_1 = __importDefault(require("mocha")); +const glob_1 = require("glob"); +function run() { + const mocha = new mocha_1.default({ + ui: 'tdd', + color: true, + }); + const testsRoot = path.resolve(__dirname, '..'); + return new Promise((c, e) => { + (0, glob_1.glob)('**/**.test.js', { cwd: testsRoot }) + .then(files => { + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + try { + mocha.run(failures => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } + else { + c(); + } + }); + } + catch (err) { + e(err); + } + }) + .catch(err => { + return e(err); + }); + }); +} diff --git a/build/test/suite/utils/openTextDocument.js b/build/test/suite/utils/openTextDocument.js new file mode 100644 index 0000000..e1f2deb --- /dev/null +++ b/build/test/suite/utils/openTextDocument.js @@ -0,0 +1,51 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const vscode = __importStar(require("vscode")); +exports.default = (content) => __awaiter(void 0, void 0, void 0, function* () { + return yield vscode.workspace.openTextDocument({ + language: 'textwire', + content, + }); +}); diff --git a/build/test/suite/utils/triggerAutocomplete.js b/build/test/suite/utils/triggerAutocomplete.js new file mode 100644 index 0000000..8fe40f6 --- /dev/null +++ b/build/test/suite/utils/triggerAutocomplete.js @@ -0,0 +1,48 @@ +"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 () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const vscode = __importStar(require("vscode")); +exports.default = (pos, uri) => __awaiter(void 0, void 0, void 0, function* () { + return yield vscode.commands.executeCommand('vscode.executeCompletionItemProvider', uri, pos); +}); diff --git a/package.json b/package.json index 731418f..cb1de66 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "textwire", "displayName": "Textwire", - "main": "./build/extension.js", + "main": "./out/extension.js", "description": "Textwire templating language support for VSCode", "version": "1.5.3", "homepage": "https://github.com/textwire/vscode-textwire/blob/main/README.md", @@ -59,8 +59,8 @@ "scripts": { "login": "vsce login SerhiiCho", "deploy": "vsce publish", - "build": "tsc -p .", - "test": "npm run build && node ./build/test/runTests.js" + "compile": "tsc -p .", + "test": "npm run compile && node ./out/test/runTests.js" }, "devDependencies": { "@types/mocha": "^10.0.10", diff --git a/tsconfig.json b/tsconfig.json index 7e3b11d..16039e8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,12 +3,12 @@ "module": "commonjs", "target": "es2016", "rootDir": "./src", - "outDir": "./build", + "outDir": "out", "strict": true, "removeComments": true, "esModuleInterop": true, "types": ["node", "vscode", "mocha"] }, "include": ["src/**/*.ts", "src/test/**/*.ts"], - "exclude": ["node_modules", "build", ".vscode-test"] + "exclude": ["node_modules", "out", ".vscode-test"] } From 609dc2906142b399dce4588262c838a14bf37489 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:54:02 +0200 Subject: [PATCH 08/39] Remove unused launch configuration for extension tests --- .vscode/launch.json | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 9c05c23..ace09d7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,17 +10,6 @@ "type": "extensionHost", "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}"] - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" - ], - "outFiles": ["${workspaceFolder}/out/test/**/*.js"] } ] } From 0dd88706b351fb6bdd30c57dd8d13d53b5452c60 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:54:07 +0200 Subject: [PATCH 09/39] Update launch arguments to avoid GPU errors and disable extensions --- src/test/runTests.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 956e648..87b69dc 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -17,7 +17,8 @@ async function main() { extensionDevelopmentPath, extensionTestsPath, version: 'stable', - launchArgs: ['--no-sandbox'], // Don't use --disable-gpu, gives error on Mac M1 Pro + // Don't use --disable-gpu, gives error on Mac M1 Pro + launchArgs: ['--no-sandbox', '--disable-extensions'], }) console.log('✅ All tests passed. Exiting process cleanly.') From a80a48dbe6008179e8ed2bdbafa4c9ab6d119f86 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 15:54:28 +0200 Subject: [PATCH 10/39] Add launch configuration for extension tests with appropriate arguments --- .vscode/launch.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.vscode/launch.json b/.vscode/launch.json index ace09d7..49f16de 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,6 +10,18 @@ "type": "extensionHost", "request": "launch", "args": ["--extensionDevelopmentPath=${workspaceFolder}"] + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": ["${workspaceFolder}/out/test/**/*.js"] } ] } From 8928b52baf8be11bf8c72d3907a6e95382e89f9d Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 16:13:28 +0200 Subject: [PATCH 11/39] Remove build directory --- build/completions/directiveCompletion.js | 85 -------- build/completions/loopObjCompletion.js | 72 ------- build/extension.js | 13 -- build/modules/Cursor.js | 66 ------ build/modules/completionItem.js | 46 ---- build/static/info/directivesInfo.js | 201 ------------------ build/static/info/loopInfo.js | 39 ---- build/static/snippets/directivesSnippets.js | 68 ------ build/test/runTests.js | 67 ------ .../completion/loopObjCompletion.test.js | 79 ------- build/test/suite/index.js | 71 ------- build/test/suite/utils/openTextDocument.js | 51 ----- build/test/suite/utils/triggerAutocomplete.js | 48 ----- src/test/suite/utils/triggerAutocomplete.ts | 9 - 14 files changed, 915 deletions(-) delete mode 100644 build/completions/directiveCompletion.js delete mode 100644 build/completions/loopObjCompletion.js delete mode 100644 build/extension.js delete mode 100644 build/modules/Cursor.js delete mode 100644 build/modules/completionItem.js delete mode 100644 build/static/info/directivesInfo.js delete mode 100644 build/static/info/loopInfo.js delete mode 100644 build/static/snippets/directivesSnippets.js delete mode 100644 build/test/runTests.js delete mode 100644 build/test/suite/completion/loopObjCompletion.test.js delete mode 100644 build/test/suite/index.js delete mode 100644 build/test/suite/utils/openTextDocument.js delete mode 100644 build/test/suite/utils/triggerAutocomplete.js delete mode 100644 src/test/suite/utils/triggerAutocomplete.ts diff --git a/build/completions/directiveCompletion.js b/build/completions/directiveCompletion.js deleted file mode 100644 index 21cadf2..0000000 --- a/build/completions/directiveCompletion.js +++ /dev/null @@ -1,85 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __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 }); -const vscode = __importStar(require("vscode")); -const completionItem_1 = __importDefault(require("../modules/completionItem")); -const directivesInfo_1 = __importDefault(require("../static/info/directivesInfo")); -const directivesSnippets_1 = __importDefault(require("../static/snippets/directivesSnippets")); -const Cursor_1 = __importDefault(require("../modules/Cursor")); -const DIR_START_REG = /(? d.label.slice(1).startsWith(partialDir)); - }, -}, ...triggerChars); diff --git a/build/completions/loopObjCompletion.js b/build/completions/loopObjCompletion.js deleted file mode 100644 index 71844a4..0000000 --- a/build/completions/loopObjCompletion.js +++ /dev/null @@ -1,72 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __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 }); -const vscode = __importStar(require("vscode")); -const completionItem_1 = __importDefault(require("../modules/completionItem")); -const loopInfo_1 = __importDefault(require("../static/info/loopInfo")); -const Cursor_1 = __importDefault(require("../modules/Cursor")); -const IGNORE_REG = /{{--\s*@(each|for)/g; -const START_REG = /@(each|for)/g; -const END_REG = /@end/g; -const LOOP_VAR_REG = /\bloop\./g; -const triggerChars = ['.', 'l']; -exports.default = vscode.languages.registerCompletionItemProvider({ language: 'textwire' }, { - provideCompletionItems(doc, pos) { - const cursor = new Cursor_1.default(pos, doc); - if (cursor.notBetween(START_REG, END_REG, IGNORE_REG)) { - return []; - } - if (cursor.prevCharIs('l')) { - const field = vscode.CompletionItemKind.Variable; - return [(0, completionItem_1.default)('loop', loopInfo_1.default.loop, field)]; - } - const range = new vscode.Range(pos.with(pos.line, 0), pos); - const textBefore = doc.getText(range); - const match = LOOP_VAR_REG.test(textBefore.trim()); - if (!match) { - return []; - } - const field = vscode.CompletionItemKind.Field; - return [ - (0, completionItem_1.default)('index', loopInfo_1.default.index, field), - (0, completionItem_1.default)('first', loopInfo_1.default.first, field), - (0, completionItem_1.default)('last', loopInfo_1.default.last, field), - (0, completionItem_1.default)('iter', loopInfo_1.default.iter, field), - ]; - }, -}, ...triggerChars); diff --git a/build/extension.js b/build/extension.js deleted file mode 100644 index 6c898c9..0000000 --- a/build/extension.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.activate = activate; -exports.deactivate = deactivate; -const loopObjCompletion_1 = __importDefault(require("./completions/loopObjCompletion")); -const directiveCompletion_1 = __importDefault(require("./completions/directiveCompletion")); -function activate(ctx) { - ctx.subscriptions.push(loopObjCompletion_1.default, directiveCompletion_1.default); -} -function deactivate() { } diff --git a/build/modules/Cursor.js b/build/modules/Cursor.js deleted file mode 100644 index b1dec51..0000000 --- a/build/modules/Cursor.js +++ /dev/null @@ -1,66 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -const vscode = __importStar(require("vscode")); -class Cursor { - constructor(pos, doc) { - this.pos = pos; - this.doc = doc; - } - prevChar() { - return this.doc.getText(new vscode.Range(this.pos.translate(0, -1), this.pos)); - } - prevCharIs(char) { - return this.prevChar() === char; - } - isBetween(startReg, endReg, ignoreReg) { - const textBefore = this.textBefore(); - if (ignoreReg.test(textBefore)) { - return false; - } - const startMatches = Array.from(textBefore.matchAll(startReg)); - const endMatches = Array.from(textBefore.matchAll(endReg)); - const lastStart = startMatches.pop() || false; - const lastEnd = endMatches.pop() || false; - return lastStart && (!lastEnd || lastStart.index > lastEnd.index); - } - notBetween(startReg, endReg, ignoreReg) { - return !this.isBetween(startReg, endReg, ignoreReg); - } - textBefore() { - return this.doc.getText(new vscode.Range(new vscode.Position(0, 0), this.pos)); - } -} -exports.default = Cursor; diff --git a/build/modules/completionItem.js b/build/modules/completionItem.js deleted file mode 100644 index 73b7d7f..0000000 --- a/build/modules/completionItem.js +++ /dev/null @@ -1,46 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.default = completionItem; -const vscode = __importStar(require("vscode")); -function completionItem(label, documentation, kind, insert) { - const item = new vscode.CompletionItem(label, kind); - item.insertText = new vscode.SnippetString(insert || label); - item.documentation = new vscode.MarkdownString(documentation.replace(/\n/g, ' \n')); - if (label.startsWith('@')) { - item.filterText = label.slice(1); - } - return item; -} diff --git a/build/static/info/directivesInfo.js b/build/static/info/directivesInfo.js deleted file mode 100644 index 3bd4e7a..0000000 --- a/build/static/info/directivesInfo.js +++ /dev/null @@ -1,201 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const _if = `(directive) -Conditionally render content. - -\`\`\`textwire -@if(condition) -

content

-@end -\`\`\``; -const ifElse = `(directive) -Conditionally render content and provide an alternative that will be rendered when the condition is \`false\`. - -\`\`\`textwire -@if(condition) -

content

-@else -

alternative content

-@end -\`\`\``; -const each = `(directive) -Loop that iterates over arrays. - -\`\`\`textwire -@each(item in items) -

{{ item }}

-@end -\`\`\``; -const _for = `(directive) -For loop that iterates while the condition is \`true\`. - -\`\`\`textwire -@for(i = 0; i < items.len(); i++) -

{{ items[i] }}

-@end -\`\`\``; -const forElse = `(directive) -For loop that iterates while the condition is true, with a fallback for initially falsy conditions. For example, when the array is empty. - -\`\`\`textwire -@for(i = 0; i < items.len(); i++) -

{{ items[i] }}

-@else -

No items available.

-@end -\`\`\``; -const eachElse = `(directive) -Loop that iterates over arrays, with a fallback for empty arrays. - -\`\`\`textwire -@each(item in items) -

{{ item }}

-@else -

No items available.

-@end -\`\`\` - -Use the \`@else\` directive to specify content that will be displayed if the array is empty.`; -const dump = `(directive) -Debugging helper to output the value of variables. - -\`\`\`textwire -@dump(variable) -\`\`\` - -Outputs the value of variables, objects, arrays, strings, and other data types to the screen.`; -const use = `(directive) -Specify the layout file to be used for rendering the current template. - -\`\`\`textwire -@use('layoutName') -\`\`\` - -This directive includes the layout file specified, which defines the overall structure of the page.`; -const insert = `(directive) -Inject content into reserved placeholders defined in the layout file by providing a second argument as content. - -\`\`\`textwire -@insert('reservedName', 'content') -\`\`\` - -Use this directive in to specify content for placeholders in the layout file.`; -const insertEnd = `(directive) -Inject content into reserved placeholders defined in the layout file by providing a block of content. - -\`\`\`textwire -@insert('reservedName') -

content

-@end -\`\`\` - -Use this directive to specify content for placeholders in the layout file.`; -const reserve = `(directive) -Reserve placeholders for dynamic content to be injected by templates using the \`@insert\` directive. - -\`\`\`textwire -@reserve('reservedName') -\`\`\` - -Use this directive to specify a placeholder only in the layout file.`; -const comp = `(directive) -Components help organize and structure templates by encapsulating reusable parts of your UI. - -\`\`\`textwire -@component('path/to', { prop }) -\`\`\` - -Use this directive to include a component in your template.`; -const compSlot = `(directive) -Components help organize and structure templates by encapsulating reusable parts of your UI. - -\`\`\`textwire -@component('path/to', { prop }) - @slot -

content

- @end -@end -\`\`\` - -Use this directive to include a component in your template with slots.`; -const slot = `(directive) -Define a default slot in a component to provide a placeholder for content. - -\`\`\`textwire -@slot -

content

-@end -\`\`\``; -const slotDef = `(directive) -Define a named slot in a component to provide a placeholder for content. - -\`\`\`textwire -@slot('name') -

content

-@end -\`\`\``; -const ifElseif = `(directive) -Conditionally render content with additional conditions using \`@elseif\`. - -\`\`\`textwire -@if(condition1) -

condition1 is true

-@elseif(condition2) -

condition2 is true

-@end -\`\`\` - -Use the \`@elseif\` directive to handle additional conditional branches. If none of the conditions are met, use \`@else\` to provide fallback content.`; -const end = `(directive) -End a directive block by using the \`@end\` directive. - -\`\`\`textwire -@end -\`\`\``; -const _break = `(directive) -Exits a loop early when used inside a loop block. - -\`\`\`textwire -@break -\`\`\``; -const _continue = `(directive) -Skips the current iteration of a loop when used inside a loop block. - -\`\`\`textwire -@continue -\`\`\``; -const breakIf = `(directive) -Exits a loop early when the specified condition is met. - -\`\`\`textwire -@breakIf(condition) -\`\`\``; -const continueIf = `(directive) -Skips the current iteration of a loop when the specified condition is met. - -\`\`\`textwire -@continueIf(condition) -\`\`\``; -exports.default = { - if: _if, - ifElse, - ifElseif, - each, - eachElse, - for: _for, - forElse, - dump, - use, - insert, - insertEnd, - reserve, - comp, - compSlot, - slot, - slotDef, - end, - break: _break, - breakIf, - continue: _continue, - continueIf, -}; diff --git a/build/static/info/loopInfo.js b/build/static/info/loopInfo.js deleted file mode 100644 index a17f472..0000000 --- a/build/static/info/loopInfo.js +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const index = `(property) index: int -The current iteration of the loop. Starts with 0. - -\`\`\`textwire -{{ loop.index }} -\`\`\``; -const first = `(property) first: bool -Returns \`true\` if this is the first iteration of the loop. - -\`\`\`textwire -{{ loop.first }} -\`\`\``; -const last = `(property) last: bool -Returns \`true\` if this is the last iteration of the loop. - -\`\`\`textwire -{{ loop.last }} -\`\`\``; -const iter = `(property) iter: int -The current iteration of the loop. Starts with 1. - -\`\`\`textwire -{{ loop.iter }} -\`\`\``; -const loop = `(variable) loop: object -The loop object contains information about the current loop iteration. - -\`\`\`textwire -{{ loop.last }} -\`\`\``; -exports.default = { - index, - first, - last, - iter, - loop, -}; diff --git a/build/static/snippets/directivesSnippets.js b/build/static/snippets/directivesSnippets.js deleted file mode 100644 index 2340566..0000000 --- a/build/static/snippets/directivesSnippets.js +++ /dev/null @@ -1,68 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const _if = `if($1) - $2 -@end`; -const ifElse = `if($1) - $2 -@else - $3 -@end`; -const each = `each($1 in $2) - $3 -@end`; -const eachElse = `each($1 in $2) - $3 -@else - $4 -@end`; -const _for = `for(i = 0; i < $1; i++) - $3 -@end`; -const forElse = `for(i = 0; i < $1; i++) - $3 -@else - $4 -@end`; -const insertEnd = `insert($1) - $2 -@end`; -const compSlot = `component($1) - @slot - $2 - @end -@end`; -const slot = `slot($1) - $1 -@end`; -const slotDef = `slot - $2 -@end`; -const ifElseif = `if($1) - $2 -@elseif($2) - $3 -@end`; -exports.default = { - if: _if, - ifElse, - ifElseif, - each, - eachElse, - for: _for, - forElse, - compSlot, - slot, - slotDef, - insert: 'insert($1, $2)', - insertEnd, - dump: 'dump($1)', - use: 'use($1)', - reserve: 'reserve($1)', - comp: 'component($1)', - end: 'end', - break: 'break', - continue: 'continue', - breakIf: 'breakIf($1)', - continueIf: 'continueIf($1)', -}; diff --git a/build/test/runTests.js b/build/test/runTests.js deleted file mode 100644 index 8644a7a..0000000 --- a/build/test/runTests.js +++ /dev/null @@ -1,67 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const path = __importStar(require("path")); -const test_electron_1 = require("@vscode/test-electron"); -function main() { - return __awaiter(this, void 0, void 0, function* () { - try { - const extensionDevelopmentPath = path.resolve(__dirname, '../../'); - const extensionTestsPath = path.resolve(__dirname, './suite/index'); - yield (0, test_electron_1.runTests)({ - extensionDevelopmentPath, - extensionTestsPath, - version: 'stable', - launchArgs: ['--no-sandbox'], - }); - console.log('✅ All tests passed. Exiting process cleanly.'); - process.exit(0); - } - catch (err) { - console.error('❌ Error running tests:', err); - process.exit(1); - } - }); -} -main(); diff --git a/build/test/suite/completion/loopObjCompletion.test.js b/build/test/suite/completion/loopObjCompletion.test.js deleted file mode 100644 index 1a6206d..0000000 --- a/build/test/suite/completion/loopObjCompletion.test.js +++ /dev/null @@ -1,79 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const assert = __importStar(require("assert")); -const vscode = __importStar(require("vscode")); -const triggerAutocomplete_1 = __importDefault(require("../utils/triggerAutocomplete")); -const openTextDocument_1 = __importDefault(require("../utils/openTextDocument")); -suite('Autocomplete - loop variable', () => { - test('Should suggest loop properties inside @each', () => __awaiter(void 0, void 0, void 0, function* () { - const content = `@each(item in items){{ loop. }}@end`; - const doc = yield (0, openTextDocument_1.default)(content); - const pos = new vscode.Position(0, 29); - const completions = yield (0, triggerAutocomplete_1.default)(pos, doc.uri); - if (!completions) { - assert.fail('No completions found!'); - } - const expectedLabels = ['index', 'first', 'last', 'iter']; - for (const expectedLabel of expectedLabels) { - assert.ok(completions.items.some(actualItem => actualItem.label === expectedLabel), `Expected completion '${expectedLabel}' not found!`); - } - })); - test('Should not suggest loop properties outside @each', () => __awaiter(void 0, void 0, void 0, function* () { - const content = `
{{ loop. }}
@each(item in items){{ item }}@end`; - const doc = yield (0, openTextDocument_1.default)(content); - const pos = new vscode.Position(0, 12); - const completions = yield (0, triggerAutocomplete_1.default)(pos, doc.uri); - if (!completions) { - assert.fail('No completions found! Should have HTML native completions'); - } - const shouldNotHave = ['index', 'first', 'last', 'iter']; - for (const expectedLabel of shouldNotHave) { - assert.ok(!completions.items.some(actualItem => actualItem.label === expectedLabel), `Unexpected completion '${expectedLabel}' found!`); - } - })); -}); diff --git a/build/test/suite/index.js b/build/test/suite/index.js deleted file mode 100644 index 73f3671..0000000 --- a/build/test/suite/index.js +++ /dev/null @@ -1,71 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __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.run = run; -const path = __importStar(require("path")); -const mocha_1 = __importDefault(require("mocha")); -const glob_1 = require("glob"); -function run() { - const mocha = new mocha_1.default({ - ui: 'tdd', - color: true, - }); - const testsRoot = path.resolve(__dirname, '..'); - return new Promise((c, e) => { - (0, glob_1.glob)('**/**.test.js', { cwd: testsRoot }) - .then(files => { - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); - try { - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } - else { - c(); - } - }); - } - catch (err) { - e(err); - } - }) - .catch(err => { - return e(err); - }); - }); -} diff --git a/build/test/suite/utils/openTextDocument.js b/build/test/suite/utils/openTextDocument.js deleted file mode 100644 index e1f2deb..0000000 --- a/build/test/suite/utils/openTextDocument.js +++ /dev/null @@ -1,51 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const vscode = __importStar(require("vscode")); -exports.default = (content) => __awaiter(void 0, void 0, void 0, function* () { - return yield vscode.workspace.openTextDocument({ - language: 'textwire', - content, - }); -}); diff --git a/build/test/suite/utils/triggerAutocomplete.js b/build/test/suite/utils/triggerAutocomplete.js deleted file mode 100644 index 8fe40f6..0000000 --- a/build/test/suite/utils/triggerAutocomplete.js +++ /dev/null @@ -1,48 +0,0 @@ -"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 () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const vscode = __importStar(require("vscode")); -exports.default = (pos, uri) => __awaiter(void 0, void 0, void 0, function* () { - return yield vscode.commands.executeCommand('vscode.executeCompletionItemProvider', uri, pos); -}); diff --git a/src/test/suite/utils/triggerAutocomplete.ts b/src/test/suite/utils/triggerAutocomplete.ts deleted file mode 100644 index 6afe632..0000000 --- a/src/test/suite/utils/triggerAutocomplete.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as vscode from 'vscode' - -export default async (pos: vscode.Position, uri: vscode.Uri) => { - return await vscode.commands.executeCommand( - 'vscode.executeCompletionItemProvider', - uri, - pos, - ) -} From a9a97bde9f4d73a441898a3381e49943b7dcfce8 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 16:13:43 +0200 Subject: [PATCH 12/39] Add colors to the test output --- .vscode-test.js | 3 ++- package.json | 2 +- src/test/suite/index.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.vscode-test.js b/.vscode-test.js index aa4e254..9d37094 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -2,12 +2,13 @@ const { defineConfig } = require('@vscode/test-cli') module.exports = defineConfig([ { - label: 'unitTests', + label: 'tests', files: 'out/test/**/*.test.js', version: 'insiders', mocha: { ui: 'tdd', timeout: 20000, + color: true, }, }, ]) diff --git a/package.json b/package.json index cb1de66..568aea9 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "login": "vsce login SerhiiCho", "deploy": "vsce publish", "compile": "tsc -p .", - "test": "npm run compile && node ./out/test/runTests.js" + "test": "npm run compile && FORCE_COLOR=1 node ./out/test/runTests.js" }, "devDependencies": { "@types/mocha": "^10.0.10", diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index d029ec8..6ab3329 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -5,6 +5,7 @@ import { glob } from 'glob' export function run(): Promise { const mocha = new Mocha({ ui: 'tdd', + timeout: 20000, color: true, }) From 3f62c719682f882a0583584c143ca5f8eb9eb6d5 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 16:13:51 +0200 Subject: [PATCH 13/39] Remove some comments --- src/test/runTests.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/test/runTests.ts b/src/test/runTests.ts index 87b69dc..6feae74 100644 --- a/src/test/runTests.ts +++ b/src/test/runTests.ts @@ -4,12 +4,7 @@ import { runTests } from '@vscode/test-electron' async function main() { try { - // The folder containing the Extension Manifest package.json - // Passed to `--extensionDevelopmentPath` const extensionDevelopmentPath = path.resolve(__dirname, '../../') - - // The path to the extension test runner script - // Passed to --extensionTestsPath const extensionTestsPath = path.resolve(__dirname, './suite/index') // Download VS Code, unzip it and run the integration test From 019b2295ee78d7048e7981b9549c051e4d24e2de Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 16:13:57 +0200 Subject: [PATCH 14/39] Refactor test --- .../completion/directivesCompletion.test.ts | 10 ++++++++++ ...letion.test.ts => loopsCompletion.test.ts} | 19 ++++++++++++------- src/test/suite/utils/triggerCompletion.ts | 9 +++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 src/test/suite/completion/directivesCompletion.test.ts rename src/test/suite/completion/{loopObjCompletion.test.ts => loopsCompletion.test.ts} (68%) create mode 100644 src/test/suite/utils/triggerCompletion.ts diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts new file mode 100644 index 0000000..3c15e26 --- /dev/null +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -0,0 +1,10 @@ +import * as assert from 'assert' +import * as vscode from 'vscode' +import triggerCompletion from '../utils/triggerCompletion' +import openTextDocument from '../utils/openTextDocument' + +suite('Directives Completion', () => { + test('Should suggest directives in HTML', async () => { + return + }) +}) diff --git a/src/test/suite/completion/loopObjCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts similarity index 68% rename from src/test/suite/completion/loopObjCompletion.test.ts rename to src/test/suite/completion/loopsCompletion.test.ts index 938828c..5820e1a 100644 --- a/src/test/suite/completion/loopObjCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -1,15 +1,15 @@ import * as assert from 'assert' import * as vscode from 'vscode' -import triggerAutocomplete from '../utils/triggerAutocomplete' +import triggerCompletion from '../utils/triggerCompletion' import openTextDocument from '../utils/openTextDocument' -suite('Autocomplete - loop variable', () => { - test('Should suggest loop properties inside @each', async () => { +suite('Loops Completion', () => { + test('suggests loop properties inside @each', async () => { const content = `@each(item in items){{ loop. }}@end` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 29) - const completions = await triggerAutocomplete(pos, doc.uri) + const completions = await triggerCompletion(pos, doc.uri) if (!completions) { assert.fail('No completions found!') @@ -25,12 +25,12 @@ suite('Autocomplete - loop variable', () => { } }) - test('Should not suggest loop properties outside @each', async () => { + test('does not suggest loop properties outside @each', async () => { const content = `
{{ loop. }}
@each(item in items){{ item }}@end` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 12) - const completions = await triggerAutocomplete(pos, doc.uri) + const completions = await triggerCompletion(pos, doc.uri) if (!completions) { assert.fail('No completions found! Should have HTML native completions') @@ -42,7 +42,12 @@ suite('Autocomplete - loop variable', () => { for (const expectedLabel of shouldNotHave) { assert.ok( - !completions.items.some(actualItem => actualItem.label === expectedLabel), + !completions.items.some(actualItem => { + return ( + actualItem.label === expectedLabel && + actualItem.kind === vscode.CompletionItemKind.Field + ) + }), `Unexpected completion '${expectedLabel}' found!`, ) } diff --git a/src/test/suite/utils/triggerCompletion.ts b/src/test/suite/utils/triggerCompletion.ts new file mode 100644 index 0000000..6afe632 --- /dev/null +++ b/src/test/suite/utils/triggerCompletion.ts @@ -0,0 +1,9 @@ +import * as vscode from 'vscode' + +export default async (pos: vscode.Position, uri: vscode.Uri) => { + return await vscode.commands.executeCommand( + 'vscode.executeCompletionItemProvider', + uri, + pos, + ) +} From e029bf6fff9148bda2b176f6d6794247a8831d19 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 17:03:00 +0200 Subject: [PATCH 15/39] Fix tests --- package.json | 2 +- src/completions/loopObjCompletion.ts | 4 +- .../completion/directivesCompletion.test.ts | 2 +- .../suite/completion/loopsCompletion.test.ts | 108 +++++++++++------- 4 files changed, 71 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index 568aea9..549fee2 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "login": "vsce login SerhiiCho", "deploy": "vsce publish", "compile": "tsc -p .", - "test": "npm run compile && FORCE_COLOR=1 node ./out/test/runTests.js" + "test": "rm -r ./out && npm run compile && FORCE_COLOR=1 node ./out/test/runTests.js" }, "devDependencies": { "@types/mocha": "^10.0.10", diff --git a/src/completions/loopObjCompletion.ts b/src/completions/loopObjCompletion.ts index 8364c12..26bd6e3 100644 --- a/src/completions/loopObjCompletion.ts +++ b/src/completions/loopObjCompletion.ts @@ -6,7 +6,7 @@ import Cursor from '../modules/Cursor' const IGNORE_REG = /{{--\s*@(each|for)/g const START_REG = /@(each|for)/g const END_REG = /@end/g -const LOOP_VAR_REG = /\bloop\./g +const LOOP_VAR_REG = /loop\./ const triggerChars = ['.', 'l'] @@ -30,7 +30,7 @@ export default vscode.languages.registerCompletionItemProvider( const range = new vscode.Range(pos.with(pos.line, 0), pos) const textBefore = doc.getText(range) - const match = LOOP_VAR_REG.test(textBefore.trim()) + const match = LOOP_VAR_REG.test(textBefore) if (!match) { return [] diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 3c15e26..958d573 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -4,7 +4,7 @@ import triggerCompletion from '../utils/triggerCompletion' import openTextDocument from '../utils/openTextDocument' suite('Directives Completion', () => { - test('Should suggest directives in HTML', async () => { + test('suggests directives in HTML', async () => { return }) }) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 5820e1a..458b348 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -4,52 +4,78 @@ import triggerCompletion from '../utils/triggerCompletion' import openTextDocument from '../utils/openTextDocument' suite('Loops Completion', () => { - test('suggests loop properties inside @each', async () => { - const content = `@each(item in items){{ loop. }}@end` - const doc = await openTextDocument(content) - const pos = new vscode.Position(0, 29) + const loopTests = [ + { + name: '@each loop', + content: `@each(item in items){{ loop. }}@end`, + position: new vscode.Position(0, 28), + expected: ['index', 'first', 'last', 'iter'], + }, + { + name: '@for loop', + content: `@for(i = 0; i < 5; i++){{ loop. }}@end`, + position: new vscode.Position(0, 31), + expected: ['index', 'first', 'last', 'iter'], + }, + ] - const completions = await triggerCompletion(pos, doc.uri) + loopTests.forEach(({ name, content, position, expected }) => { + test(`suggests loop properties inside ${name}`, async () => { + const doc = await openTextDocument(content) + const completions = await triggerCompletion(position, doc.uri) - if (!completions) { - assert.fail('No completions found!') - } + if (!completions) { + assert.fail('No completions found!') + } - const expectedLabels = ['index', 'first', 'last', 'iter'] + for (const expectedLabel of expected) { + assert.ok( + completions.items.some(item => item.label === expectedLabel), + `Expected completion '${expectedLabel}' not found!`, + ) - for (const expectedLabel of expectedLabels) { - assert.ok( - completions.items.some(actualItem => actualItem.label === expectedLabel), - `Expected completion '${expectedLabel}' not found!`, - ) - } + assert.ok( + completions.items.length === expected.length, + 'Unexpected completions found!', + ) + } + }) }) - test('does not suggest loop properties outside @each', async () => { - const content = `
{{ loop. }}
@each(item in items){{ item }}@end` - const doc = await openTextDocument(content) - const pos = new vscode.Position(0, 12) - - const completions = await triggerCompletion(pos, doc.uri) - - if (!completions) { - assert.fail('No completions found! Should have HTML native completions') - } - - // There are still going to be some HTML completions, we are - // just making sure that suggestions don't include: - const shouldNotHave = ['index', 'first', 'last', 'iter'] - - for (const expectedLabel of shouldNotHave) { - assert.ok( - !completions.items.some(actualItem => { - return ( - actualItem.label === expectedLabel && - actualItem.kind === vscode.CompletionItemKind.Field - ) - }), - `Unexpected completion '${expectedLabel}' found!`, - ) - } + const outsideLoopTests = [ + { + name: '@each loop', + content: `
{{ loop. }}
@each(item in items){{ item }}@end`, + position: new vscode.Position(0, 13), + }, + { + name: '@for loop', + content: `
{{ loop. }}
@for(i = 0; i < 3; i++){{ i }}@end`, + position: new vscode.Position(0, 13), + }, + ] + + outsideLoopTests.forEach(({ name, content, position }) => { + test(`does not suggest loop properties outside ${name}`, async () => { + const doc = await openTextDocument(content) + const completions = await triggerCompletion(position, doc.uri) + + if (!completions) { + assert.fail('No completions found! Should have HTML native completions') + } + + const shouldNotHave = ['index', 'first', 'last', 'iter'] + + for (const expectedLabel of shouldNotHave) { + assert.ok( + !completions.items.some( + item => + item.label === expectedLabel && + item.kind === vscode.CompletionItemKind.Field, + ), + `Unexpected completion '${expectedLabel}' found!`, + ) + } + }) }) }) From 6835d6b200cbd4043b55637b3e0283edc620e6ae Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 17:07:18 +0200 Subject: [PATCH 16/39] Refactor loop object completion logic to simplify variable check --- src/completions/loopObjCompletion.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/completions/loopObjCompletion.ts b/src/completions/loopObjCompletion.ts index 26bd6e3..32db1b7 100644 --- a/src/completions/loopObjCompletion.ts +++ b/src/completions/loopObjCompletion.ts @@ -6,7 +6,6 @@ import Cursor from '../modules/Cursor' const IGNORE_REG = /{{--\s*@(each|for)/g const START_REG = /@(each|for)/g const END_REG = /@end/g -const LOOP_VAR_REG = /loop\./ const triggerChars = ['.', 'l'] @@ -29,10 +28,9 @@ export default vscode.languages.registerCompletionItemProvider( } const range = new vscode.Range(pos.with(pos.line, 0), pos) - const textBefore = doc.getText(range) - const match = LOOP_VAR_REG.test(textBefore) + const textBefore = doc.getText(range).trimEnd() - if (!match) { + if (!textBefore.endsWith('loop.')) { return [] } From 15cabdd7feafd6e83d5cd05489948bdadf2b8450 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Fri, 31 Jan 2025 17:08:32 +0200 Subject: [PATCH 17/39] Simplify completion check in loops test by removing unnecessary item kind condition --- src/test/suite/completion/loopsCompletion.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 458b348..876d73a 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -68,11 +68,7 @@ suite('Loops Completion', () => { for (const expectedLabel of shouldNotHave) { assert.ok( - !completions.items.some( - item => - item.label === expectedLabel && - item.kind === vscode.CompletionItemKind.Field, - ), + !completions.items.some(item => item.label === expectedLabel), `Unexpected completion '${expectedLabel}' found!`, ) } From 38efc985c250614fe017bd680752ce907aa98c9e Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sat, 1 Feb 2025 10:50:08 +0200 Subject: [PATCH 18/39] Simplified test file --- src/test/suite/completion/loopsCompletion.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 876d73a..94eb06e 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -6,13 +6,13 @@ import openTextDocument from '../utils/openTextDocument' suite('Loops Completion', () => { const loopTests = [ { - name: '@each loop', + name: '@each', content: `@each(item in items){{ loop. }}@end`, position: new vscode.Position(0, 28), expected: ['index', 'first', 'last', 'iter'], }, { - name: '@for loop', + name: '@for', content: `@for(i = 0; i < 5; i++){{ loop. }}@end`, position: new vscode.Position(0, 31), expected: ['index', 'first', 'last', 'iter'], @@ -20,7 +20,7 @@ suite('Loops Completion', () => { ] loopTests.forEach(({ name, content, position, expected }) => { - test(`suggests loop properties inside ${name}`, async () => { + test(`suggests loop properties inside ${name} loop`, async () => { const doc = await openTextDocument(content) const completions = await triggerCompletion(position, doc.uri) @@ -44,19 +44,19 @@ suite('Loops Completion', () => { const outsideLoopTests = [ { - name: '@each loop', + name: '@each', content: `
{{ loop. }}
@each(item in items){{ item }}@end`, position: new vscode.Position(0, 13), }, { - name: '@for loop', + name: '@for', content: `
{{ loop. }}
@for(i = 0; i < 3; i++){{ i }}@end`, position: new vscode.Position(0, 13), }, ] outsideLoopTests.forEach(({ name, content, position }) => { - test(`does not suggest loop properties outside ${name}`, async () => { + test(`does not suggest loop properties outside ${name} loop`, async () => { const doc = await openTextDocument(content) const completions = await triggerCompletion(position, doc.uri) From c5f913bb9cd85de2a77b815e554ca9337d9755ab Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sat, 1 Feb 2025 18:38:00 +0200 Subject: [PATCH 19/39] Refactor loop completion tests to standardize position variable naming --- .../suite/completion/loopsCompletion.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 94eb06e..a5b8ee4 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -8,21 +8,21 @@ suite('Loops Completion', () => { { name: '@each', content: `@each(item in items){{ loop. }}@end`, - position: new vscode.Position(0, 28), + pos: new vscode.Position(0, 28), expected: ['index', 'first', 'last', 'iter'], }, { name: '@for', content: `@for(i = 0; i < 5; i++){{ loop. }}@end`, - position: new vscode.Position(0, 31), + pos: new vscode.Position(0, 31), expected: ['index', 'first', 'last', 'iter'], }, ] - loopTests.forEach(({ name, content, position, expected }) => { + loopTests.forEach(({ name, content, pos, expected }) => { test(`suggests loop properties inside ${name} loop`, async () => { const doc = await openTextDocument(content) - const completions = await triggerCompletion(position, doc.uri) + const completions = await triggerCompletion(pos, doc.uri) if (!completions) { assert.fail('No completions found!') @@ -46,19 +46,19 @@ suite('Loops Completion', () => { { name: '@each', content: `
{{ loop. }}
@each(item in items){{ item }}@end`, - position: new vscode.Position(0, 13), + pos: new vscode.Position(0, 13), }, { name: '@for', content: `
{{ loop. }}
@for(i = 0; i < 3; i++){{ i }}@end`, - position: new vscode.Position(0, 13), + pos: new vscode.Position(0, 13), }, ] - outsideLoopTests.forEach(({ name, content, position }) => { + outsideLoopTests.forEach(({ name, content, pos }) => { test(`does not suggest loop properties outside ${name} loop`, async () => { const doc = await openTextDocument(content) - const completions = await triggerCompletion(position, doc.uri) + const completions = await triggerCompletion(pos, doc.uri) if (!completions) { assert.fail('No completions found! Should have HTML native completions') From 646d020d32a29da45d2afc9b57c179e0622791c1 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sat, 1 Feb 2025 18:57:05 +0200 Subject: [PATCH 20/39] Add test for directives --- .../completion/directivesCompletion.test.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 958d573..097a115 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -2,9 +2,32 @@ import * as assert from 'assert' import * as vscode from 'vscode' import triggerCompletion from '../utils/triggerCompletion' import openTextDocument from '../utils/openTextDocument' +import { DIRECTIVES } from '../utils/static/directiveNames' suite('Directives Completion', () => { test('suggests directives in HTML', async () => { - return + const content = `

@

` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 5) + const completions = await triggerCompletion(pos, doc.uri) + + if (!completions) { + assert.fail('No completions found!') + } + + assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') + + assert.strictEqual( + completions.items.length, + DIRECTIVES.length, + `Expected ${DIRECTIVES.length} completions, but got ${completions.items.length}`, + ) + + for (const item of completions.items) { + assert.ok( + DIRECTIVES.includes(item.label), + `Unexpected completion found: ${item.label}`, + ) + } }) }) From 1fb9866abf0fc13c8c98caa7e39486027eea5330 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sat, 1 Feb 2025 18:57:11 +0200 Subject: [PATCH 21/39] Add directive names constants for improved code clarity --- src/test/suite/utils/static/directiveNames.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/suite/utils/static/directiveNames.ts diff --git a/src/test/suite/utils/static/directiveNames.ts b/src/test/suite/utils/static/directiveNames.ts new file mode 100644 index 0000000..d60396b --- /dev/null +++ b/src/test/suite/utils/static/directiveNames.ts @@ -0,0 +1,23 @@ +export const DIRECTIVES = [ + '@if', + '@if @else', + '@if @elseif', + '@use', + '@insert', + '@insert @end', + '@reserve', + '@component', + '@component @slot', + '@slot', + '@slot(name)', + '@each', + '@each @else', + '@for', + '@for @else', + '@dump', + '@end', + '@break', + '@continue', + '@continueIf', + '@breakIf', +] From 9c1ba1cb6cd5a7d5153e6b245c1aca5e972cf0b1 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sat, 1 Feb 2025 19:03:02 +0200 Subject: [PATCH 22/39] Code refactoring. Change importing files --- src/completions/directiveCompletion.ts | 8 ++++---- src/completions/loopObjCompletion.ts | 16 ++++++++-------- src/modules/Cursor.ts | 2 +- src/modules/completionItem.ts | 6 ++---- src/static/info/directivesInfo.ts | 2 +- src/static/info/loopInfo.ts | 2 +- src/static/snippets/directivesSnippets.ts | 2 +- .../completion/directivesCompletion.test.ts | 4 ++-- .../suite/completion/loopsCompletion.test.ts | 4 ++-- src/test/suite/utils/openTextDocument.ts | 2 +- src/test/suite/utils/triggerCompletion.ts | 2 +- 11 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/completions/directiveCompletion.ts b/src/completions/directiveCompletion.ts index 1e07098..fe1467e 100644 --- a/src/completions/directiveCompletion.ts +++ b/src/completions/directiveCompletion.ts @@ -1,8 +1,8 @@ import * as vscode from 'vscode' -import completionItem from '../modules/completionItem' -import info from '../static/info/directivesInfo' -import snip from '../static/snippets/directivesSnippets' -import Cursor from '../modules/Cursor' +import { completionItem } from '../modules/completionItem' +import { directivesInfo as info } from '../static/info/directivesInfo' +import { directivesSnippets as snip } from '../static/snippets/directivesSnippets' +import { Cursor } from '../modules/Cursor' const DIR_START_REG = /(? { diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index a5b8ee4..2ff4456 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -1,7 +1,7 @@ import * as assert from 'assert' import * as vscode from 'vscode' -import triggerCompletion from '../utils/triggerCompletion' -import openTextDocument from '../utils/openTextDocument' +import { triggerCompletion } from '../utils/triggerCompletion' +import { openTextDocument } from '../utils/openTextDocument' suite('Loops Completion', () => { const loopTests = [ diff --git a/src/test/suite/utils/openTextDocument.ts b/src/test/suite/utils/openTextDocument.ts index e4567d7..abfbafd 100644 --- a/src/test/suite/utils/openTextDocument.ts +++ b/src/test/suite/utils/openTextDocument.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode' -export default async (content: string): Promise => { +export async function openTextDocument(content: string): Promise { return await vscode.workspace.openTextDocument({ language: 'textwire', content, diff --git a/src/test/suite/utils/triggerCompletion.ts b/src/test/suite/utils/triggerCompletion.ts index 6afe632..8bb6832 100644 --- a/src/test/suite/utils/triggerCompletion.ts +++ b/src/test/suite/utils/triggerCompletion.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode' -export default async (pos: vscode.Position, uri: vscode.Uri) => { +export async function triggerCompletion(pos: vscode.Position, uri: vscode.Uri) { return await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', uri, From 2711c0129721ccbde0fb2360060c28819283f93d Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 11:31:01 +0200 Subject: [PATCH 23/39] Rename var --- src/test/suite/completion/directivesCompletion.test.ts | 8 ++++---- src/test/suite/utils/static/directiveNames.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index bedec77..8ce321a 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -2,7 +2,7 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' -import { DIRECTIVES } from '../utils/static/directiveNames' +import { DIRECTIVE_NAMES } from '../utils/static/directiveNames' suite('Directives Completion', () => { test('suggests directives in HTML', async () => { @@ -19,13 +19,13 @@ suite('Directives Completion', () => { assert.strictEqual( completions.items.length, - DIRECTIVES.length, - `Expected ${DIRECTIVES.length} completions, but got ${completions.items.length}`, + DIRECTIVE_NAMES.length, + `Expected ${DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, ) for (const item of completions.items) { assert.ok( - DIRECTIVES.includes(item.label), + DIRECTIVE_NAMES.includes(item.label), `Unexpected completion found: ${item.label}`, ) } diff --git a/src/test/suite/utils/static/directiveNames.ts b/src/test/suite/utils/static/directiveNames.ts index d60396b..fd6a90e 100644 --- a/src/test/suite/utils/static/directiveNames.ts +++ b/src/test/suite/utils/static/directiveNames.ts @@ -1,4 +1,4 @@ -export const DIRECTIVES = [ +export const DIRECTIVE_NAMES = [ '@if', '@if @else', '@if @elseif', From 15f5912acdc261fbaa6255b3609b5e906c0da977 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 11:41:15 +0200 Subject: [PATCH 24/39] Code refactoring --- src/completions/directiveCompletion.ts | 5 +---- src/completions/loopObjCompletion.ts | 6 +----- src/modules/Cursor.ts | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/completions/directiveCompletion.ts b/src/completions/directiveCompletion.ts index fe1467e..183e95f 100644 --- a/src/completions/directiveCompletion.ts +++ b/src/completions/directiveCompletion.ts @@ -5,9 +5,6 @@ import { directivesSnippets as snip } from '../static/snippets/directivesSnippet import { Cursor } from '../modules/Cursor' const DIR_START_REG = /(? lastEnd.index) } - public notBetween(startReg: RegExp, endReg: RegExp, ignoreReg: RegExp): boolean { - return !this.isBetween(startReg, endReg, ignoreReg) + public isInsideBraces(): boolean { + const IGNORE = /\\\{\{/g + const START = /\{\{/g + const END = /\}\}/g + + return this.isBetween(START, END, IGNORE) + } + + public isInsideLoop(): boolean { + const IGNORE = /{{--\s*@(each|for)/g + const START = /@(each|for)/g + const END = /@end/g + + return this.isBetween(START, END, IGNORE) } private textBefore(): string { From 1d01972a4f253ece106bf6b12c0356c7c42cc0b4 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 11:49:23 +0200 Subject: [PATCH 25/39] Don't show loop directories outside of loops --- CHANGELOG.md | 1 + src/completions/directiveCompletion.ts | 69 ++++++++++++------- src/test/suite/utils/static/directiveNames.ts | 6 +- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d3ed59..47577c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [1.5.3] - 2025-01-30 - Added tests coverage to test all the necessary features +- Change so that you can only see @break, @breakIf, @continue, @continueIf directives when you are inside a loop. This is to avoid showing them when they are not needed ## [1.5.2] - 2025-01-30 - Fixed issue where loop object would only show autocomplete suggestions when it was inside `{{` and `}}` brackets diff --git a/src/completions/directiveCompletion.ts b/src/completions/directiveCompletion.ts index 183e95f..fcd36ac 100644 --- a/src/completions/directiveCompletion.ts +++ b/src/completions/directiveCompletion.ts @@ -28,32 +28,51 @@ export default vscode.languages.registerCompletionItemProvider( const partialDir = match[1] const field = vscode.CompletionItemKind.Snippet - const dirs = [ - completionItem('@if', info.if, field, snip.if), - completionItem('@if @else', info.ifElse, field, snip.ifElse), - completionItem('@if @elseif', info.ifElseif, field, snip.ifElseif), - completionItem('@use', info.use, field, snip.use), - completionItem('@insert', info.insert, field, snip.insert), - completionItem('@insert @end', info.insertEnd, field, snip.insertEnd), - completionItem('@reserve', info.reserve, field, snip.reserve), - completionItem('@component', info.comp, field, snip.comp), - completionItem('@component @slot', info.compSlot, field, snip.compSlot), - completionItem('@slot', info.slot, field, snip.slotDef), - completionItem('@slot(name)', info.slotDef, field, snip.slot), - completionItem('@each', info.each, field, snip.each), - completionItem('@each @else', info.eachElse, field, snip.eachElse), - completionItem('@for', info.for, field, snip.for), - completionItem('@for @else', info.forElse, field, snip.forElse), - completionItem('@dump', info.dump, field, snip.dump), - completionItem('@end', info.end, field, snip.end), - completionItem('@break', info.break, field, snip.break), - completionItem('@continue', info.continue, field, snip.continue), - completionItem('@continueIf', info.continueIf, field, snip.continueIf), - completionItem('@breakIf', info.breakIf, field, snip.breakIf), - ] - - return dirs.filter(d => d.label.slice(1).startsWith(partialDir)) + const dirs = cursor.isInsideLoop() + ? loopDirectories(field) + : otherDirectories(field) + + return dirs.filter(d => { + // remove the @ from the label + const label = d.label.slice(1) + return label.startsWith(partialDir) + }) }, }, ...triggerChars, ) + +function loopDirectories( + field: vscode.CompletionItemKind.Snippet, +): vscode.CompletionItem[] { + return [ + completionItem('@break', info.break, field, snip.break), + completionItem('@continue', info.continue, field, snip.continue), + completionItem('@continueIf', info.continueIf, field, snip.continueIf), + completionItem('@breakIf', info.breakIf, field, snip.breakIf), + ] +} + +function otherDirectories( + field: vscode.CompletionItemKind.Snippet, +): vscode.CompletionItem[] { + return [ + completionItem('@if', info.if, field, snip.if), + completionItem('@if @else', info.ifElse, field, snip.ifElse), + completionItem('@if @elseif', info.ifElseif, field, snip.ifElseif), + completionItem('@use', info.use, field, snip.use), + completionItem('@insert', info.insert, field, snip.insert), + completionItem('@insert @end', info.insertEnd, field, snip.insertEnd), + completionItem('@reserve', info.reserve, field, snip.reserve), + completionItem('@component', info.comp, field, snip.comp), + completionItem('@component @slot', info.compSlot, field, snip.compSlot), + completionItem('@slot', info.slot, field, snip.slotDef), + completionItem('@slot(name)', info.slotDef, field, snip.slot), + completionItem('@each', info.each, field, snip.each), + completionItem('@each @else', info.eachElse, field, snip.eachElse), + completionItem('@for', info.for, field, snip.for), + completionItem('@for @else', info.forElse, field, snip.forElse), + completionItem('@dump', info.dump, field, snip.dump), + completionItem('@end', info.end, field, snip.end), + ] +} diff --git a/src/test/suite/utils/static/directiveNames.ts b/src/test/suite/utils/static/directiveNames.ts index fd6a90e..1e88819 100644 --- a/src/test/suite/utils/static/directiveNames.ts +++ b/src/test/suite/utils/static/directiveNames.ts @@ -16,8 +16,6 @@ export const DIRECTIVE_NAMES = [ '@for @else', '@dump', '@end', - '@break', - '@continue', - '@continueIf', - '@breakIf', ] + +export const INSIDE_LOOP_DIRECTIVES = ['@break', '@continue', '@continueIf', '@breakIf'] From c67ca2f51994ac4ac19e990c5f3effb445ac6d71 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 11:56:01 +0200 Subject: [PATCH 26/39] Add test --- .../completion/directivesCompletion.test.ts | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 8ce321a..580fde2 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -2,7 +2,10 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' -import { DIRECTIVE_NAMES } from '../utils/static/directiveNames' +import { + DIRECTIVE_NAMES, + INSIDE_LOOP_DIRECTIVES as INSIDE_LOOP_DIRECTIVE_NAMES, +} from '../utils/static/directiveNames' suite('Directives Completion', () => { test('suggests directives in HTML', async () => { @@ -26,7 +29,43 @@ suite('Directives Completion', () => { for (const item of completions.items) { assert.ok( DIRECTIVE_NAMES.includes(item.label), - `Unexpected completion found: ${item.label}`, + `Directive ${item.label} not found in HTML`, + ) + + assert.ok( + !INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), + `Directive ${item.label} should not be suggested outside of a loop`, + ) + } + }) + + test('suggests loop directives inside @each loop', async () => { + const content = `
@each(item in items) {{ item }} @ @end
` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 38) + const completions = await triggerCompletion(pos, doc.uri) + + if (!completions) { + assert.fail('No completions found!') + } + + assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') + + assert.strictEqual( + completions.items.length, + INSIDE_LOOP_DIRECTIVE_NAMES.length, + `Expected ${INSIDE_LOOP_DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, + ) + + for (const item of completions.items) { + assert.ok( + INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), + `Directive ${item.label} not found inside a loop`, + ) + + assert.ok( + !DIRECTIVE_NAMES.includes(item.label), + `Directive ${item.label} should not be suggested inside of a loop`, ) } }) From dbae6cf246a1bbbbf9238d543dfeb8a37ac327ff Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 11:59:22 +0200 Subject: [PATCH 27/39] Add one more test --- .../completion/directivesCompletion.test.ts | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 580fde2..3a22530 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -39,34 +39,47 @@ suite('Directives Completion', () => { } }) - test('suggests loop directives inside @each loop', async () => { - const content = `
@each(item in items) {{ item }} @ @end
` - const doc = await openTextDocument(content) - const pos = new vscode.Position(0, 38) - const completions = await triggerCompletion(pos, doc.uri) + const loopTests = [ + { + name: '@each', + content: `
@each(item in items) {{ item }} @ @end
`, + pos: new vscode.Position(0, 38), + }, + { + name: '@for', + content: `
@for(c = 1; c < items.length(); c++) {{ items[c] }} @ @end
`, + pos: new vscode.Position(0, 58), + }, + ] - if (!completions) { - assert.fail('No completions found!') - } + loopTests.forEach(({ name, content, pos }) => { + test(`suggests loop directives inside ${name} loop`, async () => { + const doc = await openTextDocument(content) + const completions = await triggerCompletion(pos, doc.uri) - assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') + if (!completions) { + assert.fail('No completions found!') + } - assert.strictEqual( - completions.items.length, - INSIDE_LOOP_DIRECTIVE_NAMES.length, - `Expected ${INSIDE_LOOP_DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, - ) + assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') - for (const item of completions.items) { - assert.ok( - INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} not found inside a loop`, + assert.strictEqual( + completions.items.length, + INSIDE_LOOP_DIRECTIVE_NAMES.length, + `Expected ${INSIDE_LOOP_DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, ) - assert.ok( - !DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} should not be suggested inside of a loop`, - ) - } + for (const item of completions.items) { + assert.ok( + INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), + `Directive ${item.label} not found inside a loop`, + ) + + assert.ok( + !DIRECTIVE_NAMES.includes(item.label), + `Directive ${item.label} should not be suggested inside of a loop`, + ) + } + }) }) }) From 4e3bab49dfe9373f1e102093726d2ff81ecf410c Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 12:24:04 +0200 Subject: [PATCH 28/39] Refactor tests --- src/completions/directiveCompletion.ts | 8 ++- .../completion/directivesCompletion.test.ts | 72 ++++++++++--------- .../suite/completion/loopsCompletion.test.ts | 5 ++ src/test/suite/utils/assert/assertHasItems.ts | 6 ++ src/test/suite/utils/static/directiveNames.ts | 7 +- 5 files changed, 60 insertions(+), 38 deletions(-) create mode 100644 src/test/suite/utils/assert/assertHasItems.ts diff --git a/src/completions/directiveCompletion.ts b/src/completions/directiveCompletion.ts index fcd36ac..77818b4 100644 --- a/src/completions/directiveCompletion.ts +++ b/src/completions/directiveCompletion.ts @@ -28,9 +28,11 @@ export default vscode.languages.registerCompletionItemProvider( const partialDir = match[1] const field = vscode.CompletionItemKind.Snippet - const dirs = cursor.isInsideLoop() - ? loopDirectories(field) - : otherDirectories(field) + const dirs = otherDirectories(field) + + if (cursor.isInsideLoop()) { + dirs.push(...loopDirectories(field)) + } return dirs.filter(d => { // remove the @ from the label diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 3a22530..328caf0 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -2,10 +2,8 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' -import { - DIRECTIVE_NAMES, - INSIDE_LOOP_DIRECTIVES as INSIDE_LOOP_DIRECTIVE_NAMES, -} from '../utils/static/directiveNames' +import { DIRECTIVES, INSIDE_LOOP_DIRECTIVES } from '../utils/static/directiveNames' +import { assertHasItems } from '../utils/assert/assertHasItems' suite('Directives Completion', () => { test('suggests directives in HTML', async () => { @@ -18,25 +16,16 @@ suite('Directives Completion', () => { assert.fail('No completions found!') } - assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') + assertHasItems(completions) assert.strictEqual( completions.items.length, - DIRECTIVE_NAMES.length, - `Expected ${DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, + DIRECTIVES.length, + `Expected ${DIRECTIVES.length} completions, but got ${completions.items.length}`, ) - for (const item of completions.items) { - assert.ok( - DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} not found in HTML`, - ) - - assert.ok( - !INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} should not be suggested outside of a loop`, - ) - } + assertHasDirectives(DIRECTIVES, completions.items, 'HTML') + assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, 'HTML') }) const loopTests = [ @@ -61,25 +50,44 @@ suite('Directives Completion', () => { assert.fail('No completions found!') } - assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') + assertHasItems(completions) + + const lengthMustBe = INSIDE_LOOP_DIRECTIVES.length + DIRECTIVES.length assert.strictEqual( completions.items.length, - INSIDE_LOOP_DIRECTIVE_NAMES.length, - `Expected ${INSIDE_LOOP_DIRECTIVE_NAMES.length} completions, but got ${completions.items.length}`, + lengthMustBe, + `Expected ${lengthMustBe} completions, but got ${completions.items.length}`, ) - for (const item of completions.items) { - assert.ok( - INSIDE_LOOP_DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} not found inside a loop`, - ) - - assert.ok( - !DIRECTIVE_NAMES.includes(item.label), - `Directive ${item.label} should not be suggested inside of a loop`, - ) - } + assertHasDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, name) + assertHasDirectives(DIRECTIVES, completions.items, name) }) }) }) + +function assertHasDirectives( + dirNames: string[], + items: vscode.CompletionItem[], + blockName: string, +) { + for (const dirName of dirNames) { + assert.ok( + items.some(item => item.label === dirName), + `Directive ${dirName} must be suggested inside ${blockName}`, + ) + } +} + +function assertMissingDirectives( + dirNames: string[], + items: vscode.CompletionItem[], + blockName: string, +) { + for (const dirName of dirNames) { + assert.ok( + !items.some(item => item.label === dirName), + `Directive ${dirName} must not be suggested inside ${blockName}`, + ) + } +} diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 2ff4456..01a8746 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -2,6 +2,7 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' +import { assertHasItems } from '../utils/assert/assertHasItems' suite('Loops Completion', () => { const loopTests = [ @@ -28,6 +29,8 @@ suite('Loops Completion', () => { assert.fail('No completions found!') } + assertHasItems(completions) + for (const expectedLabel of expected) { assert.ok( completions.items.some(item => item.label === expectedLabel), @@ -64,6 +67,8 @@ suite('Loops Completion', () => { assert.fail('No completions found! Should have HTML native completions') } + assertHasItems(completions) + const shouldNotHave = ['index', 'first', 'last', 'iter'] for (const expectedLabel of shouldNotHave) { diff --git a/src/test/suite/utils/assert/assertHasItems.ts b/src/test/suite/utils/assert/assertHasItems.ts new file mode 100644 index 0000000..c3667e4 --- /dev/null +++ b/src/test/suite/utils/assert/assertHasItems.ts @@ -0,0 +1,6 @@ +import * as assert from 'assert' +import * as vscode from 'vscode' + +export function assertHasItems(completions: vscode.CompletionList) { + return assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') +} diff --git a/src/test/suite/utils/static/directiveNames.ts b/src/test/suite/utils/static/directiveNames.ts index 1e88819..62fe312 100644 --- a/src/test/suite/utils/static/directiveNames.ts +++ b/src/test/suite/utils/static/directiveNames.ts @@ -1,4 +1,4 @@ -export const DIRECTIVE_NAMES = [ +export const DIRECTIVES = [ '@if', '@if @else', '@if @elseif', @@ -8,14 +8,15 @@ export const DIRECTIVE_NAMES = [ '@reserve', '@component', '@component @slot', - '@slot', - '@slot(name)', '@each', '@each @else', '@for', '@for @else', '@dump', '@end', + '@slot', + '@slot(name)', ] export const INSIDE_LOOP_DIRECTIVES = ['@break', '@continue', '@continueIf', '@breakIf'] +// export const INSIDE_COMP_DIRECTIVES = ['@slot', '@slot(name)'] From d02f0c4d5411013adf5cee78b88a9344fed4dccf Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 14:33:41 +0200 Subject: [PATCH 29/39] Add assertLength helper --- .../suite/completion/directivesCompletion.test.ts | 14 +++----------- src/test/suite/completion/loopsCompletion.test.ts | 2 ++ src/test/suite/utils/assert/assertLength.ts | 5 +++++ 3 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 src/test/suite/utils/assert/assertLength.ts diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 328caf0..e6313dd 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -4,6 +4,7 @@ import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' import { DIRECTIVES, INSIDE_LOOP_DIRECTIVES } from '../utils/static/directiveNames' import { assertHasItems } from '../utils/assert/assertHasItems' +import { assertLength } from '../utils/assert/assertLength' suite('Directives Completion', () => { test('suggests directives in HTML', async () => { @@ -17,12 +18,7 @@ suite('Directives Completion', () => { } assertHasItems(completions) - - assert.strictEqual( - completions.items.length, - DIRECTIVES.length, - `Expected ${DIRECTIVES.length} completions, but got ${completions.items.length}`, - ) + assertLength(DIRECTIVES.length, completions.items.length) assertHasDirectives(DIRECTIVES, completions.items, 'HTML') assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, 'HTML') @@ -54,11 +50,7 @@ suite('Directives Completion', () => { const lengthMustBe = INSIDE_LOOP_DIRECTIVES.length + DIRECTIVES.length - assert.strictEqual( - completions.items.length, - lengthMustBe, - `Expected ${lengthMustBe} completions, but got ${completions.items.length}`, - ) + assertLength(lengthMustBe, completions.items.length) assertHasDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, name) assertHasDirectives(DIRECTIVES, completions.items, name) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 01a8746..74e1a4c 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -3,6 +3,7 @@ import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' import { assertHasItems } from '../utils/assert/assertHasItems' +import { assertLength } from '../utils/assert/assertLength' suite('Loops Completion', () => { const loopTests = [ @@ -30,6 +31,7 @@ suite('Loops Completion', () => { } assertHasItems(completions) + assertLength(expected.length, completions.items.length) for (const expectedLabel of expected) { assert.ok( diff --git a/src/test/suite/utils/assert/assertLength.ts b/src/test/suite/utils/assert/assertLength.ts new file mode 100644 index 0000000..ea51e3a --- /dev/null +++ b/src/test/suite/utils/assert/assertLength.ts @@ -0,0 +1,5 @@ +import * as assert from 'assert' + +export function assertLength(expect: number, actual: number) { + return assert.strictEqual(1, 1, `Expected length is ${expect}, but got ${actual}`) +} From cb9974ff5f6bdfa0d8dd83136992bcf8eba109eb Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 14:43:43 +0200 Subject: [PATCH 30/39] Refactor tests --- .../completion/directivesCompletion.test.ts | 30 +++++++++---------- .../suite/completion/loopsCompletion.test.ts | 20 +++++-------- src/test/suite/utils/assert/assertHasItems.ts | 6 ---- src/test/suite/utils/triggerCompletion.ts | 13 ++++++-- 4 files changed, 34 insertions(+), 35 deletions(-) delete mode 100644 src/test/suite/utils/assert/assertHasItems.ts diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index e6313dd..61cf9df 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -2,8 +2,11 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' -import { DIRECTIVES, INSIDE_LOOP_DIRECTIVES } from '../utils/static/directiveNames' -import { assertHasItems } from '../utils/assert/assertHasItems' +import { + DIRECTIVES, + INSIDE_LOOP_DIRECTIVES, + INSIDE_COMP_DIRECTIVES, +} from '../utils/static/directiveNames' import { assertLength } from '../utils/assert/assertLength' suite('Directives Completion', () => { @@ -11,17 +14,16 @@ suite('Directives Completion', () => { const content = `

@

` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 5) - const completions = await triggerCompletion(pos, doc.uri) + const items = await triggerCompletion(pos, doc.uri) - if (!completions) { + if (!items) { assert.fail('No completions found!') } - assertHasItems(completions) - assertLength(DIRECTIVES.length, completions.items.length) + assertLength(DIRECTIVES.length, items.length) - assertHasDirectives(DIRECTIVES, completions.items, 'HTML') - assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, 'HTML') + assertHasDirectives(DIRECTIVES, items, 'HTML') + assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, items, 'HTML') }) const loopTests = [ @@ -40,20 +42,18 @@ suite('Directives Completion', () => { loopTests.forEach(({ name, content, pos }) => { test(`suggests loop directives inside ${name} loop`, async () => { const doc = await openTextDocument(content) - const completions = await triggerCompletion(pos, doc.uri) + const items = await triggerCompletion(pos, doc.uri) - if (!completions) { + if (items.length === 0) { assert.fail('No completions found!') } - assertHasItems(completions) - const lengthMustBe = INSIDE_LOOP_DIRECTIVES.length + DIRECTIVES.length - assertLength(lengthMustBe, completions.items.length) + assertLength(lengthMustBe, items.length) - assertHasDirectives(INSIDE_LOOP_DIRECTIVES, completions.items, name) - assertHasDirectives(DIRECTIVES, completions.items, name) + assertHasDirectives(INSIDE_LOOP_DIRECTIVES, items, name) + assertHasDirectives(DIRECTIVES, items, name) }) }) }) diff --git a/src/test/suite/completion/loopsCompletion.test.ts b/src/test/suite/completion/loopsCompletion.test.ts index 74e1a4c..6574576 100644 --- a/src/test/suite/completion/loopsCompletion.test.ts +++ b/src/test/suite/completion/loopsCompletion.test.ts @@ -2,7 +2,6 @@ import * as assert from 'assert' import * as vscode from 'vscode' import { triggerCompletion } from '../utils/triggerCompletion' import { openTextDocument } from '../utils/openTextDocument' -import { assertHasItems } from '../utils/assert/assertHasItems' import { assertLength } from '../utils/assert/assertLength' suite('Loops Completion', () => { @@ -24,23 +23,22 @@ suite('Loops Completion', () => { loopTests.forEach(({ name, content, pos, expected }) => { test(`suggests loop properties inside ${name} loop`, async () => { const doc = await openTextDocument(content) - const completions = await triggerCompletion(pos, doc.uri) + const items = await triggerCompletion(pos, doc.uri) - if (!completions) { + if (!items) { assert.fail('No completions found!') } - assertHasItems(completions) - assertLength(expected.length, completions.items.length) + assertLength(expected.length, items.length) for (const expectedLabel of expected) { assert.ok( - completions.items.some(item => item.label === expectedLabel), + items.some(item => item.label === expectedLabel), `Expected completion '${expectedLabel}' not found!`, ) assert.ok( - completions.items.length === expected.length, + items.length === expected.length, 'Unexpected completions found!', ) } @@ -63,19 +61,17 @@ suite('Loops Completion', () => { outsideLoopTests.forEach(({ name, content, pos }) => { test(`does not suggest loop properties outside ${name} loop`, async () => { const doc = await openTextDocument(content) - const completions = await triggerCompletion(pos, doc.uri) + const items = await triggerCompletion(pos, doc.uri) - if (!completions) { + if (!items) { assert.fail('No completions found! Should have HTML native completions') } - assertHasItems(completions) - const shouldNotHave = ['index', 'first', 'last', 'iter'] for (const expectedLabel of shouldNotHave) { assert.ok( - !completions.items.some(item => item.label === expectedLabel), + !items.some(item => item.label === expectedLabel), `Unexpected completion '${expectedLabel}' found!`, ) } diff --git a/src/test/suite/utils/assert/assertHasItems.ts b/src/test/suite/utils/assert/assertHasItems.ts deleted file mode 100644 index c3667e4..0000000 --- a/src/test/suite/utils/assert/assertHasItems.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as assert from 'assert' -import * as vscode from 'vscode' - -export function assertHasItems(completions: vscode.CompletionList) { - return assert.ok(completions.hasOwnProperty('items'), 'Property "items" not found!') -} diff --git a/src/test/suite/utils/triggerCompletion.ts b/src/test/suite/utils/triggerCompletion.ts index 8bb6832..d489063 100644 --- a/src/test/suite/utils/triggerCompletion.ts +++ b/src/test/suite/utils/triggerCompletion.ts @@ -1,9 +1,18 @@ import * as vscode from 'vscode' -export async function triggerCompletion(pos: vscode.Position, uri: vscode.Uri) { - return await vscode.commands.executeCommand( +export async function triggerCompletion( + pos: vscode.Position, + uri: vscode.Uri, +): Promise { + const completions = await vscode.commands.executeCommand( 'vscode.executeCompletionItemProvider', uri, pos, ) + + if (!completions) { + return [] + } + + return completions.items } From 2686dceec73ea0691183be447a443bed228cffb1 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 14:44:04 +0200 Subject: [PATCH 31/39] Add test for component directives completion --- .../completion/directivesCompletion.test.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index 61cf9df..ffb7bcd 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -56,6 +56,25 @@ suite('Directives Completion', () => { assertHasDirectives(DIRECTIVES, items, name) }) }) + + test('suggests component directives in component', async () => { + const content = `@component('name') @ @end` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 20) + const items = await triggerCompletion(pos, doc.uri) + + if (!items) { + assert.fail('No completions found!') + } + + const lengthMustBe = INSIDE_COMP_DIRECTIVES.length + DIRECTIVES.length + + assertLength(lengthMustBe, items.length) + + assertHasDirectives(INSIDE_COMP_DIRECTIVES, items, 'component') + assertHasDirectives(DIRECTIVES, items, 'component') + assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, items, 'component') + }) }) function assertHasDirectives( From cfc959aa25a473cbfacd5d60853ef38d9742a5a6 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 14:44:39 +0200 Subject: [PATCH 32/39] Add showing slot directives only inside components --- CHANGELOG.md | 3 ++- src/completions/directiveCompletion.ts | 15 +++++++++++++-- src/modules/Cursor.ts | 8 ++++++++ src/test/suite/utils/static/directiveNames.ts | 4 +--- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47577c9..b97af70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ ## [1.5.3] - 2025-01-30 - Added tests coverage to test all the necessary features -- Change so that you can only see @break, @breakIf, @continue, @continueIf directives when you are inside a loop. This is to avoid showing them when they are not needed +- Change so that you can only see `@break`, `@breakIf`, `@continue`, `@continueIf` directives when you are inside a loop +- Change so that you can only see `@slot` and `@slot(name)` directives when you are inside a component ## [1.5.2] - 2025-01-30 - Fixed issue where loop object would only show autocomplete suggestions when it was inside `{{` and `}}` brackets diff --git a/src/completions/directiveCompletion.ts b/src/completions/directiveCompletion.ts index 77818b4..1d6cf08 100644 --- a/src/completions/directiveCompletion.ts +++ b/src/completions/directiveCompletion.ts @@ -34,6 +34,10 @@ export default vscode.languages.registerCompletionItemProvider( dirs.push(...loopDirectories(field)) } + if (cursor.isInsideComponent()) { + dirs.push(...compDirectories(field)) + } + return dirs.filter(d => { // remove the @ from the label const label = d.label.slice(1) @@ -68,8 +72,6 @@ function otherDirectories( completionItem('@reserve', info.reserve, field, snip.reserve), completionItem('@component', info.comp, field, snip.comp), completionItem('@component @slot', info.compSlot, field, snip.compSlot), - completionItem('@slot', info.slot, field, snip.slotDef), - completionItem('@slot(name)', info.slotDef, field, snip.slot), completionItem('@each', info.each, field, snip.each), completionItem('@each @else', info.eachElse, field, snip.eachElse), completionItem('@for', info.for, field, snip.for), @@ -78,3 +80,12 @@ function otherDirectories( completionItem('@end', info.end, field, snip.end), ] } + +function compDirectories( + field: vscode.CompletionItemKind.Snippet, +): vscode.CompletionItem[] { + return [ + completionItem('@slot', info.slot, field, snip.slotDef), + completionItem('@slot(name)', info.slotDef, field, snip.slot), + ] +} diff --git a/src/modules/Cursor.ts b/src/modules/Cursor.ts index 9e039bc..1a59125 100644 --- a/src/modules/Cursor.ts +++ b/src/modules/Cursor.ts @@ -45,6 +45,14 @@ export class Cursor { return this.isBetween(START, END, IGNORE) } + public isInsideComponent(): boolean { + const IGNORE = /{{--\s*@component/g + const START = /@component/g + const END = /@end/g + + return this.isBetween(START, END, IGNORE) + } + private textBefore(): string { return this.doc.getText(new vscode.Range(new vscode.Position(0, 0), this.pos)) } diff --git a/src/test/suite/utils/static/directiveNames.ts b/src/test/suite/utils/static/directiveNames.ts index 62fe312..995c987 100644 --- a/src/test/suite/utils/static/directiveNames.ts +++ b/src/test/suite/utils/static/directiveNames.ts @@ -14,9 +14,7 @@ export const DIRECTIVES = [ '@for @else', '@dump', '@end', - '@slot', - '@slot(name)', ] export const INSIDE_LOOP_DIRECTIVES = ['@break', '@continue', '@continueIf', '@breakIf'] -// export const INSIDE_COMP_DIRECTIVES = ['@slot', '@slot(name)'] +export const INSIDE_COMP_DIRECTIVES = ['@slot', '@slot(name)'] From 1fec3aae751b2cfaadae3a357a9c3738e8ecdf99 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 14:53:31 +0200 Subject: [PATCH 33/39] Code refactor. Improve readablity of tests --- .../completion/directivesCompletion.test.ts | 62 +++++++++---------- .../suite/utils/assert/assertHasDirectives.ts | 15 +++++ .../utils/assert/assertMissingDirectives.ts | 15 +++++ 3 files changed, 60 insertions(+), 32 deletions(-) create mode 100644 src/test/suite/utils/assert/assertHasDirectives.ts create mode 100644 src/test/suite/utils/assert/assertMissingDirectives.ts diff --git a/src/test/suite/completion/directivesCompletion.test.ts b/src/test/suite/completion/directivesCompletion.test.ts index ffb7bcd..c685ccf 100644 --- a/src/test/suite/completion/directivesCompletion.test.ts +++ b/src/test/suite/completion/directivesCompletion.test.ts @@ -8,9 +8,11 @@ import { INSIDE_COMP_DIRECTIVES, } from '../utils/static/directiveNames' import { assertLength } from '../utils/assert/assertLength' +import { assertHasDirectives } from '../utils/assert/assertHasDirectives' +import { assertMissingDirectives } from '../utils/assert/assertMissingDirectives' suite('Directives Completion', () => { - test('suggests directives in HTML', async () => { + test('suggests proper directives in HTML', async () => { const content = `

@

` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 5) @@ -40,7 +42,7 @@ suite('Directives Completion', () => { ] loopTests.forEach(({ name, content, pos }) => { - test(`suggests loop directives inside ${name} loop`, async () => { + test(`suggests proper directives inside of ${name} loop`, async () => { const doc = await openTextDocument(content) const items = await triggerCompletion(pos, doc.uri) @@ -57,7 +59,7 @@ suite('Directives Completion', () => { }) }) - test('suggests component directives in component', async () => { + test('suggests proper directives inside of component', async () => { const content = `@component('name') @ @end` const doc = await openTextDocument(content) const pos = new vscode.Position(0, 20) @@ -71,34 +73,30 @@ suite('Directives Completion', () => { assertLength(lengthMustBe, items.length) - assertHasDirectives(INSIDE_COMP_DIRECTIVES, items, 'component') - assertHasDirectives(DIRECTIVES, items, 'component') - assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, items, 'component') + assertHasDirectives(INSIDE_COMP_DIRECTIVES, items, '@component') + assertHasDirectives(DIRECTIVES, items, '@component') + assertMissingDirectives(INSIDE_LOOP_DIRECTIVES, items, '@component') }) -}) -function assertHasDirectives( - dirNames: string[], - items: vscode.CompletionItem[], - blockName: string, -) { - for (const dirName of dirNames) { - assert.ok( - items.some(item => item.label === dirName), - `Directive ${dirName} must be suggested inside ${blockName}`, - ) - } -} - -function assertMissingDirectives( - dirNames: string[], - items: vscode.CompletionItem[], - blockName: string, -) { - for (const dirName of dirNames) { - assert.ok( - !items.some(item => item.label === dirName), - `Directive ${dirName} must not be suggested inside ${blockName}`, - ) - } -} + test('suggests proper directives inside of a @each loop within a component', async () => { + const content = `@component('name')@each(item in items) @ @end@end` + const doc = await openTextDocument(content) + const pos = new vscode.Position(0, 40) + const items = await triggerCompletion(pos, doc.uri) + + if (!items) { + assert.fail('No completions found!') + } + + const lengthMustBe = + INSIDE_COMP_DIRECTIVES.length + + DIRECTIVES.length + + INSIDE_LOOP_DIRECTIVES.length + + assertLength(lengthMustBe, items.length) + + assertHasDirectives(INSIDE_COMP_DIRECTIVES, items, '@component @each') + assertHasDirectives(INSIDE_LOOP_DIRECTIVES, items, '@component @each') + assertHasDirectives(DIRECTIVES, items, '@component @each') + }) +}) diff --git a/src/test/suite/utils/assert/assertHasDirectives.ts b/src/test/suite/utils/assert/assertHasDirectives.ts new file mode 100644 index 0000000..ec200b4 --- /dev/null +++ b/src/test/suite/utils/assert/assertHasDirectives.ts @@ -0,0 +1,15 @@ +import * as assert from 'assert' +import * as vscode from 'vscode' + +export function assertHasDirectives( + dirNames: string[], + items: vscode.CompletionItem[], + blockName: string, +) { + for (const dirName of dirNames) { + assert.ok( + items.some(item => item.label === dirName), + `Directive ${dirName} must be suggested inside ${blockName}`, + ) + } +} diff --git a/src/test/suite/utils/assert/assertMissingDirectives.ts b/src/test/suite/utils/assert/assertMissingDirectives.ts new file mode 100644 index 0000000..d28c07b --- /dev/null +++ b/src/test/suite/utils/assert/assertMissingDirectives.ts @@ -0,0 +1,15 @@ +import * as assert from 'assert' +import * as vscode from 'vscode' + +export function assertMissingDirectives( + dirNames: string[], + items: vscode.CompletionItem[], + blockName: string, +) { + for (const dirName of dirNames) { + assert.ok( + !items.some(item => item.label === dirName), + `Directive ${dirName} must not be suggested inside ${blockName}`, + ) + } +} From 05713d98bd87f8f28ca2018abe933fd17b9f709e Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:00:04 +0200 Subject: [PATCH 34/39] Add github actions --- .github/workflows/tests.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..8eb7afb --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20.x' + cache: 'npm' # Cache npm dependencies + + - name: Install dependencies + run: npm install + + - name: Run tests + run: npm run tests \ No newline at end of file From 0d96914d4e55122e4a911d882cc09dabf26b4fa4 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:01:00 +0200 Subject: [PATCH 35/39] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b97af70..f31a831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [1.5.3] - 2025-01-30 +## [1.5.3] - 2025-02-02 - Added tests coverage to test all the necessary features - Change so that you can only see `@break`, `@breakIf`, `@continue`, `@continueIf` directives when you are inside a loop - Change so that you can only see `@slot` and `@slot(name)` directives when you are inside a component From 627123b94802105b3ba880505d793f1b67513350 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:02:28 +0200 Subject: [PATCH 36/39] Add lock file to git --- .gitignore | 2 - package-lock.json | 2786 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2786 insertions(+), 2 deletions(-) create mode 100644 package-lock.json diff --git a/.gitignore b/.gitignore index 3a9856b..0e94e7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ node_modules *.vsix -yarn.lock -package-lock.json out .vscode-test \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..f8bf9bb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2786 @@ +{ + "name": "textwire", + "version": "1.5.3", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "textwire", + "version": "1.5.3", + "license": "MIT", + "devDependencies": { + "@types/mocha": "^10.0.10", + "@types/node": "^22.12.0", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.4.1", + "glob": "^11.0.1", + "typescript": "^5.7.3", + "vscode": "^1.1.37" + }, + "engines": { + "vscode": "^1.8.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.12.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.12.0.tgz", + "integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==", + "dev": true, + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz", + "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/test-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vscode/test-cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vscode/test-cli/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/test-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@vscode/test-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@vscode/test-cli/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/test-cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/test-cli/node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/@vscode/test-cli/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/test-cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/test-cli/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/@vscode/test-cli/node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@vscode/test-cli/node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/test-cli/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/test-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/test-cli/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@vscode/test-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@vscode/test-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz", + "integrity": "sha512-Gc6EdaLANdktQ1t+zozoBVRynfIsMKMc94Svu1QreOBC8y76x4tvaK32TljrLi1LI2+PK58sDVbL7ALdqf3VRQ==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^7.0.1", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@vscode/test-electron/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-electron/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-electron/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@vscode/test-electron/node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", + "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha512-z/GDPjlRMNOa2XJiB4em8wJpuuBfrFOlYKTZxtpkdr1uPdibHI8rYA3MY0KDObpVyaes0e/aunid/t88ZI2EKA==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", + "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", + "dev": true, + "dependencies": { + "chalk": "^5.0.0", + "is-unicode-supported": "^1.1.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-7.0.1.tgz", + "integrity": "sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw==", + "dev": true, + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^4.0.0", + "cli-spinners": "^2.9.0", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^1.3.0", + "log-symbols": "^5.1.0", + "stdin-discarder": "^0.1.0", + "string-width": "^6.1.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/stdin-discarder": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", + "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", + "dev": true, + "dependencies": { + "bl": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-6.1.0.tgz", + "integrity": "sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^10.2.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vscode": { + "version": "1.1.37", + "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.37.tgz", + "integrity": "sha512-vJNj6IlN7IJPdMavlQa1KoFB3Ihn06q1AiN3ZFI/HfzPNzbKZWPPuiU+XkpNOfGU5k15m4r80nxNPlM7wcc0wg==", + "deprecated": "This package is deprecated in favor of @types/vscode and vscode-test. For more information please read: https://code.visualstudio.com/updates/v1_36#_splitting-vscode-package-into-typesvscode-and-vscodetest", + "dev": true, + "dependencies": { + "glob": "^7.1.2", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "mocha": "^5.2.0", + "semver": "^5.4.1", + "source-map-support": "^0.5.0", + "vscode-test": "^0.4.1" + }, + "bin": { + "vscode-install": "bin/install" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/vscode-test": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.4.3.tgz", + "integrity": "sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/vscode-test/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/vscode-test/node_modules/http-proxy-agent": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", + "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "4", + "debug": "3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/vscode-test/node_modules/https-proxy-agent": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", + "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/vscode-test/node_modules/https-proxy-agent/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/vscode-test/node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-test/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/vscode/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/vscode/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} From 2900b10081c86dd40b01151f7984790551994455 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:03:26 +0200 Subject: [PATCH 37/39] Fix tests.yml file --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8eb7afb..fac8e43 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,4 +24,4 @@ jobs: run: npm install - name: Run tests - run: npm run tests \ No newline at end of file + run: npm run test \ No newline at end of file From 4bb636208577246d67346cb86d6b257f3892dc07 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:04:34 +0200 Subject: [PATCH 38/39] Update package.json file --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 549fee2..568aea9 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "login": "vsce login SerhiiCho", "deploy": "vsce publish", "compile": "tsc -p .", - "test": "rm -r ./out && npm run compile && FORCE_COLOR=1 node ./out/test/runTests.js" + "test": "npm run compile && FORCE_COLOR=1 node ./out/test/runTests.js" }, "devDependencies": { "@types/mocha": "^10.0.10", From 7f0b53b69b0f76da325780d6cafe1a5b67fad209 Mon Sep 17 00:00:00 2001 From: SerhiiCho Date: Sun, 2 Feb 2025 15:10:32 +0200 Subject: [PATCH 39/39] Update github actions yaml file --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fac8e43..de8ce3b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -23,5 +23,5 @@ jobs: - name: Install dependencies run: npm install - - name: Run tests - run: npm run test \ No newline at end of file + - name: Run tests with xvfb + run: xvfb-run --auto-servernum npm run test \ No newline at end of file