├── .editorconfig ├── .github └── workflows │ ├── build-test.yaml │ ├── codeql-analysis.yml │ └── markets-publish.yml ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── .yarnrc ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── eslint.config.mjs ├── language-configuration.yaml ├── media └── wikitext-icon.png ├── package.json ├── snippets └── snippets.yaml ├── src ├── export_command │ ├── cite_function │ │ └── web.ts │ ├── commandRegistrar.ts │ ├── uri_function │ │ ├── editPage.ts │ │ ├── uri.ts │ │ ├── viewPage.ts │ │ └── writeLine.ts │ ├── vscode_function │ │ └── wikiparser.ts │ └── wikimedia_function │ │ ├── args.ts │ │ ├── bot.ts │ │ ├── err_msg.ts │ │ ├── page.ts │ │ └── view.ts ├── extension-node.ts ├── extension-web.ts ├── interface_definition │ ├── IObjectConverter.ts │ ├── api_interface │ │ ├── archive.ts │ │ ├── commonInterface.ts │ │ ├── getView.ts │ │ ├── oldTokens.ts │ │ ├── readPage.ts │ │ ├── tags.ts │ │ └── tokens.ts │ ├── convertFunction.ts │ └── mwbot.d.ts └── test │ ├── runTest.ts │ └── suite │ ├── extension.test.ts │ ├── index-node.ts │ └── index-web.ts ├── syntaxes └── wikitext.tmLanguage.yaml ├── tsconfig.json ├── webpack.config.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | tab_width = 4 10 | end_of_line = crlf 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | [*.md] 16 | 17 | [*.{js,ts}] 18 | 19 | [*.{yaml,yml,json}] 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | # Licensed under the MIT License. See License.txt in the project root for license information. 4 | ################################################################################################ 5 | 6 | --- 7 | name: Build 8 | 9 | on: 10 | push: 11 | branches: [main, master] 12 | pull_request: 13 | branches: [main, master] 14 | # schedule: 15 | # - cron: "35 23 * * 5" 16 | 17 | jobs: 18 | build-and-test: 19 | name: Build and Test 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Node.js 10 has been deprecated since May 2020 26 | # Node.js 12 has been deprecated since Oct 2020 27 | # Node.js 14 has been deprecated since Oct 2021 28 | # Node.js 15 has been deprecated since Apr 2021 29 | # Node.js 16 has been deprecated since Oct 2023 30 | nodejs_version: [20, 22] 31 | 32 | steps: 33 | - name: Checkout repository 34 | uses: actions/checkout@v4 35 | 36 | - name: Install Node.js 37 | uses: actions/setup-node@v4 38 | with: 39 | node-version: ${{ matrix.nodejs_version }} 40 | cache: 'yarn' 41 | 42 | 43 | - name: Initialize 44 | shell: bash 45 | # export DISPLAY=':99.0' 46 | # usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & 47 | run: yarn install 48 | 49 | - name: Test for Linux 50 | if: runner.os == 'Linux' 51 | shell: bash 52 | run: xvfb-run -a yarn run test 53 | 54 | - name: Test for else 55 | if: runner.os != 'Linux' 56 | shell: bash 57 | run: yarn run test 58 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | # Licensed under the MIT License. See License.txt in the project root for license information. 4 | ################################################################################################ 5 | # For most projects, this workflow file will not need changing; you simply need 6 | # to commit it to your repository. 7 | # 8 | # You may wish to alter this file to override the set of languages analyzed, 9 | # or to provide custom queries or build logic. 10 | # 11 | # ******** NOTE ******** 12 | # We have attempted to detect the languages in your repository. Please check 13 | # the `language` matrix defined below to confirm you have the correct set of 14 | # supported CodeQL languages. 15 | 16 | --- 17 | name: "CodeQL" 18 | 19 | on: 20 | push: 21 | branches: [ master ] 22 | pull_request: 23 | # The branches below must be a subset of the branches above 24 | branches: [ master ] 25 | schedule: 26 | - cron: '35 23 * * 5' 27 | 28 | jobs: 29 | analyze: 30 | name: Analyze 31 | runs-on: ubuntu-latest 32 | permissions: 33 | actions: read 34 | contents: read 35 | security-events: write 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | language: [ 'javascript' ] 41 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 42 | # Learn more: 43 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 44 | 45 | steps: 46 | - name: Checkout repository 47 | uses: actions/checkout@v4 48 | 49 | # Initializes the CodeQL tools for scanning. 50 | - name: Initialize CodeQL 51 | uses: github/codeql-action/init@v3 52 | with: 53 | languages: ${{ matrix.language }} 54 | # If you wish to specify custom queries, you can do so here or in a config file. 55 | # By default, queries listed here will override any specified in a config file. 56 | # Prefix the list here with "+" to use these queries and those in the config file. 57 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 58 | 59 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 60 | # If this step fails, then you should remove it and run the build manually (see below) 61 | - name: Autobuild 62 | uses: github/codeql-action/autobuild@v3 63 | 64 | # ℹ️ Command-line programs to run using the OS shell. 65 | # 📚 https://git.io/JvXDl 66 | 67 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 68 | # and modify them (or add more) to build your code if your project 69 | # uses a compiled language 70 | 71 | #- run: | 72 | # make bootstrap 73 | # make release 74 | 75 | - name: Perform CodeQL Analysis 76 | uses: github/codeql-action/analyze@v3 77 | -------------------------------------------------------------------------------- /.github/workflows/markets-publish.yml: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | # Licensed under the MIT License. See License.txt in the project root for license information. 4 | ################################################################################################ 5 | 6 | --- 7 | name: "MarketsPublish" 8 | 9 | on: 10 | # Start action after created a release 11 | # release: 12 | # types: 13 | # - created 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | # This workflow contains a single job called "build" 20 | build: 21 | # The type of runner that the job will run on 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Checkout repository 25 | uses: actions/checkout@v4 26 | 27 | - name: Install Node.js 28 | uses: actions/setup-node@v4 29 | with: 30 | node-version: 20.x 31 | 32 | - name: Build 33 | run: | 34 | npm install -g yarn semver vsce ovsx 35 | yarn install 36 | 37 | - name: Test 38 | run: xvfb-run -a yarn test 39 | 40 | - name: Publish to VSCE 41 | if: ${{ success() }} 42 | run: vsce publish --yarn 43 | env: 44 | VSCE_PAT: ${{ secrets.VSCE_PAT }} 45 | - name: Publish to OVSX 46 | if: ${{ success() }} 47 | run: ovsx publish --yarn 48 | env: 49 | OVSX_PAT: ${{ secrets.OVSX_PAT }} 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled dictories 2 | out/ 3 | dist/ 4 | 5 | #Compiled JSON 6 | syntaxes/*.json 7 | snippets/*.json 8 | language-configuration.json 9 | 10 | # Dependencies 11 | node_modules/ 12 | .vscode-test/ 13 | .vscode-test-web/ 14 | 15 | # Packaged extensions 16 | **/*.vsix 17 | 18 | # Log file 19 | **/*.log 20 | 21 | .fake 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | { 6 | // See http://go.microsoft.com/fwlink/?LinkId=827846 7 | // for the documentation about the extensions.json format 8 | "recommendations": [ 9 | "dbaeumer.vscode-eslint", 10 | // "pedro-w.tmlanguage", 11 | "redhat.vscode-yaml", 12 | "gamunu.vscode-yarn", 13 | "editorconfig.editorconfig" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | // A launch configuration that launches the extension inside a new window 6 | // Use IntelliSense to learn about possible attributes. 7 | // Hover to view descriptions of existing attributes. 8 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 9 | { 10 | "version": "0.2.0", 11 | "configurations": [ 12 | { 13 | "name": "Launch Extension", 14 | "type": "extensionHost", 15 | "request": "launch", 16 | "runtimeExecutable": "${execPath}", 17 | "args": [ 18 | "--extensionDevelopmentPath=${workspaceFolder}" 19 | ], 20 | "outFiles": [ 21 | "${workspaceFolder}/dist/**/*.js" 22 | ], 23 | "preLaunchTask": "npm: pretest" 24 | }, 25 | { 26 | "name": "Launch Extension without Other Extensions", 27 | "type": "extensionHost", 28 | "request": "launch", 29 | "runtimeExecutable": "${execPath}", 30 | "args": [ 31 | "--disable-extensions", 32 | "--extensionDevelopmentPath=${workspaceFolder}" 33 | ], 34 | "outFiles": [ 35 | "${workspaceFolder}/dist/**/*.js" 36 | ], 37 | "preLaunchTask": "npm: pretest" 38 | }, 39 | { 40 | "name": "Launch Extension of The Last Building", 41 | "type": "extensionHost", 42 | "request": "launch", 43 | "runtimeExecutable": "${execPath}", 44 | "args": [ 45 | "--disable-extensions", 46 | "--extensionDevelopmentPath=${workspaceFolder}" 47 | ], 48 | "outFiles": [ 49 | "${workspaceFolder}/dist/**/*.js" 50 | ], 51 | }, 52 | { 53 | "name": "Extension Tests", 54 | "type": "extensionHost", 55 | "request": "launch", 56 | "runtimeExecutable": "${execPath}", 57 | "args": [ 58 | // "--disable-extensions", 59 | "--extensionDevelopmentPath=${workspaceFolder}", 60 | "--extensionTestsPath=${workspaceFolder}/dist/test/suite/index" 61 | ], 62 | "outFiles": [ 63 | "${workspaceFolder}/dist/test/**/*.js" 64 | ], 65 | "preLaunchTask": "npm: pretest" 66 | }, 67 | { 68 | "name": "Launch Web Extension", 69 | "type": "extensionHost", 70 | "debugWebWorkerHost": true, 71 | "request": "launch", 72 | "args": [ 73 | "--extensionDevelopmentPath=${workspaceFolder}", 74 | // "--disable-extensions", 75 | "--extensionDevelopmentKind=web" 76 | ], 77 | "outFiles": [ 78 | "${workspaceFolder}/dist/test/**/*.js" 79 | ], 80 | "preLaunchTask": "npm: pretest" 81 | }, 82 | { 83 | "name": "Launch Web Extension of The Last Building", 84 | "type": "extensionHost", 85 | "debugWebWorkerHost": true, 86 | "request": "launch", 87 | "args": [ 88 | "--extensionDevelopmentPath=${workspaceFolder}", 89 | "--disable-extensions", 90 | "--extensionDevelopmentKind=web" 91 | ], 92 | "outFiles": [ 93 | "${workspaceFolder}/dist/test/**/*.js" 94 | ] 95 | } 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | // Place your settings in this file to overwrite default and user settings. 6 | { 7 | "files.exclude": { 8 | "out": false, 9 | "dist": false, 10 | "**/.classpath": false, 11 | "**/.project": false, 12 | "**/.settings": false, 13 | "**/.factorypath": false 14 | }, 15 | "search.exclude": { 16 | "**/node_modules": true, 17 | "out": true, // set this to false to include "out" folder in search results 18 | "dist": true, 19 | ".vscode-test": true, 20 | ".vscode-test-web": true 21 | }, 22 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 23 | "typescript.tsc.autoDetect": "off", 24 | "typescript.tsdk": "./node_modules/typescript/lib", 25 | "cmake.configureOnOpen": false, 26 | "cSpell.words": [ 27 | "bibtex", 28 | "caltaojihun", 29 | "eqeqeq", 30 | "luxon", 31 | "mimetypes", 32 | "mwbot", 33 | "parsoid", 34 | "Scribunto", 35 | "syntaxhighlight", 36 | "uncast", 37 | "vsix", 38 | "wikiparser", 39 | "wikitable", 40 | "wikixml", 41 | "Xvfb" 42 | ], 43 | "files.associations": { 44 | "*.tmLanguage.json": "json-tmlanguage", 45 | "*.tmLanguage.yaml": "yaml-tmlanguage" 46 | }, 47 | "jshint.packageManager": "yarn", 48 | "eslint.packageManager": "yarn", 49 | } 50 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | // See https://go.microsoft.com/fwlink/?LinkId=733558 6 | // for the documentation about the tasks.json format 7 | { 8 | "version": "2.0.0", 9 | "tasks": [ 10 | { 11 | "type": "npm", 12 | "script": "watch", 13 | "label": "webpack: watch", 14 | "detail": "convert & webpack --watch", 15 | "isBackground": true, 16 | "presentation": { 17 | "echo": true, 18 | "reveal": "never", 19 | "focus": false, 20 | "panel": "shared", 21 | "showReuseMessage": true, 22 | "clear": false 23 | }, 24 | "group": { 25 | "kind": "build", 26 | "isDefault": true 27 | } 28 | }, 29 | { 30 | "type": "npm", 31 | "script": "package", 32 | "label": "vsce: package", 33 | "detail": "vsce package --yarn", 34 | "presentation": { 35 | "echo": true, 36 | "reveal": "always", 37 | "focus": false, 38 | "panel": "shared", 39 | "showReuseMessage": true, 40 | "clear": false 41 | }, 42 | "group": "build" 43 | }, 44 | { 45 | "type": "npm", 46 | "script": "test", 47 | "label": "vscode extension: test", 48 | "detail": "tsc & lint & run test", 49 | "presentation": { 50 | "echo": true, 51 | "reveal": "always", 52 | "focus": false, 53 | "panel": "shared", 54 | "showReuseMessage": true, 55 | "clear": false 56 | }, 57 | "group": { 58 | "kind": "test", 59 | "isDefault": true 60 | } 61 | }, 62 | { 63 | "type": "npm", 64 | "script": "lint", 65 | "label": "npm: eslint", 66 | "problemMatcher": "$eslint-stylish", 67 | "detail": "eslint" 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | # Workspace config 2 | .vs/ 3 | .vscode/ 4 | .github/ 5 | .git/ 6 | 7 | # Test 8 | .vscode-test/ 9 | .vscode-test-web/ 10 | **/test/ 11 | 12 | # Source code 13 | out/ 14 | src/ 15 | scripts/ 16 | 17 | # Modules 18 | node_modules/ 19 | 20 | # YAML files 21 | snippets/*.yaml 22 | syntaxes/*.yaml 23 | language-configuration.yaml 24 | 25 | # Compiled config 26 | **/tsconfig.json 27 | **/tslint.json 28 | **/.eslintrc.* 29 | webpack.config.js 30 | .yarnrc 31 | 32 | # Uncompiled files 33 | **/*.map 34 | **/*.ts 35 | 36 | # Git Ignore file 37 | .gitignore 38 | 39 | # Packaged extensions 40 | **/*.vsix 41 | 42 | # Log file 43 | **/*.log 44 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | --ignore-engines true 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Change Log 3 | 4 | All notable changes to the "Wikitext" extension will be documented in this file. 5 | 6 | (The version marked with an asterisk(\*) means that the version has been adjusted internally and has not been released.) 7 | 8 | ## [4.0.1] - 2025-01-02 9 | 10 | ### Fixed 11 | 12 | - Remove redundant blocking when LSP is not found. 13 | 14 | ## [4.0.0] - 2024-12-01 15 | 16 | ### Added 17 | 18 | - Add the support of [WikiParser Language Server](https://github.com/bhsd-harry/vscode-extension-wikiparser). 19 | 20 | ## [3.8.3] - 2024-11-14 21 | 22 | ### Changed 23 | 24 | - The syntax of the table has been improved. 25 | 26 | ## [3.8.2] - 2024-08-02 27 | 28 | ### Added 29 | 30 | - When getting the page preview, the API request will refer to `PageTitle` and `ContentModel` in the `PAGE_INFO` part. 31 | 32 | ## [3.8.1] - 2024-01-24 33 | 34 | ### Added 35 | 36 | - Added more syntaxes of wikitext tags. 37 | 38 | ### Changed 39 | 40 | - Removed '<>' pairs. 41 | - Made summary shorter. 42 | 43 | ### Fixed 44 | 45 | - Alleviated the issue of the extension posting edits without logging in. 46 | 47 | ## [3.8.0] - 2023-04-20 48 | 49 | ### Added 50 | 51 | - Added web extension support. 52 | - Added syntaxhighlight tag support. 53 | 54 | ### Changed 55 | 56 | - Revert removing incorrect bracket highlighting. 57 | - Improved partial syntax highlighting. 58 | 59 | ### Fixed 60 | 61 | - Fixed some syntax errors. 62 | 63 | ## [3.7.0] - 2022-11-07 64 | 65 | ### Added 66 | 67 | - Added apostrophe surrounding pairs. 68 | 69 | ### Changed 70 | 71 | - VSCode minimum version requirements are now changed to 1.64.0 and above. 72 | 73 | ### Fixed 74 | 75 | - Fixed extra line breaks when posting pages. 76 | - Temporarily fixed incorrect bracket highlighting issue. 77 | 78 | ## [3.6.5] - 2022-07-29 79 | 80 | ### Added 81 | 82 | - Added MediaWiki version checking in pullPage. 83 | - Added Scribunto to lua mapping. 84 | 85 | ### Changed 86 | 87 | - Enhanced table syntaxes. 88 | 89 | ## [3.6.4] - 2022-04-07 90 | 91 | ### Changed 92 | 93 | - Modified how some input boxes interacts. 94 | 95 | ### Fixed 96 | 97 | - Error with pushing changes when there is a number in the tags. 98 | - Some errors in Wikitext syntax. 99 | 100 | ## [3.6.3] - 2022-02-05 101 | 102 | ### Added 103 | 104 | - Now Wikitext Extension is available in Open VSX. 105 | - Added `sanitized-css` support. 106 | - Added more snippets. 107 | 108 | ### Changed 109 | 110 | - Adjusted the syntax priority. 111 | - Enhanced bracket matching. 112 | 113 | ### Fixed 114 | 115 | - Fixed some security issues. 116 | 117 | ## [3.6.2] - 2021-12-16 118 | 119 | ### Added 120 | 121 | - LaTeX syntax support for `` tags. 122 | 123 | ### Fixed 124 | 125 | - The syntax error that caused `` and `` to fail to close. 126 | 127 | ## [3.6.1] - 2021-11-27 128 | 129 | ### Added 130 | 131 | - When page is not exist, the extension will ask if still open document to create new page. 132 | - The post function will try to add `WikitextExtensionForVSCode` tag in the edit history. 133 | 134 | ### Fixed 135 | 136 | - Fixed the bug that user edit summary cannot be posted. 137 | 138 | ## [3.6.0] - 2021-11-02 139 | 140 | ### Added 141 | 142 | - Add a command to close editor without saving. 143 | - Add the option of auto login. 144 | - Add option to allow skip filling in page title. 145 | 146 | ### Changed 147 | 148 | - Change the base syntax from XML(XHTML) to HTML. 149 | 150 | ### Fixed 151 | 152 | - Remove ansi-regex dependency to potential security vulnerabilities. 153 | 154 | ## [3.5.0] - 2021-09-24 155 | 156 | ### Added 157 | 158 | - PageInfo support for the file of CSS, JavaScript, Lua, etc. 159 | - Added PageInfo syntax. 160 | 161 | ### Changed 162 | 163 | - Enhanced info message of pull page function. 164 | - Clearer error prompts. 165 | 166 | ### Fixed 167 | 168 | - Errors about syntax. 169 | - Some bugs of web cite method. 170 | 171 | ## [3.4.0] - 2021-07-30 172 | 173 | ### Added 174 | 175 | - The function of pulling pages through URI. 176 | - The function of displaying the action status through the status bar. 177 | 178 | ### Fixed 179 | 180 | - Some typos. 181 | 182 | ## [3.3.0] - 2021-05-30 183 | 184 | ### Added 185 | 186 | - Table caption syntax. 187 | - `` tag syntax. 188 | 189 | ### Changed 190 | 191 | - Enhanced URI view page function. 192 | - Some of textmate scopes. 193 | - Turn part of the analysis into a built-in language extension that depends on VSCode such as XML and JSON. 194 | - The extension will be activated when language is set to wikitext to speed up user commands. 195 | - Removed unnecessary assets. 196 | 197 | ### Fixed 198 | 199 | - Errors about XML syntax. 200 | 201 | ## [3.2.4] - 2021-03-18 202 | 203 | ### Added 204 | 205 | - More snippets and auto closing pairs. 206 | - Provided CSS adjustment support for Previewer. 207 | 208 | ## [3.2.3] - 2020-12-28 209 | 210 | ### Added 211 | 212 | - Optional for transfer protocol. 213 | - Support of pull function for old version mediawiki. 214 | 215 | ### Changed 216 | 217 | - Adjust the conversion period of some interfaces to runtime. Incorrect JSON format will be thrown error. 218 | 219 | ### Fixed 220 | 221 | - Some logic errors. 222 | 223 | ## [3.2.2] - 2020-12-12 224 | 225 | ### Added 226 | 227 | - The function of quick citation to references. 228 | 229 | ### Fixed 230 | 231 | - Some typos in the setting and command name. 232 | 233 | ## [3.2.1] - 2020-12-10 234 | 235 | ### Added 236 | 237 | - Preview icon in the title menu bar. 238 | 239 | ### Changed 240 | 241 | - Logic optimization and detailed problem correction. (Viewer and getHost) 242 | 243 | ### Fixed 244 | 245 | - PAGE_INFO part is output in preview because of a mistake. 246 | 247 | ## [3.2.0] - 2020-12-08 248 | 249 | ### Added 250 | 251 | - Categories view. 252 | 253 | ### Changed 254 | 255 | - Optimized the packaging structure. 256 | - URI can make extension activate now. 257 | 258 | ### Removed 259 | 260 | - The setHost command has been removed, you should use Settings instead. 261 | 262 | ### Fixed 263 | 264 | - CSS & image rendering. 265 | - Some syntaxes errors, such as: Template, Patterns,Bare ampersand, and Tag Stuff. 266 | 267 | ## [3.1.1] - 2020-10-07 268 | 269 | ### Added 270 | 271 | - Pre-added support for URI invocation, but no official functions have been added yet. 272 | 273 | ### Changed 274 | 275 | - Disabled edit section links in preview. 276 | - Removed some unusable code to improve performance. 277 | 278 | ### Fixed 279 | 280 | - With PST turned on, Substituted templates, signatures, etc. can now be rendered correctly. 281 | - Modified the operation logic of GetHost. 282 | - Some syntaxes errors, such as: Template, Patterns,Bare ampersand, and Tag Stuff. 283 | 284 | ## [3.1.0] - 2020-10-05 285 | 286 | ### Added 287 | 288 | - Redirect snippet. 289 | - More Magic Words supports. 290 | - ISBN/RFC/PMID supports. 291 | 292 | ### Changed 293 | 294 | - Modified some command names. 295 | - The View, Preview, and Pull functions will now first try to use the login account to execute. 296 | 297 | ### Fixed 298 | 299 | - Fixed the problem of incomplete logout. 300 | 301 | ## [3.0.0] - 2020-09-20 302 | 303 | ### Added 304 | 305 | - Added View Page feature. 306 | 307 | ### Changed 308 | 309 | - Improved some functions. 310 | 311 | ## [2.3.0]\* - 2020-08-09 312 | 313 | ### Added 314 | 315 | - Support for Redirect rendering. 316 | - Support for some other magic words. 317 | - The push and pull features for editing. Now you can modify the website content by logging in directly in VSCode. 318 | - API Path Setting. 319 | - Support for header parsing in Previewer. 320 | - Javascript toggle switch support for Previewer. 321 | 322 | ### Changed 323 | 324 | - All of Behaviour Switches will now act as `constant.language` instead of `keywords`. 325 | - Adjusted the program structure and optimized performance. 326 | - Javascript support for Previewer is disabled by default. 327 | 328 | ### Fixed 329 | 330 | - Corrected some programming logic errors. 331 | 332 | ## [2.2.1] - 2020-07-10 333 | 334 | ### Added 335 | 336 | - Added more snippets. 337 | 338 | ### Changed 339 | 340 | - Now, the setting of host have been migrated to the Settings page. 341 | 342 | ### Fixed 343 | 344 | - Fixed some typos and other details. 345 | - Fixed an issue where the dialog for changing host would not appear. 346 | 347 | ## [2.2.0] - 2020-05-19 348 | 349 | ### Added 350 | 351 | - Added the support of JSON for Graph and Templatedata tags. 352 | - Support preparation for CSS. 353 | 354 | ### Changed 355 | 356 | - Changed the rendering of word conversion. 357 | 358 | ### Fixed 359 | 360 | - Bold and italic closing conditions. 361 | 362 | ## [2.1.2]\* - 2020-05-13 363 | 364 | ### Fixed 365 | 366 | - Some typos. 367 | 368 | ## [2.1.1] - 2020-05-13 369 | 370 | ### Added 371 | 372 | - Nowiki tags support. 373 | 374 | ### Fixed 375 | 376 | - Some rendering issues. 377 | 378 | ## [2.1.0] - 2020-04-29 379 | 380 | ### Added 381 | 382 | - Added more snippets and bracket support. 383 | 384 | ### Changed 385 | 386 | - Changed the minimum version requirements. 387 | - Added rule to capture keyword.operator. 388 | - Improve syntax for internal link and template. 389 | 390 | ### Fixed 391 | 392 | - Fixed Some typos. 393 | - Fixed pattern for tag issues. 394 | - Fixed Some mistakes of Template. 395 | 396 | ## [2.0.1]\* - 2020-04-04 397 | 398 | ### Fixed 399 | 400 | - Some mistakes of Template and Internal Link rendering. 401 | 402 | ## [2.0.0] - 2020-03-30 403 | 404 | ### Added 405 | 406 | - Wikitext preview now supports host changes. 407 | - Added support for some code snippets. 408 | - Added several parenthesis auto-closing rules. 409 | 410 | ### Changed 411 | 412 | - Changed preview API from flow-parsoid-utils to parse. 413 | 414 | ### Security 415 | 416 | - Updated minimist to prevent a known security vulnerability. 417 | 418 | ### Fixed 419 | 420 | - Fixed some issues with brackets closing automatically. 421 | - Fixed some rendering issues in table syntax. 422 | 423 | ## [1.1.3]\* - 2020-03-14 424 | 425 | ### Added 426 | 427 | - Chinese Wikipedia Wikitext preview via flow-parsoid-utils feature has been added. 428 | 429 | ## [1.1.2]\* - 2020-02-23 430 | 431 | ### Added 432 | 433 | - Node.js & WebPack environments. 434 | - `Hello World` test command. 435 | 436 | ### Fixed 437 | 438 | - Some mistakes of Template arguments rendering. 439 | 440 | ## [1.1.1] - 2020-02-22 441 | 442 | ### Added 443 | 444 | - Added support for Signature. 445 | 446 | ### Fixed 447 | 448 | - Fixed an issue where the Template name rendering function was removed. 449 | - Space characters are no longer needed between Magic Words. 450 | 451 | ## [1.1.0] - 2020-02-18 452 | 453 | ### Added 454 | 455 | - Added the extension icon. 456 | - Added more description of this extension. 457 | 458 | ### Fixed 459 | 460 | - Addressed some issue with template and internal link rendering. 461 | 462 | ## [1.0.0] - 2020-02-06 463 | 464 | ### Added 465 | 466 | - Initial Release. 467 | - Published to GitHub. 468 | - Added new extended name support. 469 | 470 | ## [0.6.1]\* - 2020-02-02 471 | 472 | ### Fixed 473 | 474 | - Fixed some mistakes about license. 475 | 476 | ## [0.6.0]RC - 2020-02-02 477 | 478 | - Candidate Release. 479 | 480 | ### Added 481 | 482 | - Added some useful auto-closing content. 483 | 484 | ### Changed 485 | 486 | - Open source under the MIT license. 487 | 488 | ## [0.5.0]α - 2020-02-02 489 | 490 | - Alpha Release. 491 | 492 | ### Added 493 | 494 | - Special support for chinese characters. 495 | 496 | ## [0.4.0]\* - 2020-02-02 497 | 498 | ### Added 499 | 500 | - Wrote the __README__ File. 501 | - Added Table support. 502 | - Color rendering support for template content. 503 | 504 | ### Fixed 505 | 506 | - Fixed some errors of Internal Link. 507 | 508 | ### Changed 509 | 510 | - Adjusted rendering order. 511 | 512 | ## [0.3.0]\* - 2020-02-01 513 | 514 | ### Added 515 | 516 | - Add External Link support. 517 | 518 | ### Fixed 519 | 520 | - Fixed some errors of Internal Link. 521 | 522 | ## [0.2.0]\* - 2020-01-31 523 | 524 | ### Added 525 | 526 | - Wikitext Internal Link now supports with richer color markers. 527 | - List highlight. 528 | - Support for bold and italics. 529 | 530 | ### Changed 531 | 532 | - The heading no longer closes automatically, and there must be at least one content character in the heading. 533 | 534 | ### Fixed 535 | 536 | - In XHTML block comments, \\--\[^>\]\\ no longer reports an error. 537 | - Fixed some rendering syntax errors. 538 | 539 | ## [0.1.1]\* - 2020-01-30 540 | 541 | ### Added 542 | 543 | - Added Wikitext Template support. 544 | - Added Wikitext Internal Link support. 545 | 546 | ### Changed 547 | 548 | - Revised parsing of comments. 549 | 550 | ## [0.1.0]\* - 2020-01-29 551 | 552 | ### Added 553 | 554 | - Added support for XHTML. 555 | - Updated __CHANGELOG__ file. After that, the file will be continuously updated. 556 | 557 | ## [0.0.1]\* - 2020-01-28 558 | 559 | ### Added 560 | 561 | - Built for Visual Studio Code ^1.41.0. 562 | - Establish basic language support. 563 | 564 | 572 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2022 Rowe Wilson Frederisk Holme 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Wikitext Markup Language Support Extension for Visual Studio Code 3 | 4 | [![VSMarket: wikitext extension](https://vsmarketplacebadges.dev/version-short/rowewilsonfrederiskholme.wikitext.webp?color=blueviolet&logo=visual-studio-code&style=?style=for-the-badge)](https://marketplace.visualstudio.com/items?itemName=RoweWilsonFrederiskHolme.wikitext) 5 | [![Open VSX: wikitext extension](https://img.shields.io/open-vsx/v/RoweWilsonFrederiskHolme/Wikitext?color=purple&label=Open%20VSX)](https://open-vsx.org/extension/RoweWilsonFrederiskHolme/wikitext) 6 | [![GitHub: wikitext extension](https://img.shields.io/badge/GitHub-wikitext-lightgray)](https://github.com/Frederisk/Wikitext-VSCode-Extension) 7 | [![Toolhub: wikitext extension](https://img.shields.io/badge/Toolhub-wikitext_extension-36C)](https://toolhub.wikimedia.org/tools/wikitext-vscode-extension) 8 | 9 | [![Build](https://github.com/Frederisk/Wikitext-VSCode-Extension/actions/workflows/build-test.yaml/badge.svg)](https://github.com/Frederisk/Wikitext-VSCode-Extension/actions/workflows/build-test.yaml) 10 | [![GitHub Actions CodeQL](https://github.com/Frederisk/Wikitext-VSCode-Extension/actions/workflows/codeql-analysis.yml/badge.svg/)](https://github.com/Frederisk/Wikitext-VSCode-Extension/actions?query=workflow%3ACodeQL) 11 | [![CodeFactor Status](https://www.codefactor.io/repository/github/frederisk/wikitext-vscode-extension/badge)](https://www.codefactor.io/repository/github/frederisk/wikitext-vscode-extension) 12 | 13 | [![@mastodon.social@rwfholme](https://img.shields.io/badge/mastodon.social-%40rwfholme-66F)](https://mastodon.social/@rwfholme) 14 | [![Patreon Donate](https://img.shields.io/badge/donate-patreon-orange)](https://www.patreon.com/rwfholme) 15 | 16 | This Visual Studio Code Extension provides support of Wikitext Markup language. With this extension, you can more easily discover your grammatical problems through the marked and styled text. The plugin is based on MediaWiki's Wikitext standard, but the rules are somewhat stricter, which helps users write text that is easier to read and maintain. 17 | 18 | Of course, the development of this extension is short, and there may be some errors in operation and labeling. If you find a problem, please report it to me immediately for resolution. 19 | 20 | If you get help with this project, give this project a star or recommend it to others, thanks!😸 21 | 22 | ## Enhancement Tools 23 | 24 | ### WikiParser Language Server 25 | 26 | - [WikiParser Language Server](https://github.com/bhsd-harry/vscode-extension-wikiparser) is a powerful tool that brings you better code folding, syntax error prompts, file outline and other features! 27 | 28 | #### WikiParser Usage 29 | 30 | - Install WikiParser Language Server through the extensions store (such as [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=Bhsd.vscode-extension-wikiparser)) or [download the vsix file yourself](https://github.com/bhsd-harry/vscode-extension-wikiparser). 31 | - Find `wikitext.wikiparser.enable` in settings and enable it. 32 | - Press `F1` and execute the command `wikitext.restartLsp` or restart VSCode to apply changes. 33 | 34 | #### WikiParser Previews 35 | 36 | - 37 | - 38 | 39 | ### Wikitext Extension Gadget 40 | 41 | - Now you can add [Wikitext Extension Gadget](https://github.com/Frederisk/Wikitext-Extension-Gadget) as a user gadget on your wiki site. And you will get a button to open VSCode directly in your browser to edit the page! 42 | 43 | Go to the gadget's repository page to learn more. 44 | 45 | Wikitext Extension Gadget 46 | 47 | ## Features 48 | 49 | - Color and style annotations of Wikitext can make it easier for users to intuitively find problems in writing grammatical formats. 50 | 51 | Code main page 52 | 53 | - Automatic matching and closing of simple parentheses reduces unnecessary double typing. 54 | 55 | Code video 56 | 57 | - The special comment syntax\(``&``\) can folds the code for easy reading. 58 | 59 | Code region 60 | 61 | - Press `Ctrl + Shift + V` or click the Preview icon in the title menu bar directly in the Wikitext content to get a parsed preview of Wikitext in the currently active text editor. 62 | 63 | Code previewer 64 | 65 | - Enter `@[name]` to get a snippet of wikitext. Such as `@table`, `@region`, `@title`... 66 | 67 | Code snippets 68 | 69 | - You can modify the website content by logging in directly in VSCode! Enter the settings page, search Wikitext and find the username and password items, and fill in them. Then press `F1` in the text editor, select `Post your page to the website`! 70 | 71 | - Obtain the Wikitext source code directly in VSCode according to the page name without opening the web page. Press `F1` and select `Pull page to edit` to use this function. 72 | 73 | - Browse the page by entering the page name. Press `F1` then select `View the page`. 74 | 75 | ## Release Notes 76 | 77 | - Add the support of [WikiParser Language Server](https://github.com/bhsd-harry/vscode-extension-wikiparser). 78 | - Remove redundant blocking when LSP is not found. 79 | 80 | ## Usage 81 | 82 | ### Usage Requirements 83 | 84 | Please ensure that your VSCode version is higher than 1.64.0, this version requirements may change in the future. 85 | 86 | Generally, make sure that your VSCode is always the latest version. 87 | 88 | ## Development 89 | 90 | ### Development Requirements 91 | 92 | - [Node.js](https://nodejs.org) (with npm) at least v16. 93 | 94 | ### How to build this extension yourself 95 | 96 | Firstly, clone this repository and change directory to the repository, then install VS Code Extension Manager and other packages: 97 | 98 | ```bash 99 | npm install -g yarn # install yarn classic 100 | yarn install # Install all dependencies 101 | ``` 102 | 103 | Package this project and you will get a `.vsix` file: 104 | 105 | ```bash 106 | yarn run package 107 | ``` 108 | 109 | This is it! 110 | 111 | 117 | 119 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin"; 2 | import tsParser from "@typescript-eslint/parser"; 3 | import path from "node:path"; 4 | import { fileURLToPath } from "node:url"; 5 | import js from "@eslint/js"; 6 | import { FlatCompat } from "@eslint/eslintrc"; 7 | 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = path.dirname(__filename); 10 | const compat = new FlatCompat({ 11 | baseDirectory: __dirname, 12 | recommendedConfig: js.configs.recommended, 13 | allConfig: js.configs.all 14 | }); 15 | 16 | export default [ 17 | // ...compat.extends("eslint:recommended", "plugin:@typescript-eslint/recommended"), 18 | { 19 | files: ['**/*.ts'], 20 | ignores: ["**/out", "**/dist", "**/*.d.ts", "**/webpack.config.js", '**/eslint.config.mjs'], 21 | plugins: { 22 | "@typescript-eslint": typescriptEslint, 23 | }, 24 | languageOptions: { 25 | parser: tsParser, 26 | ecmaVersion: 5, 27 | sourceType: "module" 28 | }, 29 | rules: { 30 | "@typescript-eslint/naming-convention": "warn", 31 | semi: "warn", 32 | curly: "warn", 33 | eqeqeq: "warn", 34 | "no-throw-literal": "warn", 35 | "@typescript-eslint/no-explicit-any": "warn", 36 | }, 37 | 38 | }, 39 | ]; 40 | -------------------------------------------------------------------------------- /language-configuration.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | # Licensed under the MIT License. See License.txt in the project root for license information. 4 | ################################################################################################ 5 | 6 | --- 7 | comments: 8 | # symbol used for single line comment. Remove this entry if your language does not support line comments 9 | # lineComment: "//" 10 | # symbols used for start and end a block comment. Remove this entry if your language does not support block comments 11 | blockComment: [''] 12 | # symbols used as brackets 13 | brackets: 14 | - ['[', ']'] 15 | - ['-{', '}-'] 16 | - ['{|', '|}'] 17 | - ['{{', '}}'] 18 | - ['{{{', '}}}'] 19 | # symbols that are auto closed when typing 20 | autoClosingPairs: 21 | - ['{', '}'] 22 | - ['[', ']'] 23 | - ['-{', '}-'] 24 | - [''] 25 | # more tags: https://www.mediawiki.org/wiki/Parser_extension_tags 26 | - [, ] 27 | - [
,
] 28 | - [, ] 29 | - [, ] 30 | - [, ] 31 | - [
,
] 32 | - [, ] 33 | - [, ] 34 | - [, ] 35 | - [, ] 36 | - [, ] 37 | - [, ] 38 | - [, ] 39 | - [, ] 40 | - [
, 
] 41 | - [, ] 42 | - [, ] 43 | - [, ] 44 | - [, ] 45 | - [, ] 46 | - [, ] 47 | # symbols that that can be used to surround a selection 48 | surroundingPairs: 49 | - ['{', '}'] 50 | - ['[', ']'] 51 | - ['(', ')'] 52 | - ["'", "'"] 53 | 54 | folding: 55 | markers: 56 | start: ^\s* 57 | end: ^\s* 58 | -------------------------------------------------------------------------------- /media/wikitext-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Frederisk/Wikitext-VSCode-Extension/dd080d00e2e48f1d13f950dd089bb591a7942da1/media/wikitext-icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wikitext", 3 | "displayName": "Wikitext", 4 | "description": "Wikitext is a document written in a wiki markup language. This extension provides functional support for the Wikitext language with MediaWiki such as Wikipedia.", 5 | "version": "4.0.1", 6 | "publisher": "RoweWilsonFrederiskHolme", 7 | "license": "SEE LICENSE IN LICENSE.txt", 8 | "author": { 9 | "name": "Rowe Wilson Frederisk Holme", 10 | "email": "frederisk@outlook.com", 11 | "url": "https://twitter.com/rwfholme" 12 | }, 13 | "homepage": "https://github.com/Frederisk/Wikitext-VSCode-Extension/blob/master/README.md", 14 | "icon": "media/wikitext-icon.png", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/Frederisk/Wikitext-VSCode-Extension.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/Frederisk/Wikitext-VSCode-Extension/issues", 21 | "email": "frederisk@outlook.com" 22 | }, 23 | "qna": "https://github.com/Frederisk/Wikitext-VSCode-Extension/discussions/categories/q-a", 24 | "engines": { 25 | "vscode": "^1.64.0" 26 | }, 27 | "categories": [ 28 | "Programming Languages", 29 | "Snippets", 30 | "Visualization", 31 | "Other" 32 | ], 33 | "activationEvents": [ 34 | "onUri" 35 | ], 36 | "main": "./dist/extension-node", 37 | "browser": "./dist/extension-web", 38 | "contributes": { 39 | "commands": [ 40 | { 41 | "command": "wikitext.getPreview", 42 | "title": "Get preview", 43 | "category": "Wikitext", 44 | "icon": "$(open-preview)" 45 | }, 46 | { 47 | "command": "wikitext.login", 48 | "title": "Login with your account", 49 | "category": "Wikitext" 50 | }, 51 | { 52 | "command": "wikitext.logout", 53 | "title": "Logout, if you are already logged in", 54 | "category": "Wikitext" 55 | }, 56 | { 57 | "command": "wikitext.readPage", 58 | "title": "Pull page to edit", 59 | "category": "Wikitext" 60 | }, 61 | { 62 | "command": "wikitext.writePage", 63 | "title": "Post your edit to the website", 64 | "category": "Wikitext" 65 | }, 66 | { 67 | "command": "wikitext.viewPage", 68 | "title": "View the page", 69 | "category": "Wikitext" 70 | }, 71 | { 72 | "command": "wikitext.citeWeb", 73 | "title": "Add a web citation", 74 | "category": "Wikitext" 75 | }, 76 | { 77 | "command": "wikitext.closeEditor", 78 | "title": "Close this editor without saving", 79 | "category": "Wikitext" 80 | }, 81 | { 82 | "command": "wikitext.restartLsp", 83 | "title": "Restart WikiParser LSP", 84 | "category": "Wikitext" 85 | } 86 | ], 87 | "menus": { 88 | "editor/title": [ 89 | { 90 | "command": "wikitext.getPreview", 91 | "when": "editorLangId == wikitext", 92 | "group": "navigation" 93 | } 94 | ], 95 | "commandPalette": [ 96 | { 97 | "command": "wikitext.getPreview", 98 | "when": "editorLangId == wikitext", 99 | "group": "navigation" 100 | }, 101 | { 102 | "command": "wikitext.login", 103 | "group": "navigation" 104 | }, 105 | { 106 | "command": "wikitext.logout", 107 | "group": "navigation" 108 | }, 109 | { 110 | "command": "wikitext.readPage", 111 | "group": "navigation" 112 | }, 113 | { 114 | "command": "wikitext.writePage", 115 | "group": "navigation" 116 | }, 117 | { 118 | "command": "wikitext.viewPage", 119 | "group": "navigation" 120 | }, 121 | { 122 | "command": "wikitext.citeWeb", 123 | "group": "navigation", 124 | "when": "editorLangId == wikitext" 125 | }, 126 | { 127 | "command": "wikitext.closeEditor", 128 | "group": "navigation", 129 | "when": "editorLangId == wikitext" 130 | } 131 | ] 132 | }, 133 | "keybindings": [ 134 | { 135 | "command": "wikitext.getPreview", 136 | "key": "shift+ctrl+v", 137 | "mac": "shift+cmd+v", 138 | "when": "editorLangId == wikitext" 139 | } 140 | ], 141 | "languages": [ 142 | { 143 | "id": "wikitext", 144 | "extensions": [ 145 | ".wikitext", 146 | ".wt", 147 | ".mediawiki", 148 | ".mw", 149 | ".wiki" 150 | ], 151 | "aliases": [ 152 | "Wikitext", 153 | "WikiText" 154 | ], 155 | "mimetypes": [ 156 | "text/x-wiki" 157 | ], 158 | "configuration": "./language-configuration.json" 159 | } 160 | ], 161 | "grammars": [ 162 | { 163 | "language": "wikitext", 164 | "scopeName": "source.wikitext", 165 | "path": "./syntaxes/wikitext.tmLanguage.json", 166 | "embeddedLanguages": { 167 | "source.json": "json", 168 | "text.html.basic": "html", 169 | "text.tex.latex": "latex" 170 | } 171 | } 172 | ], 173 | "snippets": [ 174 | { 175 | "language": "wikitext", 176 | "path": "./snippets/snippets.json" 177 | } 178 | ], 179 | "configuration": { 180 | "type": "object", 181 | "title": "Wikitext", 182 | "properties": { 183 | "wikitext.host": { 184 | "type": "string", 185 | "description": "The host that provides the API, such as 'en.wikipedia.org'.", 186 | "default": "en.wikipedia.org" 187 | }, 188 | "wikitext.userName": { 189 | "type": "string", 190 | "description": "The user name of wikimedia site." 191 | }, 192 | "wikitext.password": { 193 | "type": "string", 194 | "description": "The password of user. It will be fixed in the software settings, please do not fill in the device in public places." 195 | }, 196 | "wikitext.redirects": { 197 | "type": "boolean", 198 | "description": "When the input page is a redirect page, jump automatically. Note that posting command will **not** be redirected.", 199 | "default": true 200 | }, 201 | "wikitext.apiPath": { 202 | "type": "string", 203 | "markdownDescription": "Set the basic path to obtain the API. If you are not sure, please check the `Special:Version` page of your site.", 204 | "default": "/w/api.php" 205 | }, 206 | "wikitext.getCss": { 207 | "type": "boolean", 208 | "description": "Get CSS when previewing the page. Please note that this feature will greatly increase the extended workload, and some rendering effects may conflict with VSCode. To get the correct display, you may need to enable Previewer's JS support.", 209 | "default": false 210 | }, 211 | "wikitext.enableJavascript": { 212 | "type": "boolean", 213 | "description": "Enable Previewer's JS support. This feature may affect the performance of VSCode and introduce potential security risks.", 214 | "default": false 215 | }, 216 | "wikitext.articlePath": { 217 | "type": "string", 218 | "markdownDescription": "Set the basic path to obtain the article. please check the `Special:Version` page of your site (No need for `$1`).", 219 | "default": "/wiki/" 220 | }, 221 | "wikitext.webCiteFormat": { 222 | "type": "string", 223 | "markdownDescription": "Setting the generation of web reference tags. The configuration of this option is similar to the code section of MakeRef in meta wikimedia. The available fields: `url`, `title`, `accessdate`, `website`, `archiveurl` ,and `archivedate`.\n\n**This function is only a preview function, it may change or become a separate new extension in the future. Please pay attention to the changelog of this function every time you update this extension.**", 224 | "default": "{{cite web |url={$url} |title={$title} |access-date={$accessdate} |website={$website} |publication-date={$publicationdate} |archive-url={$archiveurl} |archive-date={$archivedate}}}" 225 | }, 226 | "wikitext.transferProtocol": { 227 | "type": "string", 228 | "default": "https://", 229 | "enum": [ 230 | "http://", 231 | "https://" 232 | ], 233 | "enumDescriptions": [ 234 | "HyperText Transfer Protocol", 235 | "HyperText Transfer Protocol Secure" 236 | ], 237 | "description": "Select the transfer protocol used when accessing the API." 238 | }, 239 | "wikitext.previewCssStyle": { 240 | "type": "string", 241 | "markdownDescription": "CSS attached to Previewer. This content always works, regardless of whether [GetCss](#wikitext.getCss) is enabled or not. To enable CSS for a specific category of themes, you can use the class filter name `vscode-light`, `vscode-dark`, `vscode-high-contrast` etc. See [Extension guides](https://code.visualstudio.com/api/extension-guides/webview#theming-webview-content) to learn more.", 242 | "default": ".vscode-dark img[class^=\"mwe-math-fallback-image\"]{filter: invert(85%);} .vscode-light {} .vscode-high-contrast img[class^=\"mwe-math-fallback-image\"]{background-color: white; filter: invert(100%);}" 243 | }, 244 | "wikitext.autoLogin": { 245 | "type": "string", 246 | "default": "Ask me", 247 | "enum": [ 248 | "Always", 249 | "Never", 250 | "Ask me" 251 | ], 252 | "enumDescriptions": [ 253 | "Always log in automatically when I am not logged in", 254 | "Never log in automatically", 255 | "Ask me if I want to log in when I am not logged in" 256 | ], 257 | "description": "Whether to automatically log in when performing an action that requires an account to be logged in." 258 | }, 259 | "wikitext.skipEnteringPageTitle": { 260 | "type": "boolean", 261 | "default": false, 262 | "markdownDescription": "If `PageTitle` is filled in `PAGE_INFO`, skip entering the title when posting." 263 | }, 264 | "wikitext.wikiparser.enable": { 265 | "type": "boolean", 266 | "default": false, 267 | "description": "Enable WikiParser, a LSP for Wikitext to provide better writing assistance. Restart WikiParser by command to apply changes." 268 | }, 269 | "wikitext.wikiparser.syncArticlePath": { 270 | "type": "boolean", 271 | "default": true, 272 | "markdownDescription": "Force update WikiParser's [Article Path](#wikiparser.articlePath) every time WikiParser Server is started." 273 | } 274 | } 275 | } 276 | }, 277 | "scripts": { 278 | "vscode:prepublish": "yarn run compile", 279 | "compile": "yarn run convert && webpack --config webpack.config.js --mode production", 280 | "compile-debug": "yarn run convert && webpack --config webpack.config.js --mode development", 281 | "lint": "eslint -c ./eslint.config.mjs ./src", 282 | "watch": "yarn run convert && webpack --config webpack.config.js --mode development --watch", 283 | "pretest": "yarn run compile-debug && yarn run lint", 284 | "test": "yarn run test-node && yarn run test-web", 285 | "test-node": "node ./dist/test/runTest.js", 286 | "test-web": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=./dist/test/suite/index-web.js", 287 | "convert": "js-yaml snippets/snippets.yaml > snippets/snippets.json && js-yaml syntaxes/wikitext.tmLanguage.yaml > syntaxes/wikitext.tmLanguage.json && js-yaml language-configuration.yaml > language-configuration.json", 288 | "package": "vsce package --yarn" 289 | }, 290 | "devDependencies": { 291 | "@types/cheerio": "^0.22.28", 292 | "@types/glob": "^7.2.0", 293 | "@types/luxon": "^2.0.7", 294 | "@types/mocha": "^10.0.0", 295 | "@types/node": "^22.0.0", 296 | "@types/node-fetch": "^2.5.12", 297 | "@types/vscode": "^1.64.0", 298 | "@types/webpack-env": "^1.18.0", 299 | "@typescript-eslint/eslint-plugin": "^8.0.0", 300 | "@typescript-eslint/parser": "^8.0.0", 301 | "@vscode/test-electron": "^2.2.0", 302 | "@vscode/test-web": "^0.0.64", 303 | "@vscode/vsce": "^3.2.1", 304 | "assert": "^2.0.0", 305 | "async-hook-browser": "^0.0.6", 306 | "browser-request": "^0.3.3", 307 | "browserify-fs": "^1.0.0", 308 | "browserify-zlib": "^0.2.0", 309 | "buffer": "^6.0.3", 310 | "circular-dependency-plugin": "^5.2.2", 311 | "crypto-browserify": "^3.12.0", 312 | "eslint": "^9.8.0", 313 | "glob": "^7.2.0", 314 | "https-browserify": "^1.0.0", 315 | "js-yaml": "^4.1.0", 316 | "mocha": "^10.1.3", 317 | "mwbot": "^2.0.0", 318 | "net": "^1.0.2", 319 | "os-browserify": "^0.3.0", 320 | "path-browserify": "^1.0.1", 321 | "process": "^0.11.10", 322 | "querystring-es3": "^0.2.1", 323 | "stream-browserify": "^3.0.0", 324 | "stream-http": "^3.2.0", 325 | "tls-browserify": "^0.2.2", 326 | "ts-loader": "^9.2.3", 327 | "tslib": "^2.6.3", 328 | "typescript": "^5.6.0", 329 | "url": "^0.11.0", 330 | "vm-browserify": "^1.1.2", 331 | "vscode-languageclient": "^9.0.1", 332 | "webpack": "^5.94.0", 333 | "webpack-cli": "^5.1.0" 334 | }, 335 | "dependencies": { 336 | "cheerio": "^1.0.0-rc.9", 337 | "luxon": "^2.5.2", 338 | "node-fetch": "^2.6.5" 339 | }, 340 | "resolutions": { 341 | "eslint/**/strip-ansi": "6.0.1" 342 | } 343 | } 344 | -------------------------------------------------------------------------------- /snippets/snippets.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | # Licensed under the MIT License. See License.txt in the project root for license information. 4 | ################################################################################################ 5 | 6 | --- 7 | region: 8 | prefix: '@region' 9 | body: | 10 | 11 | ${2:} 12 | 13 | description: Region 14 | scope: markup.list.unnumbered.wikitext 15 | redirect: 16 | prefix: '@redirect' 17 | body: '#REDIRECT [[${1:PageName}]]' 18 | description: Redirection 19 | scope: '' 20 | table: 21 | prefix: '@table' 22 | body: | 23 | {| ${1:class="wikitable"} 24 | |+Title 25 | ! 26 | ! 27 | ! 28 | ! 29 | |- 30 | | 31 | | 32 | | 33 | | 34 | |- 35 | | 36 | | 37 | | 38 | | 39 | |- 40 | | 41 | | 42 | | 43 | | 44 | |} 45 | description: Table 46 | codeBlock: 47 | prefix: '@codeblock' 48 | body: | 49 | 50 | 51 | 52 | description: Code Block 53 | scope: '' 54 | comments: 55 | prefix: '@comments' 56 | body: '' 57 | description: Comments 58 | todo: 59 | prefix: '@todo' 60 | body: '' 61 | description: Todo Task 62 | title: 63 | prefix: '@title' 64 | body: "== ${1:title} ==\n" 65 | description: Title<2> 66 | subtitle1: 67 | prefix: '@subt1' 68 | body: "=== ${1:title} ===\n" 69 | description: Subtitle1<3> 70 | subtitle2: 71 | prefix: '@subt2' 72 | body: "==== ${1:title} ====\n" 73 | description: Subtitle2<4> 74 | subtitle3: 75 | prefix: '@subt3' 76 | body: "===== ${1:title} =====\n" 77 | description: Subtitle3<5> 78 | subtitle4: 79 | prefix: '@subt4' 80 | body: "====== ${1:title} ======\n" 81 | description: Subtitle4<6> 82 | pageTitle: 83 | prefix: '@pagetitle' 84 | body: "= ${1:title} =\n" 85 | description: PageTitle<1> 86 | pageInfo: 87 | prefix: '@pageinfo' 88 | body: | 89 | <%-- [PAGE_INFO] 90 | Comment=#${1:}# 91 | PageTitle=#${2:}# 92 | PageID=#${3:}# 93 | RevisionID=#${4:}# 94 | ContentModel=#${5:wikitext}# 95 | ContentFormat=#${6:text/x-wiki}# 96 | [END_PAGE_INFO] --%> 97 | description: Page Info Struct 98 | refName: 99 | prefix: '@refname' 100 | body: 101 | description: Reference tag with name 102 | refDefine: 103 | prefix: '@refdefine' 104 | body: ${2:definition} 105 | description: Reference tag with name and definition 106 | -------------------------------------------------------------------------------- /src/export_command/cite_function/web.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | // Part of the content comes from https://github.com/jasonwilliams/mediawiki-support/blob/master/src/webCitation.ts under license Apache-2.0 6 | 7 | import * as vscode from "vscode"; 8 | import * as cheerio from "cheerio"; 9 | import fetch from "node-fetch"; 10 | import type { Response } from "node-fetch"; 11 | import { DateTime } from "luxon"; 12 | import { ArchiveConvert, ArchiveResult } from "../../interface_definition/api_interface/archive"; 13 | 14 | export function addWebCiteFactory(){ 15 | return async function addWebCite(): Promise { 16 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 17 | 18 | const url: string | undefined = await vscode.window.showInputBox({ 19 | prompt: "input the URL that you want to ref.", 20 | placeHolder: "https://sample.com", 21 | ignoreFocusOut: true 22 | }); 23 | if (!url) { return undefined; } 24 | 25 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Parsing..."); 26 | try { 27 | const citeInfo: WebCiteInfo = new WebCiteInfo(url); 28 | await citeInfo.buildInfo(); 29 | const result: string = citeInfo.toString(config.get("webCiteFormat") ?? ""); 30 | 31 | const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; 32 | const selection: vscode.Selection | undefined = editor?.selection; 33 | 34 | if (selection) { 35 | editor?.edit((editorBuilder: vscode.TextEditorEdit): void => { 36 | editorBuilder.insert(selection.active, result); 37 | }); 38 | } 39 | } 40 | catch (error) { 41 | if (error instanceof Error) { 42 | vscode.window.showErrorMessage(`ErrorName: ${error.name}; ErrorMessage: ${error.message}.`); 43 | } else { 44 | vscode.window.showErrorMessage(`addWebCite ERROR: ${JSON.stringify(error)}.`); 45 | } 46 | } 47 | finally { 48 | barMessage.dispose(); 49 | } 50 | }; 51 | } 52 | 53 | class WebCiteInfo { 54 | 55 | url: string; 56 | title?: string; 57 | accessDate: string; 58 | siteName?: string; 59 | publishDate?: string; 60 | archivedUrl?: string; 61 | archivedDate?: string; 62 | private archiveApiUrl: string; 63 | private metaData!: cheerio.Root; 64 | 65 | constructor(url: string) { 66 | this.url = url; 67 | this.accessDate = DateTime.utc().toISODate(); 68 | this.archiveApiUrl = `https://archive.org/wayback/available?url=${url}`; 69 | } 70 | 71 | public toString(format: string): string { 72 | format = getReplacedString(format, "url", this.url); 73 | format = getReplacedString(format, "title", this.title); 74 | format = getReplacedString(format, "accessdate", this.accessDate); 75 | format = getReplacedString(format, "website", this.siteName); 76 | format = getReplacedString(format, "publicationdate", this.publishDate); 77 | format = getReplacedString(format, "archiveurl", this.archivedUrl); 78 | format = getReplacedString(format, "archivedate", this.archivedDate); 79 | return format.replace(/[\n\r]/g, ' '); 80 | } 81 | 82 | public async buildInfo(): Promise { 83 | await this.fetchArchive(); 84 | this.setTitle(); 85 | this.setPublishedDate(); 86 | this.setSiteName(); 87 | } 88 | 89 | private async fetchArchive(): Promise { 90 | // Fetch content and archive in parallel 91 | const websiteResponse: Promise = fetch(this.url); 92 | const archiveResponse: Promise = fetch(this.archiveApiUrl); 93 | const results: [Response, Response] = await Promise.all([websiteResponse, archiveResponse]); 94 | 95 | const websiteText: string = await results[0].text(); 96 | this.metaData = cheerio.load(websiteText); 97 | 98 | const archiveJSON = await results[1].json(); 99 | const re: ArchiveResult = ArchiveConvert.toResult(archiveJSON); 100 | 101 | // Check archive and get the closest 102 | if (re.archivedSnapshots.closest) { 103 | this.archivedUrl = re.archivedSnapshots.closest.url; 104 | this.archivedDate = DateTime.fromFormat(re.archivedSnapshots.closest.timestamp, "yyyyMMddhhmmss").toISODate(); 105 | } 106 | } 107 | 108 | private setTitle(): void { 109 | this.title = 110 | this.getAttr("meta[property='og:og:title']") || 111 | this.getAttr("meta[property='twitter:title']") || 112 | this.getText("h1") || 113 | this.getText("title"); 114 | } 115 | 116 | private setPublishedDate(): void { 117 | const date: string | undefined = 118 | this.getAttr("meta[property='article:published_time']") || 119 | this.getAttr("time", "datetime"); 120 | if (date) { 121 | this.publishDate = DateTime.fromISO(date).toISODate(); 122 | } 123 | } 124 | 125 | private setSiteName(): void { 126 | this.siteName = 127 | this.getAttr("meta[property='og:site_name']") || 128 | this.getAttr("meta[property='twitter:site']"); 129 | } 130 | 131 | private getAttr(ioName: string, attrName = 'content'): string | undefined { 132 | const io: cheerio.Cheerio = this.metaData(ioName); 133 | if (io.length) { 134 | return io.attr(attrName) || undefined; 135 | } 136 | } 137 | 138 | private getText(ioName: string): string | undefined { 139 | const io: cheerio.Cheerio = this.metaData(ioName); 140 | if (io.length) { 141 | return io.text() || undefined; 142 | } 143 | } 144 | } 145 | 146 | /** 147 | * Replace all argument. 148 | */ 149 | export function getReplacedString(formatStr: string, argStr: string, replaceStr: string | undefined): string { 150 | // /\{$arg\}/ 151 | const argRegExp = new RegExp(`\\{\\$${argStr}\\}`, 'g'); 152 | if (replaceStr) { 153 | // remove all and 154 | // /\<\/?\!arg\>/ 155 | formatStr = formatStr.replace(new RegExp(`<\\/?!${argStr}>`, 'g'), ''); 156 | // replace all {$arg} 157 | formatStr = formatStr.replace(argRegExp, replaceStr); 158 | } else { 159 | // remove all substring between and 160 | // /\<\!arg\>[\s\S]*?\<\/\!arg\>/ 161 | formatStr = formatStr.replace(new RegExp(`[\\s\\S]*?<\\/!${argStr}>`, 'g'), ''); 162 | // clear all argument 163 | formatStr = formatStr.replace(argRegExp, ''); 164 | } 165 | return formatStr; 166 | } 167 | -------------------------------------------------------------------------------- /src/export_command/commandRegistrar.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | 8 | export type CommandFactory = (context: vscode.ExtensionContext, isBrowser: boolean) => ((...args: unknown[]) => unknown); 9 | 10 | export class WikitextCommandRegistrar { 11 | private context: vscode.ExtensionContext; 12 | private isBrowser: boolean; 13 | constructor(context: vscode.ExtensionContext, isBrowser: boolean) { 14 | this.context = context; 15 | this.isBrowser = isBrowser; 16 | } 17 | 18 | public register(name: string, factory: CommandFactory) { 19 | const fullName = `wikitext.${name}`; 20 | const command: ((...args: unknown[]) => unknown) = factory(this.context, this.isBrowser); 21 | this.context.subscriptions.push( 22 | vscode.commands.registerCommand(fullName, command) 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/export_command/uri_function/editPage.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import MWBot from 'mwbot'; 8 | import { getDefaultBot } from '../wikimedia_function/bot'; 9 | import { Action, alterNativeValues, Prop, RvProp } from '../wikimedia_function/args'; 10 | import { getPageCode } from '../wikimedia_function/page'; 11 | import { isRemoteBot, parseArgs } from './uri'; 12 | 13 | export async function editPage(query: string): Promise { 14 | // vscode-insiders://rowewilsonfrederiskholme.wikitext/PullPage?Title=1 15 | const pars: Record = parseArgs(query); 16 | 17 | const tBot: MWBot | undefined = isRemoteBot(pars) ? new MWBot({ 18 | apiUrl: pars["TransferProtocol"] + pars["SiteHost"] + pars["APIPath"] 19 | }) : await getDefaultBot(); 20 | 21 | const title: string | undefined = pars['Title']; 22 | 23 | if (!title || !tBot) { 24 | vscode.window.showErrorMessage(`${!title ? 'title ' : ''}${!tBot ? 'tbot ' : ''}is undefined or empty.`); 25 | return undefined; 26 | } 27 | 28 | const args: Record = { 29 | 'action': Action.query, 30 | 'prop': Prop.reVisions, 31 | 'rvprop': alterNativeValues(RvProp.content, RvProp.ids), 32 | 'rvslots': "*", 33 | 'titles': title 34 | }; 35 | 36 | const document: vscode.TextDocument | undefined = await getPageCode(args, tBot); 37 | if (document === undefined) { return undefined; } 38 | 39 | vscode.window.showTextDocument(document); 40 | } 41 | -------------------------------------------------------------------------------- /src/export_command/uri_function/uri.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { editPage } from './editPage'; 8 | import { viewPage } from './viewPage'; 9 | import { writeLine } from './writeLine'; 10 | 11 | export function baseUriProcess(uri: vscode.Uri): void { 12 | // vscode://rowewilsonfrederiskholme.wikitext/WriteLine?你好,世界! 13 | switch (uri.path) { 14 | case "/WriteLine": 15 | case "/Write": 16 | writeLine(uri.query); 17 | break; 18 | case "/Edit": 19 | case "/EditPage": 20 | case "/PullPage": 21 | case "/ReadPage": 22 | editPage(uri.query); 23 | break; 24 | case "/ViewPage": 25 | viewPage(uri.query); 26 | break; 27 | default: 28 | break; 29 | } 30 | } 31 | 32 | export function isRemoteBot(pars: Record): boolean { 33 | return !!(pars['RemoteBot'] || pars['SiteHost']); 34 | } 35 | 36 | export function parseArgs(query: string): Record { 37 | const queries: string[] = query.split("&"); 38 | const pars: Record = {}; 39 | for (const item of queries) { 40 | const eq: number = item.indexOf("="); 41 | if (eq >= 0) { 42 | pars[item.substring(0, eq)] = item.substring(eq + 1); 43 | } else { 44 | pars[item] = ''; 45 | } 46 | } 47 | return pars; 48 | } 49 | -------------------------------------------------------------------------------- /src/export_command/uri_function/viewPage.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import MWBot from 'mwbot'; 8 | import { getDefaultBot } from '../wikimedia_function/bot'; 9 | import { Action, alterNativeValues, Prop } from '../wikimedia_function/args'; 10 | import { showViewer } from '../wikimedia_function/view'; 11 | import { isRemoteBot, parseArgs } from './uri'; 12 | 13 | export async function viewPage(query: string): Promise { 14 | function setArgs(par: string, defaultValue?: string): void { 15 | args[par.toLowerCase()] = pars[par] ?? defaultValue; 16 | } 17 | 18 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 19 | const pars: Record = parseArgs(query); 20 | 21 | const tBot: MWBot | undefined = isRemoteBot(pars) ? new MWBot({ 22 | apiUrl: pars["TransferProtocol"] + pars["SiteHost"] + pars["APIPath"] 23 | }) : await getDefaultBot(); 24 | 25 | if (!tBot) { 26 | vscode.window.showErrorMessage("The bot is undefined or empty."); 27 | return undefined; 28 | } 29 | 30 | const baseHref: string = isRemoteBot(pars) 31 | ? pars["TransferProtocol"] + pars["SiteHost"] + pars["APIPath"] 32 | : config.get("transferProtocol") as string + config.get('host') + config.get("articlePath"); 33 | 34 | // args value 35 | const args: Record = { 'action': Action.parse }; 36 | setArgs('Prop', alterNativeValues( 37 | Prop.text, 38 | Prop.displayTitle, 39 | Prop.categoriesHTML, 40 | (config.get("getCss") ? Prop.headHTML : undefined) 41 | )); 42 | const undefParNames: string[] = ['Text', 'Title', 'Summary', 'RevID', 'Page', 43 | 'PageID', 'OldID', 'Redirects', 'OnlyPST', 'Section', 44 | 'SectionTitle', 'UseSkin', 'ContentFormat', 'ContentModel']; 45 | undefParNames.forEach((value: string): void => setArgs(value)); 46 | setArgs("PST", "true"); 47 | 48 | showViewer("pageViewer", "WikiViewer", args, tBot, baseHref); 49 | } 50 | -------------------------------------------------------------------------------- /src/export_command/uri_function/writeLine.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | export function writeLine(query: string): void { 8 | vscode.window.showInformationMessage(query); 9 | } 10 | -------------------------------------------------------------------------------- /src/export_command/vscode_function/wikiparser.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import * as path from 'path'; 8 | import { LanguageClient as BrowserLanguageClient } from 'vscode-languageclient/browser'; 9 | import { LanguageClient as NodeLanguageClient, ServerOptions } from 'vscode-languageclient/node'; 10 | import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient'; 11 | 12 | export let client: BaseLanguageClient | undefined = undefined; 13 | 14 | export function restartLspFactory(_: unknown, isBrowser: boolean) { 15 | return async function restartLsp(): Promise { 16 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('wikitext'); 17 | await client?.stop(); 18 | await client?.dispose(); 19 | 20 | if (!config.get('wikiparser.enable')) { 21 | return; 22 | } 23 | 24 | if (config.get('wikiparser.syncArticlePath')) { 25 | const configWikiParserServer = vscode.workspace.getConfiguration('wikiparser'); 26 | // if (configWikiParserServer.has('articlePath')){ 27 | configWikiParserServer.update('articlePath', config.get("transferProtocol") as string + config.get('host') + config.get("articlePath"), true); 28 | // } 29 | } 30 | 31 | const serverExtension: vscode.Extension | undefined = vscode.extensions.getExtension('Bhsd.vscode-extension-wikiparser'); 32 | 33 | if (serverExtension === undefined) { 34 | vscode.window.showWarningMessage('Extension `Bhsd.vscode-extension-wikiparser` not found. Please install it before use, then execute `Restart WikiParser LSP` command or restart VSCode.', 'Install WikiParser', 'Cancel') 35 | .then((selection: string | undefined) => { 36 | if (selection === 'Install WikiParser') { 37 | vscode.commands.executeCommand('workbench.extensions.search', 'Bhsd.vscode-extension-wikiparser'); 38 | } 39 | }); 40 | return; 41 | } 42 | 43 | const name: string = 'WikiParser Language Server'; 44 | const clientOptions: LanguageClientOptions = { 45 | documentSelector: [ 46 | { scheme: 'file', language: 'wikitext' }, 47 | { scheme: 'untitled', language: 'wikitext' }, 48 | ], 49 | }; 50 | 51 | if (isBrowser) { 52 | const serverUri: vscode.Uri | undefined = serverExtension.extensionUri; 53 | const serverMain: vscode.Uri = vscode.Uri.joinPath(serverUri, 'server', 'dist', 'server.js'); 54 | const worker = new Worker(serverMain.toString(true)); 55 | client = new BrowserLanguageClient('lsp-vscode-extension-wikiparser', name, clientOptions, worker); 56 | } else { 57 | const serverPath: string | undefined = serverExtension.extensionPath; 58 | const serverMain: string = path.join(serverPath, 'server', 'dist', 'server.js'); 59 | const serverOptions: ServerOptions = { 60 | run: { 61 | module: serverMain, 62 | }, 63 | debug: { 64 | module: serverMain, 65 | args: ['--debug'], 66 | }, 67 | }; 68 | client = new NodeLanguageClient(name, serverOptions, clientOptions); 69 | } 70 | await client.start(); 71 | }; 72 | } 73 | -------------------------------------------------------------------------------- /src/export_command/wikimedia_function/args.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | export function alterNativeValues(...values: (string | undefined)[]): string { 7 | values = values.filter( 8 | (item: string | undefined): boolean => item !== undefined 9 | ); 10 | return values.join("|"); 11 | } 12 | 13 | export const enum Action { 14 | abuseFilterCheckMatch = "abusefiltercheckmatch", 15 | abuseFilterCheckSyntax = "abusefilterchecksyntax", 16 | abuseFilterEvalExpression = "abusefilterevalexpression", 17 | abuseFilterUnblockAutoPromote = "abusefilterunblockautopromote", 18 | abuseLogPrivateDetails = "abuselogprivatedetails", 19 | antiSpoof = "antispoof", 20 | block = "block", 21 | centralAuthToken = "centralauthtoken", 22 | centralNoticeCdnCacheUpdateBanner = "centralnoticecdncacheupdatebanner", 23 | centralNoticeChoiceData = "centralnoticechoicedata", 24 | // ... 25 | edit = "edit", 26 | // ... 27 | login = "login", 28 | logout = "logout", 29 | // ... 30 | parse = "parse", 31 | // ... 32 | query = "query", 33 | // ... 34 | } 35 | 36 | export const enum Prop { 37 | reVisions = "revisions", 38 | text = "text", 39 | langLinks = "langlinks", 40 | categories = "categories", 41 | links = "links", 42 | templates = "templates", 43 | images = "images", 44 | externalLinks = "externallinks", 45 | sections = "sections", 46 | revid = "revid", 47 | displayTitle = "displaytitle", 48 | iwLinks = "iwlinks", 49 | properties = "properties", 50 | parseWarnings = "parsewarnings", 51 | categoriesHTML = "categorieshtml", 52 | headHTML = "headhtml", 53 | modules = "modules", 54 | jSConfigVars = "jsconfigvars", 55 | encodedJSConfigVars = "encodedjsconfigvars", 56 | indicators = "indicators", 57 | wikitext = "wikitext", 58 | limitReportData = "limitreportdata", 59 | limitReportHTML = "limitreporthtml", 60 | parsetree = "parsetree" 61 | } 62 | 63 | export const enum Meta { 64 | allMessages = "allmessages", 65 | // ... 66 | siteInfo = "siteinfo", 67 | // ... 68 | } 69 | 70 | 71 | export const enum List { 72 | // ... 73 | tags = "tags", 74 | // ... 75 | } 76 | 77 | export const enum RvProp { 78 | ids = "ids", 79 | flags = "flags", 80 | timeStamp = "timestamp", 81 | user = "user", 82 | userId = "userid", 83 | size = "size", 84 | slotSize = "slotsize", 85 | sha1 = "sha1", 86 | slotSha1 = "slotsha1", 87 | contentModel = "contentmodel", 88 | comment = "comment", 89 | parsedComment = "parsedcomment", 90 | content = "content", 91 | tags = "tags", 92 | roles = "roles", 93 | oresscores = "oresscores" 94 | } 95 | 96 | export const enum Format { 97 | jSON = "json", 98 | jSONFm = "jsonfm", 99 | none = "none", 100 | pHP = "php", 101 | pHPFm = "phpfm", 102 | rawFm = "rawfm", 103 | xML = "xml", 104 | xMLFm = "xmlfm" 105 | } 106 | 107 | export const enum ContextModel { 108 | gadgetDefinition = "GadgetDefinition", 109 | jsonSchema = "JsonSchema", 110 | massMessageListContent = "MassMessageListContent", 111 | scribunto = "Scribunto", 112 | cSS = "css", 113 | flowBoard = "flow-board", 114 | javascript = "javascript", 115 | jSON = "json", 116 | sanitizedCSS = "sanitized-css", 117 | text = "text", 118 | unknown = "unknown", 119 | wikitext = "wikitext" 120 | } 121 | 122 | export const enum TokenType { 123 | createAccount = "createaccount", 124 | cSRF = "csrf", 125 | deleteGlobalAccount = "deleteglobalaccount", 126 | login = "login", 127 | patrol = "patrol", 128 | rollback = "rollback", 129 | setGlobalAccountStatus = "setglobalaccountstatus", 130 | userRights = "userrights", 131 | watch = "watch" 132 | } 133 | -------------------------------------------------------------------------------- /src/export_command/wikimedia_function/bot.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import MWBot from 'mwbot'; 7 | import * as vscode from 'vscode'; 8 | import { Action, Meta } from './args'; 9 | import { showMWErrorMessage } from './err_msg'; 10 | 11 | let bot: MWBot | undefined; 12 | 13 | export function loginFactory() { 14 | return login; 15 | } 16 | 17 | export function logoutFactory() { 18 | return async function logout(): Promise { 19 | await bot?.getEditToken(); 20 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Logout..."); 21 | try { 22 | // it will be {} if success 23 | await bot?.request({ 24 | 'action': Action.logout, 25 | 'token': bot.editToken 26 | }); 27 | // clear bot 28 | bot = undefined; 29 | vscode.window.showInformationMessage('result: Success'); 30 | } 31 | catch (error) { 32 | showMWErrorMessage('logout', error); 33 | } 34 | finally { 35 | barMessage.dispose(); 36 | } 37 | }; 38 | } 39 | 40 | async function login(): Promise { 41 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 42 | 43 | const userInfo: { username?: string; password?: string } = { 44 | username: config.get('userName'), 45 | password: config.get('password') 46 | }; 47 | 48 | if (!userInfo.username || !userInfo.password) { 49 | vscode.window.showWarningMessage("You have not filled in the user name or password, please go to the settings to edit them and try again."); 50 | return false; 51 | } 52 | 53 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Login..."); 54 | try { 55 | bot = new MWBot({ 56 | apiUrl: config.get("transferProtocol") as string + config.get('host') + config.get("apiPath") 57 | }); 58 | // TODO: 59 | const response: any = await bot.login(userInfo); 60 | if (response.result === 'Success') { 61 | vscode.window.showInformationMessage(`User "${response.lgusername}"(UserID:"${response.lguserid}") Login Result is "${response.result}". Login Token is "${response.token}".` 62 | ); 63 | return true; 64 | } 65 | else { 66 | vscode.window.showErrorMessage(`User "${response.lgusername}"(UserID:"${response.lguserid}") Login Result is "${response.result}". Login Token is "${response.token}".` 67 | ); 68 | return false; 69 | } 70 | } 71 | catch (error) { 72 | bot = undefined; 73 | showMWErrorMessage('login', error); 74 | return false; 75 | } 76 | finally { 77 | barMessage.dispose(); 78 | } 79 | } 80 | 81 | export async function getDefaultBot(): Promise { 82 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 83 | let tBot: MWBot; 84 | if (bot) { 85 | tBot = bot; 86 | } else { 87 | tBot = new MWBot({ 88 | apiUrl: config.get("transferProtocol") as string + config.get('host') + config.get("apiPath") 89 | }); 90 | } 91 | return tBot; 92 | } 93 | 94 | export async function getLoggedInBot(): Promise { 95 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 96 | // if bot is not be created or not logged in 97 | if (bot === undefined || !bot.loggedIn) { 98 | switch (config.get('autoLogin')) { 99 | case 'Always': 100 | return await login() ? bot : undefined; 101 | case 'Never': 102 | vscode.window.showWarningMessage('You are not logged in. Please log in and try again.'); 103 | return undefined; 104 | case 'Ask me': 105 | default: 106 | switch (await vscode.window.showWarningMessage("You are not logged in. Do you want to login now?", 'Yes', 'No', 'Always', 'Never')) { 107 | case 'Always': 108 | config.update('autoLogin', 'Always', true); 109 | return await login() ? bot : undefined; 110 | case 'Yes': 111 | return await login() ? bot : undefined; 112 | case 'Never': 113 | config.update('autoLogin', 'Never', true); 114 | return undefined; 115 | case 'No': 116 | case undefined: 117 | default: 118 | return undefined; 119 | } 120 | } 121 | } 122 | return bot; 123 | } 124 | 125 | export async function compareVersion(tBot: MWBot, major: number, minor: number, revision: number): Promise { 126 | // if (bot === undefined) { 127 | // return undefined; 128 | // } 129 | const args: Record = { 130 | action: Action.query, 131 | meta: Meta.siteInfo, 132 | }; 133 | 134 | const result: unknown = await tBot.request(args); 135 | const re: any = result as any; 136 | // TODO: cast 137 | 138 | const generator: string = re.query.general.generator; 139 | 140 | const generatorInfo: RegExpMatchArray | null = generator.match(/^MediaWiki ([0-9]+)\.([0-9]+)\.([0-9]+)(.*)$/); 141 | if (generatorInfo === null) { 142 | return undefined; 143 | } 144 | 145 | const siteMajor: number = parseInt(generatorInfo[1]); 146 | const siteMinor: number = parseInt(generatorInfo[2]); 147 | const siteRevision: number = parseInt(generatorInfo[3]); 148 | 149 | if (isNaN(siteMajor + siteMinor + siteRevision)) { 150 | return undefined; 151 | } 152 | 153 | if (siteMajor !== major) { 154 | return siteMajor > major; 155 | } 156 | if (siteMinor !== minor) { 157 | return siteMinor > minor; 158 | } 159 | return siteRevision >= revision; 160 | 161 | } 162 | -------------------------------------------------------------------------------- /src/export_command/wikimedia_function/err_msg.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { instanceOfMWError } from '../../interface_definition/api_interface/commonInterface'; 8 | 9 | export function showMWErrorMessage(name: string, error: unknown, moreInfo = ''): void { 10 | if (instanceOfMWError(error)) { 11 | vscode.window.showErrorMessage(`ErrorCode: ${error.code}; ErrorInfo: ${error.info}; ${moreInfo}`.trim()); 12 | } else if (error instanceof Error) { 13 | vscode.window.showErrorMessage(`ErrorName: ${error.name}; ErrorMessage: ${error.message}; ${moreInfo}`.trim()); 14 | } else { 15 | vscode.window.showErrorMessage(`${name} ERROR: ${JSON.stringify(error)}; ${moreInfo}`.trim()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/export_command/wikimedia_function/page.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import type MWBot from 'mwbot'; 8 | import { Action, Prop, RvProp, alterNativeValues, List } from './args'; 9 | import { ReadPageConvert, ReadPageResult, Main, Revision, Jump, Page } from '../../interface_definition/api_interface/readPage'; 10 | import { OldTokensConvert, OldTokensResult } from '../../interface_definition/api_interface/oldTokens'; 11 | import { compareVersion, getDefaultBot, getLoggedInBot } from './bot'; 12 | import { TokensConvert, TokensResult } from '../../interface_definition/api_interface/tokens'; 13 | import { showMWErrorMessage } from './err_msg'; 14 | import { TagsConvert, TagsResult } from '../../interface_definition/api_interface/tags'; 15 | 16 | interface ContentInfo { 17 | content: string; 18 | info?: Record; 19 | } 20 | 21 | export function postPageFactory() { 22 | /** 23 | * Write/Post Page 24 | */ 25 | return async function postPage(): Promise { 26 | async function getEditToken(bot: MWBot): Promise { 27 | const errors: unknown[] = [undefined, undefined]; 28 | try { 29 | const args: Record = { 30 | action: Action.query, 31 | meta: 'tokens', 32 | type: 'csrf' 33 | }; 34 | const result: unknown = await bot.request(args); 35 | const reNew: TokensResult = TokensConvert.toResult(result); 36 | const token: string | undefined = reNew.query?.tokens?.csrftoken; 37 | if (token) { 38 | return token; 39 | } 40 | } 41 | catch (error) { 42 | errors[0] = error; 43 | } 44 | if (errors[0] !== undefined) { 45 | try { 46 | const args: Record = { 47 | action: "tokens", 48 | type: "edit" 49 | }; 50 | const result: unknown = await bot.request(args); 51 | const reOld: OldTokensResult = OldTokensConvert.toResult(result); 52 | const token: string | undefined = reOld.tokens?.edittoken; 53 | if (token) { 54 | return token; 55 | } 56 | } 57 | catch (error) { 58 | errors[1] = error; 59 | } 60 | } 61 | 62 | const error: Error = new Error('Could not get edit token:' + 63 | ' NEW: ' + ((errors[0] instanceof Error) ? errors[0].message : '') + 64 | ' OLD: ' + ((errors[1] instanceof Error) ? errors[1].message : '')); 65 | throw error; 66 | } 67 | 68 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration('wikitext'); 69 | const tBot: MWBot | undefined = await getLoggedInBot(); 70 | 71 | if (tBot === undefined) { 72 | vscode.window.showErrorMessage("Bot is undefined! You may be not logged in."); 73 | return undefined; 74 | } 75 | 76 | const wikiContent: string | undefined = vscode.window.activeTextEditor?.document.getText(); 77 | if (wikiContent === undefined) { 78 | vscode.window.showWarningMessage("There is no active text editor."); 79 | return undefined; 80 | } 81 | 82 | const contentInfo: ContentInfo = getContentInfo(wikiContent); 83 | 84 | const skip: boolean = config.get("skipEnteringPageTitle") as boolean; 85 | 86 | const wikiTitle: string | undefined = skip && contentInfo.info?.pageTitle || await vscode.window.showInputBox({ 87 | value: contentInfo.info?.pageTitle || "", 88 | ignoreFocusOut: true, 89 | prompt: "Enter the page name here." 90 | }); 91 | 92 | if (!wikiTitle) { 93 | return undefined; 94 | } 95 | let wikiSummary: string | undefined = await vscode.window.showInputBox({ 96 | ignoreFocusOut: true, 97 | prompt: 'Enter the summary of this edit action.', 98 | placeHolder: '// via Wikitext Extension for VSCode' 99 | }); 100 | if (wikiSummary === undefined) { 101 | return undefined; 102 | } 103 | wikiSummary = `${wikiSummary} // via Wikitext Extension for VSCode`.trim(); 104 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Posting..."); 105 | try { 106 | const args: Record = { 107 | action: Action.edit, 108 | title: wikiTitle, 109 | text: contentInfo.content, 110 | summary: wikiSummary, 111 | // tags: 'WikitextExtensionForVSCode', 112 | token: await getEditToken(tBot) 113 | }; 114 | const wikitextTag = 'WikitextExtensionForVSCode'; 115 | const tagList: (number | string)[] = await getValidTagList(tBot); 116 | if (tagList.includes(wikitextTag)) { 117 | args['tags'] = wikitextTag; 118 | } 119 | 120 | // if (config.get("redirect")) { 121 | // args['redirect'] = "true"; 122 | // } 123 | const result: any = await tBot.request(args); 124 | // TODO: Convert 125 | if (result.edit.nochange !== undefined) { 126 | vscode.window.showWarningMessage( 127 | `No changes have occurred: "${result.edit.nochange}", Edit page "${result.edit.title}" (Page ID: "${result.edit.pageid}") action status is "${result.edit.result}" with Content Model "${result.edit.contentmodel}". Watched by: "${result.edit.watched}".` 128 | ); 129 | } else { 130 | vscode.window.showInformationMessage( 131 | `Edit page "${result.edit.title}" (Page ID: "${result.edit.pageid}") action status is "${result.edit.result}" with Content Model "${result.edit.contentmodel}" (Version: "${result.edit.oldrevid}" => "${result.edit.newrevid}", Time: "${result.edit.newtimestamp}"). Watched by: "${result.edit.watched}".` 132 | ); 133 | } 134 | } 135 | catch (error) { 136 | showMWErrorMessage('postPage', error, `Your Token: ${tBot?.editToken}.`); 137 | } 138 | finally { 139 | barMessage.dispose(); 140 | } 141 | }; 142 | } 143 | 144 | export function pullPageFactory() { 145 | /** 146 | * Read/Pull Page 147 | */ 148 | return async function pullPage(): Promise { 149 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 150 | 151 | // constructing 152 | const tBot: MWBot | undefined = await getDefaultBot(); 153 | if (tBot === undefined) { return undefined; } 154 | 155 | // get title name 156 | const title: string | undefined = await vscode.window.showInputBox({ 157 | prompt: "Enter the page name here.", 158 | ignoreFocusOut: true 159 | }); 160 | // if title is null or empty, do nothing 161 | if (!title) { return undefined; } 162 | 163 | const newVer = await compareVersion(tBot, 1, 32, 0); 164 | 165 | if (!newVer) { 166 | vscode.window.showWarningMessage("Your MediaWiki version may be too old. This may cause some compatibility issues. Please update to the v1.32.0 or later."); 167 | // return undefined; 168 | } 169 | 170 | const args: Record = { 171 | action: Action.query, 172 | prop: Prop.reVisions, 173 | rvprop: alterNativeValues(RvProp.content, RvProp.ids), 174 | rvslots: "*", 175 | titles: title 176 | }; 177 | if (config.get("redirects")) { 178 | args['redirects'] = "true"; 179 | } 180 | 181 | const document: vscode.TextDocument | undefined = await getPageCode(args, tBot); 182 | if (document === undefined) { return undefined; } 183 | 184 | vscode.window.showTextDocument(document); 185 | }; 186 | } 187 | 188 | export function closeEditorFactory() { 189 | return async function closeEditor(): Promise { 190 | const editor: vscode.TextEditor | undefined = vscode.window.activeTextEditor; 191 | 192 | await editor?.edit((editBuilder: vscode.TextEditorEdit): void => 193 | // delete all text 194 | editBuilder.delete( 195 | new vscode.Range( // the range of all document: from the beginning to the end 196 | new vscode.Position(0, 0), // beginning 197 | editor.document.lineAt(editor.document.lineCount - 1).rangeIncludingLineBreak.end // end 198 | ) 199 | ) 200 | ).then(() => 201 | // close the activate editor 202 | vscode.commands.executeCommand('workbench.action.closeActiveEditor') 203 | ); 204 | }; 205 | } 206 | 207 | type PageInfo = "pageTitle" | "pageID" | "revisionID" | "contentModel" | "contentFormat"; 208 | 209 | export async function getPageCode(args: Record, tBot: MWBot): Promise { 210 | function modelNameToLanguage(modelName: string | undefined): string { 211 | switch (modelName) { 212 | case undefined: 213 | return 'wikitext'; 214 | case 'flow-board': 215 | return 'jsonc'; 216 | case 'sanitized-css': 217 | return 'css'; 218 | case 'Scribunto': 219 | return 'lua'; 220 | default: 221 | return modelName; 222 | } 223 | } 224 | function getInfoHead(info: Record): string { 225 | const commentList: Record = { 226 | wikitext: ["", ""], 227 | jsonc: ["/*", "*/"], 228 | lua: ["--[=[", "--]=]"], 229 | javascript: ["/*", "*/"], 230 | css: ["/*", "*/"], 231 | php: ["/*", "*/"], 232 | // 'flow-board': ["/*", "*/"], 233 | // 'sanitized-css': ["/*", "*/"], 234 | // 'Scribunto' : ["--[=[", "--]=]"], 235 | }; 236 | const headInfo: Record = { 237 | comment: "Please do not remove this struct. It's record contains some important information of edit. This struct will be removed automatically after you push edits.", 238 | ...info 239 | }; 240 | const infoLine: string = Object.keys(headInfo). 241 | map((key: string) => ` ${key} = #${headInfo[key] ?? ''}#`). 242 | join("\r"); 243 | console.log('GetInfoHead' + info?.contentModel); 244 | const comment: [string, string] | undefined = commentList[modelNameToLanguage(info?.contentModel)]; 245 | if (comment === undefined) { 246 | throw new Error(`Unsupported content model: ${info?.contentModel}. Please report this issue to the author of this extension.`); 247 | } 248 | return comment.join(`<%-- [PAGE_INFO] 249 | ${infoLine} 250 | [END_PAGE_INFO] --%>`); 251 | } 252 | 253 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Getting code..."); 254 | try { 255 | // get request result 256 | const result: unknown = await tBot.request(args); 257 | // console.log(result); 258 | // Convert result as class 259 | const re: ReadPageResult = ReadPageConvert.toResult(result); 260 | if (re.query?.interwiki) { 261 | vscode.window.showWarningMessage( 262 | `Interwiki page "${re.query.interwiki[0].title}" in space "${re.query.interwiki[0].iw}" are currently not supported. Please try to modify host.` 263 | ); 264 | } 265 | 266 | // get first page 267 | const page: Page | undefined = re.query?.pages?.[Object.keys(re.query.pages)[0]]; 268 | // need a page elements 269 | if (!page) { return undefined; } 270 | 271 | // first revision 272 | const revision: Revision | undefined = page.revisions?.[0]; 273 | 274 | const content: Main | Revision | undefined = revision?.slots?.main || revision; 275 | 276 | const normalized: Jump | undefined = re.query?.normalized?.[0]; 277 | const redirects: Jump | undefined = re.query?.redirects?.[0]; 278 | 279 | vscode.window.showInformationMessage( 280 | `Opened page "${page.title}" with Model ${content?.contentmodel}.` + 281 | (normalized ? ` Normalized: ${normalized.from} => ${normalized.to}` : "") + 282 | (redirects ? ` Redirect: ${redirects.from} => ${redirects.to}` : "") 283 | ); 284 | 285 | if (page.missing !== undefined || page.invalid !== undefined) { 286 | const choice: string | undefined = await vscode.window.showWarningMessage( 287 | `The page "${page.title}" you are looking for does not exist. ${page.invalidreason ?? ''}`.trim() + ' Do you want to create one?', 'Yes', 'No'); 288 | if (choice === 'Yes') { 289 | const info: Record = { 290 | pageTitle: page.title, 291 | pageID: undefined, 292 | revisionID: undefined, 293 | contentModel: undefined, 294 | contentFormat: undefined, 295 | }; 296 | const infoHead: string = getInfoHead(info); 297 | const textDocument: vscode.TextDocument = await vscode.workspace.openTextDocument({ 298 | language: info.contentModel ?? 'wikitext', 299 | content: infoHead + "\r\r" 300 | }); 301 | return textDocument; 302 | } else { return undefined; } 303 | } else { 304 | const info: Record = { 305 | pageTitle: page.title, 306 | pageID: page.pageid?.toString(), 307 | revisionID: revision?.revid?.toString(), 308 | contentModel: content?.contentmodel, 309 | contentFormat: content?.contentformat 310 | }; 311 | const infoHead: string = getInfoHead(info); 312 | const textDocument: vscode.TextDocument = await vscode.workspace.openTextDocument({ 313 | language: modelNameToLanguage(info.contentModel), 314 | content: infoHead + "\r\r" + content?.["*"] 315 | }); 316 | return textDocument; 317 | } 318 | } 319 | catch (error) { 320 | showMWErrorMessage('getPageCode', error); 321 | } 322 | finally { 323 | barMessage.dispose(); 324 | } 325 | } 326 | 327 | export function getContentInfo(content: string): ContentInfo { 328 | const info: string | undefined = content.match( 329 | /(?<=<%--\s*\[PAGE_INFO\])[\s\S]*?(?=\[END_PAGE_INFO\]\s*--%>)/ 330 | )?.[0]; 331 | 332 | let pageInfo: Record | undefined; 333 | if (info) { 334 | const getInfo = (infoName: PageInfo): string | undefined => { 335 | const nameFirst: string = infoName[0]; 336 | const nameRest: string = infoName.substring(1); 337 | const reg = new RegExp(`(?<=[${nameFirst.toLowerCase()}${nameFirst.toUpperCase()}]${nameRest}\\s*=\\s*#).*?(?=#)`); 338 | return info.match(reg)?.[0]; 339 | }; 340 | pageInfo = { 341 | pageTitle: getInfo("pageTitle"), 342 | pageID: getInfo("pageID"), 343 | revisionID: getInfo("revisionID"), 344 | contentModel: getInfo("contentModel"), 345 | contentFormat: getInfo("contentFormat") 346 | }; 347 | 348 | content = content.replace(/\s*(?:\/\*|--\[=\[)?\s*<%--\s*\[PAGE_INFO\][\s\S]*?\[END_PAGE_INFO\]\s*--%>\s*(?:\*\/|--\]=\])?\s*/, ''); 349 | } 350 | 351 | return { content: content, info: pageInfo }; 352 | } 353 | 354 | async function getValidTagList(tBot: MWBot): Promise<(number | string)[]> { 355 | const args: Record = { 356 | action: Action.query, 357 | list: List.tags, 358 | tglimit: 'max', 359 | tgprop: alterNativeValues('active', 'defined') 360 | }; 361 | 362 | const tagList: (number | string)[] = []; 363 | for (; ;) { 364 | const result: unknown = await tBot.request(args); 365 | const re: TagsResult = TagsConvert.toResult(result); 366 | 367 | tagList.push( 368 | ...re.query.tags.filter(tag => 369 | tag.active !== undefined && tag.defined !== undefined 370 | ).map(tag => tag.name)); 371 | if (re.continue !== undefined) { Object.assign(args, re.continue); } 372 | else { break; } 373 | } 374 | 375 | return tagList; 376 | } 377 | -------------------------------------------------------------------------------- /src/export_command/wikimedia_function/view.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import type MWBot from 'mwbot'; 8 | import { Action, ContextModel, alterNativeValues, Prop } from './args'; 9 | import { GetViewResult, ViewConvert } from '../../interface_definition/api_interface/getView'; 10 | import { getDefaultBot } from './bot'; 11 | import { getContentInfo } from './page'; 12 | import { showMWErrorMessage } from './err_msg'; 13 | 14 | /** 15 | * webview panel 16 | */ 17 | let previewCurrentPanel: vscode.WebviewPanel | undefined; 18 | 19 | export function getPageViewFactory() { 20 | return async function getPageView(): Promise { 21 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 22 | 23 | const pageTitle: string | undefined = await vscode.window.showInputBox({ 24 | prompt: "Enter the page name here.", 25 | ignoreFocusOut: true 26 | }); 27 | if (!pageTitle) { return undefined; } 28 | 29 | const args: Record = { 30 | action: Action.parse, 31 | page: pageTitle, 32 | prop: alterNativeValues( 33 | Prop.text, 34 | Prop.displayTitle, 35 | Prop.categoriesHTML, 36 | (config.get("getCss") ? Prop.headHTML : undefined) 37 | ), 38 | }; 39 | if (config.get("redirects")) { 40 | args['redirects'] = "true"; 41 | } 42 | 43 | const tBot: MWBot | undefined = await getDefaultBot(); 44 | if (!tBot) { 45 | return undefined; 46 | } 47 | 48 | const baseHref: string = config.get("transferProtocol") as string + config.get('host') + config.get("articlePath"); 49 | 50 | showViewer("pageViewer", "WikiViewer", args, tBot, baseHref); 51 | }; 52 | } 53 | 54 | export function getPreviewFactory(extension: vscode.ExtensionContext) { 55 | return async function getPreview(): Promise { 56 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 57 | 58 | /** document text */ 59 | const sourceText: string | undefined = vscode.window.activeTextEditor?.document.getText(); 60 | if (!sourceText) { return undefined; } 61 | const contentInfo = getContentInfo(sourceText); 62 | 63 | /** arguments */ 64 | const args: Record = { 65 | 'action': Action.parse, 66 | 'text': contentInfo.content, 67 | 'title': !(contentInfo.info?.pageTitle) ? undefined : contentInfo.info.pageTitle, 68 | 'prop': alterNativeValues( 69 | Prop.text, 70 | Prop.displayTitle, 71 | Prop.categoriesHTML, 72 | (config.get("getCss") ? Prop.headHTML : undefined) 73 | ), 74 | 'contentmodel': !(contentInfo.info?.contentModel) ? ContextModel.wikitext : contentInfo.info.contentModel, 75 | 'pst': "why_not", 76 | 'disableeditsection': "yes" 77 | }; 78 | 79 | const viewerTitle = "WikitextPreviewer"; 80 | 81 | // if no panel, create one 82 | if (!previewCurrentPanel) { 83 | // if there is no panel, try to create new one. 84 | previewCurrentPanel = vscode.window.createWebviewPanel( 85 | "previewer", viewerTitle, vscode.ViewColumn.Beside, { 86 | enableScripts: config.get("enableJavascript"), 87 | }); 88 | // register for events that release resources. 89 | previewCurrentPanel.onDidDispose(() => { 90 | previewCurrentPanel = undefined; 91 | }, null, extension.subscriptions); 92 | } 93 | 94 | const tBot: MWBot | undefined = await getDefaultBot(); 95 | if (!tBot) { 96 | return undefined; 97 | } 98 | 99 | const baseHref: string = config.get("transferProtocol") as string + config.get('host') + config.get("articlePath"); 100 | 101 | showViewer(previewCurrentPanel, viewerTitle, args, tBot, baseHref); 102 | }; 103 | } 104 | 105 | /** 106 | * 107 | * @param currentPanel where to show 108 | * @param viewerTitle viewer title 109 | * @param args post args 110 | * @param tBot account 111 | * @param baseURI url base 112 | * @returns task 113 | */ 114 | export async function showViewer(currentPanel: vscode.WebviewPanel | string, viewerTitle: string, args: Record, tBot: MWBot, baseURI: string): Promise { 115 | const config: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration("wikitext"); 116 | 117 | const barMessage: vscode.Disposable = vscode.window.setStatusBarMessage("Wikitext: Getting view..."); 118 | try { 119 | const result: unknown = await tBot.request(args); 120 | const re: GetViewResult = ViewConvert.toResult(result); 121 | if (!re.parse) { return undefined; } 122 | 123 | const baseElem = ``; 124 | 125 | const style = ``; 126 | 127 | const htmlHead: string = re.parse.headhtml?.["*"]?.replace("", "" + baseElem + style) ?? `${baseElem + style}`; 128 | const htmlText: string = re.parse.text?.["*"] ?? ''; 129 | const htmlCategories: string = re.parse.categorieshtml?.["*"] ? "
" + re.parse.categorieshtml?.["*"] : ""; 130 | const htmlEnd = ""; 131 | 132 | const html: string = htmlHead + htmlText + htmlCategories + htmlEnd; 133 | 134 | if (typeof (currentPanel) === "string") { 135 | currentPanel = vscode.window.createWebviewPanel(currentPanel, viewerTitle, vscode.ViewColumn.Active, { enableScripts: config.get("enableJavascript") }); 136 | } 137 | currentPanel.webview.html = html; 138 | currentPanel.title = `${viewerTitle}: ${re.parse.displaytitle}`; 139 | } 140 | catch (error) { 141 | showMWErrorMessage('getView', error); 142 | } 143 | finally { 144 | barMessage.dispose(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/extension-node.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { getPageViewFactory, getPreviewFactory } from './export_command/wikimedia_function/view'; 8 | import { loginFactory, logoutFactory } from './export_command/wikimedia_function/bot'; 9 | import { closeEditorFactory, postPageFactory, pullPageFactory } from './export_command/wikimedia_function/page'; 10 | import { baseUriProcess } from './export_command/uri_function/uri'; 11 | import { addWebCiteFactory } from './export_command/cite_function/web'; 12 | import { WikitextCommandRegistrar } from './export_command/commandRegistrar'; 13 | import { client, restartLspFactory } from './export_command/vscode_function/wikiparser'; 14 | 15 | export async function activate(context: vscode.ExtensionContext): Promise { 16 | console.log("Wikitext Extension is active."); 17 | // extensionContext = context; 18 | // URI 19 | context.subscriptions.push(vscode.window.registerUriHandler({ handleUri: baseUriProcess })); 20 | 21 | const commandRegistrar = new WikitextCommandRegistrar(context, false); 22 | // Bot 23 | commandRegistrar.register('login', loginFactory); 24 | commandRegistrar.register('logout', logoutFactory); 25 | // Core 26 | commandRegistrar.register('readPage', pullPageFactory); 27 | commandRegistrar.register('writePage', postPageFactory); 28 | commandRegistrar.register('closeEditor', closeEditorFactory); 29 | // View 30 | commandRegistrar.register('getPreview', getPreviewFactory); 31 | commandRegistrar.register('viewPage', getPageViewFactory); 32 | // Cite 33 | commandRegistrar.register('citeWeb', addWebCiteFactory); 34 | 35 | // Lsp 36 | commandRegistrar.register('restartLsp', restartLspFactory); 37 | await vscode.commands.executeCommand('wikitext.restartLsp'); 38 | } 39 | 40 | export async function deactivate(): Promise { 41 | await client?.stop(); 42 | console.log("Wikitext Extension is deactivate."); 43 | } 44 | -------------------------------------------------------------------------------- /src/extension-web.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as vscode from 'vscode'; 7 | import { closeEditorFactory } from './export_command/wikimedia_function/page'; 8 | import { WikitextCommandRegistrar } from './export_command/commandRegistrar'; 9 | import { client, restartLspFactory } from './export_command/vscode_function/wikiparser'; 10 | 11 | export async function activate(context: vscode.ExtensionContext): Promise { 12 | function showUnsupportedMessageFactory() { 13 | return () => { 14 | vscode.window.showErrorMessage('Web extension does not support this function.'); 15 | }; 16 | } 17 | 18 | console.log("Wikitext Extension is active."); 19 | 20 | const commandRegister = new WikitextCommandRegistrar(context, true); 21 | // Bot 22 | commandRegister.register('login', showUnsupportedMessageFactory); 23 | commandRegister.register('logout', showUnsupportedMessageFactory); 24 | // Core 25 | commandRegister.register('readPage', showUnsupportedMessageFactory); 26 | commandRegister.register('writePage', showUnsupportedMessageFactory); 27 | commandRegister.register('closeEditor', closeEditorFactory); 28 | // View 29 | commandRegister.register('getPreview', showUnsupportedMessageFactory); 30 | commandRegister.register('viewPage', showUnsupportedMessageFactory); 31 | // Cite 32 | commandRegister.register('citeWeb', showUnsupportedMessageFactory); 33 | // Lsp 34 | commandRegister.register('restartLsp', restartLspFactory); 35 | await vscode.commands.executeCommand('wikitext.restartLsp'); 36 | } 37 | 38 | export async function deactivate(): Promise { 39 | await client?.stop(); 40 | console.log("Wikitext Extension is deactivate."); 41 | } 42 | -------------------------------------------------------------------------------- /src/interface_definition/IObjectConverter.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-expressions */ 2 | interface IObjectConverter { 3 | toResult(json: unknown): TargetType; 4 | resultToJson(value: TargetType): unknown; 5 | } 6 | 7 | function staticImplements(): (constructor: U) => void { 8 | return (constructor: U): void => { constructor; }; 9 | } 10 | 11 | export function staticObjectConverter(): (constructor: IObjectConverter) => void { 12 | return staticImplements>(); 13 | } 14 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/archive.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { u, o, r, cast, uncast, TypeMap } from "../convertFunction"; 7 | import { staticObjectConverter } from "../IObjectConverter"; 8 | 9 | // https://archive.org/help/wayback_api.php 10 | 11 | /* 12 | ArchiveResult { 13 | archivedSnapshots: ArchivedSnapshots { 14 | closest?: Closest { 15 | available: boolean, 16 | url: string, 17 | timestamp: string, 18 | status: string 19 | } 20 | }, 21 | url?: string 22 | } 23 | */ 24 | 25 | export interface ArchiveResult { 26 | archivedSnapshots: ArchivedSnapshots; 27 | url?: string; 28 | } 29 | 30 | export interface ArchivedSnapshots { 31 | closest?: Closest; 32 | } 33 | 34 | export interface Closest { 35 | available: boolean; 36 | url: string; 37 | timestamp: string; 38 | status: string; 39 | } 40 | 41 | @staticObjectConverter() 42 | export class ArchiveConvert { 43 | public static toResult(json: unknown): ArchiveResult { 44 | return cast(json, r("ArchiveResult"), archiveTypeMap); 45 | } 46 | 47 | public static resultToJson(value: ArchiveResult): unknown { 48 | return uncast(value, r("ArchiveResult"), archiveTypeMap); 49 | } 50 | } 51 | 52 | /* eslint-disable @typescript-eslint/naming-convention */ 53 | const archiveTypeMap: TypeMap = { 54 | "ArchiveResult": o([ 55 | { json: "archived_snapshots", js: "archivedSnapshots", typ: r("ArchivedSnapshots") }, 56 | { json: "url", js: "url", typ: u(undefined, "") }, 57 | ], false), 58 | "ArchivedSnapshots": o([ 59 | { json: "closest", js: "closest", typ: u(undefined, r("Closest")) }, 60 | ], false), 61 | "Closest": o([ 62 | { json: "available", js: "available", typ: true }, 63 | { json: "url", js: "url", typ: "" }, 64 | { json: "timestamp", js: "timestamp", typ: "" }, 65 | { json: "status", js: "status", typ: "" }, 66 | ], false), 67 | }; 68 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/commonInterface.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { u, o, r, TypeMap } from "../convertFunction"; 7 | 8 | //#region Error 9 | 10 | export interface MWError { 11 | code: string; 12 | info: string; 13 | "*": string; 14 | } 15 | 16 | export const mWErrorTypeMapInline: { json: string; js: string; typ: any; } = { 17 | json: "warnings", js: "warnings", typ: u(undefined, r("MWError")) 18 | }; 19 | 20 | /* eslint-disable @typescript-eslint/naming-convention */ 21 | export const mWErrorTypeMapOutline: TypeMap = { 22 | "MWError": o([ 23 | { json: "code", js: "code", typ: "" }, 24 | { json: "info", js: "info", typ: "" }, 25 | { json: "*", js: "*", typ: "" } 26 | ], false), 27 | }; 28 | 29 | export function instanceOfMWError(o: any): o is MWError { 30 | return ('code' in o) && ('info' in o) && ('*' in o); 31 | } 32 | 33 | //#endregion Error 34 | 35 | //#region Warnings 36 | 37 | export interface MWWarnings { 38 | main: WarnMain; 39 | } 40 | 41 | export interface WarnMain { 42 | "*": string; 43 | } 44 | 45 | export const mWWarningsTypeMapInline: { json: string; js: string; typ: any; } = { 46 | json: "warnings", js: "warnings", typ: u(undefined, r("MWWarnings")) 47 | }; 48 | 49 | export const mWWarningsTypeMapOutline: any = { 50 | "MWWarnings": o([ 51 | { json: "main", js: "main", typ: r("WarnMain") }, 52 | ], false), 53 | "WarnMain": o([ 54 | { json: "*", js: "*", typ: "" }, 55 | ], false), 56 | }; 57 | 58 | export function instanceOfMWWarnings(o: any): o is MWWarnings { 59 | return 'main' in o; 60 | } 61 | 62 | //#endregion Warnings 63 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/getView.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { MWError, mWErrorTypeMapInline, mWErrorTypeMapOutline, MWWarnings, mWWarningsTypeMapInline, mWWarningsTypeMapOutline } from "./commonInterface"; 7 | import { a, u, o, r, cast, uncast, TypeMap } from "../convertFunction"; 8 | import { staticObjectConverter } from "../IObjectConverter"; 9 | 10 | 11 | /* 12 | GetViewResult { 13 | parse?: Parse { 14 | title?: string, 15 | pageid?: number, 16 | redirects?: Redirect[] { 17 | from?: string, 18 | to?: string 19 | }, 20 | text?: HtmlContent { 21 | *: string 22 | }, 23 | displaytitle?: string, 24 | headhtml?: HtmlContent { 25 | *: string 26 | }, 27 | categorieshtml?: CategoriesHTML { 28 | *: string 29 | } 30 | }, 31 | error?: Error { 32 | code: string, 33 | info: string, 34 | *: string 35 | }, 36 | warnings?: MWWarnings { 37 | main: WarnMain { 38 | *: string 39 | } 40 | } 41 | servedby?: string 42 | } 43 | */ 44 | 45 | export interface GetViewResult { 46 | parse?: Parse; 47 | error?: MWError; 48 | warnings?: MWWarnings; 49 | servedby?: string; 50 | } 51 | 52 | export interface Parse { 53 | title?: string; 54 | pageid?: number; 55 | redirects?: Redirect[]; 56 | text?: HtmlContent; 57 | displaytitle?: string; 58 | headhtml?: HtmlContent; 59 | categorieshtml?: HtmlContent; 60 | } 61 | 62 | export interface HtmlContent { 63 | "*"?: string; 64 | } 65 | 66 | export interface Redirect { 67 | from?: string; 68 | to?: string; 69 | } 70 | 71 | // Converts JSON types to/from your types 72 | // and asserts the results at runtime 73 | @staticObjectConverter() 74 | export class ViewConvert { 75 | public static toResult(json: unknown): GetViewResult { 76 | return cast(json, r("GetViewResult"), getViewTypeMap); 77 | } 78 | public static resultToJson(value: GetViewResult): unknown { 79 | return uncast(value, r("GetViewResult"), getViewTypeMap); 80 | } 81 | } 82 | 83 | /* eslint-disable @typescript-eslint/naming-convention */ 84 | const getViewTypeMap: TypeMap = { 85 | "GetViewResult": o([ 86 | { json: "parse", js: "parse", typ: u(undefined, r("Parse")) }, 87 | mWErrorTypeMapInline, 88 | mWWarningsTypeMapInline, 89 | { json: "servedby", js: "servedby", typ: u(undefined, "") }, 90 | ], false), 91 | ...mWErrorTypeMapOutline, 92 | ...mWWarningsTypeMapOutline, 93 | "Parse": o([ 94 | { json: "title", js: "title", typ: u(undefined, "") }, 95 | { json: "pageid", js: "pageid", typ: u(undefined, 0) }, 96 | { json: "redirects", js: "redirects", typ: u(undefined, a(r("Redirect"))) }, 97 | { json: "text", js: "text", typ: u(undefined, r("HtmlContent")) }, 98 | { json: "displaytitle", js: "displaytitle", typ: u(undefined, "") }, 99 | { json: "headhtml", js: "headhtml", typ: u(undefined, r("HtmlContent")) }, 100 | { json: "categorieshtml", js: "categorieshtml", typ: u(undefined, r("HtmlContent")) }, 101 | ], false), 102 | "HtmlContent": o([ 103 | { json: "*", js: "*", typ: u(undefined, "") }, 104 | ], false), 105 | "Redirect": o([ 106 | { json: "from", js: "from", typ: u(undefined, "") }, 107 | { json: "to", js: "to", typ: u(undefined, "") }, 108 | ], false), 109 | }; 110 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/oldTokens.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { u, o, r, cast, uncast, TypeMap } from "../convertFunction"; 7 | import { staticObjectConverter } from "../IObjectConverter"; 8 | 9 | /* 10 | OldTokensResult { 11 | tokens?: Tokens { 12 | edittoken?: string 13 | } 14 | } 15 | */ 16 | 17 | export interface OldTokensResult { 18 | tokens?: Tokens; 19 | } 20 | 21 | export interface Tokens { 22 | edittoken?: string; 23 | } 24 | 25 | @staticObjectConverter() 26 | export class OldTokensConvert { 27 | public static toResult(json: unknown): OldTokensResult { 28 | return cast(json, r("OldTokensResult"), oldTokensTypeMap); 29 | } 30 | 31 | public static resultToJson(value: OldTokensResult): unknown { 32 | return uncast(value, r("OldTokensResult"), oldTokensTypeMap); 33 | } 34 | } 35 | 36 | /* eslint-disable @typescript-eslint/naming-convention */ 37 | const oldTokensTypeMap: TypeMap = { 38 | "OldTokensResult": o([ 39 | { json: "tokens", js: "tokens", typ: u(undefined, r("Tokens")) }, 40 | ], false), 41 | "Tokens": o([ 42 | { json: "edittoken", js: "edittoken", typ: u(undefined, "") }, 43 | ], false), 44 | }; 45 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/readPage.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { MWError, mWErrorTypeMapInline, mWErrorTypeMapOutline, MWWarnings, mWWarningsTypeMapInline, mWWarningsTypeMapOutline } from "./commonInterface"; 7 | import { a, u, o, m, r, uncast, cast, TypeMap } from "../convertFunction"; 8 | import { staticObjectConverter } from "../IObjectConverter"; 9 | 10 | /* 11 | ReadPageResult { 12 | error?: Error { 13 | code: string, 14 | info: string, 15 | *: string 16 | }, 17 | warnings?: MWWarnings { 18 | main: WarnMain { 19 | *: string 20 | } 21 | } 22 | batchcomplete?: string, 23 | query?: Query { 24 | normalized?: Jump[] { 25 | from?: string, 26 | to?: string 27 | }, 28 | redirects?: Jump[] { 29 | from?: string, 30 | to?: string 31 | }, 32 | pages?: { 33 | [key: string]: Page { 34 | pageid?: number, 35 | ns?: number, 36 | title?: string, 37 | revisions?: Revision[] { 38 | revid?: number, 39 | parentid?: number, 40 | slots?: Slots { 41 | main?: Main { 42 | main?: Main { 43 | contentmodel?: string, 44 | contentformat?: string, 45 | *?: string 46 | } 47 | } 48 | } 49 | // Old 50 | contentformat?: string, 51 | contentmodel?: string, 52 | *?: string 53 | }, 54 | missing?: string, 55 | invalidreason?: string, 56 | invalid?: string 57 | } 58 | }, 59 | interwiki?: Interwiki[] { 60 | title?: string, 61 | iw?: string 62 | } 63 | } 64 | } 65 | */ 66 | 67 | export interface ReadPageResult { 68 | batchcomplete?: string; 69 | query?: Query; 70 | error?: MWError; 71 | warnings?: MWWarnings; 72 | 73 | } 74 | 75 | export interface Query { 76 | normalized?: Jump[]; 77 | redirects?: Jump[]; 78 | pages?: { [key: string]: Page }; 79 | interwiki?: Interwiki[]; 80 | } 81 | 82 | export interface Interwiki { 83 | title?: string; 84 | iw?: string; 85 | } 86 | 87 | export interface Jump { 88 | from?: string; 89 | to?: string; 90 | } 91 | 92 | export interface Page { 93 | pageid?: number; 94 | ns?: number; 95 | title?: string; 96 | revisions?: Revision[]; 97 | missing?: string; 98 | invalidreason?: string; 99 | invalid?: string; 100 | } 101 | 102 | export interface Revision { 103 | revid?: number; 104 | parentid?: number; 105 | slots?: Slots; 106 | /** 107 | * Outdated 108 | * 109 | * slots.main.contentformat: string 110 | */ 111 | contentformat?: string; 112 | /** 113 | * Outdated 114 | * 115 | * slots.main.contentmodel: string 116 | */ 117 | contentmodel?: string; 118 | /** 119 | * Outdated 120 | * 121 | * slots.main.*: string 122 | */ 123 | "*"?: string; 124 | } 125 | 126 | export interface Slots { 127 | main?: Main; 128 | } 129 | 130 | export interface Main { 131 | contentmodel?: string; 132 | contentformat?: string; 133 | "*"?: string; 134 | } 135 | 136 | /** ReadPageResultConvert */ 137 | @staticObjectConverter() 138 | export class ReadPageConvert { 139 | public static toResult(json: unknown): ReadPageResult { 140 | return cast(json, r("ReadPageResult"), readPageResultTypeMap); 141 | } 142 | 143 | public static resultToJson(value: ReadPageResult): unknown { 144 | return uncast(value, r("ReadPageResult"), readPageResultTypeMap); 145 | } 146 | } 147 | 148 | /* eslint-disable @typescript-eslint/naming-convention */ 149 | const readPageResultTypeMap: TypeMap = { 150 | "ReadPageResult": o([ 151 | mWWarningsTypeMapInline, 152 | mWErrorTypeMapInline, 153 | { json: "batchcomplete", js: "batchcomplete", typ: u(undefined, "") }, 154 | { json: "query", js: "query", typ: u(undefined, r("Query")) }, 155 | ], false), 156 | ...mWWarningsTypeMapOutline, 157 | ...mWErrorTypeMapOutline, 158 | "Query": o([ 159 | { json: "normalized", js: "normalized", typ: u(undefined, a(r("Jump"))) }, 160 | { json: "redirects", js: "redirects", typ: u(undefined, a(r("Jump"))) }, 161 | { json: "pages", js: "pages", typ: u(undefined, m(r("Page"))) }, 162 | { json: "interwiki", js: "interwiki", typ: u(undefined, a(r("Interwiki"))) }, 163 | ], false), 164 | "Interwiki": o([ 165 | { json: "title", js: "title", typ: u(undefined, "") }, 166 | { json: "iw", js: "iw", typ: u(undefined, "") }, 167 | ], false), 168 | "Jump": o([ 169 | { json: "from", js: "from", typ: u(undefined, "") }, 170 | { json: "to", js: "to", typ: u(undefined, "") }, 171 | ], false), 172 | "Page": o([ 173 | { json: "pageid", js: "pageid", typ: u(undefined, 0) }, 174 | { json: "ns", js: "ns", typ: u(undefined, 0) }, 175 | { json: "title", js: "title", typ: u(undefined, "") }, 176 | { json: "revisions", js: "revisions", typ: u(undefined, a(r("Revision"))) }, 177 | { json: "missing", js: "missing", typ: u(undefined, "") }, 178 | { json: "invalidreason", js: "invalidreason", typ: u(undefined, "") }, 179 | { json: "invalid", js: "invalid", typ: u(undefined, "") }, 180 | ], false), 181 | "Revision": o([ 182 | { json: "revid", js: "revid", typ: u(undefined, 0) }, 183 | { json: "parentid", js: "parentid", typ: u(undefined, 0) }, 184 | { json: "slots", js: "slots", typ: u(undefined, r("Slots")) }, 185 | // Outdated 186 | { json: "contentmodel", js: "contentmodel", typ: u(undefined, "") }, 187 | { json: "contentformat", js: "contentformat", typ: u(undefined, "") }, 188 | { json: "*", js: "*", typ: u(undefined, "") }, 189 | ], false), 190 | "Slots": o([ 191 | { json: "main", js: "main", typ: u(undefined, r("Main")) }, 192 | ], false), 193 | "Main": o([ 194 | { json: "contentmodel", js: "contentmodel", typ: u(undefined, "") }, 195 | { json: "contentformat", js: "contentformat", typ: u(undefined, "") }, 196 | { json: "*", js: "*", typ: u(undefined, "") }, 197 | ], false), 198 | }; 199 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/tags.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { a, cast, o, r, TypeMap, u, uncast } from "../convertFunction"; 7 | import { staticObjectConverter } from "../IObjectConverter"; 8 | 9 | /* 10 | TagsResult { 11 | batchcomplete?: string, 12 | continue?: Continue { 13 | tgcontinue: string, 14 | continue: string 15 | }, 16 | query: Query { 17 | tags: Tag[] { 18 | name: number | string, 19 | defined?: string, 20 | active?: string 21 | } 22 | }, 23 | limits?: Limits { 24 | tags?: number 25 | } 26 | } 27 | */ 28 | 29 | export interface TagsResult { 30 | batchcomplete?: string; 31 | continue?: Continue; 32 | query: Query; 33 | limits?: Limits; 34 | } 35 | 36 | export interface Continue { 37 | tgcontinue: string; 38 | continue: string; 39 | } 40 | 41 | export interface Limits { 42 | tags: number; 43 | } 44 | 45 | export interface Query { 46 | tags: Tag[]; 47 | } 48 | 49 | export interface Tag { 50 | name: number | string; 51 | defined?: string; 52 | active?: string; 53 | } 54 | 55 | @staticObjectConverter() 56 | export class TagsConvert { 57 | public static toResult(json: unknown): TagsResult { 58 | return cast(json, r("TagsResult"), tagsTypeMap); 59 | } 60 | 61 | public static resultToJson(value: TagsResult): unknown { 62 | return uncast(value, r("TagsResult"), tagsTypeMap); 63 | } 64 | } 65 | 66 | /* eslint-disable @typescript-eslint/naming-convention */ 67 | const tagsTypeMap: TypeMap = { 68 | "TagsResult": o([ 69 | { json: "batchcomplete", js: "batchcomplete", typ: u(undefined, "") }, 70 | { json: "continue", js: "continue", typ: u(undefined, r("Continue")) }, 71 | { json: "query", js: "query", typ: r("Query") }, 72 | { json: "limits", js: "limits", typ: u(undefined, r("Limits")) }, 73 | ], false), 74 | "Continue": o([ 75 | { json: "tgcontinue", js: "tgcontinue", typ: "" }, 76 | { json: "continue", js: "continue", typ: "" }, 77 | ], false), 78 | "Limits": o([ 79 | { json: "tags", js: "tags", typ: 0 }, 80 | ], false), 81 | "Query": o([ 82 | { json: "tags", js: "tags", typ: a(r("Tag")) }, 83 | ], false), 84 | "Tag": o([ 85 | { json: "name", js: "name", typ: u(0, "") }, 86 | { json: "defined", js: "defined", typ: u(undefined, "") }, 87 | { json: "active", js: "active", typ: u(undefined, "") }, 88 | ], false), 89 | }; 90 | -------------------------------------------------------------------------------- /src/interface_definition/api_interface/tokens.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import { u, o, r, cast, uncast, TypeMap } from "../convertFunction"; 7 | import { staticObjectConverter } from "../IObjectConverter"; 8 | 9 | /* 10 | TokensResult { 11 | batchcomplete?: string, 12 | query?: Query { 13 | tokens?: Tokens { 14 | csrftoken?: string 15 | } 16 | } 17 | } 18 | */ 19 | 20 | export interface TokensResult { 21 | batchcomplete?: string; 22 | query?: Query; 23 | } 24 | 25 | export interface Query { 26 | tokens?: Tokens; 27 | } 28 | 29 | export interface Tokens { 30 | csrftoken?: string; 31 | } 32 | 33 | @staticObjectConverter() 34 | export class TokensConvert { 35 | public static toResult(json: unknown): TokensResult { 36 | return cast(json, r("TokensResult"), tokensTypeMap); 37 | } 38 | 39 | public static resultToJson(value: TokensResult): unknown { 40 | return uncast(value, r("TokensResult"), tokensTypeMap); 41 | } 42 | } 43 | 44 | /* eslint-disable @typescript-eslint/naming-convention */ 45 | const tokensTypeMap: TypeMap = { 46 | "TokensResult": o([ 47 | { json: "batchcomplete", js: "batchcomplete", typ: u(undefined, "") }, 48 | { json: "query", js: "query", typ: u(undefined, r("Query")) }, 49 | ], false), 50 | "Query": o([ 51 | { json: "tokens", js: "tokens", typ: u(undefined, r("Tokens")) }, 52 | ], false), 53 | "Tokens": o([ 54 | { json: "csrftoken", js: "csrftoken", typ: u(undefined, "") }, 55 | ], false), 56 | }; 57 | -------------------------------------------------------------------------------- /src/interface_definition/convertFunction.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | // These functions will throw an error if the JSON doesn't 7 | // match the expected interface, even if the JSON is valid. 8 | 9 | /* eslint-disable @typescript-eslint/no-explicit-any */ 10 | 11 | function invalidValue(typ: Typ, val: unknown, key: any = ''): never { 12 | if (key) { 13 | throw Error(`Invalid value for key "${key}". Expected type ${JSON.stringify(typ)} but got ${JSON.stringify(val)}`); 14 | } 15 | throw Error(`Invalid value ${JSON.stringify(val)} for type ${JSON.stringify(typ)}`,); 16 | } 17 | 18 | function jsonToJSProps(typ: Typ): any { 19 | if (typ.jsonToJS === undefined) { 20 | const map: any = {}; 21 | typ.props.forEach((p: any) => map[p.json] = { key: p.js, typ: p.typ }); 22 | typ.jsonToJS = map; 23 | } 24 | return typ.jsonToJS; 25 | } 26 | 27 | function jsToJSONProps(typ: Typ): any { 28 | if (typ.jsToJSON === undefined) { 29 | const map: any = {}; 30 | typ.props.forEach((p: any) => map[p.js] = { key: p.json, typ: p.typ }); 31 | typ.jsToJSON = map; 32 | } 33 | return typ.jsToJSON; 34 | } 35 | 36 | function transform(value: unknown, type: Typ, getProps: any, key: any, typeMap: TypeMap): unknown { 37 | function transformPrimitive(typ: string, val: any): any { 38 | if (typeof typ === typeof val) { return val; } 39 | return invalidValue(typ, val, key); 40 | } 41 | 42 | function transformUnion(typs: any[], val: any): any { 43 | // val must validate against one typ in typs 44 | const l = typs.length; 45 | for (let i = 0; i < l; i++) { 46 | const typ = typs[i]; 47 | try { 48 | return transform(val, typ, getProps, "", typeMap); 49 | } catch { 50 | continue; 51 | } 52 | } 53 | return invalidValue(typs, val); 54 | } 55 | 56 | function transformEnum(cases: string[], val: any): any { 57 | if (cases.indexOf(val) !== -1) { return val; } 58 | return invalidValue(cases, val); 59 | } 60 | 61 | function transformArray(typ: any, val: any): any { 62 | // val must be an array with no invalid elements 63 | if (!Array.isArray(val)) { return invalidValue("array", val); } 64 | return val.map(el => transform(el, typ, getProps, "", typeMap)); 65 | } 66 | 67 | function transformDate(val: any): any { 68 | if (val === null) { 69 | return null; 70 | } 71 | const d = new Date(val); 72 | if (isNaN(d.valueOf())) { 73 | return invalidValue("Date", val); 74 | } 75 | return d; 76 | } 77 | 78 | function transformObject(props: { [k: string]: any }, additional: any, val: any): any { 79 | if (val === null || typeof val !== "object" || Array.isArray(val)) { 80 | return invalidValue("object", val); 81 | } 82 | const result: any = {}; 83 | Object.getOwnPropertyNames(props).forEach(item => { 84 | const prop = props[item]; 85 | const v = Object.prototype.hasOwnProperty.call(val, item) ? val[item] : undefined; 86 | result[prop.key] = transform(v, prop.typ, getProps, prop.key, typeMap); 87 | }); 88 | Object.getOwnPropertyNames(val).forEach(item => { 89 | if (!Object.prototype.hasOwnProperty.call(props, item)) { 90 | result[item] = transform(val[item], additional, getProps, item, typeMap);/* val[key]; */ 91 | } 92 | }); 93 | return result; 94 | } 95 | 96 | function instanceOfR(o: any): o is R { 97 | return o.ref !== undefined; 98 | } 99 | 100 | if (type === "any") { return value; } 101 | if (type === null) { 102 | if (value === null) { return value; } 103 | return invalidValue(type, value); 104 | } 105 | if (type === false) { return invalidValue(type, value); } 106 | while (typeof type === "object" && instanceOfR(type)) { 107 | type = typeMap[type.ref]; 108 | } 109 | if (Array.isArray(type)) { return transformEnum(type, value); } 110 | if (typeof type === "object") { 111 | // eslint-disable-next-line no-prototype-builtins 112 | return type.hasOwnProperty("unionMembers") ? transformUnion(type.unionMembers, value) 113 | // eslint-disable-next-line no-prototype-builtins 114 | : type.hasOwnProperty("arrayItems") ? transformArray(type.arrayItems, value) 115 | // eslint-disable-next-line no-prototype-builtins 116 | : type.hasOwnProperty("props") ? transformObject(getProps(type), type.additional, value) 117 | : invalidValue(type, value); 118 | } 119 | // Numbers can be parsed by Date but shouldn't be. 120 | if (type === Date && typeof value !== "number") { return transformDate(value); } 121 | return transformPrimitive(type, value); 122 | } 123 | 124 | export function cast(val: unknown, typ: Typ, typeMap: TypeMap): T { 125 | return transform(val, typ, jsonToJSProps, "", typeMap) as T; 126 | } 127 | 128 | export function uncast(val: T, typ: Typ, typeMap: TypeMap): unknown { 129 | return transform(val, typ, jsToJSONProps, "", typeMap); 130 | } 131 | 132 | export function a(typ: unknown): A { 133 | return { arrayItems: typ }; 134 | } 135 | 136 | export function u(...typs: unknown[]): U { 137 | return { unionMembers: typs }; 138 | } 139 | 140 | export function o(props: Prop[], additional: unknown): MO { 141 | return { props, additional }; 142 | } 143 | 144 | export function m(additional: unknown): MO { 145 | return { props: [], additional }; 146 | } 147 | 148 | export function r(name: string): R { 149 | return { ref: name }; 150 | } 151 | 152 | type A = { arrayItems: unknown }; 153 | type U = { unionMembers: unknown[] }; 154 | type MO = { props: Prop[], additional: unknown }; 155 | type R = { ref: string }; 156 | 157 | type Prop = { json: string, js: string, typ: unknown }; 158 | export type TypeMap = any; 159 | 160 | // type Typ = R | 'any' | false | null; 161 | type Typ = any; 162 | -------------------------------------------------------------------------------- /src/interface_definition/mwbot.d.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | declare module 'mwbot' { 7 | 8 | interface CounterInterface { 9 | total: number; 10 | resolved: number; 11 | fulfilled: number; 12 | rejected: number; 13 | } 14 | 15 | /** 16 | * MWBot library 17 | * 18 | * @author Simon Heimler 19 | */ 20 | class MWBot { 21 | 22 | ////////////////////////////////////////// 23 | // FIELD // 24 | ////////////////////////////////////////// 25 | 26 | state: object; 27 | editToken: string; 28 | loggedIn: boolean; 29 | createaccountToken: string; 30 | counter: CounterInterface; 31 | // defaultOptions: 32 | 33 | ////////////////////////////////////////// 34 | // CONSTRUCTOR // 35 | ////////////////////////////////////////// 36 | 37 | /** 38 | * Constructs a new MWBot instance 39 | * It is advised to create one bot instance for every API to use 40 | * A bot instance has its own state (e.g. tokens) that is 41 | necessary for some operations 42 | * 43 | * @param {{}} [customOptions] Custom options 44 | * @param {{}} [customRequestOptions] Custom request options 45 | */ 46 | constructor(customOptions?: object, customRequestOptions?: object); 47 | 48 | ////////////////////////////////////////// 49 | // GETTER & SETTER // 50 | ////////////////////////////////////////// 51 | 52 | /** 53 | * Get mwbot version number 54 | * Uses ES5 getter 55 | */ 56 | get version(): any; 57 | 58 | 59 | /** 60 | * Set and overwrite mwbot options 61 | * 62 | * @param {Object} customOptions 63 | */ 64 | public setOptions(customOptions: object): void; 65 | 66 | /** 67 | * Sets and overwrites the raw request options, used by the "request" library 68 | * See https://www.npmjs.com/package/request 69 | * 70 | * @param {{}} customRequestOptions 71 | */ 72 | setGlobalRequestOptions(customRequestOptions: object): void; 73 | 74 | /** 75 | * Sets the API URL for MediaWiki requests 76 | * This can be uses instead of a login, if no actions are used that require one. 77 | * 78 | * @param {String} apiUrl API Url to MediaWiki, e.g. 'https://www.semantic-mediawiki.org/w/api.php' 79 | */ 80 | setApiUrl(apiUrl: string): void; 81 | 82 | ////////////////////////////////////////// 83 | // CORE REQUESTS // 84 | ////////////////////////////////////////// 85 | 86 | /** 87 | * Executes a promisified raw request 88 | * Uses the npm request library 89 | * 90 | * @param {object} requestOptions 91 | * 92 | * @returns {PromiseLike} 93 | */ 94 | rawRequest(requestOptions: object): PromiseLike; 95 | 96 | /** 97 | *Executes a request with the ability to use custom parameters and custom request options 98 | * 99 | * @param {object} params Request Parameters 100 | * @param {object} customRequestOptions Custom request options 101 | * 102 | * @returns {PromiseLike} 103 | */ 104 | request(params: object, customRequestOptions?: object): PromiseLike; 105 | 106 | ////////////////////////////////////////// 107 | // CORE FUNCTIONS // 108 | ////////////////////////////////////////// 109 | 110 | /** 111 | * Executes a Login 112 | * 113 | * @see https://www.mediawiki.org/wiki/API:Login 114 | * 115 | * @param {object} [loginOptions] 116 | * 117 | * @returns {PromiseLike} 118 | */ 119 | login(loginOptions?: object): PromiseLike; 120 | 121 | /** 122 | * Gets an edit token 123 | * This is currently only compatible with MW >= 1.24 124 | * 125 | * @returns {PromiseLike} 126 | */ 127 | getEditToken(): PromiseLike; 128 | 129 | /** 130 | * Gets an edit token 131 | * Requires MW 1.27+ 132 | * 133 | * @returns {PromiseLike} 134 | */ 135 | getCreateaccountToken(): PromiseLike; 136 | 137 | /** 138 | * Combines Login with GetEditToken 139 | * 140 | * @param {object} loginOptions 141 | * 142 | * @returns {PromiseLike} 143 | */ 144 | loginGetEditToken(loginOptions: object): PromiseLike; 145 | 146 | /** 147 | * Combines Login with GetCreateAccountToken 148 | * 149 | * @param {object} loginOptions 150 | * 151 | * @returns {PromiseLike} 152 | */ 153 | loginGetCreateaccountToken(loginOptions: object): PromiseLike; 154 | 155 | ////////////////////////////////////////// 156 | // CRUD OPERATIONS // 157 | ////////////////////////////////////////// 158 | 159 | /** 160 | * Creates a new wiki pages. Does not edit existing ones 161 | * 162 | * @param {string} title 163 | * @param {string} content 164 | * @param {string} [summary] 165 | * @param {object} [customRequestOptions] 166 | * 167 | * @returns {PromiseLike} 168 | */ 169 | create(title: string, content: string, summary?: string, customRequestOptions?: object): PromiseLike; 170 | 171 | /** 172 | * Reads the content / and meta-data of one (or many) wikipages 173 | * 174 | * Wrapper for readWithProps 175 | * 176 | * @param {string} title For multiple Pages use: PageA|PageB|PageC 177 | * @param {object} [customRequestOptions] 178 | * 179 | * @returns {PromiseLike} 180 | */ 181 | read(title: string,/*redirect?: boolean, */ customRequestOptions?: object): PromiseLike; 182 | 183 | // /** 184 | // * Reads the content / and meta-data of one (or many) wikipages 185 | // * 186 | // * Wrapper for readWithPropsFromID 187 | // * 188 | // * @param {number} pageid For multiple Pages use: PageA|PageB|PageC 189 | // * @param {boolean} redirect If the page is a redirection, follow it or stay in the page 190 | // * @param {object} [customRequestOptions] 191 | // * 192 | // * @returns {PromiseLike} 193 | // */ 194 | // readFromID(pageid: number, redirect: boolean, customRequestOptions?: object): PromiseLike; 195 | 196 | // /** 197 | // * Reads the content / and meta-data of one (or many) wikipages based on specific parameters 198 | // * 199 | // * @param {string} title For multiple Pages use: PageA|PageB|PageC 200 | // * @param {string} props For multiple Props use: user|userid|content 201 | // * @param {boolean} redirect If the page is a redirection, follow it or stay in the page 202 | // * @param {object} [customRequestOptions] 203 | // * 204 | // * @returns {PromiseLike} 205 | // */ 206 | // readWithProps(title: string, props: string, redirect: boolean, customRequestOptions?: object): PromiseLike; 207 | 208 | // /** 209 | // * Reads the content / and meta-data of one (or many) wikipages based on specific parameters 210 | // * 211 | // * @param {number} pageid For multiple Pages use: PageA|PageB|PageC 212 | // * @param {string} props For multiple Props use: user|userid|content 213 | // * @param {boolean} redirect If the page is a redirection, follow it or stay in the page 214 | // * @param {object} [customRequestOptions] 215 | // * 216 | // * @returns {PromiseLike} 217 | // */ 218 | // readWithPropsFromID(pageid: number, props: string, redirect: boolean, customRequestOptions?: object): PromiseLike; 219 | 220 | /** 221 | * Edits a new wiki pages. Creates a new page if it does not exist yet. 222 | * 223 | * @param {string} title 224 | * @param {string} content 225 | * @param {string} [summary] 226 | * @param {object} [customRequestOptions] 227 | * 228 | * @returns {PromiseLike} 229 | */ 230 | edit(title: string, content: string, summary?: string, customRequestOptions?: object): PromiseLike; 231 | 232 | /** 233 | * Updates existing wiki pages. Does not create new ones. 234 | * 235 | * @param {string} title 236 | * @param {string} content 237 | * @param {string} [summary] 238 | * @param {object} [customRequestOptions] 239 | * 240 | * @returns {PromiseLike} 241 | */ 242 | update(title: string, content: string, summary?: string, customRequestOptions?: object): PromiseLike; 243 | 244 | /** 245 | * Updates existing wiki pages. Does not create new ones. 246 | * 247 | * @param {number} pageid 248 | * @param {string} content 249 | * @param {string} [summary] 250 | * @param {object} [customRequestOptions] 251 | * 252 | * @returns {PromiseLike} 253 | */ 254 | updateFromID(pageid: number, content: string, summary?: string, customRequestOptions?: object): PromiseLike; 255 | 256 | /** 257 | * Deletes a new wiki page 258 | * 259 | * @param {string} title 260 | * @param {string} [reason] 261 | * @param {object} [customRequestOptions] 262 | * 263 | * @returns {PromiseLike} 264 | */ 265 | delete(title: string, reason?: string, customRequestOptions?: object): PromiseLike; 266 | 267 | /** 268 | * Moves a wiki page 269 | * 270 | * @param {string} oldName 271 | * @param {string} newName 272 | * @param {string} [reason] 273 | * @param {object} [customRequestOptions] 274 | * 275 | * @returns {PromiseLike} 276 | */ 277 | move(oldTitle: string, newTitle: string, reason?: string, customRequestOptions?: object): PromiseLike; 278 | 279 | /** 280 | * Uploads a file 281 | * 282 | * @param {string} [title] nullable, if null, it will be the same as the basename of pathToFile arg. 283 | * @param {string} pathToFile 284 | * @param {string} [comment] 285 | * @param {object} [customParams] 286 | * @param {object} [customRequestOptions] 287 | * 288 | * @returns {PromiseLike} 289 | */ 290 | upload(title: string, pathToFile: string, comment?: string, customParams?: object, customRequestOptions?: object): PromiseLike; 291 | 292 | /** 293 | * Uploads a file and overwrites existing ones 294 | * 295 | * @param {string} [title] nullable, if null, it will be the same as the basename of pathToFile arg. 296 | * @param {string} pathToFile 297 | * @param {string} [comment] 298 | * @param {object} [customParams] 299 | * @param {object} [customRequestOptions] 300 | * 301 | * @returns {PromiseLike} 302 | */ 303 | uploadOverwrite(title: string, pathToFile: string, comment?: string, customParams?: object, customRequestOptions?: object): PromiseLike; 304 | 305 | 306 | ////////////////////////////////////////// 307 | // CONVENIENCE FUNCTIONS // 308 | ////////////////////////////////////////// 309 | 310 | /** 311 | * Combines all standard CRUD operations into one concurrent batch operation 312 | * The batch request will also print log messages about the current job status 313 | * It includes some more detailed error handling 314 | * 315 | * If the concurrency is set to 1, it ensures a sequential order 316 | * by switching from Promise.map to Promise.mapSeries 317 | * 318 | * @param {object|array} jobs 319 | * @param {string} [summary] 320 | * @param {number} [concurrency] 321 | * @param {object} [customRequestOptions] 322 | * 323 | * @returns {PromiseLike} 324 | */ 325 | batch(jobs: object | any[], summary?: string, concurrency?: number, customRequestOptions?: object): PromiseLike; 326 | 327 | /** 328 | * Execute an ASK Query 329 | * 330 | * @param {string} query 331 | * @param {string} [apiUrl] 332 | * @param {object} [customRequestOptions] 333 | * 334 | * @returns {PromiseLike} 335 | */ 336 | askQuery(query: string, apiUrl?: string, customRequestOptions?: object): PromiseLike; 337 | 338 | /** 339 | * Executes a SPARQL Query 340 | * Defaults to use the wikidata endpoint 341 | * 342 | * @param {string} query 343 | * @param {string} [endpointUrl] 344 | * @param {object} [customRequestOptions] 345 | * 346 | * @returns {PromiseLike} 347 | */ 348 | sparqlQuery(query: string, endpointUrl?: string, customRequestOptions?: object): PromiseLike; 349 | 350 | ////////////////////////////////////////// 351 | // HELPER FUNCTIONS // 352 | ////////////////////////////////////////// 353 | 354 | /** 355 | * Recursively merges two objects 356 | * Takes care that the two objects are not mutated 357 | * 358 | * @param {object} parent Parent Object 359 | * @param {object} child Child Object; overwrites parent properties 360 | * 361 | * @returns {object} Merged Object 362 | */ 363 | static merge(parent: object, child: object): object; 364 | 365 | /** 366 | * Prints status information about a completed request 367 | * 368 | * @param status 369 | * @param currentCounter 370 | * @param totalCounter 371 | * @param operation 372 | * @param pageName 373 | * @param reason 374 | */ 375 | static logStatus(status: any, currentCounter: any, totalCounter: any, operation: any, pageName: any, reason: any): void; 376 | } 377 | 378 | export = MWBot; 379 | } 380 | -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | 8 | import { runTests } from '@vscode/test-electron'; 9 | 10 | async function main(): Promise { 11 | try { 12 | console.log("Starting test."); 13 | // The folder containing the Extension Manifest package.json 14 | // Passed to `--extensionDevelopmentPath` 15 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 16 | 17 | // The path to test runner 18 | // Passed to --extensionTestsPath 19 | const extensionTestsPath = path.resolve(__dirname, './suite/index-node'); 20 | console.log("Running test."); 21 | // Download VS Code, unzip it and run the integration test 22 | await runTests({ 23 | extensionDevelopmentPath, 24 | extensionTestsPath, 25 | launchArgs: ["--disable-extensions"] 26 | }); 27 | } catch (err) { 28 | console.log(err); 29 | console.error('Failed to run tests'); 30 | process.exit(1); 31 | } 32 | } 33 | 34 | main(); 35 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | // You can import and use all API from the 'vscode' module 7 | // as well as import your extension to test it 8 | // import * as vscode from 'vscode'; 9 | import * as assert from 'assert'; 10 | 11 | // import * as myExtension from '../extension'; 12 | 13 | // suite('Extension TestSuite', () => { 14 | // vscode.window.showInformationMessage('Start all tests.'); 15 | 16 | // test('Sample Test', () => { 17 | // assert.strictEqual(-1, [1, 2, 3].indexOf(5)); 18 | // assert.strictEqual(-1, [1, 2, 3].indexOf(0)); 19 | // }); 20 | // }); 21 | 22 | 23 | import { alterNativeValues } from '../../export_command/wikimedia_function/args'; 24 | suite('WikimediaFunction Args TestSuite', () => { 25 | test('AlterNativeValues Test', () => { 26 | 27 | // Arrange 28 | // meta 29 | const first = "first", second = "second", third = "third", alt = "|"; 30 | 31 | // Act 32 | const firstSecond = alterNativeValues(first, second); 33 | const undUndThird = alterNativeValues(undefined, undefined, third); 34 | 35 | // Assert 36 | assert.strictEqual(firstSecond, first + alt + second); 37 | assert.strictEqual(undUndThird, third); 38 | }); 39 | }); 40 | 41 | import { getContentInfo } from '../../export_command/wikimedia_function/page'; 42 | suite('WikimediaFunction Core TestSuite', () => { 43 | test('GetContentInfo Test', () => { 44 | 45 | // Arrange 46 | // meta 47 | const pageTitle = "Some String"; 48 | const content = "Content here"; 49 | // test set 50 | const hasStr = `<%-- [PAGE_INFO] PageTitle= #${pageTitle}# 51 | pageID = #1# 52 | [END_PAGE_INFO] 53 | --%>\r${content}`; 54 | const noStr = content; 55 | const mutiStr = `<%-- [PAGE_INFO] 56 | Comment=#Please do not remove this struct. It's record contains some important informations of edit. This struct will be removed automatically after you push edits.# 57 | PageTitle=#User:Rowe Wilson Frederisk Holme# 58 | PageID=#6364830# 59 | RevisionID=#60746059# 60 | ContentModel=#wikitext# 61 | ContentFormat=#text/x-wiki# 62 | [END_PAGE_INFO] --%> 63 | {{Soft redirect|User:Роу Уилсон Фредериск Холм}} 64 | {{patrol}}`; 65 | 66 | // Act 67 | const hasInfo = getContentInfo(hasStr); 68 | const noInfo = getContentInfo(noStr); 69 | const mutiInfo = getContentInfo(mutiStr); 70 | 71 | // Assert 72 | // hasInfo 73 | assert.strictEqual(hasInfo.content, content, "hasInfo content failed"); 74 | assert.deepStrictEqual(hasInfo.info, { 75 | pageTitle: pageTitle, 76 | pageID: "1", 77 | revisionID: undefined, 78 | contentFormat: undefined, 79 | contentModel: undefined 80 | }, "hasInfo info failed"); 81 | // noInfo 82 | assert.strictEqual(noInfo.content, content, "noInfo content failed"); 83 | assert.deepStrictEqual(noInfo.info, undefined, "noInfo info failed"); 84 | // mutiInfo 85 | assert.notStrictEqual(mutiInfo.info, undefined, "mutiInfo info failed"); 86 | }); 87 | }); 88 | 89 | import { parseArgs } from '../../export_command/uri_function/uri'; 90 | suite('URIFunction URI TestSuite', () => { 91 | 92 | test('Parse Test', () => { 93 | 94 | // Arrange 95 | // meta 96 | const arg1 = "arg1", arg2 = "arg2", bare = "string"; 97 | // test sets 98 | const singleStr = `${arg1}=1`; 99 | const mutiStr = `${arg1}=1&${arg2}=2`; 100 | const bareStr = bare; 101 | const mutiWithBareStr = `${arg1}=1&${bare}`; 102 | 103 | // Act 104 | const singlePar = parseArgs(singleStr); 105 | const mutiPar = parseArgs(mutiStr); 106 | const barePar = parseArgs(bareStr); 107 | const mutiWithBarePar = parseArgs(mutiWithBareStr); 108 | 109 | // Assert 110 | // singlePar 111 | assert.strictEqual(singlePar[arg1], "1"); 112 | // mutiPar 113 | assert.strictEqual(mutiPar[arg1], "1"); 114 | assert.strictEqual(mutiPar[arg2], "2"); 115 | // barePar 116 | assert.strictEqual(barePar[bare], ""); 117 | // muti&barePar 118 | assert.strictEqual(mutiWithBarePar[arg1], "1"); 119 | assert.strictEqual(mutiWithBarePar[bare], ""); 120 | }); 121 | }); 122 | 123 | import { getReplacedString } from '../../export_command/cite_function/web'; 124 | suite('CiteFunction Web TestSuite', () => { 125 | test('GetReplacedString Test', () => { 126 | 127 | // Arrange 128 | // meta data 129 | const tagName = "title"; 130 | const tagBegin = ``; 131 | const tagEnd = ``; 132 | const argStr = `{$${tagName}}`; 133 | const content = "some string here"; 134 | const title = "TITLE"; 135 | const noTitle = undefined; 136 | // test set 137 | const onlyContentStr = content + content; 138 | const onlyTagStr = tagBegin + content + tagEnd + content; 139 | const onlyArgStr = content + argStr + content; 140 | const bothInStr = tagBegin + content + argStr + content + tagEnd; 141 | const bothOutStr = tagBegin + content + tagEnd + content + argStr; 142 | 143 | //Act 144 | // title = "TITLE" 145 | const tOnlyContentStr = getReplacedString(onlyContentStr, tagName, title); 146 | const tOnlyTagStr = getReplacedString(onlyTagStr, tagName, title); 147 | const tOnlyArgStr = getReplacedString(onlyArgStr, tagName, title); 148 | const tBothInStr = getReplacedString(bothInStr, tagName, title); 149 | const tBothOutStr = getReplacedString(bothOutStr, tagName, title); 150 | // noTitle = undefined 151 | const nOnlyContentStr = getReplacedString(onlyContentStr, tagName, noTitle); 152 | const nOnlyTagStr = getReplacedString(onlyTagStr, tagName, noTitle); 153 | const nOnlyArgStr = getReplacedString(onlyArgStr, tagName, noTitle); 154 | const nBothInStr = getReplacedString(bothInStr, tagName, noTitle); 155 | const nBothOutStr = getReplacedString(bothOutStr, tagName, noTitle); 156 | 157 | // Assert 158 | // title 159 | assert.strictEqual(tOnlyContentStr, content + content, "title only content failed"); 160 | assert.strictEqual(tOnlyTagStr, content + content, "title only tag failed"); 161 | assert.strictEqual(tOnlyArgStr, content + title + content, "title only arg failed"); 162 | assert.strictEqual(tBothInStr, content + title + content, "title both in failed"); 163 | assert.strictEqual(tBothOutStr, content + content + title, "title both out failed"); 164 | // notitle 165 | assert.strictEqual(nOnlyContentStr, content + content, "no only content failed"); 166 | assert.strictEqual(nOnlyTagStr, content, "no only tag failed"); 167 | assert.strictEqual(nOnlyArgStr, content + content, "no only arg failed"); 168 | assert.strictEqual(nBothInStr, "", "no both in falid"); 169 | assert.strictEqual(nBothOutStr, content, "no both out failed"); 170 | }); 171 | }); 172 | -------------------------------------------------------------------------------- /src/test/suite/index-node.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | import * as path from 'path'; 7 | import Mocha from 'mocha'; 8 | import glob from 'glob'; 9 | 10 | export function run(): Promise { 11 | // Create the mocha test 12 | const mocha: Mocha = new Mocha({ 13 | ui: 'tdd', 14 | color: true 15 | }); 16 | // mocha.useColors(true); 17 | 18 | const testsRoot: string = path.resolve(__dirname, '..'); 19 | 20 | return new Promise((c, e): void => { 21 | glob('**/**.test.js', { cwd: testsRoot }, (err: Error | null, files: string[]) => { 22 | if (err) { 23 | return e(err); 24 | } 25 | 26 | // Add files to the test suite 27 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 28 | 29 | try { 30 | // Run the mocha test 31 | mocha.run((failures: number) => { 32 | if (failures > 0) { 33 | e(new Error(`${failures} tests failed.`)); 34 | } else { 35 | c(); 36 | } 37 | }); 38 | } catch (err) { 39 | console.error(err); 40 | e(err); 41 | } 42 | }); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /src/test/suite/index-web.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | // eslint-disable-next-line @typescript-eslint/no-require-imports 7 | require('mocha/mocha'); // import the mocha web build 8 | 9 | export function run(): Promise { 10 | 11 | return new Promise((c, e) => { 12 | mocha.setup({ 13 | ui: 'tdd', 14 | reporter: undefined 15 | }); 16 | 17 | // bundles all files in the current directory matching `*.test` 18 | const importAll = (r: __WebpackModuleApi.RequireContext) => r.keys().forEach(r); 19 | 20 | importAll(require.context('.', true, /\.test$/)); 21 | 22 | try { 23 | // Run the mocha test 24 | mocha.run(failures => { 25 | if (failures > 0) { 26 | e(new Error(`${failures} tests failed.`)); 27 | } else { 28 | c(); 29 | } 30 | }); 31 | } catch (err) { 32 | console.error(err); 33 | e(err); 34 | } 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Rowe Wilson Frederisk Holme. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | { 6 | "compilerOptions": { 7 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 8 | /* Basic Options */ 9 | "target": "ES6", 10 | "module": "CommonJS", 11 | "lib": [ 12 | "ES6", 13 | "WebWorker" 14 | ], 15 | "sourceMap": true, 16 | "outDir": "./out/", 17 | "rootDir": "./src/", 18 | /* Strict Type-Checking Options */ 19 | "strict": true, /* enable all strict type-checking options */ 20 | /* Additional Checks */ 21 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 22 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 23 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 24 | // "allowSyntheticDefaultImports": true, 25 | "esModuleInterop": true, 26 | "importHelpers": true, 27 | "experimentalDecorators": true, 28 | "forceConsistentCasingInFileNames": true, 29 | }, 30 | "exclude": [ 31 | "./.vscode-test/*" 32 | ], 33 | "include": [ 34 | "./src/**/*.ts", 35 | "./node_modules/vscode/vscode.d.ts", 36 | "./node_modules/vscode/lib/*" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | //@ts-check 6 | 7 | "use strict"; 8 | 9 | const path = require('path'); 10 | const webpack = require('webpack'); 11 | // const CircularDependencyPlugin = require('circular-dependency-plugin'); 12 | 13 | /** @type {webpack.Configuration} */ 14 | const webConfig = { 15 | context: __dirname, 16 | mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 17 | target: "webworker", // web extensions run in a webworker context 18 | entry: { 19 | "extension-web": "./src/extension-web.ts", // source of the web extension main file 20 | "test/suite/index-web": "./src/test/suite/index-web.ts", // source of the web extension test runner 21 | }, 22 | output: { 23 | filename: "[name].js", 24 | path: path.join(__dirname, "./dist"), 25 | libraryTarget: "commonjs", 26 | }, 27 | resolve: { 28 | mainFields: ["browser", "module", "main"], // look for `browser` entry point in imported node modules 29 | extensions: [".ts", ".js"], // support ts-files and js-files 30 | alias: { 31 | // provides alternate implementation for node module and source files 32 | }, 33 | fallback: { 34 | // Webpack 5 no longer polyfills Node.js core modules automatically. 35 | // see https://webpack.js.org/configuration/resolve/#resolvefallback 36 | // for the list of Node.js core module polyfills. 37 | assert: require.resolve('assert'), 38 | http: require.resolve('stream-http'), 39 | https: require.resolve('https-browserify'), 40 | url: require.resolve('url/'), 41 | crypto: require.resolve('crypto-browserify'), 42 | zlib: require.resolve('browserify-zlib'), 43 | stream: require.resolve('stream-browserify'), 44 | querystring: require.resolve('querystring-es3'), 45 | path: require.resolve('path-browserify'), 46 | os: require.resolve('os-browserify/browser'), 47 | // request: require.resolve('browser-request'), 48 | tls: require.resolve('tls-browserify'), 49 | net: require.resolve('net'), 50 | async_hooks: require.resolve('async-hook-browser'), 51 | fs: require.resolve('browserify-fs'), 52 | buffer: require.resolve('buffer'), 53 | vm: false, 54 | }, 55 | }, 56 | module: { 57 | rules: [ 58 | { 59 | test: /\.ts$/, 60 | exclude: /node_modules/, 61 | use: [ 62 | { 63 | loader: "ts-loader", 64 | }, 65 | ], 66 | }, 67 | ], 68 | }, 69 | plugins: [ 70 | // Work around for Buffer is undefined: 71 | // https://github.com/webpack/changelog-v5/issues/10 72 | new webpack.ProvidePlugin({ 73 | Buffer: ['buffer', 'Buffer'], 74 | }), 75 | new webpack.ProvidePlugin({ 76 | process: "process/browser", // provide a shim for the global `process` variable 77 | }), 78 | // new CircularDependencyPlugin({ 79 | // allowAsyncCycles: true, 80 | // cwd: process.cwd(), 81 | // }), 82 | ], 83 | externals: { 84 | vscode: "commonjs vscode", // ignored because it doesn't exist 85 | }, 86 | performance: { 87 | hints: false, 88 | }, 89 | devtool: "nosources-source-map", // create a source map that points to the original source file 90 | }; 91 | 92 | /**@type {webpack.Configuration} */ 93 | const nodeConfig = { 94 | context: __dirname, 95 | mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 96 | target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 97 | // entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 98 | entry: { 99 | 'extension-node': "./src/extension-node.ts", // source of the node extension main file 100 | 'test/suite/index-node': "./src/test/suite/index-node.ts", // source of the node extension test runner 101 | "test/suite/extension.test": "./src/test/suite/extension.test.ts", // create a separate file for the tests, to be found by glob 102 | "test/runTest": "./src/test/runTest", // used to start the VS Code test runner (@vscode/test-electron) 103 | }, 104 | output: { // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 105 | path: path.resolve(__dirname, 'dist'), 106 | filename: '[name].js', 107 | libraryTarget: "commonjs2", 108 | devtoolModuleFilenameTemplate: "../[resource-path]", 109 | }, 110 | resolve: { 111 | mainFields: ["module", "main"], 112 | // support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader 113 | extensions: ['.ts', '.js'], 114 | }, 115 | module: { 116 | rules: [{ 117 | test: /\.ts$/, 118 | exclude: /node_modules/, 119 | use: [{ 120 | loader: 'ts-loader', 121 | }] 122 | }] 123 | }, 124 | externals: { 125 | 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/ 126 | mocha: "commonjs mocha", // don't bundle 127 | '@vscode/test-electron': "commonjs @vscode/test-electron" // don't bundle 128 | }, 129 | performance: { 130 | hints: false 131 | }, 132 | // mode: "production" 133 | devtool: 'nosources-source-map', // create a source map that points to the original source file 134 | }; 135 | 136 | module.exports = [webConfig, nodeConfig]; 137 | --------------------------------------------------------------------------------