├── .eslintignore ├── images ├── logo.png ├── preview.gif ├── snippet.png ├── resxEditor.png ├── sortByKeys.gif ├── webPreview.gif ├── resxEditorOption.png ├── textVsResxEditor.png ├── createNewResxFile.gif └── updateResxNamespace.gif ├── src ├── updateNote.ts ├── settings.ts ├── util.ts ├── webpanelPostMessage.ts ├── webpanelMessageKind.ts ├── nameof.ts ├── dispose.ts ├── resxJsonHelper.ts ├── textInputBoxOptions.ts ├── constants.ts ├── notificationService.ts ├── logger.ts ├── fileHelper.ts ├── resxEditorProvider.ts ├── previewEditPanel.ts ├── resxEditor.ts ├── webpanelScript.ts └── extension.ts ├── styles ├── ma-plus-thick.svg ├── fa-right-left.svg ├── fa-pen-to-square.svg ├── fa-arrow-down-a-z-solid-full.svg └── webpanel.css ├── .gitignore ├── .vscodeignore ├── tsconfig.json ├── snippets └── resx.json ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── .eslintrc.json ├── .github ├── FUNDING.yml └── workflows │ ├── main.yml │ └── pullrequest.yml ├── LICENSE ├── webpack.config.js ├── README.md ├── package.json ├── CHANGELOG.md └── pnpm-lock.yaml /.eslintignore: -------------------------------------------------------------------------------- 1 | webpack.config.js -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/logo.png -------------------------------------------------------------------------------- /images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/preview.gif -------------------------------------------------------------------------------- /images/snippet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/snippet.png -------------------------------------------------------------------------------- /images/resxEditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/resxEditor.png -------------------------------------------------------------------------------- /images/sortByKeys.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/sortByKeys.gif -------------------------------------------------------------------------------- /images/webPreview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/webPreview.gif -------------------------------------------------------------------------------- /images/resxEditorOption.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/resxEditorOption.png -------------------------------------------------------------------------------- /images/textVsResxEditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/textVsResxEditor.png -------------------------------------------------------------------------------- /images/createNewResxFile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/createNewResxFile.gif -------------------------------------------------------------------------------- /images/updateResxNamespace.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pmahend1/resxpress/HEAD/images/updateResxNamespace.gif -------------------------------------------------------------------------------- /src/updateNote.ts: -------------------------------------------------------------------------------- 1 | export class UpdateNote { 2 | public version: string = ""; 3 | public updateNotes: string = ""; 4 | } -------------------------------------------------------------------------------- /styles/ma-plus-thick.svg: -------------------------------------------------------------------------------- 1 | 3 | plus-thick 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | dist 6 | .pnp.* 7 | .yarn/* 8 | .idea 9 | !.yarn/patches 10 | !.yarn/plugins 11 | !.yarn/releases 12 | !.yarn/sdks 13 | !.yarn/versions -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/tslint.json 9 | **/*.map 10 | **/*.ts 11 | node_modules 12 | webpack.config.js 13 | *.yml 14 | .github/** -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | export class Settings { 2 | public static indentSpaceLength = 4; 3 | public static shouldGenerateStronglyTypedResourceClassOnSave = false; 4 | public static shouldUseFileScopedNamespace = true; 5 | public static enableLocalLogs = false; 6 | } -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | export function getNonce() { 2 | let text = ""; 3 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 4 | for (let i = 0; i < 32; i++) { 5 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 6 | } 7 | return text; 8 | } -------------------------------------------------------------------------------- /src/webpanelPostMessage.ts: -------------------------------------------------------------------------------- 1 | import { WebpanelPostMessageKind } from "./webpanelMessageKind"; 2 | 3 | export class WebpanelPostMessage { 4 | constructor(public type: WebpanelPostMessageKind, public text?: string) { 5 | this.type = type; 6 | this.text = text; 7 | } 8 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES2022", 5 | "lib": [ 6 | "ES2022", 7 | "dom" 8 | ], 9 | "outDir": "out", 10 | "sourceMap": true, 11 | "strict": true, 12 | "rootDir": "src" 13 | }, 14 | "exclude": [ 15 | "node_modules", 16 | ".vscode-test" 17 | ] 18 | } -------------------------------------------------------------------------------- /snippets/resx.json: -------------------------------------------------------------------------------- 1 | { 2 | "Resx Data": { 3 | "prefix": "resx", 4 | "body": [ 5 | "", 6 | " ${2:value}", 7 | " $0", 8 | "" 9 | ], 10 | "description": "Resx key-value-comment data" 11 | } 12 | } -------------------------------------------------------------------------------- /src/webpanelMessageKind.ts: -------------------------------------------------------------------------------- 1 | export enum WebpanelPostMessageKind { 2 | Switch = "switch", 3 | Add = "add", 4 | Delete = "delete", 5 | UpdateWebPanel = "update-web-panel", 6 | TriggerTextDocumentUpdate = "trigger-text-document-update", 7 | TriggerNamespaceUpdate = "trigger-namespace-update", 8 | Alert = "alert", 9 | NewNamespace = "new-namespace", 10 | SortByKeys = "sort-by-keys" 11 | } 12 | -------------------------------------------------------------------------------- /src/nameof.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Gets the name of a class, property (type-safe) or a function (runtime). 3 | * Usage: 4 | * nameof("Property") // "Property" 5 | * nameof(Function) // "Function" 6 | */ 7 | export function nameof(name: Extract): string; 8 | export function nameof(fn: Function): string; 9 | export function nameof(arg: any): string { 10 | if (typeof arg === "function") { 11 | return arg.name; 12 | } 13 | return arg; 14 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", // this is from package.json 9 | "problemMatcher": "$ts-checker-webpack-watch", //connor4312.esbuild-problem-matchers 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never", 13 | "showReuseMessage": true, 14 | "panel": "dedicated" 15 | }, 16 | "group": { 17 | "kind": "build", 18 | "isDefault": true 19 | } 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /styles/fa-right-left.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "eslint:recommended", 5 | "plugin:@typescript-eslint/recommended" 6 | ], 7 | "parser": "@typescript-eslint/parser", 8 | "parserOptions": { 9 | "project": [ 10 | "./tsconfig.json" 11 | ] 12 | }, 13 | "plugins": [ 14 | "@typescript-eslint" 15 | ], 16 | "rules": { 17 | "no-var": "off", 18 | "prefer-const": "off", 19 | "quotes": "error", 20 | "@typescript-eslint/no-explicit-any": "off", 21 | "@typescript-eslint/ban-ts-comment": "off" 22 | }, 23 | "ignorePatterns": [ 24 | "src/**/*.test.ts", 25 | "src/test/**/*ts" 26 | ] 27 | } -------------------------------------------------------------------------------- /styles/fa-pen-to-square.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: prateekmahendrakar 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: prateek 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: pmahend1 14 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/out/**/*.js" 14 | ], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | }, 17 | { 18 | "name": "Extension Tests", 19 | "type": "extensionHost", 20 | "request": "launch", 21 | "runtimeExecutable": "${execPath}", 22 | "args": [ 23 | "--extensionDevelopmentPath=${workspaceFolder}", 24 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 25 | ], 26 | "outFiles": [ 27 | "${workspaceFolder}/out/test/**/*.js" 28 | ], 29 | "preLaunchTask": "${defaultBuildTask}" 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /src/dispose.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | export function disposeAll(disposables: vscode.Disposable[]): void { 4 | while (disposables.length) { 5 | const item = disposables.pop(); 6 | if (item) { 7 | item.dispose(); 8 | } 9 | } 10 | } 11 | 12 | export abstract class Disposable { 13 | private _isDisposed = false; 14 | 15 | protected _disposables: vscode.Disposable[] = []; 16 | 17 | public dispose(): any { 18 | if (this._isDisposed) { 19 | return; 20 | } 21 | this._isDisposed = true; 22 | disposeAll(this._disposables); 23 | } 24 | 25 | protected _register(value: T): T { 26 | if (this._isDisposed) { 27 | value.dispose(); 28 | } 29 | else { 30 | this._disposables.push(value); 31 | } 32 | return value; 33 | } 34 | 35 | protected get isDisposed(): boolean { 36 | return this._isDisposed; 37 | } 38 | } -------------------------------------------------------------------------------- /src/resxJsonHelper.ts: -------------------------------------------------------------------------------- 1 | import * as xmljs from "xml-js"; 2 | import { Logger } from "./logger"; 3 | import { nameof } from "./nameof"; 4 | 5 | export class ResxJsonHelper { 6 | static getJsonData(text: string): any[] { 7 | var jsObj: any = xmljs.xml2js(text, { compact: true }); 8 | var dataList: any[] = []; 9 | if (jsObj?.root?.data) { 10 | if (jsObj.root.data instanceof Array) { 11 | dataList = dataList.concat(jsObj.root.data); 12 | } 13 | else { 14 | //check if empty object 15 | if (jsObj.root.data?._attributes?.name) { 16 | dataList.push(jsObj.root.data); 17 | } 18 | } 19 | } 20 | Logger.instance.info(`${nameof(ResxJsonHelper)}.${nameof(this.getJsonData)}: ${JSON.stringify(dataList)}`); 21 | return dataList; 22 | } 23 | } -------------------------------------------------------------------------------- /src/textInputBoxOptions.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | export class TextInputBoxOptions implements vscode.InputBoxOptions { 4 | constructor(public title?: string, 5 | public value?: string, 6 | public valueSelection?: [number, number], 7 | public prompt?: string, 8 | public placeHolder?: string, 9 | public ignoreFocusOut?: boolean 10 | ) { 11 | this.title = title; 12 | this.value = value; 13 | this.valueSelection = valueSelection; 14 | this.prompt = prompt; 15 | this.placeHolder = placeHolder; 16 | this.ignoreFocusOut = ignoreFocusOut; 17 | } 18 | 19 | validateInput(value: string): string | vscode.InputBoxValidationMessage | undefined | null | Thenable { 20 | if (value.length === 0) { 21 | return { message: "Should not be empty!", severity: vscode.InputBoxValidationSeverity.Error }; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Prateek Mahendrakar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /styles/fa-arrow-down-a-z-solid-full.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const DATA = "data"; 2 | export const emptyString = ""; 3 | 4 | export class Constants { 5 | /** 6 | * ResXpress 7 | */ 8 | public static readonly extensionName = "ResXpress"; 9 | public static readonly resxpress = "resxpress" 10 | public static readonly configuration = "configuration"; 11 | public static readonly editor = "editor"; 12 | 13 | /** 14 | * resxpress/namespace-mapping.json 15 | */ 16 | public static readonly namespaceMappingJsonPath = `${this.resxpress}/namespace-mapping.json`; 17 | 18 | static Commands = class { 19 | public static readonly resxpreview = `${Constants.resxpress}.resxpreview`; 20 | public static readonly newpreview = `${Constants.resxpress}.newpreview`; 21 | public static readonly sortbykeys = `${Constants.resxpress}.sortbykeys`; 22 | public static readonly setNameSpace = `${Constants.resxpress}.setNameSpace`; 23 | public static readonly createResxFile = `${Constants.resxpress}.createResxFile`; 24 | public static readonly resxeditor = `${Constants.resxpress}.resxeditor`; 25 | } 26 | 27 | static Configuration = class { 28 | public static readonly generateStronglyTypedResourceClassOnSave = "generateStronglyTypedResourceClassOnSave"; 29 | public static readonly useFileScopedNamespace = "useFileScopedNamespace"; 30 | public static readonly indentSpaceLength = "indentSpaceLength"; 31 | public static readonly enableLocalLogs = "enableLocalLogs"; 32 | } 33 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | "use strict"; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-var-requires, no-undef 6 | const path = require("path"); 7 | 8 | /**@type {import('webpack').Configuration}*/ 9 | const config = { 10 | target: "node", // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 11 | 12 | entry: { 13 | extension: "./src/extension.ts", 14 | webpanelScript: "./src/webpanelScript.ts" 15 | },// the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 16 | output: { 17 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 18 | // eslint-disable-next-line no-undef 19 | path: path.resolve(__dirname, "out"), 20 | filename: "[name].js", 21 | libraryTarget: "commonjs2", 22 | devtoolModuleFilenameTemplate: "../[resource-path]" 23 | }, 24 | devtool: "source-map", 25 | externals: { 26 | vscode: "commonjs vscode" // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/ 27 | }, 28 | resolve: { 29 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 30 | extensions: [".ts", ".js"] 31 | }, 32 | module: { 33 | rules: [{ 34 | test: /\.ts$/, 35 | exclude: /node_modules/, 36 | use: [{ 37 | loader: "ts-loader" 38 | }] 39 | }] 40 | } 41 | }; 42 | // eslint-disable-next-line no-undef 43 | module.exports = config; -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Deploy CI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | shouldPublishToVSMarketPlace: 7 | description: "Publish to Visual Studio MarketPlace?" 8 | type: boolean 9 | default: true 10 | shouldPublishToOpenVsx: 11 | description: "Publish to Open-VSX?" 12 | type: boolean 13 | default: true 14 | 15 | jobs: 16 | deploy: 17 | runs-on: windows-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4.2.2 21 | 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v4.4.0 24 | with: 25 | node-version: 22 26 | 27 | - name: Setup pnpm 28 | uses: pnpm/action-setup@v4 29 | with: 30 | version: 10 31 | run_install: true 32 | 33 | - name: Install vsce and open vsx 34 | run: | 35 | pnpm add -g @vscode/vsce 36 | pnpm add -g ovsx 37 | 38 | - name: Pack vsix 39 | id: pack 40 | run: | 41 | vsce pack --no-dependencies 42 | $VsixName = Get-ChildItem -Filter *.vsix | Select-Object -First 1 -ExpandProperty Name 43 | Write-Host "`$VsixName = $VsixName" 44 | "VSIX_FILENAME=$VsixName" >> $env:GITHUB_OUTPUT 45 | 46 | - name: VSCE Publish 47 | if: ${{ inputs.shouldPublishToVSMarketPlace }} 48 | run: | 49 | Write-Host "Running vsce publish --packagePath ${{ steps.pack.outputs.VSIX_FILENAME }} --pat `$env:VSCE_PAT" 50 | vsce publish --packagePath ${{ steps.pack.outputs.VSIX_FILENAME }} --pat $env:VSCE_PAT 51 | env: 52 | VSCE_PAT: ${{ secrets.VSCE_TOKEN }} 53 | 54 | - name: Open VSX Publish 55 | if: ${{ inputs.shouldPublishToOpenVsx }} 56 | run: | 57 | Write-Host "Running npx ovsx publish --packagePath ${{ steps.pack.outputs.VSIX_FILENAME }} --pat `$env:OPEN_VSX_PAT" 58 | npx ovsx publish --packagePath ${{ steps.pack.outputs.VSIX_FILENAME }} --pat $env:OPEN_VSX_PAT 59 | env: 60 | OPEN_VSX_PAT: ${{ secrets.OPENVSX }} 61 | -------------------------------------------------------------------------------- /src/notificationService.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { Logger } from "./logger"; 3 | 4 | export class NotificationService { 5 | private context: vscode.ExtensionContext; 6 | public readonly storageKeyPrefix: string; 7 | private readonly lastRatingPromptDateKey: string; 8 | 9 | constructor(context: vscode.ExtensionContext) { 10 | this.context = context; 11 | this.storageKeyPrefix = this.context.extension.id + "."; 12 | this.lastRatingPromptDateKey = `${this.storageKeyPrefix}lastRatingPromptDate`; 13 | } 14 | 15 | public async promptForReviewAsync(): Promise { 16 | try { 17 | var shouldDisplayPrompt = this.shouldOpenRatingPrompt(); 18 | if (shouldDisplayPrompt) { 19 | let text = "Loving ResXpress extension? Would you like to rate and review?"; 20 | let selection = await vscode.window.showInformationMessage(text, "Sure", "Later", "Don't show again"); 21 | if (selection) { 22 | if (selection === "Sure") { 23 | var appName = vscode.env.appName.toLowerCase(); 24 | let vsCodeReviewUri: vscode.Uri = vscode.Uri.parse("https://marketplace.visualstudio.com/items?itemName=PrateekMahendrakar.resxpress&ssr=false#review-details"); 25 | 26 | if (appName.includes("codium")) { 27 | var codiumReviewUri = vscode.Uri.parse("https://open-vsx.org/extension/PrateekMahendrakar/resxpress/reviews"); 28 | vscode.env.openExternal(codiumReviewUri); 29 | } 30 | else { 31 | vscode.env.openExternal(vsCodeReviewUri); 32 | } 33 | //cant check if they really reviewed 34 | //remind them after 30 days 35 | var plus30days = new Date(); 36 | plus30days.setDate(plus30days.getDate() + 30); 37 | 38 | this.context.globalState.update(this.lastRatingPromptDateKey, plus30days); 39 | } 40 | else if (selection === "Later") { 41 | this.context.globalState.update(this.lastRatingPromptDateKey, new Date()); 42 | } 43 | else if (selection === "Don't show again") { 44 | var oneYear = new Date(); 45 | oneYear.setDate(oneYear.getDate() + 365); 46 | 47 | this.context.globalState.update(this.lastRatingPromptDateKey, oneYear); 48 | } 49 | } 50 | } 51 | } 52 | catch (error) { 53 | if (error instanceof Error) { 54 | Logger.instance.error(error); 55 | } 56 | } 57 | } 58 | 59 | private shouldOpenRatingPrompt(): boolean { 60 | var lastPromptDateJunk = this.context.globalState.get(this.lastRatingPromptDateKey) as Date; 61 | var minus15days = new Date(); 62 | minus15days.setDate(minus15days.getDate() - 15); 63 | 64 | if (lastPromptDateJunk) { 65 | var lastPromptDate = new Date(lastPromptDateJunk.toString()); 66 | return lastPromptDate < minus15days; 67 | } 68 | else { 69 | return true; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { Constants } from './constants'; 3 | 4 | export interface ILogger { 5 | info(message: string): void; 6 | error(error: Error): void; 7 | warning(message: string): void; 8 | debug(message: string): void; 9 | outputChannel: vscode.OutputChannel; 10 | setIsEnabled(isEnabled: boolean): void; 11 | } 12 | 13 | enum LogLevel { 14 | error = "ERROR", 15 | warning = "WARNING", 16 | info = "INFO", 17 | debug = "DEBUG", 18 | } 19 | 20 | function isString(value: unknown): value is string { 21 | return Object.prototype.toString.call(value) === '[object String]'; 22 | } 23 | 24 | export class Logger implements ILogger { 25 | private static _instance: Logger; 26 | private isDebug: boolean = false; 27 | 28 | public static get instance() { 29 | if (!Logger._instance) { 30 | Logger._instance = new Logger(); 31 | } 32 | 33 | return Logger._instance; 34 | } 35 | 36 | public setDebug(isDebug: boolean) { 37 | this.isDebug = isDebug; 38 | } 39 | 40 | private constructor() { 41 | this.outputChannel = vscode.window.createOutputChannel(Constants.extensionName); 42 | this.setIsEnabled(); 43 | } 44 | 45 | private enableLogs?: boolean; 46 | public outputChannel: vscode.OutputChannel; 47 | 48 | public error(error: Error): void { 49 | if (error !== null) { 50 | this.log(LogLevel.error, error.stack ?? `${error.name} : ${error.message}`); 51 | } 52 | } 53 | 54 | public info(message: string): void { 55 | this.log(LogLevel.info, message); 56 | } 57 | 58 | public debug(message: string): void { 59 | this.log(LogLevel.debug, message); 60 | } 61 | 62 | public warning(message: string): void { 63 | this.log(LogLevel.warning, message); 64 | } 65 | 66 | private log(logLevel: LogLevel, message: string, data?: unknown): void { 67 | if (this.enableLogs) { 68 | const logMessage = `[ ${logLevel} - ${new Date().toLocaleTimeString()}] ${message}`; 69 | if (this.isDebug) { 70 | console.log(logMessage); 71 | } 72 | this.appendLine(logMessage); 73 | if (data) { 74 | const dataString = Logger.data2String(data); 75 | if (this.isDebug) { 76 | console.log(dataString); 77 | } 78 | this.appendLine(dataString); 79 | } 80 | } 81 | } 82 | 83 | public setIsEnabled(isEnabled: boolean = false) { 84 | this.enableLogs = isEnabled; 85 | } 86 | 87 | private appendLine(value = '') { 88 | return this.outputChannel.appendLine(value); 89 | } 90 | 91 | private append(value: string) { 92 | return this.outputChannel.append(value); 93 | } 94 | 95 | private show() { 96 | this.outputChannel.show(); 97 | } 98 | 99 | private static data2String(data: unknown): string { 100 | if (data instanceof Error) { 101 | if (isString(data.stack)) { 102 | return data.stack; 103 | } 104 | return (data as Error).message; 105 | } 106 | if (isString(data)) { 107 | return data; 108 | } 109 | return JSON.stringify(data, undefined, 2); 110 | } 111 | } -------------------------------------------------------------------------------- /.github/workflows/pullrequest.yml: -------------------------------------------------------------------------------- 1 | name: Version Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - dev 8 | workflow_dispatch: 9 | 10 | jobs: 11 | versioncheck: 12 | runs-on: windows-latest 13 | steps: 14 | - name: Checkout branch 15 | uses: actions/checkout@v4.1.2 16 | 17 | - name: "Get changed files" 18 | id: getChangedFiles 19 | uses: jitterbit/get-changed-files@v1 20 | with: 21 | format: "json" 22 | 23 | - name: "Verify ChangeLog.md and package.json files have changes" 24 | shell: pwsh 25 | run: | 26 | $fileFilters=@('package.json', 'CHANGELOG*') 27 | $changesJson='${{ steps.getChangedFiles.outputs.all }}' 28 | $changes=ConvertFrom-Json -InputObject $changesJson 29 | foreach ($filter in $fileFilters) { 30 | if ($changes -match $filter){ 31 | Write-Host "$filter file is modified." 32 | } else { 33 | throw [System.Exception]("Files with filter $filter are not modified. ❌") 34 | } 35 | } 36 | Write-Host "Pass ✅" 37 | 38 | - name: Get current version from package.json 39 | id: getPackageVersion 40 | shell: pwsh 41 | run: | 42 | $package = Get-Content .\package.json | ConvertFrom-Json 43 | $version = $package.version 44 | Write-Host 'Current version from package.json: ' $version 45 | "VERSION=$version" >> $env:GITHUB_OUTPUT 46 | 47 | - name: Get published version from VS Code extension REST API 48 | id: getPublishedVersion 49 | run: | 50 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 51 | $headers.Add("VSMarketplaceBadge", "1.0") 52 | $headers.Add("Accept", "application/json;api-version=3.0-preview.1") 53 | $headers.Add("Content-Type", "application/json") 54 | $body = "{`"filters`":[{`"criteria`":[{`"filterType`":7,`"value`":`"PrateekMahendrakar.resxpress`"},{`"filterType`":12,`"value`":4096}]}],`"flags`":914}" 55 | $response = Invoke-RestMethod 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery' -Method Post -Headers $headers -Body $body 56 | $publishedVersion= ($response.results[0].extensions.versions.version).ToString() 57 | Write-Host 'Last Published version: '$publishedVersion 58 | "PUB_VERSION=$publishedVersion" >> $env:GITHUB_OUTPUT 59 | 60 | - name: Compare local version with marketplace version 61 | id: compare 62 | shell: pwsh 63 | run: | 64 | $localVersion = [System.Version]('${{ steps.getPackageVersion.outputs.VERSION }}') 65 | $storeVersion = [System.Version]('${{ steps.getPublishedVersion.outputs.PUB_VERSION }}') 66 | 67 | if ($localVersion -gt $storeVersion) { 68 | Write-Host "Version in package.json is higher than latest published version" 69 | Write-Host "Pass" 70 | } 71 | else { 72 | throw [System.Exception]("Version can not be same as latest published version ❌! Update package version.") 73 | } 74 | -------------------------------------------------------------------------------- /styles/webpanel.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 2rem; 4 | justify-content: center; 5 | align-items: center; 6 | } 7 | 8 | .error { 9 | color: red; 10 | display: inline-block; 11 | margin: 4px 20px; 12 | position: sticky; 13 | position: -webkit-sticky; 14 | top: 0px; 15 | background-color: var(--vscode-editor-background); 16 | } 17 | 18 | 19 | #container { 20 | height: 100%; 21 | width: 100%; 22 | display: flex; 23 | position: sticky; 24 | position: -webkit-sticky; 25 | } 26 | 27 | 28 | 29 | 30 | table { 31 | text-align: left; 32 | position: relative; 33 | border-collapse: collapse; 34 | width: 100%; 35 | } 36 | 37 | th, 38 | td { 39 | padding: 0.25rem; 40 | border: 1px solid; 41 | } 42 | 43 | th { 44 | background: white; 45 | position: sticky; 46 | position: -webkit-sticky; 47 | padding: -chrome-sticky; 48 | top: 62px; 49 | margin-top: 62px; 50 | box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4); 51 | background-color: var(--vscode-sideBar-background); 52 | text-align: center; 53 | padding: 15px 15px; 54 | } 55 | 56 | tr:nth-child(even) { 57 | background-color: var(--vscode-sideBar-background); 58 | padding: 0; 59 | } 60 | 61 | input { 62 | background-color: transparent; 63 | color: var(--vscode-editor-foreground); 64 | border-color: transparent; 65 | height: 100%; 66 | width: 95%; 67 | display: inline-block; 68 | margin: 0px 3px; 69 | padding: 0px 2px; 70 | } 71 | 72 | p { 73 | text-align: center; 74 | } 75 | 76 | .sticky-div { 77 | position: sticky; 78 | top: 0; 79 | background-color: var(--vscode-editor-background); 80 | padding: 10px; 81 | padding-bottom: 20px; 82 | border-bottom: none; 83 | display: flex; 84 | flex-wrap: wrap; 85 | gap: 15px; 86 | align-items: center; 87 | z-index: 1000; 88 | } 89 | 90 | .btn { 91 | display: inline-flex; 92 | align-items: center; 93 | background-color: #067ab8; 94 | color: var(--vscode-editor-background); 95 | padding: 8px 12px; 96 | border: none; 97 | border-radius: 8px; 98 | font-size: 18px; 99 | cursor: pointer; 100 | text-decoration: none; 101 | transition: background-color 0.3s ease; 102 | white-space: nowrap; 103 | } 104 | 105 | .btn svg { 106 | margin-right: 6px; 107 | fill: currentColor; 108 | } 109 | 110 | .btn:hover { 111 | background-color: #005b98; 112 | } 113 | 114 | .btn.primary { 115 | background-color: #067ab8; 116 | color: #ffffff; 117 | font-weight: 500; 118 | } 119 | 120 | .btn.primary:hover { 121 | background-color: #005b98; 122 | box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); 123 | } 124 | 125 | /* Secondary Button Style */ 126 | .btn.secondary { 127 | background-color: #5A9BD5; 128 | color: #ffffff; 129 | } 130 | 131 | .btn.secondary:hover { 132 | background-color: #1976D2; 133 | box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); 134 | } 135 | 136 | .error-block { 137 | color: #dc3e36; 138 | font-size: 14px; 139 | margin: 0; 140 | flex-grow: 1; 141 | } 142 | 143 | .namespace-section { 144 | display: flex; 145 | align-items: center; 146 | gap: 10px; 147 | /* Spacing between text and button */ 148 | flex-grow: 1; 149 | /* Ensures alignment */ 150 | } 151 | 152 | .namespace-section span { 153 | color: var(--vscode-editor-foreground); 154 | } 155 | 156 | .namespace-section strong { 157 | font-size: large; 158 | font-weight: bold; 159 | color: #5A9BD5; 160 | } 161 | 162 | .icon { 163 | width: 16px; 164 | height: 16px; 165 | margin-right: 6px; 166 | color: white; 167 | } 168 | 169 | .filter-fefefe { 170 | filter: invert(100%) sepia(100%) saturate(1%) hue-rotate(208deg) brightness(109%) contrast(99%); 171 | } -------------------------------------------------------------------------------- /src/fileHelper.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import path = require("path"); 3 | import { readFile, writeFile } from "fs/promises"; 4 | import { existsSync, mkdirSync, readFileSync } from "fs"; 5 | import { Constants, emptyString } from "./constants"; 6 | import { Logger } from "./logger"; 7 | 8 | export class FileHelper { 9 | 10 | public static getFileNameNoExt(document: vscode.TextDocument): string { 11 | let parsedPath = path.parse(document.fileName); 12 | var fileName = parsedPath.name; 13 | return fileName; 14 | } 15 | 16 | public static getDirectory(document: vscode.TextDocument): string { 17 | let parsedPath = path.parse(document.fileName); 18 | return parsedPath.dir 19 | } 20 | 21 | public static async writeToFile(filePath: string, text: string) { 22 | if (filePath !== emptyString) { 23 | const dir = path.dirname(filePath); 24 | if (!existsSync(dir)) { 25 | mkdirSync(dir, { recursive: true }); 26 | } 27 | await writeFile(filePath, text); 28 | } 29 | } 30 | 31 | public static async tryGetNamespace(document: vscode.TextDocument): Promise { 32 | try { 33 | var namespace = "Unknown"; 34 | let fileNameNoExt = FileHelper.getFileNameNoExt(document); 35 | let workspacePath = vscode.workspace.getWorkspaceFolder(document.uri); 36 | if (workspacePath) { 37 | let pathToRead = path.join(workspacePath.uri.fsPath, `.${Constants.namespaceMappingJsonPath}`); 38 | let content = await this.getFileText(pathToRead); 39 | if (content.length > 0) { 40 | try { 41 | var namespaceMappingRec = JSON.parse(content); 42 | if (namespaceMappingRec && fileNameNoExt && namespaceMappingRec[fileNameNoExt]) { 43 | namespace = namespaceMappingRec[fileNameNoExt]; 44 | } 45 | } catch (error) { 46 | if (error instanceof Error) { 47 | Logger.instance.error(error); 48 | } 49 | } 50 | } 51 | } 52 | if ((namespace === "Unknown" || namespace.length === 0) && fileNameNoExt.length > 0) { 53 | let fileUrls = await vscode.workspace.findFiles(`**/${fileNameNoExt}.Designer.cs`, null, 1); 54 | 55 | if (fileUrls.length > 0) { 56 | const fileContent = readFileSync(fileUrls[0].fsPath, "utf-8"); 57 | 58 | if (fileContent && fileContent.length > 0) { 59 | var lines = fileContent.split("\r\n"); 60 | if (lines.length === 1) { 61 | lines = fileContent.split("\n"); 62 | } 63 | var newLines = lines.filter(x => x.startsWith("namespace ")).map(x => x.trim().replace("namespace ", emptyString).replace(" ", emptyString).replace("{", emptyString).replace(";", emptyString)); 64 | if (newLines.length > 0) { 65 | namespace = newLines[0]; 66 | } 67 | } 68 | } 69 | } 70 | return namespace; 71 | 72 | } catch (error) { 73 | if (error instanceof Error) { 74 | Logger.instance.error(error); 75 | } 76 | return null; 77 | } 78 | } 79 | 80 | public static async getFileText(filepath: string): Promise { 81 | if (existsSync(filepath)) { 82 | let content = await readFile(filepath, { encoding: "utf-8" }); 83 | return content; 84 | } 85 | return emptyString; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ResXpress 2 | 3 | Resx editor, previewer and reorganizer extension for Visual Studio Code and VSCodium. 4 | 5 | Logo 6 | 7 | [![License](https://img.shields.io/github/license/pmahend1/resxpress?style=flat-square&label=License&color=9cf)](https://choosealicense.com/licenses/mit/)![Deploy](https://img.shields.io/github/actions/workflow/status/pmahend1/resxpress/main.yml?branch=main&color=brightgreen&label=Deploy%20CI&style=flat-square&logo=github) 8 | 9 | 10 | [![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/PrateekMahendrakar.resxpress?style=for-the-badge&color=blue&logo=visualstudiocode&logoColor=blue&label=Visual%20Studio%20MarketPlace)](https://marketplace.visualstudio.com/items?itemName=PrateekMahendrakar.resxpress)![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/PrateekMahendrakar.resxpress?style=for-the-badge&color=blue)![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/PrateekMahendrakar.resxpress?style=for-the-badge&color=blue)![Visual Studio Marketplace Rating](https://img.shields.io/visual-studio-marketplace/r/PrateekMahendrakar.resxpress?style=for-the-badge&color=blue) 11 | [![Open VSX Version](https://img.shields.io/open-vsx/v/PrateekMahendrakar/resxpress?color=darkcyan&style=for-the-badge&logo=vscodium&logoColor=darkcyan)](https://open-vsx.org/extension/PrateekMahendrakar/resxpress)![Open VSX Rating](https://img.shields.io/open-vsx/rating/prateekmahendrakar/resxpress?style=for-the-badge&color=darkcyan)![Open VSX Downloads](https://img.shields.io/open-vsx/dt/PrateekMahendrakar/ResxPress?style=for-the-badge&&color=darkcyan) 12 | 13 | [](https://www.buymeacoffee.com/pmahend1) 14 | 15 | --- 16 | 17 | ## Features 18 | 19 | ### Custom Resx Editor 20 | 21 | ![Resx Editor](./images/../images/resxEditor.png) 22 | 23 | This is **active by default** when the file is opened, _however_ you can choose to open from **Explorer Panel - Right Click on the resx file - Choose ResXpress Editor** 24 | 25 | ![Custom Editor Option](./images/resxEditorOption.png) 26 | 27 | Below is the **Text Editor** vs. **Resx Editor** side by side comparision. 28 | 29 | ![Compare Editors](/images/textVsResxEditor.png) 30 | 31 | It offers the following features; 32 | 33 | - Adding a new resx data. 34 | - Editing an existing resx data. 35 | - Deleting an existing resx data. 36 | - Checks for resx data with duplicate keys and shows error if exists. 37 | - To and Fro updates between Text document and ResxEditors as soon as typed valid resx data. 38 | - To and fro updates Text document and ResxEditors when Save triggered on either. 39 | - Automatically regenerate strongly typed resource class file(controlled by setting) 40 | - Add a new resx file. 41 | - Update C# namespace of a resx file. 42 | 43 | ### ResXpress: Markdown Preview 44 | 45 | Preview resx file as Markdown table. 46 | 47 | ![Screenshot1](./images/preview.gif) 48 | 49 | ### ResXpress: Sort By Keys 50 | 51 | Reorganize file by sorting by Keys. 52 | 53 | ![Screenshot2](./images/sortByKeys.gif) 54 | 55 | ### ResXpress: Web Preview 56 | 57 | Preview resx file as a nice Webview table. 58 | 59 | ![Screenshot3](./images/webPreview.gif) 60 | 61 | ### Adding new resx file 62 | 63 | ![Adding resx file](./images/createNewResxFile.gif) 64 | 65 | ### Updating resx C# namespace 66 | 67 | ![Resx namespace](./images/updateResxNamespace.gif) 68 | 69 | ### Resx Data Snippet 70 | 71 | - Type `resx` and snippet will pop up. 72 | - If it doesnt then Control+Space(CMD+Space for Mac) to trigger intellisense. 73 | - Edit key, value and comment values(press Tab to go to next editable value) 74 | 75 | ![Snippet](./images/snippet.png) 76 | 77 | ### Settings 78 | 79 | 1. `generateStronglyTypedResourceClassOnSave`: Generate/update strongly typed resource class files when updates to resx files are made. 80 | Default: **`false`**. 81 | Ex: Resource1.resx → Resource1.Designer.cs 82 | 83 | 1. `useFileScopedNamespace`: Use File Scoped Namespace. 84 | Default: **`true`** 85 | **true**: File scoped namespaces. 86 | **false**: Block scoped namespaces. 87 | 88 | 1. `indentSpaceLength`: Indent space length for resx xml. 89 | Default: **4**. 90 | Options: **2, 4, 8**. 91 | 92 | ## Known Issues 93 | 94 | None as of now. 95 | 96 | ## Release Notes 97 | 98 | [ChangeLog](./CHANGELOG.md) 99 | -------------------------------------------------------------------------------- /src/resxEditorProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | import { ResxEditor } from "./resxEditor"; 3 | import { ResxJsonHelper } from "./resxJsonHelper"; 4 | import { WebpanelPostMessageKind } from "./webpanelMessageKind"; 5 | import { setNewNamespace, sortByKeys } from "./extension"; 6 | import { FileHelper } from "./fileHelper"; 7 | import { WebpanelPostMessage } from "./webpanelPostMessage"; 8 | import * as xmljs from "xml-js"; 9 | import { Constants, DATA, emptyString } from "./constants"; 10 | import { Logger } from "./logger"; 11 | 12 | export class ResxEditorProvider implements vscode.CustomTextEditorProvider { 13 | 14 | private readonly context: vscode.ExtensionContext; 15 | private readonly resxEditor: ResxEditor; 16 | 17 | constructor(context: vscode.ExtensionContext) { 18 | this.context = context; 19 | this.resxEditor = new ResxEditor(this.context); 20 | } 21 | 22 | public static register(context: vscode.ExtensionContext): vscode.Disposable { 23 | const provider = new ResxEditorProvider(context); 24 | const providerRegistration = vscode.window.registerCustomEditorProvider(`${Constants.resxpress}.${Constants.editor}`, provider); 25 | return providerRegistration; 26 | } 27 | 28 | /** 29 | * Called when our custom editor is opened. 30 | */ 31 | public async resolveCustomTextEditor(document: vscode.TextDocument, webviewPanel: vscode.WebviewPanel, _token: vscode.CancellationToken): Promise { 32 | // Setup initial content for the webview 33 | webviewPanel.webview.options = { 34 | enableScripts: true, 35 | enableForms: true, 36 | }; 37 | 38 | if (_token.isCancellationRequested) { 39 | return; 40 | } 41 | const namespace = await FileHelper.tryGetNamespace(document); 42 | 43 | var jsObj = xmljs.xml2js(document.getText()); 44 | var jsonData: any = []; 45 | var sorted = []; 46 | jsObj.elements[0].elements.forEach((x: any) => { 47 | if (x.name === DATA) { 48 | jsonData.push(x); 49 | } 50 | else { 51 | sorted.push(x); 52 | } 53 | }); 54 | 55 | let htmlContent = emptyString; 56 | 57 | let i = 0; 58 | jsonData.forEach((element: any) => { 59 | var valueStr = emptyString; 60 | var commentstr = emptyString; 61 | element.elements.forEach((subElement: any) => { 62 | if (subElement.name === "value" && subElement.elements?.length > 0) { 63 | valueStr = subElement.elements[0].text; 64 | } 65 | else if (subElement.name === "comment" && subElement.elements?.length > 0) { 66 | commentstr = subElement.elements[0].text; 67 | } 68 | }); 69 | htmlContent += ` 70 | 71 | 72 | 73 |

X

74 | `; 75 | i = i + 1; 76 | }); 77 | webviewPanel.webview.html = this.resxEditor.getHtmlForWebview(webviewPanel.webview, namespace ?? emptyString, htmlContent); 78 | 79 | // Receive message from the webview. 80 | let webviewListener = webviewPanel.webview.onDidReceiveMessage(async (e) => { 81 | Logger.instance.info(`webviewPanel.webview.onDidReceiveMessage: ${JSON.stringify(e)}`); 82 | switch (e.type) { 83 | case WebpanelPostMessageKind.TriggerTextDocumentUpdate: 84 | this.resxEditor.updateTextDocument(document, e.text); 85 | break; 86 | case WebpanelPostMessageKind.Add: 87 | this.resxEditor.addNewKeyValue(document, e.text); 88 | break; 89 | 90 | case WebpanelPostMessageKind.Delete: 91 | this.resxEditor.deleteKeyValue(document, e.text); 92 | break; 93 | case WebpanelPostMessageKind.Switch: 94 | vscode.window.showTextDocument(document, vscode.ViewColumn.Active); 95 | break; 96 | case WebpanelPostMessageKind.TriggerNamespaceUpdate: 97 | let newNamespace = await setNewNamespace(document); 98 | if (newNamespace) { 99 | setNewNamespaceInWebview(newNamespace); 100 | } 101 | break; 102 | case WebpanelPostMessageKind.SortByKeys: 103 | await sortByKeys(document); 104 | updateWebview(); 105 | break; 106 | } 107 | }); 108 | 109 | function setNewNamespaceInWebview(newNamespace: string) { 110 | webviewPanel.webview.postMessage(new WebpanelPostMessage(WebpanelPostMessageKind.NewNamespace, newNamespace)); 111 | } 112 | 113 | function updateWebview() { 114 | var jsonText = JSON.stringify(ResxJsonHelper.getJsonData(document.getText())); 115 | webviewPanel.webview.postMessage(new WebpanelPostMessage(WebpanelPostMessageKind.UpdateWebPanel, jsonText)); 116 | } 117 | 118 | 119 | // Make sure we get rid of the listener when our editor is closed. 120 | webviewPanel.onDidDispose(() => { 121 | webviewListener.dispose(); 122 | }); 123 | 124 | updateWebview(); 125 | } 126 | 127 | content: string = emptyString; 128 | } 129 | -------------------------------------------------------------------------------- /src/previewEditPanel.ts: -------------------------------------------------------------------------------- 1 | import path = require("path"); 2 | import * as vscode from "vscode"; 3 | import { getNonce } from "./util"; 4 | import { WebpanelPostMessageKind } from "./webpanelMessageKind"; 5 | import { Logger } from "./logger"; 6 | import { emptyString } from "./constants"; 7 | 8 | /** 9 | * Preview only. Key-Value pairs cannot be edited. 10 | */ 11 | class PreviewEditPanel { 12 | 13 | public static currentPanel: PreviewEditPanel | undefined; 14 | 15 | public static readonly viewType = "previewEdit"; 16 | 17 | private readonly panel: vscode.WebviewPanel; 18 | public content: string; 19 | private disposables: vscode.Disposable[] = []; 20 | private static title: string = "Resx Preview"; 21 | 22 | public static namespace: string = emptyString; 23 | 24 | public setNewNamespace(namespace: string) { 25 | PreviewEditPanel.namespace = namespace; 26 | if (PreviewEditPanel.currentPanel) { 27 | PreviewEditPanel.currentPanel.panel.webview.postMessage({ 28 | type: WebpanelPostMessageKind.NewNamespace, 29 | data: namespace 30 | }); 31 | } 32 | } 33 | 34 | public static createOrShow(extensionUri: vscode.Uri, title: string, content: string) { 35 | 36 | const column = vscode.window.activeTextEditor 37 | ? vscode.window.activeTextEditor.viewColumn 38 | : undefined; 39 | 40 | this.title = title; 41 | 42 | // If we already have a panel, show it. 43 | if (PreviewEditPanel.currentPanel) { 44 | PreviewEditPanel.currentPanel.panel.reveal(column); 45 | return; 46 | } 47 | 48 | // Otherwise, create a new panel. 49 | const panel = vscode.window.createWebviewPanel( 50 | PreviewEditPanel.viewType, 51 | "PreviewEdit", 52 | column || vscode.ViewColumn.One, 53 | { 54 | // Enable javascript in the webview 55 | enableScripts: true, 56 | enableForms: true, 57 | // And restrict the webview to only loading content from our extension's `webpanel` directory. 58 | localResourceRoots: [vscode.Uri.joinPath(extensionUri, "styles"), vscode.Uri.joinPath(extensionUri, "out")] 59 | } 60 | ); 61 | 62 | PreviewEditPanel.currentPanel = new PreviewEditPanel(panel, extensionUri, content); 63 | } 64 | 65 | public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, content: string) { 66 | PreviewEditPanel.currentPanel = new PreviewEditPanel(panel, extensionUri, content); 67 | } 68 | 69 | private extensionUri: vscode.Uri; 70 | private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, content: string) { 71 | this.panel = panel; 72 | this.content = content; 73 | this.extensionUri = extensionUri; 74 | // Set the webview's initial html content 75 | this.update(content); 76 | 77 | // Listen for when the panel is disposed 78 | // This happens when the user closes the panel or when the panel is closed programatically 79 | this.panel.onDidDispose(() => this.dispose(), null, this.disposables); 80 | 81 | // Update the content based on view changes 82 | this.panel.onDidChangeViewState((e: vscode.WebviewPanelOnDidChangeViewStateEvent) => { 83 | if (this.panel.visible || e.webviewPanel.visible) { 84 | this.update(this.content); 85 | } 86 | }, null, this.disposables); 87 | 88 | // Handle messages from the webview 89 | this.panel.webview.onDidReceiveMessage( 90 | message => { 91 | Logger.instance.info(message); 92 | switch (message.type) { 93 | case WebpanelPostMessageKind.Alert: 94 | vscode.window.showErrorMessage(message.text); 95 | return; 96 | } 97 | }, 98 | null, 99 | this.disposables 100 | ); 101 | } 102 | 103 | public dispose() { 104 | PreviewEditPanel.currentPanel = undefined; 105 | 106 | // Clean up our resources 107 | this.panel.dispose(); 108 | 109 | while (this.disposables.length) { 110 | const x = this.disposables.pop(); 111 | if (x) { 112 | x.dispose(); 113 | } 114 | } 115 | } 116 | 117 | private update(content: string) { 118 | const webview = this.panel.webview; 119 | 120 | // Vary the webview's content based on where it is located in the editor. 121 | switch (this.panel.viewColumn) { 122 | case vscode.ViewColumn.Two: 123 | this.updateKeyValues(webview, content); 124 | return; 125 | 126 | case vscode.ViewColumn.Three: 127 | this.updateKeyValues(webview, content); 128 | return; 129 | 130 | case vscode.ViewColumn.One: 131 | default: 132 | this.updateKeyValues(webview, content); 133 | return; 134 | } 135 | } 136 | 137 | private updateKeyValues(webview: vscode.Webview, content: string) { 138 | this.panel.title = PreviewEditPanel.title + " Preview"; 139 | this.panel.webview.html = this.getHtmlForWebview(webview, content); 140 | } 141 | 142 | private getHtmlForWebview(webview: vscode.Webview, content: string) { 143 | const scriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(this.extensionUri.path, "out", "webpanelScript.js"))); 144 | const styleUri = webview.asWebviewUri(vscode.Uri.file(path.join(this.extensionUri.path, "styles", "webpanel.css"))); 145 | const nonce = getNonce(); 146 | 147 | return ` 148 | 149 | 150 | 151 | 153 | 154 | 155 | ResxFileName 156 | 157 | 158 |
159 |

${this.panel.title}

160 |
161 | 162 | 163 | 164 | 165 | 166 | 167 | ${content} 168 |
KeyValueComment
169 | 170 | 171 | 172 | `; 173 | } 174 | } 175 | 176 | 177 | export { PreviewEditPanel }; 178 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resxpress", 3 | "displayName": "ResXpress", 4 | "license": "MIT", 5 | "description": "Resx editor, previewer and sorting extension", 6 | "publisher": "PrateekMahendrakar", 7 | "icon": "images/logo.png", 8 | "repository": { 9 | "url": "https://github.com/pmahend1/resxpress.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/pmahend1/resxpress/issues" 13 | }, 14 | "galleryBanner": { 15 | "color": "#123456", 16 | "theme": "dark" 17 | }, 18 | "version": "7.7.0", 19 | "engines": { 20 | "vscode": "^1.98.0" 21 | }, 22 | "categories": [ 23 | "Snippets", 24 | "Visualization", 25 | "Other" 26 | ], 27 | "activationEvents": [ 28 | "workspaceContains:**/*.resx" 29 | ], 30 | "main": "./out/extension.js", 31 | "contributes": { 32 | "customEditors": [ 33 | { 34 | "viewType": "resxpress.editor", 35 | "displayName": "ResXpress Editor", 36 | "selector": [ 37 | { 38 | "filenamePattern": "*.resx" 39 | } 40 | ], 41 | "priority": "default" 42 | } 43 | ], 44 | "commands": [ 45 | { 46 | "command": "resxpress.resxpreview", 47 | "title": "ResXpress: Markdown Preview" 48 | }, 49 | { 50 | "command": "resxpress.sortbykeys", 51 | "title": "ResXpress: Sort By Keys" 52 | }, 53 | { 54 | "command": "resxpress.newpreview", 55 | "title": "ResXpress: Web Preview" 56 | }, 57 | { 58 | "command": "resxpress.setNameSpace", 59 | "title": "ResXpress: Set Namespace" 60 | }, 61 | { 62 | "command": "resxpress.createResxFile", 63 | "title": "ResXpress: Create a Resx Resources file" 64 | } 65 | ], 66 | "snippets": [ 67 | { 68 | "language": "xml", 69 | "filenamePattern": "*.resx", 70 | "path": "./snippets/resx.json" 71 | } 72 | ], 73 | "menus": { 74 | "editor/context": [ 75 | { 76 | "command": "resxpress.newpreview", 77 | "group": "z_commands", 78 | "when": "resourceExtname == .resx" 79 | } 80 | ], 81 | "editor/title/context": [ 82 | { 83 | "command": "resxpress.newpreview", 84 | "group": "3_preview", 85 | "when": "resourceExtname == .resx" 86 | } 87 | ], 88 | "explorer/context": [ 89 | { 90 | "command": "resxpress.setNameSpace", 91 | "group": "3_preview", 92 | "when": "resourceExtname == .resx" 93 | }, 94 | { 95 | "command": "resxpress.createResxFile", 96 | "group": "3_preview", 97 | "when": "foldersViewVisible" 98 | } 99 | ] 100 | }, 101 | "configuration": [ 102 | { 103 | "title": "ResXpress Settings", 104 | "properties": { 105 | "resxpress.configuration.generateStronglyTypedResourceClassOnSave": { 106 | "type": "boolean", 107 | "default": false, 108 | "markdownDescription": "Generate/update strongly typed resource class files when updates to resx files are made. Default is **`false`** \n\n Ex: Resource1.resx → Resource1.Designer.cs" 109 | }, 110 | "resxpress.configuration.useFileScopedNamespace": { 111 | "type": "boolean", 112 | "default": true, 113 | "markdownDescription": "Use File Scoped Namespace. Default is **`true`** \n\n **true**: File scoped namespaces. \n\n **false**: Block scoped namespaces." 114 | }, 115 | "resxpress.configuration.indentSpaceLength": { 116 | "type": "number", 117 | "default": 4, 118 | "enum": [ 119 | 2, 120 | 4, 121 | 8 122 | ], 123 | "markdownDescription": "No. of spaces for resx xml. Default: *4*" 124 | }, 125 | "resxpress.configuration.enableLocalLogs": { 126 | "type": "boolean", 127 | "default": false, 128 | "markdownDescription": "Enable local logs. Default is **`false`** \n\n Logs are shown in Output window." 129 | } 130 | } 131 | } 132 | ] 133 | }, 134 | "capabilities": { 135 | "untrustedWorkspaces": { 136 | "supported": true 137 | } 138 | }, 139 | "keywords": [ 140 | "resx", 141 | "custom editor", 142 | "editor", 143 | "xml", 144 | "preview", 145 | "markdown", 146 | "sort" 147 | ], 148 | "markdown": "standard", 149 | "scripts": { 150 | "compile": "webpack --mode development", 151 | "watch": "webpack --mode development --watch", 152 | "vscode:prepublish": "npm run package", 153 | "package": "webpack --mode production --devtool hidden-source-map", 154 | "compile-tests": "tsc -p . --outDir out", 155 | "pretest": "npm run compile-tests", 156 | "test": "vscode-test" 157 | }, 158 | "devDependencies": { 159 | "@types/node": "^22.18.6", 160 | "@types/vscode": "^1.98.0", 161 | "@vscode/test-electron": "^2.5.2", 162 | "eslint": "^9.37.0", 163 | "ts-loader": "^9.5.4", 164 | "typescript": "^5.9.3", 165 | "webpack": "^5.102.0", 166 | "webpack-cli": "^6.0.1", 167 | "xml-js": "^1.6.11" 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/resxEditor.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as vscode from "vscode"; 3 | import { getNonce } from "./util"; 4 | import * as xmljs from "xml-js" 5 | import { ResxJsonHelper } from "./resxJsonHelper"; 6 | import { Settings } from "./settings"; 7 | import { Logger } from "./logger"; 8 | import { nameof } from "./nameof"; 9 | 10 | export class ResxEditor { 11 | private readonly context: vscode.ExtensionContext; 12 | constructor(context: vscode.ExtensionContext) { 13 | this.context = context; 14 | } 15 | 16 | public getHtmlForWebview(webview: vscode.Webview, namespace: string, content: string): string { 17 | 18 | const scriptUri = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "out", "webpanelScript.js"))); 19 | const styleUri = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "styles", "webpanel.css"))); 20 | const maPlusThick = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "styles", "ma-plus-thick.svg"))); 21 | const faPenToSquare = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "styles", "fa-pen-to-square.svg"))); 22 | const faRightLeft = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "styles", "fa-right-left.svg"))); 23 | const faSortAtoZ = webview.asWebviewUri(vscode.Uri.file(path.join(this.context.extensionPath, "styles", "fa-arrow-down-a-z-solid-full.svg"))); 24 | const nonce = getNonce(); 25 | 26 | return ` 27 | 28 | 29 | 30 | 32 | 33 | 34 | ResxFileName 35 | 36 | 37 |
38 | 41 | 42 | 45 |
46 | Namespace: ${namespace} 47 | 50 |
51 | 54 |

55 |
56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | ${content} 66 | 67 |
KeyValueComment
68 | 69 | 70 | `; 71 | } 72 | 73 | /** 74 | * Add a new key value back to text editor 75 | */ 76 | public addNewKeyValue(document: vscode.TextDocument, json: any) { 77 | const newObj = JSON.parse(json); 78 | const docDataList = ResxJsonHelper.getJsonData(document.getText()); 79 | 80 | var pos = docDataList.map((x) => { return x?._attributes?.name; }).indexOf(newObj._attributes.name); 81 | 82 | //avoid adding data with same key 83 | if (pos === -1) { 84 | docDataList.push(newObj); 85 | } 86 | else { 87 | // commented for now. its triggering twice 88 | vscode.window.showErrorMessage(`Data with same key ${newObj._attributes.name} already exists`); 89 | } 90 | return this.updateTextDocument(document, JSON.stringify(docDataList)); 91 | } 92 | 93 | /** 94 | * Delete an existing scratch from a document. 95 | */ 96 | public deleteKeyValue(document: vscode.TextDocument, json: any) { 97 | 98 | 99 | var deletedJsObj = JSON.parse(json); 100 | var currentData = ResxJsonHelper.getJsonData(document.getText()); 101 | 102 | Logger.instance.info(`${nameof(ResxEditor)}.${nameof(this.deleteKeyValue)}: Datalist before deleting ${deletedJsObj._attributes.name} : ${JSON.stringify(currentData)}`); 103 | var pos = currentData.map(e => e?._attributes?.name).indexOf(deletedJsObj._attributes.name); 104 | 105 | currentData.splice(pos, 1); 106 | Logger.instance.info(`${nameof(ResxEditor)}.${nameof(this.deleteKeyValue)}: Deleted ${deletedJsObj._attributes.name}`); 107 | return this.updateTextDocument(document, JSON.stringify(currentData)); 108 | } 109 | 110 | public updateTextDocument(document: vscode.TextDocument, dataListJson: any) { 111 | Logger.instance.info(`${nameof(ResxEditor)}.${nameof(this.updateTextDocument)}: `); 112 | 113 | var dataList = JSON.parse(dataListJson); 114 | const edit = new vscode.WorkspaceEdit(); 115 | 116 | var currentJs: any = xmljs.xml2js(document.getText(), { compact: true }) 117 | 118 | if (dataList) { 119 | switch (dataList.length) { 120 | case 0: 121 | delete currentJs.root.data; 122 | break; 123 | case 1: 124 | currentJs.root.data = dataList[0]; 125 | break; 126 | default: 127 | currentJs.root.data = dataList; 128 | break; 129 | } 130 | } 131 | else { 132 | Logger.instance.warning(`${nameof(ResxEditor)}.${nameof(this.updateTextDocument)}: empty datalist`); 133 | currentJs.root.data = {}; 134 | } 135 | var resx = xmljs.js2xml(currentJs, { spaces: Settings.indentSpaceLength, compact: true }); 136 | Logger.instance.info(`${nameof(ResxEditor)}.${nameof(this.updateTextDocument)}: ${resx}`); 137 | edit.replace(document.uri, new vscode.Range(0, 0, document.lineCount, 0), resx); 138 | return vscode.workspace.applyEdit(edit); 139 | } 140 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 7.7.0 - 06-Oct-2025 4 | 5 | - [#127: Scroll and focus to new resx key](https://github.com/pmahend1/resxpress/issues/127) 6 | - Package updates. 7 | 8 | ## 7.6.1 - 28-Sep-2025 9 | 10 | - Fixed adding new resx key not working 11 | - Sorting case sensitivity fix 12 | 13 | ## 7.6.0 - 28-Sep-2025 14 | 15 | - Added sort by keys button to webview editor. 16 | - Fixed namespace not being updated on webview editor. 17 | 18 | ## 7.5.0 - 21-Sep-2025 19 | 20 | - [#117: `focusout` event changed to `input` event for key, value and comment input fields to make it more responsive.](https://github.com/pmahend1/resxpress/issues/117) 21 | - Package updates. 22 | 23 | ## 7.4.0 - 14-Sep-2025 24 | 25 | - [#109: Added indentSpaceLength setting](https://github.com/pmahend1/resxpress/issues/109) 26 | - [#110: Added enable local logs setting](https://github.com/pmahend1/resxpress/pull/110) 27 | - [#111: Make webpanel dirty only if key/value/comment is updated for edit mode](https://github.com/pmahend1/resxpress/pull/111) 28 | - Package updates. 29 | 30 | ## 7.3.0 - 18-Jul-2025 31 | 32 | - Package updates. 33 | 34 | ## 7.2.2 - 08-Mar-2025 35 | 36 | - Fixes [namespace contains ;](https://github.com/pmahend1/resxpress/issues/106) 37 | - Package updates 38 | - Deploy workflow fix. 39 | 40 | ## 7.2.1 - 07-Mar-2025 41 | 42 | - [Fixed an issue where multiple resources C# file was getting created for different cultures#101](https://github.com/pmahend1/resxpress/issues/101) 43 | - Added setting to choose between file-scoped vs block-scoped namespace for C# resource file 44 | - Package updates. 45 | 46 | ## 7.1.1 - 03-Dec-2024 47 | 48 | - [Allows `_` in C# resource property name.](https://github.com/pmahend1/resxpress/issues/98) 49 | Thanks to [@manugparedes](https://github.com/manugparedes) for contribution. 50 | - Some package updates. 51 | 52 | ## 7.1.0 - 02-Dec-2024 53 | 54 | - Fixed a bug with unable to create file URI on windows and linux. 55 | - Switched to pnpm from yarn. 56 | - Package updates to latest. 57 | 58 | ## 7.0.0 - 29-Nov-2024 59 | 60 | - Added feature to add a new resx file. 61 | - Added feature to add/update resx namespace. 62 | - Improvements to C# resource file generation. No longer needs resgen. 63 | - Fixed some issues with generation of C# resource file. 64 | - Webview Resx editor is default now. 65 | - `yarn` upgrades. 66 | 67 | ## 6.1.1 - 18-Jun-2024 68 | 69 | - `yarn` upgrades. 70 | - Fixes [braces vulnerability](https://github.com/advisories/GHSA-grv7-fg5c-xmjg) 71 | 72 | ## 6.1.0 - 17-Apr-2024 73 | 74 | - `yarn` upgrades. 75 | - Added sponsorship. 76 | 77 | ## 6.0.2 - 10-Feb-2024 78 | 79 | - Fixed an issue which generated `undefined` string in strongly typed resource file name. 80 | 81 | ## 6.0.1 - 09-Feb-2024 82 | 83 | - Fixed an issue where webpack did not export webpanelScript.js 84 | - Updated es-lint 85 | 86 | ## 6.0.0 - 08-Feb-2024 87 | 88 | - Custom editor is set to *optional* now to be compatible with git diff. You can still open ResxEditor from editor context menu for resx files. Right click on file -> Open with -> select ResXpress Editor. 89 | - **Web Preview** now available in **editor/context** and **editor/title/context** menus too. 90 | - Added **Switch to Text Editor** button on resx editor. 91 | - Added implementation to generate strongly typed resource class files on non Windows platforms. 92 | - `yarn` upgrades. 93 | 94 | ## 5.8.0 - 31-Jan-2024 95 | 96 | - `yarn` upgrades. 97 | - Fixed snippet not popping issue. 98 | 99 | ## 5.7.0 - 02-Jan-2024 100 | 101 | - `yarn` upgrades 102 | - Strongly typed resource generation only on Windows. 103 | 104 | ## 5.6.0 - 10-Nov-2023 105 | 106 | - `yarn` upgrades. 107 | 108 | ## 5.5.0 - 20-Sep-2023 109 | 110 | - `yarn` upgrades. 111 | 112 | ## 5.4.0 - 30-Jul-2023 113 | 114 | - `yarn` upgrades. 115 | 116 | ## 5.3.1 - 9-Jun-2023 117 | 118 | - Updated broken badge icons. 119 | 120 | ## 5.3.0 - 8-Jun-2023 121 | 122 | - `yarn` upgrades. 123 | 124 | ## 5.2.0 - 18-Apr-2023 125 | 126 | - `yarn` upgrades. 127 | 128 | ## 5.1.0 - 15-Mar-2023 129 | 130 | - `yarn` upgrades. 131 | 132 | ## 5.0.0 - 5-Jan-2023 133 | 134 | - `ResGen` added as setting. 135 | - Reverted to classic `yarn` because of no VSCE support for yarn v2 or v3. 136 | - `yarn` upgrades. 137 | 138 | --- 139 | 140 | ## 4.9.3 - 31-Dec-2022 141 | 142 | - Fixed shields.io badges. 143 | - Set to modern `yarn`. 144 | - `yarn` upgrades. 145 | - pullrequest.yml checks if changelog and package.json have changed. 146 | 147 | --- 148 | 149 | ## 4.9.0 - 25-Sep-2022 150 | 151 | - Yarn upgrades. 152 | 153 | --- 154 | 155 | ## 4.8.0 - 01-Aug-2022 156 | 157 | - Yarn upgrades. 158 | 159 | --- 160 | 161 | ## 4.7.0 - 13-Jul-2022 162 | 163 | - Yarn upgrades. 164 | 165 | --- 166 | 167 | ## 4.6.0 - 17-Jun-2022 168 | 169 | - Yarn upgrades. 170 | - Added `Don't show again` for rating prompt. 171 | 172 | ## 4.5.0 - 27-Mar-2022 173 | 174 | - Yarn upgrades. 175 | 176 | ## 4.4.0 - 01-Jan-2022 177 | 178 | - Added review prompt. 179 | - Yarn upgrades 180 | 181 | ## 4.2.1 - 27-Jun-2021 182 | 183 | - Yarn upgrades 184 | 185 | ## 4.2.0 - 21-Feb-2021 186 | 187 | - Yarn upgrades 188 | 189 | ## 4.1.0 - 18-Oct-2020 190 | 191 | - Resx Key-Value-Comment snippet added with prefix `resx`. 192 | 193 | ## 4.0.6 - 18-Oct-2020 194 | 195 | - Fixed overflown content on top of sticky header visible issue. 196 | 197 | ## 4.0.5 - 17-Oct-2020 198 | 199 | - Error div moved to top. 200 | - Sticky top elements. 201 | 202 | ## 4.0.4 - 17-Oct-2020 203 | 204 | - Fixed indexing issue causing multiple errors with 'Data with same key already exists' 205 | 206 | ## 4.0.3 - 17-Oct-2020 207 | 208 | - Typo fix in package json description. 209 | 210 | ## 4.0.2 - 17-Oct-2020 211 | 212 | - Updated banner color and package.json description 213 | - Removed BuymeCoffee from VS Marketplace. 214 | - Updated editor id. 215 | - Deleted unused files 216 | 217 | ## 4.0.0 - 15-Oct-2020 218 | 219 | Resx custom editor added. Now you can edit files in a webview based custom editor similar to Visual Studio Windows. 220 | 221 | ## 3.0.0 - 7-Oct-2020 222 | 223 | Added `resxpress.newpreview` command. 224 | Added support for displaying resx comments. 225 | 226 | --- 227 | 228 | ## 2.7.1 - 6-Oct-2020 229 | 230 | Added badges. 231 | 232 | ## 2.6.1 - 2-Oct-2020 233 | 234 | Added badges. 235 | 236 | --- 237 | 238 | ## 2.6.0 - 9-Aug-2020 239 | 240 | Updated packages 241 | 242 | --- 243 | 244 | ## 2.3.0 - 26-Mar-2020 245 | 246 | ### Added 247 | 248 | - Character escaping for displaying special characters newline characters in Markdown 249 | 250 | --- 251 | 252 | ## 2.2.3 - 23-Mar-2020 253 | 254 | ### Updated 255 | 256 | - npm package updates 257 | 258 | --- 259 | 260 | ## 2.0.3 - 29-Jan-2020 261 | 262 | ### Added 263 | 264 | - Command **Sort By Keys** 265 | 266 | --- 267 | 268 | ## 1.0.5 - 26-Jan-2020 269 | 270 | ### Added 271 | 272 | - fixed camel-casing issue in markdown preview. 273 | 274 | --- 275 | 276 | ## 1.0.4 - 23-Jan-2020 277 | 278 | ### Added 279 | 280 | - ext: resx added in package.json 281 | 282 | --- 283 | 284 | ## 1.0.2 - 21-Dec-2019 285 | 286 | ### Added 287 | 288 | - Icon for the extension. 289 | 290 | --- 291 | 292 | ## 1.0.1 - 21-Dec-2019 293 | 294 | ### Added 295 | 296 | - webpack bundling added 297 | 298 | ### Updated 299 | 300 | - ReadMe updated 301 | 302 | --- 303 | 304 | ## 1.0.0 - 21-Dec-2019 305 | 306 | Initial release of ResXpress 307 | 308 | ### Added 309 | 310 | - ResXpress: Preview Resx Command 311 | -------------------------------------------------------------------------------- /src/webpanelScript.ts: -------------------------------------------------------------------------------- 1 | import { emptyString } from "./constants"; 2 | import { nameof } from "./nameof"; 3 | import { WebpanelPostMessageKind } from "./webpanelMessageKind"; 4 | import { WebpanelPostMessage } from "./webpanelPostMessage"; 5 | 6 | // @ts-check 7 | let currentResxJS: any = []; 8 | const resxpressWebPanel = "resxpress.webpanel"; 9 | const tbody = "tbody"; 10 | const errorBlock = "errorBlock"; 11 | const key = "key"; 12 | const value = "value"; 13 | const comment = "comment"; 14 | const tr = "tr"; 15 | const td = "td"; 16 | const input = "input"; 17 | const tbl = "tbl"; 18 | const p = "p"; 19 | const text = "text"; 20 | const click = "click"; 21 | const deleteStr = "delete"; 22 | const X = "X"; 23 | const strong = "strong"; 24 | const sortByKeysButton = "sortByKeysButton"; 25 | const errorDuplicateKey = (key: string) => `Error: Data with ${key} already exists`; 26 | const errorInvalidResx = "Error: Document is not valid resx"; 27 | const errorKeyValueMandatory = "Key and Value are both mandatory fields!"; 28 | const errorUpdateDuplicateKey = (key: string) => `Error while updating data : Data with ${key} already exists`; 29 | const changeNamespaceButton = "changeNamespaceButton"; 30 | const addButton = "addButton"; 31 | const switchToTextEditorButton = "switchToTextEditorButton"; 32 | const message = "message"; 33 | const none = "none"; 34 | const namespaceSpan = "namespaceSpan"; 35 | 36 | function logToConsole(text: string) { 37 | console.log(`${resxpressWebPanel}: ${text}`); 38 | } 39 | 40 | // Script run within the webview itself. 41 | (function () { 42 | // @ts-ignore acquired 43 | const vscode = acquireVsCodeApi(); 44 | 45 | let table = document.querySelector(tbody)!; 46 | 47 | const errorContainer = document.getElementById(errorBlock); 48 | 49 | function inputEvent(event: Event) { 50 | let currentElement = event.target; 51 | 52 | if (errorContainer !== null && currentElement instanceof HTMLInputElement) { 53 | errorContainer.innerText = emptyString; 54 | let idstr = currentElement.id; 55 | logToConsole(`${nameof(inputEvent)} for : ${idstr}`); 56 | var index = Number(idstr.split(".")[0]); 57 | if (index >= currentResxJS.length) { 58 | logToConsole(`${nameof(inputEvent)}.New: Index: ${index}. Current Resx Length: ${currentResxJS.length}`); 59 | var newObj: any = { _attributes: { name: emptyString, "xml:space": "preserve" }, value: { _text: emptyString } }; 60 | const keyElement = document.getElementById(`${index}.${key}`) as HTMLInputElement; 61 | const valueElement = document.getElementById(`${index}.${value}`); 62 | const commentElement = document.getElementById(`${index}.${comment}`); 63 | 64 | if (keyElement instanceof HTMLInputElement && valueElement instanceof HTMLInputElement && commentElement instanceof HTMLInputElement) { 65 | if (keyElement.value && valueElement.value) { 66 | newObj._attributes.name = keyElement?.value ?? emptyString; 67 | newObj.value._text = valueElement?.value ?? emptyString; 68 | if (commentElement.value) { 69 | newObj.comment = { _text: commentElement?.value }; 70 | } 71 | else { 72 | delete newObj.comment; 73 | } 74 | 75 | var pos = currentResxJS.map((x: any) => x?._attributes?.name).indexOf(newObj._attributes.name); 76 | 77 | //avoid adding data with same key 78 | if (pos === -1) { 79 | currentResxJS.push(newObj); 80 | logToConsole(`${nameof(inputEvent)}.New: ${JSON.stringify(newObj)}`); 81 | 82 | errorContainer.innerText = emptyString; 83 | errorContainer.style.display = emptyString; 84 | 85 | vscode.setState({ text: JSON.stringify(currentResxJS) }); 86 | vscode.postMessage(new WebpanelPostMessage( 87 | WebpanelPostMessageKind.Add, 88 | JSON.stringify(newObj) 89 | )); 90 | } 91 | else { 92 | logToConsole(`${nameof(inputEvent)}.New: Duplicate key found ${newObj._attributes.name}`); 93 | errorContainer.innerText = errorDuplicateKey(newObj._attributes.name); 94 | errorContainer.style.display = emptyString; 95 | return; 96 | } 97 | } 98 | else { 99 | errorContainer.innerText = errorKeyValueMandatory; 100 | errorContainer.style.display = emptyString; 101 | return; 102 | } 103 | } 104 | } 105 | else { 106 | var editingObj = currentResxJS[index]; 107 | 108 | const keyElement = document.getElementById(`${index}.${key}`); 109 | const valueElement = document.getElementById(`${index}.${value}`); 110 | const commentElement = document.getElementById(`${index}.${comment}`); 111 | let isKeyChanged = false; 112 | let isValueChanged = false; 113 | let isCommentChanged = false; 114 | 115 | if (keyElement instanceof HTMLInputElement && valueElement instanceof HTMLInputElement && commentElement instanceof HTMLInputElement) { 116 | 117 | isKeyChanged = keyElement.value !== editingObj._attributes.name; 118 | isValueChanged = valueElement.value !== editingObj.value._text; 119 | isCommentChanged = (commentElement?.value ?? emptyString) !== (editingObj?.comment?._text ?? emptyString); 120 | 121 | logToConsole(`${nameof(inputEvent)}.Edit: anyDataChanged : ${isKeyChanged || isValueChanged || isCommentChanged}`); 122 | if (keyElement.value && valueElement.value) { 123 | if (!isKeyChanged && !isValueChanged && !isCommentChanged) { 124 | return; 125 | } 126 | 127 | logToConsole(`${nameof(inputEvent)}.Edit: Changing values`); 128 | editingObj._attributes.name = keyElement.value ?? emptyString; 129 | editingObj.value._text = valueElement.value ?? emptyString; 130 | if (commentElement?.value) { 131 | editingObj.comment = { _text: commentElement?.value }; 132 | } 133 | else { 134 | delete editingObj.comment; 135 | } 136 | 137 | var tempArray = Array.from(currentResxJS); 138 | tempArray[index] = editingObj; 139 | 140 | var keyArray = tempArray.map((x: any) => x._attributes.name); 141 | 142 | logToConsole(`${nameof(inputEvent)}.Edit: keyArray is ${JSON.stringify(keyArray)}`); 143 | if (new Set(keyArray).size !== keyArray.length) { 144 | logToConsole(`${nameof(inputEvent)}.Edit: edited Data key already exists`); 145 | errorContainer.innerText = errorUpdateDuplicateKey(editingObj._attributes.name) 146 | errorContainer.style.display = emptyString; 147 | } 148 | else { 149 | currentResxJS[index] = editingObj; 150 | } 151 | } 152 | else { 153 | errorContainer.innerText = errorInvalidResx; 154 | errorContainer.style.display = emptyString; 155 | return; 156 | } 157 | } 158 | if (isKeyChanged || isValueChanged || isCommentChanged) { 159 | let changeText = isKeyChanged ? "Key changed" : emptyString; 160 | changeText += isValueChanged ? (changeText ? ", Value changed" : "Value changed") : emptyString; 161 | changeText += isCommentChanged ? (changeText ? ", Comment changed" : "Comment changed") : emptyString; 162 | logToConsole(`${nameof(inputEvent)}.Edit: ${changeText}`); 163 | vscode.setState({ text: JSON.stringify(currentResxJS) }); 164 | vscode.postMessage(new WebpanelPostMessage( 165 | WebpanelPostMessageKind.TriggerTextDocumentUpdate, 166 | JSON.stringify(currentResxJS) 167 | )); 168 | } 169 | } 170 | } 171 | } 172 | 173 | function deleteEvent(event: MouseEvent) { 174 | logToConsole(`${nameof(deleteEvent)}: for ${event.target}`); 175 | const td = event.target as HTMLElement; 176 | let table = document.getElementById(tbl); 177 | 178 | if (errorContainer !== null && table && td) { 179 | let idstr: string = td.id; 180 | logToConsole(`Triggered td.id : ${idstr}`); 181 | errorContainer.innerText = emptyString; 182 | 183 | if (idstr && idstr.trim()) { 184 | let indices = idstr.split("."); 185 | 186 | if (indices.length > 0) { 187 | let index = Number(indices[0]); 188 | logToConsole(`${nameof(deleteEvent)}: index to be deleted: ${index}`) 189 | if (currentResxJS.length > index) { 190 | var deleteableObj = currentResxJS[index]; 191 | 192 | //x=td -> tr -> tbody-> table 193 | let row = td.parentNode; 194 | if (row) { 195 | row.parentNode?.removeChild(row); 196 | } 197 | 198 | vscode.setState({ text: JSON.stringify(currentResxJS) });//always set the full list 199 | 200 | vscode.postMessage(new WebpanelPostMessage( 201 | WebpanelPostMessageKind.Delete, 202 | JSON.stringify(deleteableObj) 203 | )); 204 | } 205 | else { 206 | let row = td.parentNode; 207 | if (row) { 208 | row.parentNode?.removeChild(row); 209 | } 210 | } 211 | } 212 | } 213 | } 214 | } 215 | 216 | 217 | const changeNamespaceButtonElement = document.getElementById(changeNamespaceButton); 218 | if (changeNamespaceButtonElement) { 219 | changeNamespaceButtonElement.addEventListener(click, () => { 220 | vscode.postMessage(new WebpanelPostMessage( 221 | WebpanelPostMessageKind.TriggerNamespaceUpdate, 222 | JSON.stringify(emptyString))); 223 | }); 224 | } 225 | let addButtonElement = document.getElementById(addButton); 226 | 227 | var switchToTextEditor = document.getElementById(switchToTextEditorButton); 228 | 229 | if (switchToTextEditor) { 230 | switchToTextEditor.addEventListener(click, async () => { 231 | vscode.postMessage(new WebpanelPostMessage( 232 | WebpanelPostMessageKind.Switch, 233 | JSON.stringify(emptyString))); 234 | }); 235 | } 236 | 237 | if (addButtonElement) { 238 | addButtonElement.addEventListener(click, () => { 239 | logToConsole("addButton clicked"); 240 | //create tr 241 | let trElement = document.createElement(tr); 242 | 243 | let index = (currentResxJS.length > 0) ? currentResxJS.length : 0; 244 | 245 | //create key td 246 | let keyTdElement = document.createElement(td); 247 | const keyInput = document.createElement(input); 248 | keyInput.id = `${index}.${key}`; 249 | keyInput.type = text; 250 | keyInput.value = emptyString; 251 | 252 | //keyInput.onfocus =(key) =>inputEvent(key); 253 | keyInput.addEventListener(input, inputEvent); 254 | keyTdElement.appendChild(keyInput); 255 | 256 | //create value td 257 | const valueTdElement = document.createElement(td); 258 | const valueInput = document.createElement(input); 259 | valueInput.id = `${index}.${value}`; 260 | valueInput.value = emptyString; 261 | valueInput.type = text; 262 | 263 | valueInput.addEventListener(input, inputEvent, false); 264 | valueTdElement.appendChild(valueInput); 265 | 266 | //create comment td 267 | const commentTdElement = document.createElement(td); 268 | 269 | const commentInput = document.createElement(input); 270 | commentInput.id = `${index}.${comment}`; 271 | commentInput.type = text; 272 | commentInput.value = emptyString; 273 | commentInput.addEventListener(input, inputEvent, false); 274 | commentTdElement.appendChild(commentInput); 275 | 276 | //delete character X 277 | const deleteTd = document.createElement(td); 278 | deleteTd.id = `${index}.${deleteStr}.${td}`; 279 | let pElement = document.createElement(p); 280 | pElement.id = `${index}.${deleteStr}.${p}`; 281 | pElement.innerHTML = X; 282 | //p.setAttribute("style", "align:center"); 283 | deleteTd.appendChild(pElement); 284 | 285 | deleteTd.addEventListener(click, (ev) => deleteEvent(ev), false); 286 | trElement.append(keyTdElement, valueTdElement, commentTdElement, deleteTd); 287 | 288 | //add tr to table 289 | table.appendChild(trElement); 290 | 291 | trElement.scrollIntoView(); 292 | keyInput.focus(); 293 | }); 294 | } 295 | 296 | function updatePanelWebContent(text: string) { 297 | if (errorContainer !== null) { 298 | if (text) { 299 | let json; 300 | try { 301 | currentResxJS = json = JSON.parse(text); 302 | logToConsole(`${nameof(updatePanelWebContent)}: data json is : ${text}`); 303 | } 304 | catch { 305 | table.style.display = none; 306 | errorContainer.innerText = errorInvalidResx; 307 | errorContainer.style.display = emptyString; 308 | return; 309 | } 310 | table.style.display = emptyString; 311 | errorContainer.style.display = none; 312 | 313 | // Render the scratches 314 | table.innerHTML = emptyString; 315 | 316 | var index = 0; 317 | for (const node of json) { 318 | 319 | if (node) { 320 | //create tr 321 | const trElement = document.createElement(tr); 322 | //create key td 323 | const keyElement = document.createElement(td); 324 | const keyInput = document.createElement(input); 325 | keyInput.type = text; 326 | keyInput.value = node._attributes.name ?? emptyString; 327 | logToConsole(`key : ${node._attributes.name}`); 328 | 329 | keyInput.id = `${index}.${key}`; 330 | keyInput.addEventListener(input, inputEvent, false); 331 | keyElement.appendChild(keyInput); 332 | 333 | //create value td 334 | const valueTdElement = document.createElement(td); 335 | const valueInput = document.createElement(input); 336 | valueInput.value = node.value._text ?? emptyString; 337 | valueInput.type = text; 338 | valueInput.id = `${index}.${value}`; 339 | logToConsole(`${nameof(updatePanelWebContent)}: Value : ${node.value._text}`); 340 | valueInput.addEventListener(input, inputEvent, false); 341 | valueTdElement.appendChild(valueInput); 342 | 343 | //create comment td 344 | const commentTdElement = document.createElement(td); 345 | const commentInput = document.createElement(input); 346 | commentInput.id = `${index}.${comment}`; 347 | commentInput.type = text; 348 | commentInput.value = node?.comment?._text ?? emptyString; 349 | 350 | logToConsole(`comment : ${node?.comment?._text}`); 351 | commentInput.addEventListener(input, inputEvent, false); 352 | commentTdElement.appendChild(commentInput); 353 | 354 | //delete character X 355 | const deleteTd = document.createElement(td); 356 | deleteTd.id = `${index}.${deleteStr}.${td}`; 357 | const pElement = document.createElement(p); 358 | pElement.id = `${index}.${deleteStr}.${p}`; 359 | pElement.innerHTML = X; 360 | deleteTd.appendChild(pElement); 361 | deleteTd.addEventListener(click, (ev) => deleteEvent(ev), false); 362 | 363 | trElement.append(keyElement, valueTdElement, commentTdElement, deleteTd); 364 | 365 | //add tr to table 366 | table.appendChild(trElement); 367 | index++; 368 | } 369 | else { 370 | logToConsole("node is undefined or null"); 371 | } 372 | } 373 | } 374 | else { 375 | table.style.display = none; 376 | errorContainer.innerText = errorInvalidResx; 377 | errorContainer.style.display = emptyString; 378 | return; 379 | } 380 | } 381 | } 382 | 383 | const sortByKeysButtonElement = document.getElementById(sortByKeysButton); 384 | if (sortByKeysButtonElement) { 385 | sortByKeysButtonElement.addEventListener(click, () => { 386 | vscode.postMessage(new WebpanelPostMessage( 387 | WebpanelPostMessageKind.SortByKeys, 388 | JSON.stringify(emptyString))); 389 | }); 390 | } 391 | 392 | window.addEventListener(message, event => { 393 | const messageData = event.data; // data that the extension sent 394 | const text = messageData.text; 395 | logToConsole(`addEventListener ${messageData.type} message received : ${text}`); 396 | 397 | switch (messageData.type) { 398 | case WebpanelPostMessageKind.UpdateWebPanel: 399 | var sentDataListJs = JSON.parse(text) ?? []; 400 | 401 | if (sentDataListJs.length !== currentResxJS.length) { 402 | 403 | logToConsole(`addEventListener: Current data: ${JSON.stringify(currentResxJS)}`); 404 | logToConsole(`addEventListener: Received data: ${text}`); 405 | } 406 | updatePanelWebContent(text); 407 | // Then persist state information. 408 | // This state is returned in the call to `vscode.getState` below when a webview is reloaded. 409 | vscode.setState({ text }); 410 | 411 | break; 412 | case WebpanelPostMessageKind.NewNamespace: 413 | const newNamespace = text; 414 | const namespaceSpanElement = document.getElementById(namespaceSpan); 415 | if (namespaceSpanElement) { 416 | namespaceSpanElement.innerHTML = "Namespace: "; 417 | const strongElement = document.createElement(strong); 418 | strongElement.textContent = newNamespace; 419 | namespaceSpanElement.appendChild(strongElement); 420 | } 421 | break; 422 | } 423 | }); 424 | 425 | const state = vscode.getState(); 426 | if (state) { 427 | updatePanelWebContent(state.text); 428 | } 429 | }()); 430 | 431 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from "vscode"; 2 | 3 | import { promises as fsPromises } from "fs"; 4 | import { PreviewEditPanel } from "./previewEditPanel"; 5 | import * as path from "path"; 6 | import * as xmljs from "xml-js"; 7 | import { ResxEditorProvider } from "./resxEditorProvider"; 8 | import { NotificationService } from "./notificationService"; 9 | import { FileHelper } from "./fileHelper"; 10 | import { TextInputBoxOptions } from "./textInputBoxOptions"; 11 | import { Constants, DATA, emptyString } from "./constants"; 12 | import { Settings } from "./settings"; 13 | import { Logger } from "./logger"; 14 | import { nameof } from "./nameof"; 15 | 16 | 17 | let currentContext: vscode.ExtensionContext; 18 | 19 | export function activate(context: vscode.ExtensionContext) { 20 | Logger.instance.setDebug(context.extensionMode === vscode.ExtensionMode.Development); 21 | try { 22 | const notificationService = new NotificationService(context); 23 | notificationService.promptForReviewAsync(); 24 | } 25 | catch (error) { 26 | if (error instanceof Error) { 27 | Logger.instance.error(error); 28 | } 29 | } 30 | 31 | currentContext = context; 32 | loadConfiguration(); 33 | 34 | vscode.workspace.onDidChangeConfiguration(loadConfiguration); 35 | 36 | context.subscriptions.push(vscode.commands.registerTextEditorCommand(Constants.Commands.resxpreview, 37 | async () => { 38 | vscode.window.withProgress({ 39 | location: vscode.ProgressLocation.Notification, 40 | cancellable: false, 41 | title: Constants.extensionName, 42 | }, 43 | async (p) => { 44 | p.report({ message: "Showing Preview" }); 45 | await displayAsMarkdown(); 46 | } 47 | ); 48 | })); 49 | 50 | context.subscriptions.push(vscode.commands.registerTextEditorCommand(Constants.Commands.sortbykeys, 51 | async (editor) => { 52 | if (!editor.document) { 53 | return; 54 | } 55 | vscode.window.withProgress({ 56 | location: vscode.ProgressLocation.Notification, 57 | cancellable: false, 58 | title: Constants.extensionName, 59 | }, async (p) => { 60 | p.report({ message: "Sorting by keys" }); 61 | await sortByKeys(editor.document); 62 | }); 63 | })); 64 | 65 | context.subscriptions.push(vscode.commands.registerTextEditorCommand(Constants.Commands.newpreview, 66 | async () => { 67 | vscode.window.withProgress({ 68 | location: vscode.ProgressLocation.Notification, 69 | cancellable: false, 70 | title: Constants.extensionName 71 | }, async (progress) => { 72 | progress.report({ message: "Showing Web Preview" }); 73 | await newPreview(); 74 | }); 75 | })); 76 | 77 | context.subscriptions.push(vscode.commands.registerCommand(Constants.Commands.setNameSpace, async (document: vscode.TextDocument) => await setNewNamespace(document))); 78 | context.subscriptions.push(vscode.commands.registerCommand(Constants.Commands.createResxFile, async (uri: vscode.Uri) => await createResxFile(uri))); 79 | context.subscriptions.push(vscode.commands.registerTextEditorCommand(Constants.Commands.resxeditor, async () => await newPreview())); 80 | 81 | context.subscriptions.push(ResxEditorProvider.register(context)); 82 | 83 | vscode.workspace.onDidSaveTextDocument(async (document) => { 84 | try { 85 | var isResx = document.fileName.endsWith(".resx"); 86 | if (isResx && Settings.shouldGenerateStronglyTypedResourceClassOnSave) { 87 | await runResGenAsync(document); 88 | } 89 | } 90 | catch (error) { 91 | var errorMessage = emptyString; 92 | if (error instanceof Error) { 93 | Logger.instance.error(error); 94 | errorMessage = error.message; 95 | } 96 | else if (typeof error === "string") { 97 | errorMessage = error; 98 | } 99 | vscode.window.showErrorMessage(errorMessage); 100 | } 101 | }); 102 | Logger.instance.info(`Extension ${context.extension.id} activated`); 103 | } 104 | 105 | function loadConfiguration() { 106 | let resxConfig = vscode.workspace.getConfiguration(`${Constants.resxpress}.${Constants.configuration}`); 107 | Settings.shouldGenerateStronglyTypedResourceClassOnSave = resxConfig.get(Constants.Configuration.generateStronglyTypedResourceClassOnSave) ?? false; 108 | Settings.shouldUseFileScopedNamespace = resxConfig.get(Constants.Configuration.useFileScopedNamespace) ?? true; 109 | Settings.indentSpaceLength = resxConfig.get(Constants.Configuration.indentSpaceLength) ?? 4; 110 | Settings.enableLocalLogs = resxConfig.get(Constants.Configuration.enableLocalLogs) ?? false; 111 | Logger.instance.setIsEnabled(Settings.enableLocalLogs); 112 | } 113 | 114 | function convertToPascalCase(input: string): string { 115 | // Remove special characters and keep alphanumeric characters and spaces 116 | const sanitized = input.replace(/[^a-zA-Z0-9_ ]/g, emptyString); 117 | 118 | // Split the string into words by spaces 119 | const words = sanitized.split(" "); 120 | 121 | // PascalCase 122 | const pascalCaseWords = words.map(word => { 123 | if (word.length === 0) return emptyString; 124 | // If word starts with uppercase, keep it; otherwise, capitalize 125 | return word[0].toUpperCase() + word.slice(1); 126 | }); 127 | 128 | // Join all the words 129 | let pascalCaseString = pascalCaseWords.join(emptyString); 130 | 131 | // If the resulting string starts with a digit, prefix it with an underscore 132 | if (/^\d/.test(pascalCaseString)) { 133 | pascalCaseString = "_" + pascalCaseString; 134 | } 135 | 136 | return pascalCaseString; 137 | } 138 | 139 | export async function runResGenAsync(document: vscode.TextDocument): Promise { 140 | let filename = FileHelper.getFileNameNoExt(document); 141 | let csharpFileName = "Resources.cs"; 142 | if (filename !== null) { 143 | if (filename.includes(".")) { 144 | //Dont create separate C# resources file for different cultures 145 | return; 146 | } 147 | csharpFileName = `${filename}.Designer.cs`; 148 | } 149 | 150 | let nameSpace = await FileHelper.tryGetNamespace(document); 151 | 152 | if (nameSpace === null || nameSpace === emptyString) { 153 | nameSpace = path.basename(path.dirname(filename)); 154 | } 155 | 156 | 157 | let documentText = document.getText(); 158 | if (documentText.length > 0) { 159 | var jsObj = xmljs.xml2js(documentText); 160 | var resourceCSharpClassText = emptyString; 161 | let accessModifier = "public"; 162 | let workspacePath = FileHelper.getDirectory(document); 163 | var spaces = Settings.shouldUseFileScopedNamespace ? emptyString : " "; 164 | resourceCSharpClassText += `namespace ${nameSpace}${Settings.shouldUseFileScopedNamespace ? ";" : emptyString} 165 | ${Settings.shouldUseFileScopedNamespace ? emptyString : "{"} 166 | ${spaces}/// 167 | ${spaces}/// A strongly-typed resource class, for looking up localized strings, etc. 168 | ${spaces}/// 169 | ${spaces}// This class was auto-generated by the Visual Studio Code Extension PrateekMahendrakar.resxpress 170 | ${spaces}[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 171 | ${spaces}[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 172 | ${spaces}[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 173 | ${spaces}${accessModifier} class ${filename} 174 | ${spaces}{ 175 | ${spaces} private static global::System.Resources.ResourceManager resourceMan; 176 | ${spaces} [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 177 | ${spaces} ${accessModifier} ${filename}() 178 | ${spaces} { 179 | ${spaces} } 180 | ${spaces} /// 181 | ${spaces} /// Returns the cached ResourceManager instance used by this class. 182 | ${spaces} /// 183 | ${spaces} [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 184 | ${spaces} ${accessModifier} static global::System.Resources.ResourceManager ResourceManager 185 | ${spaces} { 186 | ${spaces} get 187 | ${spaces} { 188 | ${spaces} if (object.ReferenceEquals(resourceMan, null)) 189 | ${spaces} { 190 | ${spaces} global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("${nameSpace}.${filename}", typeof(${filename}).Assembly); 191 | ${spaces} resourceMan = temp; 192 | ${spaces} } 193 | ${spaces} return resourceMan; 194 | ${spaces} } 195 | ${spaces} } 196 | ${spaces} /// 197 | ${spaces} /// Overrides the current thread's CurrentUICulture property for all 198 | ${spaces} /// resource lookups using this strongly typed resource class. 199 | ${spaces} /// 200 | ${spaces} [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 201 | ${spaces} ${accessModifier} static global::System.Globalization.CultureInfo Culture { get; set; }`; 202 | 203 | if (jsObj.elements[0].elements.length > 0) { 204 | jsObj.elements[0].elements.forEach((element: any) => { 205 | if (element.name === DATA) { 206 | const resourceKey = element.attributes.name; 207 | let valueElementParent = element.elements.filter((x: any) => x.name === "value")?.[0]; 208 | let value: string = valueElementParent?.elements?.length > 0 ? valueElementParent.elements[0].text : emptyString; 209 | value = value.toString().replace(/(?:\r\n|\r|\n)/g, "\n /// "); 210 | if (resourceKey) { 211 | let propertyName = convertToPascalCase(resourceKey); 212 | resourceCSharpClassText += ` 213 | 214 | ${spaces} /// 215 | ${spaces} /// Looks up a localized string similar to ${value}. 216 | ${spaces} /// 217 | ${spaces} ${accessModifier} static string ${propertyName} => ResourceManager.GetString("${resourceKey}", Culture);`; 218 | } 219 | } 220 | }); 221 | } 222 | else { 223 | resourceCSharpClassText += ` 224 | ${spaces}}`; 225 | } 226 | 227 | 228 | resourceCSharpClassText += ` 229 | ${spaces}} 230 | ${Settings.shouldUseFileScopedNamespace ? emptyString : "}"}`; 231 | Logger.instance.info(resourceCSharpClassText); 232 | 233 | if (workspacePath.length > 0) { 234 | let pathToWrite = path.join(workspacePath, csharpFileName); 235 | await FileHelper.writeToFile(pathToWrite, resourceCSharpClassText); 236 | } 237 | } 238 | } 239 | 240 | 241 | export function deactivate() { 242 | Logger.instance.info(`${Constants.resxpress} deactivated`); 243 | } 244 | 245 | export async function sortByKeys(document: vscode.TextDocument) { 246 | try { 247 | let orderedResx: any = sortKeyValuesResx(document); 248 | var ranger = new vscode.Range(0, 0, document.lineCount, 0); 249 | const edit = vscode.TextEdit.replace(ranger, orderedResx); 250 | const editBuilder = new vscode.WorkspaceEdit(); 251 | editBuilder.set(document.uri, [edit]); 252 | const success = await vscode.workspace.applyEdit(editBuilder); 253 | if (success) { 254 | console.log('Text replaced successfully.'); 255 | } else { 256 | console.log('Failed to replace text.'); 257 | } 258 | } 259 | catch (error) { 260 | var errorMessage = emptyString; 261 | if (error instanceof Error) { 262 | errorMessage = error.message; 263 | Logger.instance.error(error); 264 | } 265 | else if (typeof error === "string") { 266 | errorMessage = error; 267 | } 268 | vscode.window.showErrorMessage(errorMessage); 269 | } 270 | } 271 | 272 | function sortKeyValuesResx(document: vscode.TextDocument, reverse?: boolean): string | undefined { 273 | try { 274 | Logger.instance.info(`${nameof(sortKeyValuesResx)}`); 275 | var text = document?.getText() ?? emptyString; 276 | var jsObj = xmljs.xml2js(text); 277 | 278 | var dataList: any = []; 279 | var sorted: any = []; 280 | jsObj.elements[0].elements.forEach((x: any) => { 281 | if (x.name === DATA) { 282 | dataList.push(x); 283 | } 284 | else { 285 | sorted.push(x); 286 | } 287 | }); 288 | 289 | var dataListsorted = dataList.sort((x1: any, x2: any) => { 290 | var firstKey = x1.attributes.name.toLowerCase(); 291 | var secondKey = x2.attributes.name.toLowerCase(); 292 | if (reverse) { 293 | return firstKey > secondKey ? -1 : firstKey < secondKey ? 1 : 0; 294 | } else { 295 | return firstKey < secondKey ? -1 : firstKey > secondKey ? 1 : 0; 296 | } 297 | }); 298 | 299 | sorted.push(...dataListsorted); 300 | jsObj.elements[0].elements = sorted; 301 | 302 | var xml = xmljs.js2xml(jsObj, { spaces: Settings.indentSpaceLength }); 303 | 304 | return xml; 305 | } 306 | catch (error) { 307 | var errorMessage = emptyString; 308 | if (error instanceof Error) { 309 | errorMessage = error.message; 310 | Logger.instance.error(error); 311 | } 312 | else if (typeof error === "string") { 313 | errorMessage = error; 314 | } 315 | vscode.window.showErrorMessage(errorMessage); 316 | } 317 | } 318 | 319 | function getDataJs(): any[] { 320 | var text = vscode.window.activeTextEditor?.document?.getText() ?? emptyString; 321 | var jsObj: any = xmljs.xml2js(text, { compact: true }); 322 | return jsObj.root.data; 323 | } 324 | 325 | async function newPreview() { 326 | var text = vscode.window.activeTextEditor?.document?.getText() ?? emptyString; 327 | var jsObj = xmljs.xml2js(text); 328 | var dataList: any = []; 329 | var sorted = []; 330 | jsObj.elements[0].elements.forEach((x: any) => { 331 | if (x.name === DATA) { 332 | dataList.push(x); 333 | } 334 | else { 335 | sorted.push(x); 336 | } 337 | }); 338 | 339 | var currentFileName = vscode.window.activeTextEditor?.document.fileName; 340 | if (currentFileName) { 341 | await displayJsonInHtml(dataList, currentFileName); 342 | } 343 | } 344 | 345 | 346 | // Markdown Preview 347 | async function displayAsMarkdown() { 348 | try { 349 | var pathObj = path.parse( 350 | vscode.window.activeTextEditor?.document.fileName ?? emptyString 351 | ); 352 | if (pathObj) { 353 | if (pathObj.ext !== ".resx") { 354 | await vscode.window.showErrorMessage("Not a Resx file."); 355 | return; 356 | } 357 | const jsonData: any[] = getDataJs(); 358 | if (!(jsonData instanceof Error)) { 359 | var currentFileName = vscode.window.activeTextEditor?.document.fileName; 360 | if (currentFileName) { 361 | var fileNameNoExt = vscode.window.activeTextEditor?.document.fileName.substring( 362 | 0, 363 | currentFileName.lastIndexOf(".") 364 | ); 365 | let mdFile = fileNameNoExt + ".md"; 366 | 367 | let fileContent = `### ${pathObj.name} Preview\n\n| Key | Value | Comment |\n`; 368 | fileContent += "| --- | --- | --- |" + "\n"; 369 | 370 | for (const jsObj of jsonData) { 371 | const regexM = /[\\`*_{}[\]()#+.!|-]/g; 372 | //clean up key 373 | var property = jsObj._attributes.name; 374 | var propertyString = property; 375 | 376 | propertyString = property.replace(regexM, "\\$&"); 377 | propertyString = property.replace(/\r?\n/g, "
"); 378 | 379 | var valueString = jsObj.value?._text; 380 | var commentString = jsObj.comment?._text ?? emptyString; 381 | 382 | valueString = valueString.replace(regexM, "\\$&"); 383 | valueString = valueString.replace(/\r?\n/g, "
"); 384 | commentString = commentString.replace(regexM, "\\$&"); 385 | commentString = commentString.replace(/\r?\n/g, "
"); 386 | 387 | fileContent += `| ${propertyString} | ${valueString} | ${commentString} |\n`; 388 | } 389 | 390 | await fsPromises.writeFile(mdFile, fileContent); 391 | 392 | let uri = vscode.Uri.file(mdFile); 393 | 394 | await vscode.commands.executeCommand("vscode.open", uri); 395 | await vscode.commands.executeCommand("markdown.showPreview"); 396 | await vscode.commands.executeCommand("markdown.preview.refresh"); 397 | await vscode.commands.executeCommand("workbench.action.previousEditor"); 398 | await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); 399 | } 400 | } 401 | else { 402 | vscode.window.showErrorMessage("Error parsing resx data"); 403 | } 404 | } 405 | else { 406 | vscode.window.showErrorMessage("Error finding path of the file"); 407 | } 408 | } 409 | catch (error) { 410 | var errorMessage = emptyString; 411 | if (error instanceof Error) { 412 | errorMessage = error.message; 413 | Logger.instance.error(error); 414 | } 415 | else if (typeof error === "string") { 416 | errorMessage = error; 417 | } 418 | vscode.window.showErrorMessage(errorMessage); 419 | } 420 | } 421 | 422 | async function displayJsonInHtml(jsonData: any[], filename: string) { 423 | try { 424 | var htmlContent = emptyString; 425 | 426 | jsonData.forEach((element) => { 427 | var valueStr = emptyString; 428 | var commentstr = emptyString; 429 | element.elements.forEach((subElement: any) => { 430 | if (subElement.name === "value" && subElement.elements?.length > 0) { 431 | valueStr = subElement.elements[0].text; 432 | } 433 | else if (subElement.name === "comment" && subElement.elements?.length > 0) { 434 | commentstr = subElement.elements[0].text; 435 | } 436 | }); 437 | htmlContent += ` 438 | ${element.attributes.name} 439 | ${valueStr} 440 | ${commentstr} 441 | `; 442 | }); 443 | var pathObj = path.parse(filename); 444 | var title = pathObj.name + pathObj.ext; 445 | 446 | PreviewEditPanel.createOrShow(currentContext.extensionUri, title, htmlContent); 447 | } 448 | catch (error) { 449 | var errorMessage = emptyString; 450 | if (error instanceof Error) { 451 | errorMessage = error.message; 452 | Logger.instance.error(error); 453 | } 454 | else if (typeof error === "string") { 455 | errorMessage = error; 456 | } 457 | vscode.window.showErrorMessage(errorMessage); 458 | } 459 | } 460 | 461 | function isStringRecord(obj: any): obj is Record { 462 | if (obj === null) 463 | return false; 464 | 465 | if (typeof obj !== "object") 466 | return false; 467 | 468 | if (Array.isArray(obj)) 469 | return false; 470 | 471 | if (Object.getOwnPropertySymbols(obj).length > 0) 472 | return false; 473 | 474 | return Object.getOwnPropertyNames(obj).every(p => typeof obj[p] === "string"); 475 | } 476 | 477 | async function createOrUpdateNamespaceMappingFile(workspaceFolder: vscode.WorkspaceFolder, fileNameNoExt: string, namespace: string) { 478 | const workspacePath = workspaceFolder.uri.fsPath; 479 | if (workspacePath) { 480 | let pathToWrite = path.join(workspacePath, `.${Constants.namespaceMappingJsonPath}`); 481 | let content = await FileHelper.getFileText(pathToWrite); 482 | var didWrite = false; 483 | if (content.length > 0) { 484 | let namespaceMaps = JSON.parse(content); 485 | if (isStringRecord(namespaceMaps)) { 486 | namespaceMaps[fileNameNoExt] = namespace; 487 | await FileHelper.writeToFile(pathToWrite, JSON.stringify(namespaceMaps)) 488 | didWrite = true; 489 | } 490 | } 491 | if (didWrite === false) { 492 | let rec: Record = {}; 493 | rec[fileNameNoExt] = namespace; 494 | await FileHelper.writeToFile(pathToWrite, JSON.stringify(rec)) 495 | } 496 | } 497 | } 498 | 499 | export async function setNewNamespace(document: vscode.TextDocument): Promise { 500 | if (!document.uri) { 501 | return; 502 | } 503 | const uri = document.uri; 504 | let parsedPath = path.parse(uri.fsPath); 505 | const fileName = parsedPath.name; 506 | if (fileName !== null) { 507 | const inputBoxOptions = new TextInputBoxOptions("Namespace", "", undefined, "Enter namespace", "Namespace", true); 508 | const namespaceValue = await vscode.window.showInputBox(inputBoxOptions); 509 | Logger.instance.info(`namespace entered : ${namespaceValue}`); 510 | if (namespaceValue) { 511 | let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); 512 | if (workspaceFolder) { 513 | await createOrUpdateNamespaceMappingFile(workspaceFolder, fileName, namespaceValue); 514 | if (Settings.shouldGenerateStronglyTypedResourceClassOnSave && parsedPath.ext === ".resx") { 515 | await runResGenAsync(document); 516 | } 517 | } 518 | } 519 | return namespaceValue; 520 | } 521 | } 522 | 523 | async function createResxFile(uri: vscode.Uri | null) { 524 | const resxFileNameOptions = new TextInputBoxOptions("New Resx File", emptyString, undefined, "Enter Resx file name", ".resx", true); 525 | let fileName = await vscode.window.showInputBox(resxFileNameOptions); 526 | if (fileName && fileName.length > 0) { 527 | if (fileName.endsWith(".resx") === false) { 528 | fileName = `${fileName}.resx`; 529 | } 530 | const newResxFileNamespaceOptions = new TextInputBoxOptions(`namespace for ${fileName}`, emptyString, undefined, `Enter namespace for ${fileName}`, emptyString, true); 531 | const namespace = await vscode.window.showInputBox(newResxFileNamespaceOptions); 532 | if (namespace && namespace.length > 0) { 533 | const workspaceEdit = new vscode.WorkspaceEdit(); 534 | let thisWorkspace = emptyString; 535 | if (uri) { 536 | thisWorkspace = uri.fsPath; 537 | } 538 | else if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { 539 | thisWorkspace = vscode.workspace.workspaceFolders[0].uri.fsPath; 540 | } 541 | else { 542 | vscode.window.showErrorMessage("Cannot create resx file!"); 543 | return; 544 | } 545 | Logger.instance.info(`Creating file at ${thisWorkspace}`) 546 | const resxFilePath = path.join(thisWorkspace, fileName); 547 | Logger.instance.info(`Filename to be created: ${resxFilePath}`) 548 | // create a Uri for a file to be created 549 | const resxFileUri = vscode.Uri.file(resxFilePath); 550 | const content = ` 551 | 552 | 553 | text/microsoft-resx 554 | 555 | 556 | 2.0 557 | 558 | 559 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 560 | 561 | 562 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 563 | 564 | 565 | `; 566 | 567 | let encoder = new TextEncoder(); 568 | let uInt8Array = encoder.encode(content); 569 | // create an edit that will create a file 570 | workspaceEdit.createFile(resxFileUri, { ignoreIfExists: false, overwrite: false, contents: uInt8Array }); 571 | 572 | //Create file 573 | const didApplyEdit = await vscode.workspace.applyEdit(workspaceEdit); 574 | if (didApplyEdit) { 575 | let document = await vscode.workspace.openTextDocument(resxFileUri); 576 | await vscode.window.showTextDocument(document); 577 | let workspaceFolder = vscode.workspace.getWorkspaceFolder(resxFileUri); 578 | 579 | if (workspaceFolder) { 580 | const fileNameNoExt = fileName.replace(".resx", emptyString); 581 | await createOrUpdateNamespaceMappingFile(workspaceFolder, fileNameNoExt, namespace); 582 | } 583 | else { 584 | Logger.instance.warning(`Unable to locate workspaceFolder for ${resxFileUri.fsPath}`); 585 | } 586 | } 587 | else { 588 | vscode.window.showErrorMessage("Unable to add resx content"); 589 | } 590 | } 591 | else { 592 | vscode.window.showErrorMessage(`Invalid namespace entered for ${fileName}`); 593 | } 594 | } 595 | else { 596 | vscode.window.showErrorMessage("Invalid resx file name entered"); 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | devDependencies: 11 | '@types/node': 12 | specifier: ^22.18.6 13 | version: 22.18.6 14 | '@types/vscode': 15 | specifier: ^1.98.0 16 | version: 1.98.0 17 | '@vscode/test-electron': 18 | specifier: ^2.5.2 19 | version: 2.5.2 20 | eslint: 21 | specifier: ^9.37.0 22 | version: 9.37.0 23 | ts-loader: 24 | specifier: ^9.5.4 25 | version: 9.5.4(typescript@5.9.3)(webpack@5.102.0) 26 | typescript: 27 | specifier: ^5.9.3 28 | version: 5.9.3 29 | webpack: 30 | specifier: ^5.102.0 31 | version: 5.102.0(webpack-cli@6.0.1) 32 | webpack-cli: 33 | specifier: ^6.0.1 34 | version: 6.0.1(webpack@5.102.0) 35 | xml-js: 36 | specifier: ^1.6.11 37 | version: 1.6.11 38 | 39 | packages: 40 | 41 | '@discoveryjs/json-ext@0.6.3': 42 | resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} 43 | engines: {node: '>=14.17.0'} 44 | 45 | '@eslint-community/eslint-utils@4.9.0': 46 | resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} 47 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 48 | peerDependencies: 49 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 50 | 51 | '@eslint-community/regexpp@4.12.1': 52 | resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} 53 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 54 | 55 | '@eslint/config-array@0.21.0': 56 | resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} 57 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 58 | 59 | '@eslint/config-helpers@0.4.0': 60 | resolution: {integrity: sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==} 61 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 62 | 63 | '@eslint/core@0.16.0': 64 | resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} 65 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 66 | 67 | '@eslint/eslintrc@3.3.1': 68 | resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 69 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 70 | 71 | '@eslint/js@9.37.0': 72 | resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} 73 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 74 | 75 | '@eslint/object-schema@2.1.6': 76 | resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} 77 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 78 | 79 | '@eslint/plugin-kit@0.4.0': 80 | resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} 81 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 82 | 83 | '@humanfs/core@0.19.1': 84 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 85 | engines: {node: '>=18.18.0'} 86 | 87 | '@humanfs/node@0.16.6': 88 | resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} 89 | engines: {node: '>=18.18.0'} 90 | 91 | '@humanwhocodes/module-importer@1.0.1': 92 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 93 | engines: {node: '>=12.22'} 94 | 95 | '@humanwhocodes/retry@0.3.1': 96 | resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} 97 | engines: {node: '>=18.18'} 98 | 99 | '@humanwhocodes/retry@0.4.2': 100 | resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==} 101 | engines: {node: '>=18.18'} 102 | 103 | '@jridgewell/gen-mapping@0.3.8': 104 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 105 | engines: {node: '>=6.0.0'} 106 | 107 | '@jridgewell/resolve-uri@3.1.2': 108 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 109 | engines: {node: '>=6.0.0'} 110 | 111 | '@jridgewell/set-array@1.2.1': 112 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 113 | engines: {node: '>=6.0.0'} 114 | 115 | '@jridgewell/source-map@0.3.6': 116 | resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} 117 | 118 | '@jridgewell/sourcemap-codec@1.5.0': 119 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 120 | 121 | '@jridgewell/trace-mapping@0.3.25': 122 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 123 | 124 | '@types/eslint-scope@3.7.7': 125 | resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} 126 | 127 | '@types/eslint@9.6.1': 128 | resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} 129 | 130 | '@types/estree@1.0.6': 131 | resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} 132 | 133 | '@types/estree@1.0.8': 134 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 135 | 136 | '@types/json-schema@7.0.15': 137 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 138 | 139 | '@types/node@22.18.6': 140 | resolution: {integrity: sha512-r8uszLPpeIWbNKtvWRt/DbVi5zbqZyj1PTmhRMqBMvDnaz1QpmSKujUtJLrqGZeoM8v72MfYggDceY4K1itzWQ==} 141 | 142 | '@types/vscode@1.98.0': 143 | resolution: {integrity: sha512-+KuiWhpbKBaG2egF+51KjbGWatTH5BbmWQjSLMDCssb4xF8FJnW4nGH4nuAdOOfMbpD0QlHtI+C3tPq+DoKElg==} 144 | 145 | '@vscode/test-electron@2.5.2': 146 | resolution: {integrity: sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==} 147 | engines: {node: '>=16'} 148 | 149 | '@webassemblyjs/ast@1.14.1': 150 | resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} 151 | 152 | '@webassemblyjs/floating-point-hex-parser@1.13.2': 153 | resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} 154 | 155 | '@webassemblyjs/helper-api-error@1.13.2': 156 | resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} 157 | 158 | '@webassemblyjs/helper-buffer@1.14.1': 159 | resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} 160 | 161 | '@webassemblyjs/helper-numbers@1.13.2': 162 | resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} 163 | 164 | '@webassemblyjs/helper-wasm-bytecode@1.13.2': 165 | resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} 166 | 167 | '@webassemblyjs/helper-wasm-section@1.14.1': 168 | resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} 169 | 170 | '@webassemblyjs/ieee754@1.13.2': 171 | resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} 172 | 173 | '@webassemblyjs/leb128@1.13.2': 174 | resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} 175 | 176 | '@webassemblyjs/utf8@1.13.2': 177 | resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} 178 | 179 | '@webassemblyjs/wasm-edit@1.14.1': 180 | resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} 181 | 182 | '@webassemblyjs/wasm-gen@1.14.1': 183 | resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} 184 | 185 | '@webassemblyjs/wasm-opt@1.14.1': 186 | resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} 187 | 188 | '@webassemblyjs/wasm-parser@1.14.1': 189 | resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} 190 | 191 | '@webassemblyjs/wast-printer@1.14.1': 192 | resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} 193 | 194 | '@webpack-cli/configtest@3.0.1': 195 | resolution: {integrity: sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==} 196 | engines: {node: '>=18.12.0'} 197 | peerDependencies: 198 | webpack: ^5.82.0 199 | webpack-cli: 6.x.x 200 | 201 | '@webpack-cli/info@3.0.1': 202 | resolution: {integrity: sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==} 203 | engines: {node: '>=18.12.0'} 204 | peerDependencies: 205 | webpack: ^5.82.0 206 | webpack-cli: 6.x.x 207 | 208 | '@webpack-cli/serve@3.0.1': 209 | resolution: {integrity: sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==} 210 | engines: {node: '>=18.12.0'} 211 | peerDependencies: 212 | webpack: ^5.82.0 213 | webpack-cli: 6.x.x 214 | webpack-dev-server: '*' 215 | peerDependenciesMeta: 216 | webpack-dev-server: 217 | optional: true 218 | 219 | '@xtuc/ieee754@1.2.0': 220 | resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} 221 | 222 | '@xtuc/long@4.2.2': 223 | resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} 224 | 225 | acorn-import-phases@1.0.4: 226 | resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} 227 | engines: {node: '>=10.13.0'} 228 | peerDependencies: 229 | acorn: ^8.14.0 230 | 231 | acorn-jsx@5.3.2: 232 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 233 | peerDependencies: 234 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 235 | 236 | acorn@8.15.0: 237 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 238 | engines: {node: '>=0.4.0'} 239 | hasBin: true 240 | 241 | agent-base@7.1.3: 242 | resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} 243 | engines: {node: '>= 14'} 244 | 245 | ajv-formats@2.1.1: 246 | resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} 247 | peerDependencies: 248 | ajv: ^8.0.0 249 | peerDependenciesMeta: 250 | ajv: 251 | optional: true 252 | 253 | ajv-keywords@5.1.0: 254 | resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} 255 | peerDependencies: 256 | ajv: ^8.8.2 257 | 258 | ajv@6.12.6: 259 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 260 | 261 | ajv@8.17.1: 262 | resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} 263 | 264 | ansi-regex@6.1.0: 265 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 266 | engines: {node: '>=12'} 267 | 268 | ansi-styles@4.3.0: 269 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 270 | engines: {node: '>=8'} 271 | 272 | argparse@2.0.1: 273 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 274 | 275 | balanced-match@1.0.2: 276 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 277 | 278 | baseline-browser-mapping@2.8.12: 279 | resolution: {integrity: sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==} 280 | hasBin: true 281 | 282 | brace-expansion@1.1.11: 283 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 284 | 285 | braces@3.0.3: 286 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 287 | engines: {node: '>=8'} 288 | 289 | browserslist@4.26.3: 290 | resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} 291 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 292 | hasBin: true 293 | 294 | buffer-from@1.1.2: 295 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 296 | 297 | callsites@3.1.0: 298 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 299 | engines: {node: '>=6'} 300 | 301 | caniuse-lite@1.0.30001748: 302 | resolution: {integrity: sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==} 303 | 304 | chalk@4.1.2: 305 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 306 | engines: {node: '>=10'} 307 | 308 | chalk@5.4.1: 309 | resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} 310 | engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} 311 | 312 | chrome-trace-event@1.0.4: 313 | resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} 314 | engines: {node: '>=6.0'} 315 | 316 | cli-cursor@5.0.0: 317 | resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} 318 | engines: {node: '>=18'} 319 | 320 | cli-spinners@2.9.2: 321 | resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} 322 | engines: {node: '>=6'} 323 | 324 | clone-deep@4.0.1: 325 | resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} 326 | engines: {node: '>=6'} 327 | 328 | color-convert@2.0.1: 329 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 330 | engines: {node: '>=7.0.0'} 331 | 332 | color-name@1.1.4: 333 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 334 | 335 | colorette@2.0.20: 336 | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 337 | 338 | commander@12.1.0: 339 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} 340 | engines: {node: '>=18'} 341 | 342 | commander@2.20.3: 343 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 344 | 345 | concat-map@0.0.1: 346 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 347 | 348 | core-util-is@1.0.3: 349 | resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} 350 | 351 | cross-spawn@7.0.6: 352 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 353 | engines: {node: '>= 8'} 354 | 355 | debug@4.4.0: 356 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 357 | engines: {node: '>=6.0'} 358 | peerDependencies: 359 | supports-color: '*' 360 | peerDependenciesMeta: 361 | supports-color: 362 | optional: true 363 | 364 | deep-is@0.1.4: 365 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 366 | 367 | electron-to-chromium@1.5.232: 368 | resolution: {integrity: sha512-ENirSe7wf8WzyPCibqKUG1Cg43cPaxH4wRR7AJsX7MCABCHBIOFqvaYODSLKUuZdraxUTHRE/0A2Aq8BYKEHOg==} 369 | 370 | emoji-regex@10.4.0: 371 | resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} 372 | 373 | enhanced-resolve@5.18.1: 374 | resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} 375 | engines: {node: '>=10.13.0'} 376 | 377 | envinfo@7.14.0: 378 | resolution: {integrity: sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==} 379 | engines: {node: '>=4'} 380 | hasBin: true 381 | 382 | es-module-lexer@1.6.0: 383 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 384 | 385 | escalade@3.2.0: 386 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 387 | engines: {node: '>=6'} 388 | 389 | escape-string-regexp@4.0.0: 390 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 391 | engines: {node: '>=10'} 392 | 393 | eslint-scope@5.1.1: 394 | resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} 395 | engines: {node: '>=8.0.0'} 396 | 397 | eslint-scope@8.4.0: 398 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 399 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 400 | 401 | eslint-visitor-keys@3.4.3: 402 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 403 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 404 | 405 | eslint-visitor-keys@4.2.1: 406 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 407 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 408 | 409 | eslint@9.37.0: 410 | resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} 411 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 412 | hasBin: true 413 | peerDependencies: 414 | jiti: '*' 415 | peerDependenciesMeta: 416 | jiti: 417 | optional: true 418 | 419 | espree@10.4.0: 420 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 421 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 422 | 423 | esquery@1.6.0: 424 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 425 | engines: {node: '>=0.10'} 426 | 427 | esrecurse@4.3.0: 428 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 429 | engines: {node: '>=4.0'} 430 | 431 | estraverse@4.3.0: 432 | resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} 433 | engines: {node: '>=4.0'} 434 | 435 | estraverse@5.3.0: 436 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 437 | engines: {node: '>=4.0'} 438 | 439 | esutils@2.0.3: 440 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 441 | engines: {node: '>=0.10.0'} 442 | 443 | events@3.3.0: 444 | resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 445 | engines: {node: '>=0.8.x'} 446 | 447 | fast-deep-equal@3.1.3: 448 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 449 | 450 | fast-json-stable-stringify@2.1.0: 451 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 452 | 453 | fast-levenshtein@2.0.6: 454 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 455 | 456 | fast-uri@3.0.6: 457 | resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} 458 | 459 | fastest-levenshtein@1.0.16: 460 | resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} 461 | engines: {node: '>= 4.9.1'} 462 | 463 | file-entry-cache@8.0.0: 464 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 465 | engines: {node: '>=16.0.0'} 466 | 467 | fill-range@7.1.1: 468 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 469 | engines: {node: '>=8'} 470 | 471 | find-up@4.1.0: 472 | resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 473 | engines: {node: '>=8'} 474 | 475 | find-up@5.0.0: 476 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 477 | engines: {node: '>=10'} 478 | 479 | flat-cache@4.0.1: 480 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 481 | engines: {node: '>=16'} 482 | 483 | flat@5.0.2: 484 | resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} 485 | hasBin: true 486 | 487 | flatted@3.3.3: 488 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 489 | 490 | function-bind@1.1.2: 491 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 492 | 493 | get-east-asian-width@1.3.0: 494 | resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} 495 | engines: {node: '>=18'} 496 | 497 | glob-parent@6.0.2: 498 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 499 | engines: {node: '>=10.13.0'} 500 | 501 | glob-to-regexp@0.4.1: 502 | resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} 503 | 504 | globals@14.0.0: 505 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 506 | engines: {node: '>=18'} 507 | 508 | graceful-fs@4.2.11: 509 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 510 | 511 | has-flag@4.0.0: 512 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 513 | engines: {node: '>=8'} 514 | 515 | hasown@2.0.2: 516 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 517 | engines: {node: '>= 0.4'} 518 | 519 | http-proxy-agent@7.0.2: 520 | resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} 521 | engines: {node: '>= 14'} 522 | 523 | https-proxy-agent@7.0.6: 524 | resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} 525 | engines: {node: '>= 14'} 526 | 527 | ignore@5.3.2: 528 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 529 | engines: {node: '>= 4'} 530 | 531 | immediate@3.0.6: 532 | resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} 533 | 534 | import-fresh@3.3.1: 535 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 536 | engines: {node: '>=6'} 537 | 538 | import-local@3.2.0: 539 | resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} 540 | engines: {node: '>=8'} 541 | hasBin: true 542 | 543 | imurmurhash@0.1.4: 544 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 545 | engines: {node: '>=0.8.19'} 546 | 547 | inherits@2.0.4: 548 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 549 | 550 | interpret@3.1.1: 551 | resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} 552 | engines: {node: '>=10.13.0'} 553 | 554 | is-core-module@2.16.1: 555 | resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} 556 | engines: {node: '>= 0.4'} 557 | 558 | is-extglob@2.1.1: 559 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 560 | engines: {node: '>=0.10.0'} 561 | 562 | is-glob@4.0.3: 563 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 564 | engines: {node: '>=0.10.0'} 565 | 566 | is-interactive@2.0.0: 567 | resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} 568 | engines: {node: '>=12'} 569 | 570 | is-number@7.0.0: 571 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 572 | engines: {node: '>=0.12.0'} 573 | 574 | is-plain-object@2.0.4: 575 | resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} 576 | engines: {node: '>=0.10.0'} 577 | 578 | is-unicode-supported@1.3.0: 579 | resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} 580 | engines: {node: '>=12'} 581 | 582 | is-unicode-supported@2.1.0: 583 | resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} 584 | engines: {node: '>=18'} 585 | 586 | isarray@1.0.0: 587 | resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} 588 | 589 | isexe@2.0.0: 590 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 591 | 592 | isobject@3.0.1: 593 | resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} 594 | engines: {node: '>=0.10.0'} 595 | 596 | jest-worker@27.5.1: 597 | resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} 598 | engines: {node: '>= 10.13.0'} 599 | 600 | js-yaml@4.1.0: 601 | resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} 602 | hasBin: true 603 | 604 | json-buffer@3.0.1: 605 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 606 | 607 | json-parse-even-better-errors@2.3.1: 608 | resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} 609 | 610 | json-schema-traverse@0.4.1: 611 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 612 | 613 | json-schema-traverse@1.0.0: 614 | resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} 615 | 616 | json-stable-stringify-without-jsonify@1.0.1: 617 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 618 | 619 | jszip@3.10.1: 620 | resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} 621 | 622 | keyv@4.5.4: 623 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 624 | 625 | kind-of@6.0.3: 626 | resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} 627 | engines: {node: '>=0.10.0'} 628 | 629 | levn@0.4.1: 630 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 631 | engines: {node: '>= 0.8.0'} 632 | 633 | lie@3.3.0: 634 | resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} 635 | 636 | loader-runner@4.3.0: 637 | resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} 638 | engines: {node: '>=6.11.5'} 639 | 640 | locate-path@5.0.0: 641 | resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 642 | engines: {node: '>=8'} 643 | 644 | locate-path@6.0.0: 645 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 646 | engines: {node: '>=10'} 647 | 648 | lodash.merge@4.6.2: 649 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 650 | 651 | log-symbols@6.0.0: 652 | resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} 653 | engines: {node: '>=18'} 654 | 655 | merge-stream@2.0.0: 656 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 657 | 658 | micromatch@4.0.8: 659 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 660 | engines: {node: '>=8.6'} 661 | 662 | mime-db@1.52.0: 663 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 664 | engines: {node: '>= 0.6'} 665 | 666 | mime-types@2.1.35: 667 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 668 | engines: {node: '>= 0.6'} 669 | 670 | mimic-function@5.0.1: 671 | resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} 672 | engines: {node: '>=18'} 673 | 674 | minimatch@3.1.2: 675 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 676 | 677 | ms@2.1.3: 678 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 679 | 680 | natural-compare@1.4.0: 681 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 682 | 683 | neo-async@2.6.2: 684 | resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} 685 | 686 | node-releases@2.0.23: 687 | resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} 688 | 689 | onetime@7.0.0: 690 | resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} 691 | engines: {node: '>=18'} 692 | 693 | optionator@0.9.4: 694 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 695 | engines: {node: '>= 0.8.0'} 696 | 697 | ora@8.2.0: 698 | resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} 699 | engines: {node: '>=18'} 700 | 701 | p-limit@2.3.0: 702 | resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} 703 | engines: {node: '>=6'} 704 | 705 | p-limit@3.1.0: 706 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 707 | engines: {node: '>=10'} 708 | 709 | p-locate@4.1.0: 710 | resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} 711 | engines: {node: '>=8'} 712 | 713 | p-locate@5.0.0: 714 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 715 | engines: {node: '>=10'} 716 | 717 | p-try@2.2.0: 718 | resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} 719 | engines: {node: '>=6'} 720 | 721 | pako@1.0.11: 722 | resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} 723 | 724 | parent-module@1.0.1: 725 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 726 | engines: {node: '>=6'} 727 | 728 | path-exists@4.0.0: 729 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 730 | engines: {node: '>=8'} 731 | 732 | path-key@3.1.1: 733 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 734 | engines: {node: '>=8'} 735 | 736 | path-parse@1.0.7: 737 | resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} 738 | 739 | picocolors@1.1.1: 740 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 741 | 742 | picomatch@2.3.1: 743 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 744 | engines: {node: '>=8.6'} 745 | 746 | pkg-dir@4.2.0: 747 | resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} 748 | engines: {node: '>=8'} 749 | 750 | prelude-ls@1.2.1: 751 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 752 | engines: {node: '>= 0.8.0'} 753 | 754 | process-nextick-args@2.0.1: 755 | resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} 756 | 757 | punycode@2.3.1: 758 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 759 | engines: {node: '>=6'} 760 | 761 | randombytes@2.1.0: 762 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 763 | 764 | readable-stream@2.3.8: 765 | resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} 766 | 767 | rechoir@0.8.0: 768 | resolution: {integrity: sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==} 769 | engines: {node: '>= 10.13.0'} 770 | 771 | require-from-string@2.0.2: 772 | resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} 773 | engines: {node: '>=0.10.0'} 774 | 775 | resolve-cwd@3.0.0: 776 | resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} 777 | engines: {node: '>=8'} 778 | 779 | resolve-from@4.0.0: 780 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 781 | engines: {node: '>=4'} 782 | 783 | resolve-from@5.0.0: 784 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 785 | engines: {node: '>=8'} 786 | 787 | resolve@1.22.10: 788 | resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} 789 | engines: {node: '>= 0.4'} 790 | hasBin: true 791 | 792 | restore-cursor@5.1.0: 793 | resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} 794 | engines: {node: '>=18'} 795 | 796 | safe-buffer@5.1.2: 797 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 798 | 799 | safe-buffer@5.2.1: 800 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 801 | 802 | sax@1.4.1: 803 | resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} 804 | 805 | schema-utils@4.3.2: 806 | resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} 807 | engines: {node: '>= 10.13.0'} 808 | 809 | semver@7.7.1: 810 | resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} 811 | engines: {node: '>=10'} 812 | hasBin: true 813 | 814 | serialize-javascript@6.0.2: 815 | resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} 816 | 817 | setimmediate@1.0.5: 818 | resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} 819 | 820 | shallow-clone@3.0.1: 821 | resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} 822 | engines: {node: '>=8'} 823 | 824 | shebang-command@2.0.0: 825 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 826 | engines: {node: '>=8'} 827 | 828 | shebang-regex@3.0.0: 829 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 830 | engines: {node: '>=8'} 831 | 832 | signal-exit@4.1.0: 833 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 834 | engines: {node: '>=14'} 835 | 836 | source-map-support@0.5.21: 837 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 838 | 839 | source-map@0.6.1: 840 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 841 | engines: {node: '>=0.10.0'} 842 | 843 | source-map@0.7.4: 844 | resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} 845 | engines: {node: '>= 8'} 846 | 847 | stdin-discarder@0.2.2: 848 | resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} 849 | engines: {node: '>=18'} 850 | 851 | string-width@7.2.0: 852 | resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} 853 | engines: {node: '>=18'} 854 | 855 | string_decoder@1.1.1: 856 | resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} 857 | 858 | strip-ansi@7.1.0: 859 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 860 | engines: {node: '>=12'} 861 | 862 | strip-json-comments@3.1.1: 863 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 864 | engines: {node: '>=8'} 865 | 866 | supports-color@7.2.0: 867 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 868 | engines: {node: '>=8'} 869 | 870 | supports-color@8.1.1: 871 | resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} 872 | engines: {node: '>=10'} 873 | 874 | supports-preserve-symlinks-flag@1.0.0: 875 | resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} 876 | engines: {node: '>= 0.4'} 877 | 878 | tapable@2.3.0: 879 | resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} 880 | engines: {node: '>=6'} 881 | 882 | terser-webpack-plugin@5.3.14: 883 | resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} 884 | engines: {node: '>= 10.13.0'} 885 | peerDependencies: 886 | '@swc/core': '*' 887 | esbuild: '*' 888 | uglify-js: '*' 889 | webpack: ^5.1.0 890 | peerDependenciesMeta: 891 | '@swc/core': 892 | optional: true 893 | esbuild: 894 | optional: true 895 | uglify-js: 896 | optional: true 897 | 898 | terser@5.39.0: 899 | resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} 900 | engines: {node: '>=10'} 901 | hasBin: true 902 | 903 | to-regex-range@5.0.1: 904 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 905 | engines: {node: '>=8.0'} 906 | 907 | ts-loader@9.5.4: 908 | resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} 909 | engines: {node: '>=12.0.0'} 910 | peerDependencies: 911 | typescript: '*' 912 | webpack: ^5.0.0 913 | 914 | type-check@0.4.0: 915 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 916 | engines: {node: '>= 0.8.0'} 917 | 918 | typescript@5.9.3: 919 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 920 | engines: {node: '>=14.17'} 921 | hasBin: true 922 | 923 | undici-types@6.21.0: 924 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 925 | 926 | update-browserslist-db@1.1.3: 927 | resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} 928 | hasBin: true 929 | peerDependencies: 930 | browserslist: '>= 4.21.0' 931 | 932 | uri-js@4.4.1: 933 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 934 | 935 | util-deprecate@1.0.2: 936 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 937 | 938 | watchpack@2.4.4: 939 | resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} 940 | engines: {node: '>=10.13.0'} 941 | 942 | webpack-cli@6.0.1: 943 | resolution: {integrity: sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==} 944 | engines: {node: '>=18.12.0'} 945 | hasBin: true 946 | peerDependencies: 947 | webpack: ^5.82.0 948 | webpack-bundle-analyzer: '*' 949 | webpack-dev-server: '*' 950 | peerDependenciesMeta: 951 | webpack-bundle-analyzer: 952 | optional: true 953 | webpack-dev-server: 954 | optional: true 955 | 956 | webpack-merge@6.0.1: 957 | resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} 958 | engines: {node: '>=18.0.0'} 959 | 960 | webpack-sources@3.3.3: 961 | resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} 962 | engines: {node: '>=10.13.0'} 963 | 964 | webpack@5.102.0: 965 | resolution: {integrity: sha512-hUtqAR3ZLVEYDEABdBioQCIqSoguHbFn1K7WlPPWSuXmx0031BD73PSE35jKyftdSh4YLDoQNgK4pqBt5Q82MA==} 966 | engines: {node: '>=10.13.0'} 967 | hasBin: true 968 | peerDependencies: 969 | webpack-cli: '*' 970 | peerDependenciesMeta: 971 | webpack-cli: 972 | optional: true 973 | 974 | which@2.0.2: 975 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 976 | engines: {node: '>= 8'} 977 | hasBin: true 978 | 979 | wildcard@2.0.1: 980 | resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} 981 | 982 | word-wrap@1.2.5: 983 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 984 | engines: {node: '>=0.10.0'} 985 | 986 | xml-js@1.6.11: 987 | resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} 988 | hasBin: true 989 | 990 | yocto-queue@0.1.0: 991 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 992 | engines: {node: '>=10'} 993 | 994 | snapshots: 995 | 996 | '@discoveryjs/json-ext@0.6.3': {} 997 | 998 | '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0)': 999 | dependencies: 1000 | eslint: 9.37.0 1001 | eslint-visitor-keys: 3.4.3 1002 | 1003 | '@eslint-community/regexpp@4.12.1': {} 1004 | 1005 | '@eslint/config-array@0.21.0': 1006 | dependencies: 1007 | '@eslint/object-schema': 2.1.6 1008 | debug: 4.4.0 1009 | minimatch: 3.1.2 1010 | transitivePeerDependencies: 1011 | - supports-color 1012 | 1013 | '@eslint/config-helpers@0.4.0': 1014 | dependencies: 1015 | '@eslint/core': 0.16.0 1016 | 1017 | '@eslint/core@0.16.0': 1018 | dependencies: 1019 | '@types/json-schema': 7.0.15 1020 | 1021 | '@eslint/eslintrc@3.3.1': 1022 | dependencies: 1023 | ajv: 6.12.6 1024 | debug: 4.4.0 1025 | espree: 10.4.0 1026 | globals: 14.0.0 1027 | ignore: 5.3.2 1028 | import-fresh: 3.3.1 1029 | js-yaml: 4.1.0 1030 | minimatch: 3.1.2 1031 | strip-json-comments: 3.1.1 1032 | transitivePeerDependencies: 1033 | - supports-color 1034 | 1035 | '@eslint/js@9.37.0': {} 1036 | 1037 | '@eslint/object-schema@2.1.6': {} 1038 | 1039 | '@eslint/plugin-kit@0.4.0': 1040 | dependencies: 1041 | '@eslint/core': 0.16.0 1042 | levn: 0.4.1 1043 | 1044 | '@humanfs/core@0.19.1': {} 1045 | 1046 | '@humanfs/node@0.16.6': 1047 | dependencies: 1048 | '@humanfs/core': 0.19.1 1049 | '@humanwhocodes/retry': 0.3.1 1050 | 1051 | '@humanwhocodes/module-importer@1.0.1': {} 1052 | 1053 | '@humanwhocodes/retry@0.3.1': {} 1054 | 1055 | '@humanwhocodes/retry@0.4.2': {} 1056 | 1057 | '@jridgewell/gen-mapping@0.3.8': 1058 | dependencies: 1059 | '@jridgewell/set-array': 1.2.1 1060 | '@jridgewell/sourcemap-codec': 1.5.0 1061 | '@jridgewell/trace-mapping': 0.3.25 1062 | 1063 | '@jridgewell/resolve-uri@3.1.2': {} 1064 | 1065 | '@jridgewell/set-array@1.2.1': {} 1066 | 1067 | '@jridgewell/source-map@0.3.6': 1068 | dependencies: 1069 | '@jridgewell/gen-mapping': 0.3.8 1070 | '@jridgewell/trace-mapping': 0.3.25 1071 | 1072 | '@jridgewell/sourcemap-codec@1.5.0': {} 1073 | 1074 | '@jridgewell/trace-mapping@0.3.25': 1075 | dependencies: 1076 | '@jridgewell/resolve-uri': 3.1.2 1077 | '@jridgewell/sourcemap-codec': 1.5.0 1078 | 1079 | '@types/eslint-scope@3.7.7': 1080 | dependencies: 1081 | '@types/eslint': 9.6.1 1082 | '@types/estree': 1.0.8 1083 | 1084 | '@types/eslint@9.6.1': 1085 | dependencies: 1086 | '@types/estree': 1.0.8 1087 | '@types/json-schema': 7.0.15 1088 | 1089 | '@types/estree@1.0.6': {} 1090 | 1091 | '@types/estree@1.0.8': {} 1092 | 1093 | '@types/json-schema@7.0.15': {} 1094 | 1095 | '@types/node@22.18.6': 1096 | dependencies: 1097 | undici-types: 6.21.0 1098 | 1099 | '@types/vscode@1.98.0': {} 1100 | 1101 | '@vscode/test-electron@2.5.2': 1102 | dependencies: 1103 | http-proxy-agent: 7.0.2 1104 | https-proxy-agent: 7.0.6 1105 | jszip: 3.10.1 1106 | ora: 8.2.0 1107 | semver: 7.7.1 1108 | transitivePeerDependencies: 1109 | - supports-color 1110 | 1111 | '@webassemblyjs/ast@1.14.1': 1112 | dependencies: 1113 | '@webassemblyjs/helper-numbers': 1.13.2 1114 | '@webassemblyjs/helper-wasm-bytecode': 1.13.2 1115 | 1116 | '@webassemblyjs/floating-point-hex-parser@1.13.2': {} 1117 | 1118 | '@webassemblyjs/helper-api-error@1.13.2': {} 1119 | 1120 | '@webassemblyjs/helper-buffer@1.14.1': {} 1121 | 1122 | '@webassemblyjs/helper-numbers@1.13.2': 1123 | dependencies: 1124 | '@webassemblyjs/floating-point-hex-parser': 1.13.2 1125 | '@webassemblyjs/helper-api-error': 1.13.2 1126 | '@xtuc/long': 4.2.2 1127 | 1128 | '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} 1129 | 1130 | '@webassemblyjs/helper-wasm-section@1.14.1': 1131 | dependencies: 1132 | '@webassemblyjs/ast': 1.14.1 1133 | '@webassemblyjs/helper-buffer': 1.14.1 1134 | '@webassemblyjs/helper-wasm-bytecode': 1.13.2 1135 | '@webassemblyjs/wasm-gen': 1.14.1 1136 | 1137 | '@webassemblyjs/ieee754@1.13.2': 1138 | dependencies: 1139 | '@xtuc/ieee754': 1.2.0 1140 | 1141 | '@webassemblyjs/leb128@1.13.2': 1142 | dependencies: 1143 | '@xtuc/long': 4.2.2 1144 | 1145 | '@webassemblyjs/utf8@1.13.2': {} 1146 | 1147 | '@webassemblyjs/wasm-edit@1.14.1': 1148 | dependencies: 1149 | '@webassemblyjs/ast': 1.14.1 1150 | '@webassemblyjs/helper-buffer': 1.14.1 1151 | '@webassemblyjs/helper-wasm-bytecode': 1.13.2 1152 | '@webassemblyjs/helper-wasm-section': 1.14.1 1153 | '@webassemblyjs/wasm-gen': 1.14.1 1154 | '@webassemblyjs/wasm-opt': 1.14.1 1155 | '@webassemblyjs/wasm-parser': 1.14.1 1156 | '@webassemblyjs/wast-printer': 1.14.1 1157 | 1158 | '@webassemblyjs/wasm-gen@1.14.1': 1159 | dependencies: 1160 | '@webassemblyjs/ast': 1.14.1 1161 | '@webassemblyjs/helper-wasm-bytecode': 1.13.2 1162 | '@webassemblyjs/ieee754': 1.13.2 1163 | '@webassemblyjs/leb128': 1.13.2 1164 | '@webassemblyjs/utf8': 1.13.2 1165 | 1166 | '@webassemblyjs/wasm-opt@1.14.1': 1167 | dependencies: 1168 | '@webassemblyjs/ast': 1.14.1 1169 | '@webassemblyjs/helper-buffer': 1.14.1 1170 | '@webassemblyjs/wasm-gen': 1.14.1 1171 | '@webassemblyjs/wasm-parser': 1.14.1 1172 | 1173 | '@webassemblyjs/wasm-parser@1.14.1': 1174 | dependencies: 1175 | '@webassemblyjs/ast': 1.14.1 1176 | '@webassemblyjs/helper-api-error': 1.13.2 1177 | '@webassemblyjs/helper-wasm-bytecode': 1.13.2 1178 | '@webassemblyjs/ieee754': 1.13.2 1179 | '@webassemblyjs/leb128': 1.13.2 1180 | '@webassemblyjs/utf8': 1.13.2 1181 | 1182 | '@webassemblyjs/wast-printer@1.14.1': 1183 | dependencies: 1184 | '@webassemblyjs/ast': 1.14.1 1185 | '@xtuc/long': 4.2.2 1186 | 1187 | '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.102.0)': 1188 | dependencies: 1189 | webpack: 5.102.0(webpack-cli@6.0.1) 1190 | webpack-cli: 6.0.1(webpack@5.102.0) 1191 | 1192 | '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.102.0)': 1193 | dependencies: 1194 | webpack: 5.102.0(webpack-cli@6.0.1) 1195 | webpack-cli: 6.0.1(webpack@5.102.0) 1196 | 1197 | '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.102.0)': 1198 | dependencies: 1199 | webpack: 5.102.0(webpack-cli@6.0.1) 1200 | webpack-cli: 6.0.1(webpack@5.102.0) 1201 | 1202 | '@xtuc/ieee754@1.2.0': {} 1203 | 1204 | '@xtuc/long@4.2.2': {} 1205 | 1206 | acorn-import-phases@1.0.4(acorn@8.15.0): 1207 | dependencies: 1208 | acorn: 8.15.0 1209 | 1210 | acorn-jsx@5.3.2(acorn@8.15.0): 1211 | dependencies: 1212 | acorn: 8.15.0 1213 | 1214 | acorn@8.15.0: {} 1215 | 1216 | agent-base@7.1.3: {} 1217 | 1218 | ajv-formats@2.1.1(ajv@8.17.1): 1219 | optionalDependencies: 1220 | ajv: 8.17.1 1221 | 1222 | ajv-keywords@5.1.0(ajv@8.17.1): 1223 | dependencies: 1224 | ajv: 8.17.1 1225 | fast-deep-equal: 3.1.3 1226 | 1227 | ajv@6.12.6: 1228 | dependencies: 1229 | fast-deep-equal: 3.1.3 1230 | fast-json-stable-stringify: 2.1.0 1231 | json-schema-traverse: 0.4.1 1232 | uri-js: 4.4.1 1233 | 1234 | ajv@8.17.1: 1235 | dependencies: 1236 | fast-deep-equal: 3.1.3 1237 | fast-uri: 3.0.6 1238 | json-schema-traverse: 1.0.0 1239 | require-from-string: 2.0.2 1240 | 1241 | ansi-regex@6.1.0: {} 1242 | 1243 | ansi-styles@4.3.0: 1244 | dependencies: 1245 | color-convert: 2.0.1 1246 | 1247 | argparse@2.0.1: {} 1248 | 1249 | balanced-match@1.0.2: {} 1250 | 1251 | baseline-browser-mapping@2.8.12: {} 1252 | 1253 | brace-expansion@1.1.11: 1254 | dependencies: 1255 | balanced-match: 1.0.2 1256 | concat-map: 0.0.1 1257 | 1258 | braces@3.0.3: 1259 | dependencies: 1260 | fill-range: 7.1.1 1261 | 1262 | browserslist@4.26.3: 1263 | dependencies: 1264 | baseline-browser-mapping: 2.8.12 1265 | caniuse-lite: 1.0.30001748 1266 | electron-to-chromium: 1.5.232 1267 | node-releases: 2.0.23 1268 | update-browserslist-db: 1.1.3(browserslist@4.26.3) 1269 | 1270 | buffer-from@1.1.2: {} 1271 | 1272 | callsites@3.1.0: {} 1273 | 1274 | caniuse-lite@1.0.30001748: {} 1275 | 1276 | chalk@4.1.2: 1277 | dependencies: 1278 | ansi-styles: 4.3.0 1279 | supports-color: 7.2.0 1280 | 1281 | chalk@5.4.1: {} 1282 | 1283 | chrome-trace-event@1.0.4: {} 1284 | 1285 | cli-cursor@5.0.0: 1286 | dependencies: 1287 | restore-cursor: 5.1.0 1288 | 1289 | cli-spinners@2.9.2: {} 1290 | 1291 | clone-deep@4.0.1: 1292 | dependencies: 1293 | is-plain-object: 2.0.4 1294 | kind-of: 6.0.3 1295 | shallow-clone: 3.0.1 1296 | 1297 | color-convert@2.0.1: 1298 | dependencies: 1299 | color-name: 1.1.4 1300 | 1301 | color-name@1.1.4: {} 1302 | 1303 | colorette@2.0.20: {} 1304 | 1305 | commander@12.1.0: {} 1306 | 1307 | commander@2.20.3: {} 1308 | 1309 | concat-map@0.0.1: {} 1310 | 1311 | core-util-is@1.0.3: {} 1312 | 1313 | cross-spawn@7.0.6: 1314 | dependencies: 1315 | path-key: 3.1.1 1316 | shebang-command: 2.0.0 1317 | which: 2.0.2 1318 | 1319 | debug@4.4.0: 1320 | dependencies: 1321 | ms: 2.1.3 1322 | 1323 | deep-is@0.1.4: {} 1324 | 1325 | electron-to-chromium@1.5.232: {} 1326 | 1327 | emoji-regex@10.4.0: {} 1328 | 1329 | enhanced-resolve@5.18.1: 1330 | dependencies: 1331 | graceful-fs: 4.2.11 1332 | tapable: 2.3.0 1333 | 1334 | envinfo@7.14.0: {} 1335 | 1336 | es-module-lexer@1.6.0: {} 1337 | 1338 | escalade@3.2.0: {} 1339 | 1340 | escape-string-regexp@4.0.0: {} 1341 | 1342 | eslint-scope@5.1.1: 1343 | dependencies: 1344 | esrecurse: 4.3.0 1345 | estraverse: 4.3.0 1346 | 1347 | eslint-scope@8.4.0: 1348 | dependencies: 1349 | esrecurse: 4.3.0 1350 | estraverse: 5.3.0 1351 | 1352 | eslint-visitor-keys@3.4.3: {} 1353 | 1354 | eslint-visitor-keys@4.2.1: {} 1355 | 1356 | eslint@9.37.0: 1357 | dependencies: 1358 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0) 1359 | '@eslint-community/regexpp': 4.12.1 1360 | '@eslint/config-array': 0.21.0 1361 | '@eslint/config-helpers': 0.4.0 1362 | '@eslint/core': 0.16.0 1363 | '@eslint/eslintrc': 3.3.1 1364 | '@eslint/js': 9.37.0 1365 | '@eslint/plugin-kit': 0.4.0 1366 | '@humanfs/node': 0.16.6 1367 | '@humanwhocodes/module-importer': 1.0.1 1368 | '@humanwhocodes/retry': 0.4.2 1369 | '@types/estree': 1.0.6 1370 | '@types/json-schema': 7.0.15 1371 | ajv: 6.12.6 1372 | chalk: 4.1.2 1373 | cross-spawn: 7.0.6 1374 | debug: 4.4.0 1375 | escape-string-regexp: 4.0.0 1376 | eslint-scope: 8.4.0 1377 | eslint-visitor-keys: 4.2.1 1378 | espree: 10.4.0 1379 | esquery: 1.6.0 1380 | esutils: 2.0.3 1381 | fast-deep-equal: 3.1.3 1382 | file-entry-cache: 8.0.0 1383 | find-up: 5.0.0 1384 | glob-parent: 6.0.2 1385 | ignore: 5.3.2 1386 | imurmurhash: 0.1.4 1387 | is-glob: 4.0.3 1388 | json-stable-stringify-without-jsonify: 1.0.1 1389 | lodash.merge: 4.6.2 1390 | minimatch: 3.1.2 1391 | natural-compare: 1.4.0 1392 | optionator: 0.9.4 1393 | transitivePeerDependencies: 1394 | - supports-color 1395 | 1396 | espree@10.4.0: 1397 | dependencies: 1398 | acorn: 8.15.0 1399 | acorn-jsx: 5.3.2(acorn@8.15.0) 1400 | eslint-visitor-keys: 4.2.1 1401 | 1402 | esquery@1.6.0: 1403 | dependencies: 1404 | estraverse: 5.3.0 1405 | 1406 | esrecurse@4.3.0: 1407 | dependencies: 1408 | estraverse: 5.3.0 1409 | 1410 | estraverse@4.3.0: {} 1411 | 1412 | estraverse@5.3.0: {} 1413 | 1414 | esutils@2.0.3: {} 1415 | 1416 | events@3.3.0: {} 1417 | 1418 | fast-deep-equal@3.1.3: {} 1419 | 1420 | fast-json-stable-stringify@2.1.0: {} 1421 | 1422 | fast-levenshtein@2.0.6: {} 1423 | 1424 | fast-uri@3.0.6: {} 1425 | 1426 | fastest-levenshtein@1.0.16: {} 1427 | 1428 | file-entry-cache@8.0.0: 1429 | dependencies: 1430 | flat-cache: 4.0.1 1431 | 1432 | fill-range@7.1.1: 1433 | dependencies: 1434 | to-regex-range: 5.0.1 1435 | 1436 | find-up@4.1.0: 1437 | dependencies: 1438 | locate-path: 5.0.0 1439 | path-exists: 4.0.0 1440 | 1441 | find-up@5.0.0: 1442 | dependencies: 1443 | locate-path: 6.0.0 1444 | path-exists: 4.0.0 1445 | 1446 | flat-cache@4.0.1: 1447 | dependencies: 1448 | flatted: 3.3.3 1449 | keyv: 4.5.4 1450 | 1451 | flat@5.0.2: {} 1452 | 1453 | flatted@3.3.3: {} 1454 | 1455 | function-bind@1.1.2: {} 1456 | 1457 | get-east-asian-width@1.3.0: {} 1458 | 1459 | glob-parent@6.0.2: 1460 | dependencies: 1461 | is-glob: 4.0.3 1462 | 1463 | glob-to-regexp@0.4.1: {} 1464 | 1465 | globals@14.0.0: {} 1466 | 1467 | graceful-fs@4.2.11: {} 1468 | 1469 | has-flag@4.0.0: {} 1470 | 1471 | hasown@2.0.2: 1472 | dependencies: 1473 | function-bind: 1.1.2 1474 | 1475 | http-proxy-agent@7.0.2: 1476 | dependencies: 1477 | agent-base: 7.1.3 1478 | debug: 4.4.0 1479 | transitivePeerDependencies: 1480 | - supports-color 1481 | 1482 | https-proxy-agent@7.0.6: 1483 | dependencies: 1484 | agent-base: 7.1.3 1485 | debug: 4.4.0 1486 | transitivePeerDependencies: 1487 | - supports-color 1488 | 1489 | ignore@5.3.2: {} 1490 | 1491 | immediate@3.0.6: {} 1492 | 1493 | import-fresh@3.3.1: 1494 | dependencies: 1495 | parent-module: 1.0.1 1496 | resolve-from: 4.0.0 1497 | 1498 | import-local@3.2.0: 1499 | dependencies: 1500 | pkg-dir: 4.2.0 1501 | resolve-cwd: 3.0.0 1502 | 1503 | imurmurhash@0.1.4: {} 1504 | 1505 | inherits@2.0.4: {} 1506 | 1507 | interpret@3.1.1: {} 1508 | 1509 | is-core-module@2.16.1: 1510 | dependencies: 1511 | hasown: 2.0.2 1512 | 1513 | is-extglob@2.1.1: {} 1514 | 1515 | is-glob@4.0.3: 1516 | dependencies: 1517 | is-extglob: 2.1.1 1518 | 1519 | is-interactive@2.0.0: {} 1520 | 1521 | is-number@7.0.0: {} 1522 | 1523 | is-plain-object@2.0.4: 1524 | dependencies: 1525 | isobject: 3.0.1 1526 | 1527 | is-unicode-supported@1.3.0: {} 1528 | 1529 | is-unicode-supported@2.1.0: {} 1530 | 1531 | isarray@1.0.0: {} 1532 | 1533 | isexe@2.0.0: {} 1534 | 1535 | isobject@3.0.1: {} 1536 | 1537 | jest-worker@27.5.1: 1538 | dependencies: 1539 | '@types/node': 22.18.6 1540 | merge-stream: 2.0.0 1541 | supports-color: 8.1.1 1542 | 1543 | js-yaml@4.1.0: 1544 | dependencies: 1545 | argparse: 2.0.1 1546 | 1547 | json-buffer@3.0.1: {} 1548 | 1549 | json-parse-even-better-errors@2.3.1: {} 1550 | 1551 | json-schema-traverse@0.4.1: {} 1552 | 1553 | json-schema-traverse@1.0.0: {} 1554 | 1555 | json-stable-stringify-without-jsonify@1.0.1: {} 1556 | 1557 | jszip@3.10.1: 1558 | dependencies: 1559 | lie: 3.3.0 1560 | pako: 1.0.11 1561 | readable-stream: 2.3.8 1562 | setimmediate: 1.0.5 1563 | 1564 | keyv@4.5.4: 1565 | dependencies: 1566 | json-buffer: 3.0.1 1567 | 1568 | kind-of@6.0.3: {} 1569 | 1570 | levn@0.4.1: 1571 | dependencies: 1572 | prelude-ls: 1.2.1 1573 | type-check: 0.4.0 1574 | 1575 | lie@3.3.0: 1576 | dependencies: 1577 | immediate: 3.0.6 1578 | 1579 | loader-runner@4.3.0: {} 1580 | 1581 | locate-path@5.0.0: 1582 | dependencies: 1583 | p-locate: 4.1.0 1584 | 1585 | locate-path@6.0.0: 1586 | dependencies: 1587 | p-locate: 5.0.0 1588 | 1589 | lodash.merge@4.6.2: {} 1590 | 1591 | log-symbols@6.0.0: 1592 | dependencies: 1593 | chalk: 5.4.1 1594 | is-unicode-supported: 1.3.0 1595 | 1596 | merge-stream@2.0.0: {} 1597 | 1598 | micromatch@4.0.8: 1599 | dependencies: 1600 | braces: 3.0.3 1601 | picomatch: 2.3.1 1602 | 1603 | mime-db@1.52.0: {} 1604 | 1605 | mime-types@2.1.35: 1606 | dependencies: 1607 | mime-db: 1.52.0 1608 | 1609 | mimic-function@5.0.1: {} 1610 | 1611 | minimatch@3.1.2: 1612 | dependencies: 1613 | brace-expansion: 1.1.11 1614 | 1615 | ms@2.1.3: {} 1616 | 1617 | natural-compare@1.4.0: {} 1618 | 1619 | neo-async@2.6.2: {} 1620 | 1621 | node-releases@2.0.23: {} 1622 | 1623 | onetime@7.0.0: 1624 | dependencies: 1625 | mimic-function: 5.0.1 1626 | 1627 | optionator@0.9.4: 1628 | dependencies: 1629 | deep-is: 0.1.4 1630 | fast-levenshtein: 2.0.6 1631 | levn: 0.4.1 1632 | prelude-ls: 1.2.1 1633 | type-check: 0.4.0 1634 | word-wrap: 1.2.5 1635 | 1636 | ora@8.2.0: 1637 | dependencies: 1638 | chalk: 5.4.1 1639 | cli-cursor: 5.0.0 1640 | cli-spinners: 2.9.2 1641 | is-interactive: 2.0.0 1642 | is-unicode-supported: 2.1.0 1643 | log-symbols: 6.0.0 1644 | stdin-discarder: 0.2.2 1645 | string-width: 7.2.0 1646 | strip-ansi: 7.1.0 1647 | 1648 | p-limit@2.3.0: 1649 | dependencies: 1650 | p-try: 2.2.0 1651 | 1652 | p-limit@3.1.0: 1653 | dependencies: 1654 | yocto-queue: 0.1.0 1655 | 1656 | p-locate@4.1.0: 1657 | dependencies: 1658 | p-limit: 2.3.0 1659 | 1660 | p-locate@5.0.0: 1661 | dependencies: 1662 | p-limit: 3.1.0 1663 | 1664 | p-try@2.2.0: {} 1665 | 1666 | pako@1.0.11: {} 1667 | 1668 | parent-module@1.0.1: 1669 | dependencies: 1670 | callsites: 3.1.0 1671 | 1672 | path-exists@4.0.0: {} 1673 | 1674 | path-key@3.1.1: {} 1675 | 1676 | path-parse@1.0.7: {} 1677 | 1678 | picocolors@1.1.1: {} 1679 | 1680 | picomatch@2.3.1: {} 1681 | 1682 | pkg-dir@4.2.0: 1683 | dependencies: 1684 | find-up: 4.1.0 1685 | 1686 | prelude-ls@1.2.1: {} 1687 | 1688 | process-nextick-args@2.0.1: {} 1689 | 1690 | punycode@2.3.1: {} 1691 | 1692 | randombytes@2.1.0: 1693 | dependencies: 1694 | safe-buffer: 5.2.1 1695 | 1696 | readable-stream@2.3.8: 1697 | dependencies: 1698 | core-util-is: 1.0.3 1699 | inherits: 2.0.4 1700 | isarray: 1.0.0 1701 | process-nextick-args: 2.0.1 1702 | safe-buffer: 5.1.2 1703 | string_decoder: 1.1.1 1704 | util-deprecate: 1.0.2 1705 | 1706 | rechoir@0.8.0: 1707 | dependencies: 1708 | resolve: 1.22.10 1709 | 1710 | require-from-string@2.0.2: {} 1711 | 1712 | resolve-cwd@3.0.0: 1713 | dependencies: 1714 | resolve-from: 5.0.0 1715 | 1716 | resolve-from@4.0.0: {} 1717 | 1718 | resolve-from@5.0.0: {} 1719 | 1720 | resolve@1.22.10: 1721 | dependencies: 1722 | is-core-module: 2.16.1 1723 | path-parse: 1.0.7 1724 | supports-preserve-symlinks-flag: 1.0.0 1725 | 1726 | restore-cursor@5.1.0: 1727 | dependencies: 1728 | onetime: 7.0.0 1729 | signal-exit: 4.1.0 1730 | 1731 | safe-buffer@5.1.2: {} 1732 | 1733 | safe-buffer@5.2.1: {} 1734 | 1735 | sax@1.4.1: {} 1736 | 1737 | schema-utils@4.3.2: 1738 | dependencies: 1739 | '@types/json-schema': 7.0.15 1740 | ajv: 8.17.1 1741 | ajv-formats: 2.1.1(ajv@8.17.1) 1742 | ajv-keywords: 5.1.0(ajv@8.17.1) 1743 | 1744 | semver@7.7.1: {} 1745 | 1746 | serialize-javascript@6.0.2: 1747 | dependencies: 1748 | randombytes: 2.1.0 1749 | 1750 | setimmediate@1.0.5: {} 1751 | 1752 | shallow-clone@3.0.1: 1753 | dependencies: 1754 | kind-of: 6.0.3 1755 | 1756 | shebang-command@2.0.0: 1757 | dependencies: 1758 | shebang-regex: 3.0.0 1759 | 1760 | shebang-regex@3.0.0: {} 1761 | 1762 | signal-exit@4.1.0: {} 1763 | 1764 | source-map-support@0.5.21: 1765 | dependencies: 1766 | buffer-from: 1.1.2 1767 | source-map: 0.6.1 1768 | 1769 | source-map@0.6.1: {} 1770 | 1771 | source-map@0.7.4: {} 1772 | 1773 | stdin-discarder@0.2.2: {} 1774 | 1775 | string-width@7.2.0: 1776 | dependencies: 1777 | emoji-regex: 10.4.0 1778 | get-east-asian-width: 1.3.0 1779 | strip-ansi: 7.1.0 1780 | 1781 | string_decoder@1.1.1: 1782 | dependencies: 1783 | safe-buffer: 5.1.2 1784 | 1785 | strip-ansi@7.1.0: 1786 | dependencies: 1787 | ansi-regex: 6.1.0 1788 | 1789 | strip-json-comments@3.1.1: {} 1790 | 1791 | supports-color@7.2.0: 1792 | dependencies: 1793 | has-flag: 4.0.0 1794 | 1795 | supports-color@8.1.1: 1796 | dependencies: 1797 | has-flag: 4.0.0 1798 | 1799 | supports-preserve-symlinks-flag@1.0.0: {} 1800 | 1801 | tapable@2.3.0: {} 1802 | 1803 | terser-webpack-plugin@5.3.14(webpack@5.102.0): 1804 | dependencies: 1805 | '@jridgewell/trace-mapping': 0.3.25 1806 | jest-worker: 27.5.1 1807 | schema-utils: 4.3.2 1808 | serialize-javascript: 6.0.2 1809 | terser: 5.39.0 1810 | webpack: 5.102.0(webpack-cli@6.0.1) 1811 | 1812 | terser@5.39.0: 1813 | dependencies: 1814 | '@jridgewell/source-map': 0.3.6 1815 | acorn: 8.15.0 1816 | commander: 2.20.3 1817 | source-map-support: 0.5.21 1818 | 1819 | to-regex-range@5.0.1: 1820 | dependencies: 1821 | is-number: 7.0.0 1822 | 1823 | ts-loader@9.5.4(typescript@5.9.3)(webpack@5.102.0): 1824 | dependencies: 1825 | chalk: 4.1.2 1826 | enhanced-resolve: 5.18.1 1827 | micromatch: 4.0.8 1828 | semver: 7.7.1 1829 | source-map: 0.7.4 1830 | typescript: 5.9.3 1831 | webpack: 5.102.0(webpack-cli@6.0.1) 1832 | 1833 | type-check@0.4.0: 1834 | dependencies: 1835 | prelude-ls: 1.2.1 1836 | 1837 | typescript@5.9.3: {} 1838 | 1839 | undici-types@6.21.0: {} 1840 | 1841 | update-browserslist-db@1.1.3(browserslist@4.26.3): 1842 | dependencies: 1843 | browserslist: 4.26.3 1844 | escalade: 3.2.0 1845 | picocolors: 1.1.1 1846 | 1847 | uri-js@4.4.1: 1848 | dependencies: 1849 | punycode: 2.3.1 1850 | 1851 | util-deprecate@1.0.2: {} 1852 | 1853 | watchpack@2.4.4: 1854 | dependencies: 1855 | glob-to-regexp: 0.4.1 1856 | graceful-fs: 4.2.11 1857 | 1858 | webpack-cli@6.0.1(webpack@5.102.0): 1859 | dependencies: 1860 | '@discoveryjs/json-ext': 0.6.3 1861 | '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.102.0) 1862 | '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.102.0) 1863 | '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.102.0) 1864 | colorette: 2.0.20 1865 | commander: 12.1.0 1866 | cross-spawn: 7.0.6 1867 | envinfo: 7.14.0 1868 | fastest-levenshtein: 1.0.16 1869 | import-local: 3.2.0 1870 | interpret: 3.1.1 1871 | rechoir: 0.8.0 1872 | webpack: 5.102.0(webpack-cli@6.0.1) 1873 | webpack-merge: 6.0.1 1874 | 1875 | webpack-merge@6.0.1: 1876 | dependencies: 1877 | clone-deep: 4.0.1 1878 | flat: 5.0.2 1879 | wildcard: 2.0.1 1880 | 1881 | webpack-sources@3.3.3: {} 1882 | 1883 | webpack@5.102.0(webpack-cli@6.0.1): 1884 | dependencies: 1885 | '@types/eslint-scope': 3.7.7 1886 | '@types/estree': 1.0.8 1887 | '@types/json-schema': 7.0.15 1888 | '@webassemblyjs/ast': 1.14.1 1889 | '@webassemblyjs/wasm-edit': 1.14.1 1890 | '@webassemblyjs/wasm-parser': 1.14.1 1891 | acorn: 8.15.0 1892 | acorn-import-phases: 1.0.4(acorn@8.15.0) 1893 | browserslist: 4.26.3 1894 | chrome-trace-event: 1.0.4 1895 | enhanced-resolve: 5.18.1 1896 | es-module-lexer: 1.6.0 1897 | eslint-scope: 5.1.1 1898 | events: 3.3.0 1899 | glob-to-regexp: 0.4.1 1900 | graceful-fs: 4.2.11 1901 | json-parse-even-better-errors: 2.3.1 1902 | loader-runner: 4.3.0 1903 | mime-types: 2.1.35 1904 | neo-async: 2.6.2 1905 | schema-utils: 4.3.2 1906 | tapable: 2.3.0 1907 | terser-webpack-plugin: 5.3.14(webpack@5.102.0) 1908 | watchpack: 2.4.4 1909 | webpack-sources: 3.3.3 1910 | optionalDependencies: 1911 | webpack-cli: 6.0.1(webpack@5.102.0) 1912 | transitivePeerDependencies: 1913 | - '@swc/core' 1914 | - esbuild 1915 | - uglify-js 1916 | 1917 | which@2.0.2: 1918 | dependencies: 1919 | isexe: 2.0.0 1920 | 1921 | wildcard@2.0.1: {} 1922 | 1923 | word-wrap@1.2.5: {} 1924 | 1925 | xml-js@1.6.11: 1926 | dependencies: 1927 | sax: 1.4.1 1928 | 1929 | yocto-queue@0.1.0: {} 1930 | --------------------------------------------------------------------------------