├── .github ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE │ ├── config.yml │ └── issue.yaml ├── PULL_REQUEST_TEMPLATE.md ├── media │ ├── css_frameworks.gif │ ├── dependencies.gif │ ├── devtools.gif │ ├── file_creation.gif │ ├── file_templates.gif │ ├── intellisense.gif │ ├── logo.png │ ├── modules.gif │ ├── nuxi.gif │ └── scripts.png └── workflows │ └── ci.yml ├── .gitignore ├── .npmrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets ├── icon-font │ ├── icons.woff │ └── icons │ │ ├── nuxt-disabled.svg │ │ ├── nuxt-logo.svg │ │ ├── nuxt-not-found.svg │ │ ├── nuxtr-github.svg │ │ ├── nuxtr-module.svg │ │ ├── nuxtr-npm.svg │ │ ├── nuxtr-project.svg │ │ └── nuxtr-settings.svg ├── logo.png └── logo.svg ├── eslint.config.mjs ├── knip.ts ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── snippets ├── nitro │ ├── boilerplates.json │ └── utils.json └── nuxt │ ├── boilerplates.json │ ├── components.json │ ├── composables.json │ └── utils.json ├── src ├── codelens │ ├── index.ts │ ├── layers.ts │ ├── modules.ts │ └── plugins.ts ├── commands │ ├── component.ts │ ├── composable.ts │ ├── css.ts │ ├── devtools.ts │ ├── externalLinks.ts │ ├── fileTemplates.ts │ ├── index.ts │ ├── installDependencies.ts │ ├── layout.ts │ ├── linters.ts │ ├── middleware.ts │ ├── nitro.ts │ ├── nuxi │ │ ├── commonCommands.ts │ │ ├── index.ts │ │ └── multiStepCommands.ts │ ├── page.ts │ ├── plugin.ts │ ├── project.ts │ ├── store.ts │ ├── structure.ts │ ├── templates.ts │ └── util.ts ├── content │ └── pm.ts ├── extension.ts ├── index.ts ├── intellisense │ ├── completionProviders │ │ ├── nuxtIgnore.ts │ │ ├── nuxtRC.ts │ │ └── vueFiles.ts │ └── index.ts ├── nuxi │ └── index.ts ├── sideBar │ ├── .prettierrc │ ├── README.md │ ├── build │ │ ├── assets │ │ │ ├── index.css │ │ │ └── index.js │ │ ├── icon.png │ │ └── index.html │ ├── index.html │ ├── index.ts │ ├── package.json │ ├── postcss.config.cjs │ ├── prettier.config.ts │ ├── public │ │ └── icon.png │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ ├── inter │ │ │ │ ├── Inter-Black.woff │ │ │ │ ├── Inter-Bold.woff │ │ │ │ ├── Inter-ExtraBold.woff │ │ │ │ ├── Inter-ExtraLight.woff │ │ │ │ ├── Inter-Light.woff │ │ │ │ ├── Inter-Medium.woff │ │ │ │ ├── Inter-Regular.woff │ │ │ │ ├── Inter-SemiBold.woff │ │ │ │ └── Inter-Thin.woff │ │ │ └── main.css │ │ ├── components │ │ │ ├── DepndenciesDropdown.vue │ │ │ ├── DropdownItem.vue │ │ │ ├── Icons │ │ │ │ ├── Add.vue │ │ │ │ ├── Chevron.vue │ │ │ │ ├── Code.vue │ │ │ │ ├── Dots.vue │ │ │ │ ├── Downloads.vue │ │ │ │ ├── Edit.vue │ │ │ │ ├── Feedback.vue │ │ │ │ ├── Filter.vue │ │ │ │ ├── Flash.vue │ │ │ │ ├── Message.vue │ │ │ │ ├── Module.vue │ │ │ │ ├── NPM.vue │ │ │ │ ├── Nuxt.vue │ │ │ │ ├── Placeholder.vue │ │ │ │ ├── Play.vue │ │ │ │ ├── SnippetItem.vue │ │ │ │ ├── Snippets.vue │ │ │ │ ├── Star.vue │ │ │ │ ├── Template.vue │ │ │ │ ├── Trash.vue │ │ │ │ ├── Upgrade.vue │ │ │ │ └── Vue.vue │ │ │ ├── ModuleCard.vue │ │ │ ├── Modules.vue │ │ │ ├── ModulesFilter.vue │ │ │ ├── Project.vue │ │ │ └── Project │ │ │ │ └── Section │ │ │ │ ├── Actions.vue │ │ │ │ ├── Dependencies.vue │ │ │ │ ├── Feedback.vue │ │ │ │ ├── FileTemplates.vue │ │ │ │ └── Snippets.vue │ │ ├── composables │ │ │ ├── modules.json │ │ │ └── modules.ts │ │ ├── main.ts │ │ ├── pages │ │ │ ├── Home.vue │ │ │ └── Project.vue │ │ ├── plugins │ │ │ └── globals.cjs │ │ ├── router │ │ │ └── index.ts │ │ ├── utilities │ │ │ └── vscode.ts │ │ └── vite-env.d.ts │ ├── tailwind.config.cjs │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── snippets │ └── index.ts ├── statusBar │ └── index.ts ├── templates │ ├── css.ts │ ├── index.ts │ ├── linters.ts │ ├── stores.ts │ ├── typeScriptFiles.ts │ └── vueFiles.ts ├── types.ts ├── utils │ ├── commands.ts │ ├── dependency.ts │ ├── file.ts │ ├── files.ts │ ├── global.ts │ ├── index.ts │ ├── navigation.ts │ ├── nuxt.ts │ ├── outputChannel.ts │ ├── pkgJSON.ts │ ├── vscode.ts │ └── watchers.ts └── watchers │ ├── config.ts │ ├── files.ts │ └── index.ts ├── syntaxes ├── log.langConfiguration.json ├── nuxtignore.langConfiguration.json ├── nuxtignore.tmConfiguration.json ├── nuxtrc.langConfiguration.json └── nuxtrc.tmConfiguration.json ├── taze.config.ts ├── tsconfig.json └── tsup.config.ts /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 💬 Discussions 4 | url: https://github.com/nuxtrdev/nuxtr-vscode/discussions 5 | about: Use discussions if you have an idea, an improvement or for asking questions. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.yaml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Report any issues and help us enhance Nuxtr. Please, use issues for reporting bugs and bugs only. 3 | title: "[Bug]: " 4 | labels: ["bug", "pending triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | id: bug-env 12 | attributes: 13 | label: Environment 14 | description: You can use `npx nuxi info` to fill this section 15 | placeholder: Environment 16 | validations: 17 | required: true 18 | - type: textarea 19 | id: bug-description 20 | attributes: 21 | label: What went wrong? 22 | description: Explain what were you trying to do and what happened. 23 | placeholder: Bug description 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: bug-steps-to-reproduce 28 | attributes: 29 | label: How to reproduce it? 30 | description: Explain how to reproduce the issue. 31 | placeholder: Steps to reproduce 32 | validations: 33 | required: true 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | ### 🔗 Linked issue / Discussion 9 | 10 | 11 | 12 | ### ❓ Type of change 13 | 14 | 15 | 16 | - [ ] 📖 Documentation (updates to the documentation, readme or JSdoc annotations) 17 | - [ ] 🐞 Bug fix (a non-breaking change that fixes an issue) 18 | - [ ] 👌 Enhancement (improving an existing functionality like performance) 19 | - [ ] ✨ New feature (a non-breaking change that adds functionality) 20 | - [ ] 🧹 Chore (updates to the build process or auxiliary tools and libraries) 21 | - [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change) 22 | 23 | ### 📚 Description 24 | 25 | 26 | 27 | 28 | 29 | ### 📝 Checklist 30 | 31 | 32 | 33 | 34 | 35 | - [ ] I have linked an issue or discussion. 36 | - [ ] I have updated the documentation accordingly. 37 | -------------------------------------------------------------------------------- /.github/media/css_frameworks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/css_frameworks.gif -------------------------------------------------------------------------------- /.github/media/dependencies.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/dependencies.gif -------------------------------------------------------------------------------- /.github/media/devtools.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/devtools.gif -------------------------------------------------------------------------------- /.github/media/file_creation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/file_creation.gif -------------------------------------------------------------------------------- /.github/media/file_templates.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/file_templates.gif -------------------------------------------------------------------------------- /.github/media/intellisense.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/intellisense.gif -------------------------------------------------------------------------------- /.github/media/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/logo.png -------------------------------------------------------------------------------- /.github/media/modules.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/modules.gif -------------------------------------------------------------------------------- /.github/media/nuxi.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/nuxi.gif -------------------------------------------------------------------------------- /.github/media/scripts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/.github/media/scripts.png -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 17 | - run: corepack enable 18 | - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 19 | with: 20 | node-version: 20 21 | cache: "pnpm" 22 | 23 | - name: Install dependencies 24 | run: pnpm install --no-frozen-lockfile 25 | 26 | - name: Typecheck 27 | run: pnpm typecheck 28 | 29 | - name: Build 30 | run: pnpm build 31 | 32 | - name: lint 33 | run: pnpm lint 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | dist 3 | node_modules 4 | .vscode-test/ 5 | *.vsix 6 | 7 | .DS_Store 8 | 9 | TODOs.md 10 | 11 | .env 12 | 13 | .eslintcache 14 | *.tsbuildinfo -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | ignore-workspace-root-check=true 2 | node-linker=hoisted -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "tobermory.es6-string-html", 5 | "vue.volar", 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Run Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": [ 9 | "--extensionDevelopmentPath=${workspaceFolder}" 10 | ], 11 | "outFiles": [ 12 | "${workspaceFolder}/out/**/*.js" 13 | ], 14 | "preLaunchTask": "${defaultBuildTask}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsc.autoDetect": "off", 3 | "editor.formatOnSave": true, 4 | "eslint.useFlatConfig": true 5 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "dev", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .github/** 3 | .env.example 4 | .eslintcache 5 | .eslintignore 6 | 7 | .vscode-test/** 8 | src/** 9 | !src/sideBar/build/** 10 | vsc-extension-quickstart.md 11 | **/tsconfig.json 12 | **/eslint.config.mjs 13 | **/*.map 14 | **/*.ts 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Nuxtr contributing guide 2 | 3 | The goal is to offer the best developer experience for Nuxt developers within VSCode. We're open to any suggestions and ideas that can help us achieve that goal. 4 | 5 | One of the main challenges is to keep the extension as lightweight as possible. We want to avoid adding too many features that can be easily achieved with other extensions. We want to avoid turning Nuxtr into an opinionated extension that tries to do everything. Any features we already have that can be labeled as "opinionated" should be optional and should be justified by a good reason why adding it will improve the developer experience. 6 | 7 | As part of our roadmap we're exploring the options of building separate extensions that be used on top of Nuxtr VSCode or offering APIs that can be used by other extensions. 8 | 9 | ## Repo 10 | 11 | Nuxtr VSCode contains a typical [VSCode extension structure](https://code.visualstudio.com/api/get-started/extension-anatomy) with [sidebar](./src/sideBar/) logic written using Vue 3 + TypeScript project and imported using [WebViews](https://code.visualstudio.com/api/extension-guides/webview). 12 | 13 | We're using PNPM, so make sure you have it installed. Here's a [quick guide](https://pnpm.io/installation) on how to install it. 14 | 15 | ## PRs 16 | 17 | ### Discuss first 18 | 19 | We're always happy to have contributors to the project. Nuxtr is actively developed and we want to avoid wasting time and effort. [Starting a discussion](https://github.com/nuxtrdev/nuxtr-vscode/discussions/new?category=ideas) first will help save time and effort for both maintainers and contributors. 20 | 21 | ### Commit conventions 22 | 23 | We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) to automate the release process. Please follow the commit conventions to make sure your PRs are included in the next release. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Nuxtr 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 | -------------------------------------------------------------------------------- /assets/icon-font/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/assets/icon-font/icons.woff -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxt-disabled.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 12 | 14 | 15 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxt-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxt-not-found.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxtr-github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxtr-module.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxtr-npm.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxtr-project.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /assets/icon-font/icons/nuxtr-settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/assets/logo.png -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import unjs from 'eslint-config-unjs'; 2 | 3 | export default unjs({ 4 | rules: { 5 | '@typescript-eslint/naming-convention': 'off', 6 | '@typescript-eslint/no-duplicate-enum-values': 'off', 7 | '@typescript-eslint/semi': 'off', 8 | 'curly': ['error', 'all'], 9 | 'eqeqeq': 'warn', 10 | 'no-throw-literal': 'warn', 11 | 'semi': 'off', 12 | '@typescript-eslint/indent': ['error', 4], 13 | 'no-multiple-empty-lines': 'warn', 14 | 'no-empty': 'warn', 15 | 'unicorn/filename-case': 'off', 16 | 'unicorn/prefer-event-target': 'off', 17 | 'unicorn/no-null': 'off', 18 | 'unicorn/no-array-for-each': 'off', 19 | 'dot-notation': 'error', 20 | 'no-lonely-if': 'error', 21 | 'no-useless-rename': 'error', 22 | 'object-shorthand': 'error', 23 | 'prefer-const': ['error', { destructuring: 'any', ignoreReadBeforeAssign: false }], 24 | 'require-await': 'error', 25 | 'sort-imports': ['error', { ignoreDeclarationSort: true }], 26 | 'no-unused-vars': 'off', 27 | '@typescript-eslint/no-unused-vars': 'off', 28 | 'no-template-curly-in-string': 'off', 29 | 'array-callback-return': 'off', 30 | 'camelcase': 'off', 31 | 'no-console': 'off', 32 | 'quotes': ['error', 'single'], 33 | 'comma-spacing': ['error', { before: false, after: true }], 34 | 'keyword-spacing': ['error', { before: true, after: true }], 35 | 'space-before-function-paren': ['error', 'always'], 36 | 'object-curly-spacing': ['error', 'always'], 37 | 'arrow-spacing': ['error', { before: true, after: true }], 38 | 'key-spacing': ['error', { beforeColon: false, afterColon: true, mode: 'strict' }], 39 | 'space-before-blocks': ['error', 'always'], 40 | 'space-infix-ops': ['error', { int32Hint: false }], 41 | '@typescript-eslint/no-explicit-any': 'off', 42 | '@typescript-eslint/ban-types': 'off', 43 | '@typescript-eslint/ban-ts-comment': 'off', 44 | '@typescript-eslint/no-extraneous-class': 'off', 45 | 'comma-dangle': 'off', 46 | }, 47 | ignores: [ 48 | 'out', 49 | 'node_modules', 50 | '*.js', 51 | 'src/sideBar/build' 52 | ] 53 | }); 54 | -------------------------------------------------------------------------------- /knip.ts: -------------------------------------------------------------------------------- 1 | import type { KnipConfig } from 'knip'; 2 | 3 | const config: KnipConfig = { 4 | workspaces: { 5 | '.': { 6 | entry: 'src/index.ts', 7 | ignoreDependencies: ['taze', 'terser', 'eslint-config-unjs'], 8 | ignoreBinaries: ['vsce'], 9 | ignore: ['taze.config.ts', 'eslint.config.mjs'], 10 | tsup: { config: 'tsup.config.ts' }, 11 | }, 12 | 'src/sideBar': { 13 | ignoreDependencies: ['ofetch', '@types/vscode-webview', 'prettier', 'prettier-plugin-tailwindcss'], 14 | ignore: ['build/**', 'prettier.config.ts'], 15 | vite: { 16 | 'config': [ 17 | 'vite.config.ts' 18 | ] 19 | } 20 | } 21 | }, 22 | }; 23 | 24 | export default config; -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "src/sideBar" 3 | -------------------------------------------------------------------------------- /snippets/nitro/boilerplates.json: -------------------------------------------------------------------------------- 1 | { 2 | "defineNitroPlugin": { 3 | "prefix": "defineNitroPlugin", 4 | "description": "Define Nitro Plugins", 5 | "body": [ 6 | "export default defineNitroPlugin((${1:plugin}) => {", 7 | " $2", 8 | "})" 9 | ], 10 | "isFileTemplate": true 11 | }, 12 | "defineRenderHandler": { 13 | "prefix": "defineRenderHandler", 14 | "body": [ 15 | "export function defineRenderHandler(${1:handler}) {", 16 | " return eventHandler(async (event) => {", 17 | " ${2:}", 18 | " })", 19 | "}" 20 | ], 21 | "description": "defineRenderHandler" 22 | } 23 | } -------------------------------------------------------------------------------- /snippets/nitro/utils.json: -------------------------------------------------------------------------------- 1 | { 2 | "getRouteRules": { 3 | "prefix": "getRouteRules", 4 | "description": "Get Route Rules", 5 | "body": ["const ${1:routeRules} = getRouteRules($2);"] 6 | }, 7 | "useNitroApp": { 8 | "prefix": "useNitroApp", 9 | "description": "Use Nitro App", 10 | "body": ["const ${1:nitro} = useNitroApp();"] 11 | }, 12 | "useStorage": { 13 | "prefix": "useStorage", 14 | "description": "Use Storage", 15 | "body": ["const ${1:storage} = useStorage();"] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /snippets/nuxt/boilerplates.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": { 3 | "prefix": "template", 4 | "body": [""], 5 | "description": "template tag" 6 | }, 7 | "setupJSScriptTag": { 8 | "prefix": "setupjscript", 9 | "body": [""], 10 | "description": "JavaScript setup script tag" 11 | }, 12 | "setupTSScriptTag": { 13 | "prefix": "setuptscript", 14 | "body": [""], 15 | "description": "JavaScript setup script tag" 16 | }, 17 | "scriptJSTag": { 18 | "prefix": "scriptjs", 19 | "body": [""], 20 | "description": "script tag" 21 | }, 22 | "scriptTSTag": { 23 | "prefix": "scriptts", 24 | "body": [""], 25 | "description": "script tag" 26 | }, 27 | "styleTag": { 28 | "prefix": "style", 29 | "body": [""], 30 | "description": "style tag" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /snippets/nuxt/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "Nuxt | NuxtLink": { 3 | "prefix": "nuxtLink", 4 | "body": ["$2"], 5 | "description": "Nuxt NuxtLink component." 6 | }, 7 | "Nuxt | Teleport": { 8 | "prefix": "nuxtTeleport", 9 | "body": ["$2"], 10 | "description": "Nuxt Teleport component" 11 | }, 12 | "Nuxt | NuxtLayout": { 13 | "prefix": "nuxtLayout", 14 | "body": ["$2"], 15 | "description": "Nuxt | NuxtLayout" 16 | }, 17 | "Nuxt | NuxtPage": { 18 | "prefix": "nuxtPage", 19 | "body": [""], 20 | "description": "Nuxt | NuxtPage" 21 | }, 22 | "Nuxt | NuxtWelcome": { 23 | "prefix": "nuxtWelcome", 24 | "body": [""], 25 | "description": "NuxtWelcome Component" 26 | }, 27 | "Nuxt | NuxtErrorBoundary": { 28 | "prefix": "nuxtErrorBoundary", 29 | "body": [ 30 | "", 31 | "$3", 32 | "" 33 | ], 34 | "description": "Nuxt | NuxtErrorBoundary" 35 | }, 36 | "Nuxt | NuxtLoadingIndicator": { 37 | "prefix": "nuxtLoadingIndicator", 38 | "body": [""], 39 | "description": "Nuxt | NuxtLoadingIndicator" 40 | }, 41 | "Nuxt | ClientOnly": { 42 | "prefix": "clientonly", 43 | "body": ["", " $2", ""], 44 | "description": "Nuxt | NuxtPortal" 45 | }, 46 | "Nuxt | NuxtClientFallback": { 47 | "prefix": "nuxtClientFallback", 48 | "body": ["", " $2", ""], 49 | "description": "Nuxt | NuxtClientFallback" 50 | }, 51 | "Nuxt | fallback": { 52 | "prefix": "fallback", 53 | "body": ["fallback=\"$1\""], 54 | "description": "Nuxt | fallback" 55 | }, 56 | "Nuxt | fallbackTag": { 57 | "prefix": "fallbackTag", 58 | "body": ["fallback-tag=\"$1\""], 59 | "description": "Nuxt | fallbackTag" 60 | }, 61 | "Nuxt | SSR Error": { 62 | "prefix": "ssrError", 63 | "body": [" @ssr-error=\"$1\""], 64 | "description": "Nuxt | SSR Error" 65 | }, 66 | "Nuxt | name": { 67 | "prefix": "name", 68 | "body": ["name=\"$1\""], 69 | "description": "Nuxt | name" 70 | }, 71 | "Nuxt | pageKey": { 72 | "prefix": "pageKey", 73 | "body": ["page-key=\"$1\""], 74 | "description": "Nuxt | pageKey" 75 | }, 76 | "Nuxt | Template with ID": { 77 | "prefix": "templateWithId", 78 | "body": [""], 79 | "description": "Nuxt | Template with ID" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /snippets/nuxt/composables.json: -------------------------------------------------------------------------------- 1 | { 2 | "useAppConfig": { 3 | "prefix": "useAppConfig", 4 | "body": ["const appConfig = useAppConfig();"], 5 | "description": "useAppConfig" 6 | }, 7 | "useAsyncData": { 8 | "prefix": "useAsyncData", 9 | "body": [ 10 | "const { data, pending, error, refresh } = await useAsyncData(", 11 | " '$2',", 12 | " () => \\$fetch('$3')", 13 | ");" 14 | ], 15 | "description": "useAsyncData" 16 | }, 17 | "useCookie": { 18 | "prefix": "useCookie", 19 | "body": ["const ${1:cookie} = useCookie('$2'${3:, })"], 20 | "description": "useCookie" 21 | }, 22 | "getCookie": { 23 | "prefix": "getCookie", 24 | "body": ["const ${1:cookie} = getCookie('$2'${3:, })"], 25 | "description": "getCookie" 26 | }, 27 | "setCookie": { 28 | "prefix": "setCookie", 29 | "body": ["setCookie('$1'${2:, })"], 30 | "description": "setCookie" 31 | }, 32 | "useError": { 33 | "prefix": "useError", 34 | "body": ["const ${1:error} = useError()"], 35 | "description": "useError" 36 | }, 37 | "useFetch": { 38 | "prefix": "useFetch", 39 | "body": [ 40 | "const { data, pending, error, refresh } = await useFetch('$1',{", 41 | " $2", 42 | "})" 43 | ], 44 | "description": "useFetch" 45 | }, 46 | "onRequest": { 47 | "prefix": "onRequest", 48 | "body": ["onRequest({ request, options }) {", " $1", "}"], 49 | "description": "onRequest" 50 | }, 51 | "onRequestError": { 52 | "prefix": "onRequestError", 53 | "body": ["onRequestError({ request, options, error }) {", " $1", "}"], 54 | "description": "onRequestError" 55 | }, 56 | "onResponse": { 57 | "prefix": "onResponse", 58 | "body": ["onResponse({ request, response, options }) {", " $1", "}"], 59 | "description": "onResponse" 60 | }, 61 | "onResponseError": { 62 | "prefix": "onResponseError", 63 | "body": [ 64 | "onResponseError({ request, response, options }) {", 65 | " $1", 66 | "}" 67 | ], 68 | "description": "onResponseError" 69 | }, 70 | "useHeadSafe": { 71 | "prefix": "useHeadSafe", 72 | "body": ["useHeadSafe({", " $1", "});"], 73 | "description": "useHeadSafe" 74 | }, 75 | "useHead": { 76 | "prefix": "useHead", 77 | "body": ["useHead({", " $1", "})"], 78 | "description": "useHead" 79 | }, 80 | "useHydration": { 81 | "prefix": "useHydration", 82 | "body": ["useHydration({", " $1", "})"], 83 | "description": "useHydration" 84 | }, 85 | "useLazyAsyncData": { 86 | "prefix": "useLazyAsyncData", 87 | "body": [ 88 | "const { $1 } = await useLazyAsyncData('$2', () => \\$fetch('$3'))" 89 | ], 90 | "description": "useLazyAsyncData" 91 | }, 92 | "useLazyFetch": { 93 | "prefix": "useLazyFetch", 94 | "body": ["const { $1 } = await useLazyFetch('$3')"], 95 | "description": "useLazyFetch" 96 | }, 97 | "useNuxtApp": { 98 | "prefix": "useNuxtApp", 99 | "body": ["const nuxtApp = useNuxtApp()", ""], 100 | "description": "useNuxtApp" 101 | }, 102 | "useNuxtData": { 103 | "prefix": "useNuxtData", 104 | "body": ["const { data: $1 } = useNuxtData('$2')"], 105 | "description": "useNuxtData" 106 | }, 107 | "useRequestEvent": { 108 | "prefix": "useRequestEvent", 109 | "body": ["const event = useRequestEvent()"], 110 | "description": "useRequestEvent" 111 | }, 112 | "useRequestHeaders": { 113 | "prefix": "useRequestHeaders", 114 | "body": ["const headers = useRequestHeaders($1)"], 115 | "description": "useRequestHeaders" 116 | }, 117 | "useRequestURL": { 118 | "prefix": "useRequestURL", 119 | "body": ["const url = useRequestURL()"], 120 | "description": "useRequestURL" 121 | }, 122 | "useRoute": { 123 | "prefix": "useRoute", 124 | "body": ["const route = useRoute();"], 125 | "description": "useRoute" 126 | }, 127 | "useRouter": { 128 | "prefix": "useRouter", 129 | "body": ["const router = useRouter();"], 130 | "description": "useRouter" 131 | }, 132 | "useRuntimeConfig": { 133 | "prefix": "useRuntimeConfig", 134 | "body": ["const config = useRuntimeConfig()"], 135 | "description": "useRuntimeConfig" 136 | }, 137 | "useSeoMeta": { 138 | "prefix": "useSeoMeta", 139 | "body": ["useSeoMeta({", " $1", "})"], 140 | "description": "useSeoMeta" 141 | }, 142 | "useServerSeoMeta": { 143 | "prefix": "useServerSeoMeta", 144 | "body": ["useServerSeoMeta({", " $1", "})"], 145 | "description": "useServerSeoMeta" 146 | }, 147 | "useState": { 148 | "prefix": "useState", 149 | "body": ["const $1 = useState('$2', () => $3)"], 150 | "description": "useState" 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /snippets/nuxt/utils.json: -------------------------------------------------------------------------------- 1 | { 2 | "$fetch": { 3 | "prefix": "fetch", 4 | "body": ["const $1 = await \\$fetch('$2')"], 5 | "description": "$fetch" 6 | }, 7 | "abortNavigation": { 8 | "prefix": "abortNavigation()", 9 | "body": ["abortNavigation($1)"], 10 | "description": "abortNavigation" 11 | }, 12 | "addRouteMiddleware": { 13 | "prefix": "addRouteMiddleware", 14 | "body": [ 15 | "addRouteMiddleware('$1', (${2:to, from}) => {", 16 | " $3", 17 | "}, { global: true })" 18 | ], 19 | "description": "addRouteMiddleware" 20 | }, 21 | "clearError": { 22 | "prefix": "clearError", 23 | "body": ["clearError($1)"], 24 | "description": "clearError" 25 | }, 26 | "clearNuxtData": { 27 | "prefix": "clearNuxtData", 28 | "body": ["clearNuxtData($1)"], 29 | "description": "clearNuxtData" 30 | }, 31 | "createError": { 32 | "prefix": "createError", 33 | "body": [ 34 | "throw createError({", 35 | " statusCode: $1,", 36 | " statusMessage: '$2'", 37 | "})" 38 | ], 39 | "description": "createError" 40 | }, 41 | "defineNuxtComponent": { 42 | "prefix": "defineNuxtComponent", 43 | "body": ["export default defineNuxtComponent({", "$1", "})"], 44 | "description": "defineNuxtComponent" 45 | }, 46 | "defineNuxtRouteMiddleware": { 47 | "prefix": "defineNuxtRouteMiddleware", 48 | "body": [ 49 | "export default defineNuxtRouteMiddleware((${1:to, from}) => {", 50 | " $2", 51 | "})", 52 | "" 53 | ], 54 | "description": "defineNuxtRouteMiddleware" 55 | }, 56 | "definePageMeta": { 57 | "prefix": "definePageMeta", 58 | "body": ["definePageMeta({", " $1", "})"], 59 | "description": "definePageMeta" 60 | }, 61 | "navigateTo": { 62 | "prefix": "navigateTo", 63 | "body": ["navigateTo('$1', $2)"], 64 | "description": "navigateTo" 65 | }, 66 | "onBeforeRouteLeave": { 67 | "prefix": "onBeforeRouteLeave", 68 | "body": ["onBeforeRouteLeave($1)"], 69 | "description": "onBeforeRouteLeave" 70 | }, 71 | "onBeforeRouteUpdate": { 72 | "prefix": "onBeforeRouteUpdate", 73 | "body": ["onBeforeRouteUpdate($1)"], 74 | "description": "onBeforeRouteUpdate" 75 | }, 76 | "onNuxtReady": { 77 | "prefix": "onNuxtReady", 78 | "body": ["onNuxtReady(async () => {", " $1", "})"], 79 | "description": "onNuxtReady" 80 | }, 81 | "prefetchComponents": { 82 | "prefix": "prefetchComponents", 83 | "body": ["prefetchComponents('$1')"], 84 | "description": "prefetchComponents" 85 | }, 86 | "preloadComponents": { 87 | "prefix": "preloadComponents", 88 | "body": ["preloadComponents('$1')"], 89 | "description": "preloadComponents" 90 | }, 91 | "preloadRouteComponents": { 92 | "prefix": "preloadRouteComponents", 93 | "body": ["preloadRouteComponents('$1')"], 94 | "description": "preloadRouteComponents" 95 | }, 96 | "refreshNuxtData": { 97 | "prefix": "refreshNuxtData", 98 | "body": ["refreshNuxtData('$1')"], 99 | "description": "refreshNuxtData" 100 | }, 101 | "reloadNuxtApp": { 102 | "prefix": "reloadNuxtApp", 103 | "body": ["reloadNuxtApp('$1')"], 104 | "description": "reloadNuxtApp" 105 | }, 106 | "setPageLayout": { 107 | "prefix": "setPageLayout", 108 | "body": ["setPageLayout('$1')"], 109 | "description": "setPageLayout" 110 | }, 111 | "setResponseStatus": { 112 | "prefix": "setResponseStatus", 113 | "body": ["setResponseStatus('$1:event', $2:404, ${3: '${4:'Message'}'})"], 114 | "description": "setResponseStatus" 115 | }, 116 | "showError": { 117 | "prefix": "showError", 118 | "body": ["showError({ statusCode: $1, statusMessage: \"$2\" })"], 119 | "description": "showError" 120 | }, 121 | "updateAppConfig": { 122 | "prefix": "updateAppConfig", 123 | "body": ["const ${1:newAppConfig} = { $2 }", "updateAppConfig($1)"], 124 | "description": "updateAppConfig" 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/codelens/index.ts: -------------------------------------------------------------------------------- 1 | import { languages } from 'vscode'; 2 | import { LayersCodeLensProvider } from './layers'; 3 | import { ModulesCodelensProvider } from './modules'; 4 | import { PluginsCodelensProvider } from './plugins'; 5 | 6 | function activateCodelenses () { 7 | const modulesLens = new ModulesCodelensProvider() 8 | const pluginsLens = new PluginsCodelensProvider() 9 | const layersLens = new LayersCodeLensProvider() 10 | 11 | languages.registerCodeLensProvider({ scheme: 'file', language: 'typescript' }, modulesLens) 12 | languages.registerCodeLensProvider({ scheme: 'file', language: 'typescript' }, pluginsLens) 13 | languages.registerCodeLensProvider({ scheme: 'file', language: 'typescript' }, layersLens) 14 | } 15 | 16 | 17 | const codelens = { 18 | activateCodelenses, 19 | } 20 | 21 | export default codelens 22 | -------------------------------------------------------------------------------- /src/codelens/layers.ts: -------------------------------------------------------------------------------- 1 | import { CodeLens, CodeLensProvider, Event, EventEmitter, Position, TextDocument, workspace } from 'vscode'; 2 | 3 | export class LayersCodeLensProvider implements CodeLensProvider { 4 | 5 | private codeLenses: CodeLens[] = [] 6 | private regex: RegExp 7 | private _onDidChangeCodeLenses: EventEmitter = new EventEmitter() 8 | public readonly onDidChangeCodeLenses: Event = this._onDidChangeCodeLenses.event 9 | 10 | constructor () { 11 | this.regex = /extends:/g, 12 | 13 | workspace.onDidChangeConfiguration((_) => { this._onDidChangeCodeLenses.fire() }) 14 | } 15 | 16 | public provideCodeLenses (document: TextDocument): CodeLens[] | Thenable { 17 | 18 | this.codeLenses = [] 19 | const regex = new RegExp(this.regex) 20 | const text = document.getText() 21 | let matches 22 | while ((matches = regex.exec(text)) !== null) { 23 | const line = document.lineAt(document.positionAt(matches.index).line) 24 | const indexOf = line.text.indexOf(matches[0]) 25 | const position = new Position(line.lineNumber, indexOf) 26 | const range = document.getWordRangeAtPosition(position, new RegExp(this.regex)) 27 | if (range) { 28 | this.codeLenses.push(new CodeLens(range)) 29 | } 30 | } 31 | return this.codeLenses 32 | } 33 | 34 | public resolveCodeLens (codeLens: CodeLens) { 35 | if (workspace.getConfiguration('codelens-sample').get('enableCodeLens', true)) { 36 | codeLens.command = { 37 | title: 'Add new layer', 38 | tooltip: 'Nuxtr: Add new layer', 39 | command: 'nuxtr.createLayer', 40 | arguments: ['Argument 1', false] 41 | } 42 | return codeLens 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/codelens/modules.ts: -------------------------------------------------------------------------------- 1 | import { CodeLens, CodeLensProvider, Event, EventEmitter, Position, TextDocument, workspace } from 'vscode'; 2 | 3 | export class ModulesCodelensProvider implements CodeLensProvider { 4 | 5 | private codeLenses: CodeLens[] = [] 6 | private regex: RegExp 7 | private _onDidChangeCodeLenses: EventEmitter = new EventEmitter() 8 | public readonly onDidChangeCodeLenses: Event = this._onDidChangeCodeLenses.event 9 | 10 | constructor () { 11 | this.regex = /modules:/g 12 | 13 | workspace.onDidChangeConfiguration((_) => { 14 | this._onDidChangeCodeLenses.fire() 15 | }) 16 | } 17 | 18 | public provideCodeLenses (document: TextDocument): CodeLens[] | Thenable { 19 | 20 | this.codeLenses = [] 21 | const regex = new RegExp(this.regex) 22 | const text = document.getText() 23 | let matches 24 | while ((matches = regex.exec(text)) !== null) { 25 | const line = document.lineAt(document.positionAt(matches.index).line) 26 | const indexOf = line.text.indexOf(matches[0]) 27 | const position = new Position(line.lineNumber, indexOf) 28 | const range = document.getWordRangeAtPosition(position, new RegExp(this.regex)) 29 | if (range) { 30 | this.codeLenses.push(new CodeLens(range)) 31 | } 32 | } 33 | return this.codeLenses 34 | } 35 | 36 | public resolveCodeLens (codeLens: CodeLens) { 37 | if (workspace.getConfiguration('codelens-sample').get('enableCodeLens', true)) { 38 | codeLens.command = { 39 | title: 'Add new module', 40 | tooltip: 'Tooltip provided by sample extension', 41 | command: 'nuxtr.createModuleAction', 42 | arguments: ['Argument 1', false] 43 | } 44 | return codeLens 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/codelens/plugins.ts: -------------------------------------------------------------------------------- 1 | import { CodeLens, CodeLensProvider, Event, EventEmitter, Position, TextDocument, workspace, } from 'vscode'; 2 | 3 | export class PluginsCodelensProvider implements CodeLensProvider { 4 | 5 | private codeLenses: CodeLens[] = [] 6 | private regex: RegExp 7 | private _onDidChangeCodeLenses: EventEmitter = new EventEmitter() 8 | public readonly onDidChangeCodeLenses: Event = this._onDidChangeCodeLenses.event 9 | 10 | constructor () { 11 | this.regex = /plugins:/g 12 | 13 | workspace.onDidChangeConfiguration((_) => { 14 | this._onDidChangeCodeLenses.fire() 15 | }) 16 | } 17 | 18 | public provideCodeLenses (document: TextDocument): CodeLens[] | Thenable { 19 | 20 | if (workspace.getConfiguration('codelens-sample').get('enableCodeLens', true)) { 21 | this.codeLenses = [] 22 | const regex = new RegExp(this.regex) 23 | const text = document.getText() 24 | let matches 25 | while ((matches = regex.exec(text)) !== null) { 26 | const line = document.lineAt(document.positionAt(matches.index).line) 27 | const indexOf = line.text.indexOf(matches[0]) 28 | const position = new Position(line.lineNumber, indexOf) 29 | const range = document.getWordRangeAtPosition(position, new RegExp(this.regex)) 30 | if (range) { 31 | this.codeLenses.push(new CodeLens(range)) 32 | } 33 | } 34 | return this.codeLenses 35 | } 36 | return [] 37 | } 38 | 39 | public resolveCodeLens (codeLens: CodeLens) { 40 | if (workspace.getConfiguration('codelens-sample').get('enableCodeLens', true)) { 41 | codeLens.command = { 42 | title: 'Add new plugin', 43 | command: 'nuxtr.createPlugin', 44 | tooltip: 'Tooltip provided by sample extension', 45 | arguments: ['Argument 1', false] 46 | } 47 | return codeLens 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/commands/component.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode'; 2 | import { createDir, createFile, createSubFolders, generateVueFileBasicTemplate, projectSrcDirectory, showSubFolderQuickPick, } from '../utils'; 3 | 4 | const createComponent = () => { 5 | window 6 | .showInputBox({ 7 | prompt: 'What is your component name?', 8 | placeHolder: 'component name', 9 | }) 10 | .then(async (name) => { 11 | if (!name) { return } 12 | 13 | const componentsDir = `${await projectSrcDirectory()}/components` 14 | 15 | await createDir('components') 16 | 17 | const subFolders = await createSubFolders(componentsDir, 'components') 18 | 19 | showSubFolderQuickPick({ 20 | name, 21 | subFolders, 22 | commandType: 'components', 23 | content: generateVueFileBasicTemplate('component'), 24 | }) 25 | }) 26 | } 27 | 28 | const directCreateComponent = (path: string) => { 29 | window 30 | .showInputBox({ 31 | prompt: 'What is your component name?', 32 | placeHolder: 'component name', 33 | }) 34 | .then((name) => { 35 | if (!name) { return } 36 | 37 | const filePath = `${path}/${name}.vue` 38 | 39 | createFile({ 40 | fileName: `${name}.vue`, 41 | content: generateVueFileBasicTemplate('component'), 42 | fullPath: filePath, 43 | }) 44 | }) 45 | } 46 | 47 | export { createComponent, directCreateComponent }; 48 | -------------------------------------------------------------------------------- /src/commands/composable.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { composableTemplate } from '../templates' 3 | import { createDir, createFile, createSubFolders, normalizeFileExtension, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 4 | 5 | const neglectUsePrefix = (name: string) => name.replace(/^use/i, '') 6 | 7 | const createComposable = () => { 8 | window 9 | .showInputBox({ 10 | prompt: 'What is your composable name?', 11 | placeHolder: 'composable name', 12 | }) 13 | .then(async (name) => { 14 | 15 | if (!name) { return } 16 | 17 | const composablesDir = `${await projectSrcDirectory()}/composables` 18 | 19 | createDir('composables') 20 | 21 | const subFolders = await createSubFolders(composablesDir, 'composables') 22 | 23 | showSubFolderQuickPick({ 24 | name, 25 | subFolders, 26 | commandType: 'composables', 27 | content: composableTemplate(neglectUsePrefix(name)), 28 | }) 29 | 30 | }) 31 | } 32 | 33 | const directCreateComposable = (path: string) => { 34 | window 35 | .showInputBox({ 36 | prompt: 'What is your composable name?', 37 | placeHolder: 'composable name', 38 | }) 39 | .then((name) => { 40 | if (!name) { return } 41 | 42 | const filePath = `${path}/${normalizeFileExtension(name, '.ts')}.ts` 43 | 44 | createFile({ 45 | fileName: `${name}.ts`, 46 | content: composableTemplate(neglectUsePrefix(name)), 47 | fullPath: filePath, 48 | }) 49 | }) 50 | } 51 | 52 | export { createComposable, directCreateComposable } 53 | -------------------------------------------------------------------------------- /src/commands/externalLinks.ts: -------------------------------------------------------------------------------- 1 | import { openExternalLink } from '../utils' 2 | 3 | const openDocumentation = () => { 4 | openExternalLink('https://nuxt.com/docs') 5 | } 6 | 7 | const openModules = () => { 8 | openExternalLink('https://nuxt.com/modules') 9 | } 10 | 11 | export { openDocumentation, openModules } 12 | -------------------------------------------------------------------------------- /src/commands/fileTemplates.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, mkdirSync } from 'node:fs' 2 | import { window } from 'vscode' 3 | import { createFile, createSubFolders, createVueTemplate, generateVueFileBasicTemplate, generateVueFileTemplate, projectRootDirectory, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 4 | 5 | 6 | function createPageTemplate () { 7 | const editor = window.activeTextEditor 8 | if (editor) { 9 | const selection = editor.selection 10 | const selectedText = editor.document.getText(selection) 11 | 12 | if (selectedText) { 13 | const content = selectedText 14 | createVueTemplate(content, 'page') 15 | } else { 16 | const content = editor.document.getText() 17 | createVueTemplate(content, 'page') 18 | } 19 | } 20 | } 21 | 22 | function createLayoutTemplate () { 23 | const editor = window.activeTextEditor 24 | if (editor) { 25 | const selection = editor.selection 26 | const selectedText = editor.document.getText(selection) 27 | 28 | if (selectedText) { 29 | const content = selectedText 30 | createVueTemplate(content, 'layout') 31 | } else { 32 | const content = editor.document.getText() 33 | createVueTemplate(content, 'layout') 34 | } 35 | } 36 | } 37 | 38 | const createFileFromTemplate = (template?: string) => { 39 | if (template && template.includes('.page-template')) { 40 | 41 | window 42 | .showInputBox({ 43 | prompt: 'What is your page name?', 44 | placeHolder: 'Page name', 45 | }) 46 | .then(async (name) => { 47 | if (!name) { return } 48 | 49 | const srcDir = `${await projectSrcDirectory()}` 50 | const pagesDir = `${srcDir}/pages` 51 | 52 | 53 | if (srcDir !== undefined && !existsSync(pagesDir)) { 54 | mkdirSync(pagesDir) 55 | } 56 | 57 | const subFolders = createSubFolders(pagesDir, 'pages') 58 | 59 | showSubFolderQuickPick({ 60 | name, 61 | subFolders, 62 | commandType: 'pages', 63 | content: generateVueFileTemplate('page', template), 64 | }) 65 | }) 66 | } else { 67 | window 68 | .showInputBox({ 69 | prompt: 'What is your layout name?', 70 | placeHolder: 'Layout name', 71 | }) 72 | .then(async (name) => { 73 | if (!name) { return } 74 | const srcDir = `${await projectSrcDirectory()}` 75 | const layoutDir = `${srcDir}/layouts` 76 | 77 | if (srcDir !== undefined && !existsSync(layoutDir)) { 78 | mkdirSync(layoutDir) 79 | } 80 | 81 | const filePath = `${srcDir}/layouts/${name}.vue` 82 | 83 | createFile({ 84 | fileName: `${name}.vue`, 85 | content: generateVueFileTemplate('layout', template), 86 | fullPath: filePath, 87 | }) 88 | }) 89 | } 90 | } 91 | 92 | const createFileTemplate = (type: string) => { 93 | 94 | window 95 | .showInputBox({ 96 | prompt: `What is your ${type} name?`, 97 | placeHolder: `${type} name`, 98 | }) 99 | .then((name) => { 100 | if (!name) { return } 101 | 102 | const filePath = `${projectRootDirectory()}/.vscode/${name}.${type}-template` 103 | 104 | createFile({ 105 | fileName: `${name}.${type}-template`, 106 | content: generateVueFileBasicTemplate(`${type}`), 107 | fullPath: filePath, 108 | }) 109 | }) 110 | } 111 | 112 | 113 | const createEmptyFileTemplate = () => { 114 | const templates = ['Page', 'Layout'] 115 | 116 | window 117 | .showQuickPick(templates, { 118 | canPickMany: false, 119 | placeHolder: 'Select file type', 120 | }) 121 | .then((selection) => { 122 | if (selection === 'Page') { 123 | createFileTemplate('page') 124 | } 125 | 126 | if (selection === 'Layout') { 127 | createFileTemplate('layout') 128 | } 129 | }) 130 | 131 | } 132 | 133 | export { createEmptyFileTemplate, createFileFromTemplate, createLayoutTemplate, createPageTemplate } 134 | -------------------------------------------------------------------------------- /src/commands/index.ts: -------------------------------------------------------------------------------- 1 | import { managePackageVersion, upgradePackage } from '../utils/dependency' 2 | import { openSettings } from '../utils/navigation' 3 | import { createComponent, directCreateComponent } from './component' 4 | import { createComposable, directCreateComposable } from './composable' 5 | import { configureCSS } from './css' 6 | import { directToggleDevTools, nuxtConfigWatcher } from './devtools' 7 | import { openDocumentation, openModules } from './externalLinks' 8 | import { createEmptyFileTemplate, createFileFromTemplate, createLayoutTemplate, createPageTemplate } from './fileTemplates' 9 | import { installDependencies } from './installDependencies' 10 | import { createLayout, directCreateLayout } from './layout' 11 | import { configureLinters } from './linters' 12 | import { createMiddleware, directCreateMiddleware } from './middleware' 13 | import { createNitroAPI, createNitroMiddleware, createNitroPlugin, createNitroRoute, createNitroUtil, directCreateNitroAPI, directCreateNitroRoute } from './nitro' 14 | import { nuxtAnalyze, nuxtBuild, nuxtCleanUp, nuxtDev, nuxtGenerate, nuxtInfo, nuxtModule, showCLICommands } from './nuxi' 15 | import { createPage, directCreatePage } from './page' 16 | import { createPlugin, directCreatePlugin } from './plugin' 17 | import { createProject } from './project' 18 | import { createStore, directCreateStore } from './store' 19 | import { appConfig, errorLayout, nuxtIgnore, nuxtRC, projectStructure } from './structure' 20 | import { configurePug } from './templates' 21 | import { createUtil, directCreateUtil } from './util' 22 | 23 | 24 | const commands = { 25 | createComponent, 26 | directCreateComponent, 27 | createPage, 28 | directCreatePage, 29 | createComposable, 30 | directCreateComposable, 31 | createLayout, 32 | directCreateLayout, 33 | createPlugin, 34 | directCreatePlugin, 35 | createMiddleware, 36 | directCreateMiddleware, 37 | createNitroAPI, 38 | createNitroRoute, 39 | directCreateNitroAPI, 40 | directCreateNitroRoute, 41 | createNitroPlugin, 42 | createNitroMiddleware, 43 | createUtil, 44 | createNitroUtil, 45 | directCreateUtil, 46 | createProject, 47 | projectStructure, 48 | openDocumentation, 49 | openModules, 50 | nuxtDev, 51 | nuxtBuild, 52 | nuxtGenerate, 53 | nuxtCleanUp, 54 | nuxtAnalyze, 55 | nuxtInfo, 56 | nuxtModule, 57 | showCLICommands, 58 | createStore, 59 | directCreateStore, 60 | appConfig, 61 | nuxtIgnore, 62 | nuxtRC, 63 | errorLayout, 64 | installDependencies, 65 | upgradePackage, 66 | managePackageVersion, 67 | openSettings, 68 | configureCSS, 69 | configureLinters, 70 | configurePug, 71 | createPageTemplate, 72 | createLayoutTemplate, 73 | createFileFromTemplate, 74 | createEmptyFileTemplate, 75 | directToggleDevTools, 76 | nuxtConfigWatcher 77 | } 78 | 79 | export default commands 80 | -------------------------------------------------------------------------------- /src/commands/installDependencies.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import pm from '../content/pm'; 3 | import { detectPackageManagerByName, newTerminal } from '../utils'; 4 | 5 | 6 | export const installDependencies = () => { 7 | const packageManager = detectPackageManagerByName() 8 | 9 | const items: vscode.QuickPickItem[] = pm.map((item) => { 10 | return { 11 | label: item.name, 12 | } 13 | }) 14 | 15 | const options: vscode.QuickPickOptions = { 16 | canPickMany: false, 17 | ignoreFocusOut: true, 18 | placeHolder: 'No package manager detected. Chose one!', 19 | } 20 | 21 | if (packageManager) { 22 | if (packageManager.installCommand.includes('yarn')) { 23 | packageManager.installCommand = 'yarn' 24 | } 25 | newTerminal('Install Dependencies', packageManager.installCommand) 26 | } else { 27 | vscode.window.showQuickPick(items, options).then((name) => { 28 | if (name) { 29 | const command = pm.find( 30 | (item) => item.name === name.label 31 | )?.installCommand 32 | if (command) { 33 | newTerminal('Install Dependencies', command) 34 | } 35 | } 36 | }) 37 | } 38 | } -------------------------------------------------------------------------------- /src/commands/layout.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { createDir, createFile, normalizeFileExtension, projectSrcDirectory } from '../utils' 3 | import { generateVueFileTemplate } from '../utils/files' 4 | 5 | 6 | const createLayout = () => { 7 | window 8 | .showInputBox({ 9 | prompt: 'What is your layout name?', 10 | placeHolder: 'Layout name', 11 | }) 12 | .then(async (name) => { 13 | if (!name) { return } 14 | 15 | createDir('layouts') 16 | 17 | const filePath = `${await projectSrcDirectory()}/layouts/${normalizeFileExtension(name, '.vue')}.vue` 18 | 19 | createFile({ 20 | fileName: `${name}.vue`, 21 | content: generateVueFileTemplate('layout'), 22 | fullPath: filePath, 23 | }) 24 | }) 25 | } 26 | 27 | const directCreateLayout = (path: string) => { 28 | window 29 | .showInputBox({ 30 | prompt: 'What is your layout name?', 31 | placeHolder: 'layout name', 32 | }) 33 | .then((name) => { 34 | if (!name) { return } 35 | 36 | const filePath = `${path}/${normalizeFileExtension(name, '.vue')}.vue` 37 | 38 | createFile({ 39 | fileName: `${name}.vue`, 40 | content: generateVueFileTemplate('layout'), 41 | fullPath: filePath, 42 | }) 43 | }) 44 | } 45 | 46 | export { createLayout, directCreateLayout } 47 | -------------------------------------------------------------------------------- /src/commands/middleware.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { nitroDefaultTemplate, nuxtMiddlewareTemplate } from '../templates' 3 | import { createDir, createFile, createSubFolders, hasServerDir, normalizeFileExtension, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 4 | 5 | const createMiddleware = () => { 6 | window 7 | .showInputBox({ 8 | prompt: 'What is your middleware name?', 9 | placeHolder: 'middleware name', 10 | }) 11 | .then(async (name) => { 12 | 13 | if (!name) { return } 14 | 15 | const middlewareDir = `${await projectSrcDirectory()}/middleware` 16 | 17 | await createDir('middleware') 18 | 19 | const subFolders = await createSubFolders(middlewareDir, 'middleware') 20 | 21 | showSubFolderQuickPick({ 22 | name, 23 | subFolders, 24 | commandType: 'middleware', 25 | content: nuxtMiddlewareTemplate 26 | }) 27 | 28 | 29 | }) 30 | } 31 | 32 | const directCreateMiddleware = async (path: string) => { 33 | const serverDir = await hasServerDir() 34 | 35 | window 36 | .showInputBox({ 37 | prompt: 'What is your middleware name?', 38 | placeHolder: 'middleware name', 39 | }) 40 | .then((name) => { 41 | if (!name) { return } 42 | 43 | const filePath = `${path}/${normalizeFileExtension(name, '.ts')}.ts` 44 | 45 | createFile({ 46 | fileName: `${name}.ts`, 47 | content: filePath.includes(`${serverDir}`) ? nitroDefaultTemplate : nuxtMiddlewareTemplate, 48 | fullPath: filePath, 49 | }) 50 | }) 51 | } 52 | 53 | export { createMiddleware, directCreateMiddleware } 54 | -------------------------------------------------------------------------------- /src/commands/nuxi/commonCommands.ts: -------------------------------------------------------------------------------- 1 | import { detectPackageManagerByName, newTerminal, projectRootDirectory } from '../../utils'; 2 | import { handleModuleCommand } from './multiStepCommands'; 3 | 4 | const pm = detectPackageManagerByName(); 5 | const runCommand = pm ? pm.runCommand : 'npx'; 6 | 7 | const nuxtDev = () => newTerminal('Nuxi: Dev', `${runCommand} nuxi dev`, `${projectRootDirectory()}`) 8 | const nuxtBuild = () => newTerminal('Nuxi: Build', `${runCommand} nuxi build`, `${projectRootDirectory()}`) 9 | const nuxtGenerate = () => newTerminal('Nuxi: Build', `${runCommand} nuxi generate`, `${projectRootDirectory()}`) 10 | const nuxtCleanUp = () => newTerminal('Nuxi: Cleanup', `${runCommand} nuxi cleanup`, `${projectRootDirectory()}`) 11 | const nuxtAnalyze = () => newTerminal('Nuxi: Analyze', `${runCommand} nuxi analyze`, `${projectRootDirectory()}`) 12 | const nuxtInfo = () => newTerminal('Nuxi: Info', `${runCommand} nuxi info`, `${projectRootDirectory()}`) 13 | const nuxtModule = async () => await handleModuleCommand() 14 | 15 | export { nuxtAnalyze, nuxtBuild, nuxtCleanUp, nuxtDev, nuxtGenerate, nuxtInfo, nuxtModule }; 16 | -------------------------------------------------------------------------------- /src/commands/nuxi/index.ts: -------------------------------------------------------------------------------- 1 | import { capitalize } from 'string-ts'; 2 | import { QuickPickItem, ThemeIcon, window } from 'vscode'; 3 | import { detectPackageManagerByName, newTerminal, projectRootDirectory } from '../../utils'; 4 | import { tryImportNuxi } from '../../nuxi'; 5 | import { handleAddCommand, handleDevtoolsCommand, handleModuleCommand } from './multiStepCommands'; 6 | 7 | const pm = detectPackageManagerByName(); 8 | const runCommand = pm ? pm.runCommand : 'npx'; 9 | 10 | enum CLICommandDescription { 11 | add = 'Create a new template file', 12 | analyze = 'Build nuxt and analyze production bundle (experimental)', 13 | 'build-module' = 'Helper command for using @nuxt/module-builder', 14 | build = 'Build nuxt for production deployment', 15 | cleanup = 'Cleanup generated nuxt files and caches', 16 | _dev = 'Run nuxt development server (internal command to start child process)', 17 | dev = 'Run nuxt development server', 18 | devtools = 'Enable or disable devtools in a Nuxt project', 19 | generate = 'Build Nuxt and prerender all routes', 20 | info = 'Get information about nuxt project', 21 | init = 'Initialize a fresh project', 22 | module = 'Manage Nuxt Modules', 23 | prepare = 'Prepare nuxt for development/build', 24 | preview = 'Launches nitro server for local testing after nuxi build', 25 | start = 'Launches nitro server for local testing after nuxi build', 26 | test = 'Run tests', 27 | typecheck = 'Runs vue-tsc to check types throughout your app', 28 | upgrade = 'Upgrade nuxt' 29 | } 30 | 31 | const directlyExecutableCommands = new Set([ 'dev', 'build', 'generate', 'cleanup', 'analyze', 'build-module', 'info', 'typecheck', 'preview', 'prepare', 'upgrade', 'start', 'test' ]); 32 | const indirectlyExecutableCommands = new Set(['module', 'add', 'devtools']); 33 | const unsupportedCommands = new Set(['init']); 34 | const moduleCommands = new Set(['build-module']) 35 | const internalCommands = new Set(['_dev']) 36 | 37 | const shouldDirectlyRun = (command: any) => directlyExecutableCommands.has(command); 38 | const shouldIndirectlyRun = (command: any) => indirectlyExecutableCommands.has(command); 39 | 40 | const showCLICommands = async () => { 41 | const nuxi = await tryImportNuxi() 42 | if (!nuxi) { 43 | console.log('nuxi not found') 44 | return 45 | } 46 | const commands = Object.keys(nuxi.main.subCommands); 47 | 48 | const options = { 49 | placeHolder: 'Select a command', 50 | }; 51 | 52 | type CLICommandDescription = { 53 | [key: string]: string; 54 | } 55 | 56 | const items = 57 | commands 58 | .filter((command) => !moduleCommands.has(command)) 59 | .filter((command) => !internalCommands.has(command)) 60 | .filter((command) => !unsupportedCommands.has(command)) 61 | .map((command) => { 62 | const description: CLICommandDescription = CLICommandDescription; 63 | const item: QuickPickItem = { 64 | label: capitalize(command), 65 | description: description[command], 66 | iconPath: new ThemeIcon('nuxt-logo'), 67 | }; 68 | 69 | return item; 70 | }); 71 | 72 | window.showQuickPick(items, options).then(async (selection) => { 73 | if (!selection) { 74 | return; 75 | } 76 | 77 | const command = selection.label; 78 | 79 | if (shouldDirectlyRun(selection.label.toLowerCase())) { 80 | const terminalName = `Nuxi: ${command}`; 81 | newTerminal(terminalName, `${runCommand} nuxi ${command.toLowerCase()}`, `${projectRootDirectory()}`); 82 | } else if (shouldIndirectlyRun(command.toLowerCase())) { 83 | if (command.toLowerCase() === 'module') { 84 | await handleModuleCommand(); 85 | } 86 | 87 | if (command.toLowerCase() === 'add') { 88 | await handleAddCommand(); 89 | } 90 | 91 | if (command.toLowerCase() === 'devtools') { 92 | handleDevtoolsCommand() 93 | } 94 | 95 | } else { 96 | window.showInformationMessage(`Command ${command} is not supported yet`); 97 | } 98 | }); 99 | }; 100 | 101 | 102 | export { showCLICommands }; 103 | 104 | export { nuxtAnalyze, nuxtBuild, nuxtCleanUp, nuxtDev, nuxtGenerate, nuxtInfo, nuxtModule } from './commonCommands'; 105 | -------------------------------------------------------------------------------- /src/commands/nuxi/multiStepCommands.ts: -------------------------------------------------------------------------------- 1 | import { ofetch } from 'ofetch'; 2 | import { QuickPickItem, QuickPickOptions, window } from 'vscode'; 3 | import type { nuxtModule } from '../../types'; 4 | import { detectPackageManagerByName, isNuxtTwo, newTerminal, projectRootDirectory } from '../../utils'; 5 | 6 | 7 | const pm = detectPackageManagerByName(); 8 | const runCommand = pm ? pm.runCommand : 'npx'; 9 | 10 | 11 | const fetchModules = async () => { 12 | const res = await ofetch('https://api.nuxt.com/modules'); 13 | return res.modules; 14 | } 15 | 16 | 17 | export const handleAddCommand = (): void => { 18 | const options: QuickPickOptions = { 19 | placeHolder: 'Select module(s)', 20 | canPickMany: false 21 | }; 22 | 23 | const templates = ['api', 'plugin', 'component', 'composable', 'middleware', 'layout', 'page']; 24 | 25 | const items = templates.map((template) => { 26 | const item: QuickPickItem = { 27 | label: template 28 | }; 29 | 30 | return item; 31 | }); 32 | 33 | window.showQuickPick(items, options).then((selection) => { 34 | if (!selection) { 35 | return; 36 | } 37 | 38 | const template = selection.label; 39 | 40 | const templateName = window.showInputBox({ 41 | placeHolder: 'Enter the name of the template file', 42 | prompt: `Template name for ${template}` 43 | }) 44 | 45 | templateName.then((templateName) => { 46 | if (!templateName) { 47 | return; 48 | } 49 | 50 | const terminalName = 'Nuxi: Add'; 51 | const command = `nuxi add ${template} ${templateName}`; 52 | newTerminal(terminalName, command, `${projectRootDirectory()}`); 53 | }); 54 | }) 55 | 56 | } 57 | 58 | 59 | export const handleModuleCommand = async () => { 60 | const modules: nuxtModule[] = await fetchModules(); 61 | 62 | if (Array.isArray(modules)) { 63 | const options: QuickPickOptions = { 64 | placeHolder: 'Select module', 65 | canPickMany: false 66 | }; 67 | 68 | const items = modules 69 | .filter(module => isNuxtTwo() ? module.compatibility.nuxt.includes('2.0.0') : module.compatibility.nuxt.includes('3.0.0')) 70 | .map((module) => { 71 | const item: QuickPickItem = { 72 | label: module.name, 73 | description: module.description 74 | }; 75 | 76 | return item; 77 | }); 78 | 79 | window.showQuickPick(items, options).then((selection) => { 80 | if (!selection) { 81 | return; 82 | } 83 | 84 | let userSelection = null 85 | 86 | userSelection = selection.label; 87 | 88 | if (!userSelection) { 89 | return; 90 | } 91 | 92 | const terminalName = 'Nuxi: Module'; 93 | const command = `${runCommand} nuxi module add ${userSelection}`; 94 | newTerminal(terminalName, command, `${projectRootDirectory()}`); 95 | }); 96 | } else { 97 | window.showErrorMessage('Error fetching Nuxt modules'); 98 | } 99 | }; 100 | 101 | export const handleDevtoolsCommand = () => { 102 | const options: QuickPickOptions = { 103 | placeHolder: 'Select state', 104 | canPickMany: false 105 | }; 106 | 107 | const commandOptions = ['enable', 'disable']; 108 | 109 | const items = commandOptions.map((template) => { 110 | const item: QuickPickItem = { 111 | label: template 112 | }; 113 | 114 | return item; 115 | }); 116 | 117 | window.showQuickPick(items, options).then((selection) => { 118 | if (!selection) { 119 | return; 120 | } 121 | 122 | const state = selection.label; 123 | const terminalName = 'Nuxi: Devtools'; 124 | const command = `nuxi devtools ${state}`; 125 | newTerminal(terminalName, command, `${projectRootDirectory()}`); 126 | }) 127 | 128 | } -------------------------------------------------------------------------------- /src/commands/page.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { createDir, createFile, createSubFolders, normalizeFileExtension, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 3 | import { generateVueFileTemplate } from '../utils/files' 4 | 5 | const createPage = () => { 6 | window 7 | .showInputBox({ 8 | prompt: 'What is your page name?', 9 | placeHolder: 'page name', 10 | }) 11 | .then(async (name) => { 12 | if (!name) {return} 13 | 14 | const pagesDir = `${await projectSrcDirectory()}/pages` 15 | 16 | await createDir('pages') 17 | 18 | const subFolders = await createSubFolders(pagesDir, 'pages') 19 | 20 | showSubFolderQuickPick({ 21 | name, 22 | subFolders, 23 | commandType: 'pages', 24 | content: generateVueFileTemplate('page'), 25 | }) 26 | }) 27 | } 28 | 29 | function directCreatePage (path: string) { 30 | window 31 | .showInputBox({ 32 | prompt: 'What is your page name?', 33 | placeHolder: 'page name', 34 | }) 35 | .then((name) => { 36 | if (!name) {return} 37 | 38 | const filePath = `${path}/${normalizeFileExtension(name, '.vue')}.vue` 39 | 40 | createFile({ 41 | fileName: `${name}.vue`, 42 | content: generateVueFileTemplate('page'), 43 | fullPath: filePath, 44 | }) 45 | }) 46 | } 47 | 48 | export { createPage, directCreatePage } 49 | -------------------------------------------------------------------------------- /src/commands/plugin.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { nitroPluginTemplate, nuxtPluginTemplate } from '../templates' 3 | import { createDir, createFile, createSubFolders, hasServerDir, normalizeFileExtension, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 4 | 5 | const createPlugin = () => { 6 | window 7 | .showInputBox({ 8 | prompt: 'What is your plugin name?', 9 | placeHolder: 'plugin name', 10 | }) 11 | .then(async (name) => { 12 | if (!name) { return } 13 | 14 | const pluginsDir = `${await projectSrcDirectory()}/plugins` 15 | 16 | await createDir('plugins') 17 | 18 | const subFolders = await createSubFolders(pluginsDir, 'plugins') 19 | 20 | showSubFolderQuickPick({ 21 | name, 22 | subFolders, 23 | commandType: 'plugins', 24 | content: nuxtPluginTemplate 25 | }) 26 | 27 | }) 28 | } 29 | 30 | 31 | const directCreatePlugin = async (path: string) => { 32 | const serverDir = await hasServerDir() 33 | 34 | window 35 | .showInputBox({ 36 | prompt: 'What is your plugin name?', 37 | placeHolder: 'plugin name', 38 | }) 39 | .then((name) => { 40 | if (!name) { return } 41 | 42 | const filePath = `${path}/${normalizeFileExtension(name, '.ts')}.ts` 43 | 44 | 45 | createFile({ 46 | fileName: `${name}.ts`, 47 | content: filePath.includes(`${serverDir}`) ? nitroPluginTemplate : nuxtPluginTemplate, 48 | fullPath: filePath, 49 | }) 50 | }) 51 | } 52 | 53 | export { createPlugin, directCreatePlugin } 54 | -------------------------------------------------------------------------------- /src/commands/store.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { 3 | createFile, 4 | generatePiniaTemplates, 5 | getInstallationCommand, 6 | isDependencyInstalled, 7 | isModuleConfigured, 8 | isNuxtTwo, 9 | normalizeFileExtension, 10 | projectSrcDirectory, 11 | runCommand, 12 | updateNuxtConfig 13 | } from '../utils' 14 | 15 | import { vuexContent } from '../templates' 16 | 17 | async function detectPiniaModule () { 18 | const moduleName = '@pinia/nuxt' 19 | const isConfigured = isModuleConfigured(moduleName) 20 | const installationCommand = await getInstallationCommand(moduleName, true) 21 | const isInstalled = await isDependencyInstalled(moduleName) 22 | 23 | 24 | if (!isInstalled ) { 25 | await runCommand({ 26 | command: installationCommand, 27 | message: 'Installing @pinia/nuxt module', 28 | successMessage: 'Pinia module installed successfully', 29 | errorMessage: 'Pinia module installation failed', 30 | docsURL: 'https://pinia.vuejs.org/' 31 | }) 32 | } 33 | 34 | if (!isConfigured) { 35 | await updateNuxtConfig('add-module', moduleName) 36 | } 37 | 38 | } 39 | 40 | const createStore = () => { 41 | window 42 | .showInputBox({ 43 | prompt: 'What is your store name?', 44 | placeHolder: 'store name', 45 | }) 46 | .then(async (name: any) => { 47 | if (!name) { return } 48 | 49 | const filePath = `${await projectSrcDirectory()}/${isNuxtTwo() ? 'store' : 'stores'}/${name}.${isNuxtTwo() ? 'js' : 'ts'}` 50 | await (isNuxtTwo() ? createFile({ 51 | fileName: name, 52 | content: vuexContent, 53 | fullPath: filePath, 54 | }) : createFile({ 55 | fileName: name, 56 | content: generatePiniaTemplates(name), 57 | fullPath: filePath, 58 | })); 59 | 60 | await detectPiniaModule() 61 | }) 62 | } 63 | 64 | const directCreateStore = (path: string) => { 65 | window 66 | .showInputBox({ 67 | prompt: 'What is your store name?', 68 | placeHolder: 'store name', 69 | }) 70 | .then(async (name) => { 71 | if (!name) { return } 72 | 73 | const filePath = `${path}/${normalizeFileExtension(name, isNuxtTwo() ? '.js' : '.ts' )}.${isNuxtTwo() ? 'js' : 'ts'}` 74 | 75 | await (isNuxtTwo() ? createFile({ 76 | fileName: name, 77 | content: vuexContent, 78 | fullPath: filePath, 79 | }) : createFile({ 80 | fileName: name, 81 | content: generatePiniaTemplates(name), 82 | fullPath: filePath, 83 | })); 84 | 85 | await detectPiniaModule() 86 | }) 87 | } 88 | 89 | export { createStore, directCreateStore } 90 | -------------------------------------------------------------------------------- /src/commands/structure.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, mkdirSync } from 'node:fs' 2 | import { window } from 'vscode' 3 | import { appConfigContent } from '../templates' 4 | import { createFile, generateVueFileTemplate, isNuxtTwo, projectRootDirectory, projectSrcDirectory } from '../utils' 5 | 6 | function promptDirectorySelection () { 7 | let directories = ['components', 'pages', 'assets', 'plugins', 'layouts', 'middleware', 'modules',] 8 | 9 | const nuxtTwoDirectories = ['static', 'store',] 10 | 11 | const nuxtThreeDirectories = ['public', 'composables', 'server', 'utils', 'stores'] 12 | 13 | isNuxtTwo() ? (directories = [...directories, ...nuxtTwoDirectories]) : (directories = [...directories, ...nuxtThreeDirectories]) 14 | 15 | directories.sort() 16 | 17 | window 18 | .showQuickPick(directories, { 19 | canPickMany: true, 20 | placeHolder: 'Select directories to create', 21 | }) 22 | .then((selectedDirs) => { 23 | if (selectedDirs !== undefined && selectedDirs.length > 0) { 24 | selectedDirs.forEach(async (dir) => { 25 | const dirPath = `${await projectSrcDirectory()}/${dir}` 26 | if (!existsSync(dirPath)) { 27 | mkdirSync(dirPath) 28 | } 29 | 30 | if (dir === 'pages') { 31 | await createFile({ 32 | fileName: 'index.vue', 33 | content: generateVueFileTemplate('page'), 34 | fullPath: `${dirPath}/index.vue`, 35 | }) 36 | } 37 | 38 | if (dir === 'layouts') { 39 | await createFile({ 40 | fileName: 'default.vue', 41 | content: generateVueFileTemplate('layout'), 42 | fullPath: `${dirPath}/default.vue`, 43 | }) 44 | } 45 | }) 46 | } 47 | }) 48 | } 49 | 50 | const projectStructure = () => { 51 | promptDirectorySelection() 52 | } 53 | 54 | const appConfig = () => { 55 | createFile({ 56 | fileName: 'app.config.ts', 57 | content: appConfigContent, 58 | fullPath: `${projectRootDirectory()}/app.config.ts`, 59 | }) 60 | } 61 | 62 | const nuxtIgnore = () => { 63 | createFile({ 64 | fileName: '.nuxtignore', 65 | content: '', 66 | fullPath: `${projectRootDirectory()}/.nuxtignore`, 67 | }) 68 | } 69 | 70 | const nuxtRC = () => { 71 | createFile({ 72 | fileName: '.nuxtrc', 73 | content: '', 74 | fullPath: `${projectRootDirectory()}/.nuxtrc`, 75 | }) 76 | } 77 | 78 | const errorLayout = async () => { 79 | const filePath = `${await projectSrcDirectory()}/error.vue` 80 | 81 | await createFile({ 82 | fileName: 'error.vue', 83 | content: generateVueFileTemplate('page'), 84 | fullPath: filePath, 85 | }) 86 | } 87 | 88 | export { appConfig, errorLayout, nuxtIgnore, nuxtRC, projectStructure } 89 | -------------------------------------------------------------------------------- /src/commands/templates.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'node:fs' 2 | import { readTSConfig, writeTSConfig } from 'pkg-types' 3 | import { window } from 'vscode' 4 | import type { TSConfigNuxt } from '../types' 5 | import { getInstallationCommand, projectRootDirectory, runCommand } from '../utils' 6 | 7 | export enum PugConfigurationSteps { 8 | installPug = 'Install Pug', 9 | installLanguagePlugin = 'Install @vue/language-plugin-pug', 10 | addPluginToTSConfig = 'Add @vue/language-plugin-pug to tsconfig.json', 11 | } 12 | 13 | const defaultOptions = Object.values(PugConfigurationSteps) 14 | 15 | 16 | export const configurePug = (options: string[] = defaultOptions) => { 17 | try { 18 | window 19 | .showQuickPick(options, { 20 | canPickMany: true, 21 | placeHolder: 'Select files to create', 22 | }) 23 | .then(async (selections) => { 24 | if (selections !== undefined && selections.length > 0) { 25 | if (selections.includes(PugConfigurationSteps.installPug)) { 26 | const moduleName = 'pug' 27 | const command = await getInstallationCommand(moduleName, true) 28 | 29 | await runCommand({ 30 | command, 31 | message: 'Installing Pug', 32 | successMessage: 'Pug installed successfully', 33 | errorMessage: 'Pug installation failed', 34 | }) 35 | } 36 | 37 | if (selections.includes(PugConfigurationSteps.installLanguagePlugin)) { 38 | const moduleName = '@vue/language-plugin-pug' 39 | const command = await getInstallationCommand(moduleName, true) 40 | 41 | await runCommand({ 42 | command, 43 | message: 'Installing @vue/language-plugin-pug', 44 | successMessage: '@vue/language-plugin-pug installed successfully', 45 | errorMessage: '@vue/language-plugin-pug installation failed', 46 | }) 47 | } 48 | 49 | if (selections.includes(PugConfigurationSteps.addPluginToTSConfig)) { 50 | const path = `${projectRootDirectory()}/tsconfig.json`; 51 | 52 | if (!existsSync(path)) { 53 | return; 54 | } 55 | 56 | const tsconfig: TSConfigNuxt = await readTSConfig(path); 57 | tsconfig.vueCompilerOptions = { 58 | plugins: [ 59 | '@vue/language-plugin-pug' 60 | ] 61 | } 62 | 63 | await writeTSConfig(path, tsconfig) 64 | 65 | window.showInformationMessage('Pug is added to tsconfig.json') 66 | } 67 | } 68 | }) 69 | } catch (error) { 70 | console.error(error) 71 | } 72 | } -------------------------------------------------------------------------------- /src/commands/util.ts: -------------------------------------------------------------------------------- 1 | import { window } from 'vscode' 2 | import { nitroUtilTemplate, nuxtUtilTemplate } from '../templates' 3 | import { createDir, createFile, createSubFolders, hasServerDir, projectSrcDirectory, showSubFolderQuickPick } from '../utils' 4 | 5 | const createUtil = () => { 6 | window 7 | .showInputBox({ 8 | prompt: 'What is your utility name?', 9 | placeHolder: 'utility name', 10 | }) 11 | .then(async (name) => { 12 | if (!name) { return } 13 | 14 | const utilsDir = `${await projectSrcDirectory()}/utils` 15 | 16 | await createDir('utils') 17 | 18 | const subFolders = await createSubFolders(utilsDir, 'nuxtUtil') 19 | 20 | showSubFolderQuickPick({ 21 | name, 22 | subFolders, 23 | commandType: 'nuxtUtil', 24 | content: nuxtUtilTemplate() 25 | }) 26 | 27 | }) 28 | } 29 | 30 | 31 | const directCreateUtil = async (path: string) => { 32 | const serverDir = await hasServerDir() 33 | 34 | window 35 | .showInputBox({ 36 | prompt: 'What is your utility name?', 37 | placeHolder: 'utility name', 38 | }) 39 | .then((name) => { 40 | if (!name) { return } 41 | 42 | const filePath = `${path}/${name}.ts` 43 | 44 | 45 | createFile({ 46 | fileName: `${name}.ts`, 47 | content: filePath.includes(`${serverDir}`) ? nitroUtilTemplate : nuxtUtilTemplate(), 48 | fullPath: filePath, 49 | }) 50 | }) 51 | } 52 | 53 | export { createUtil, directCreateUtil } 54 | -------------------------------------------------------------------------------- /src/content/pm.ts: -------------------------------------------------------------------------------- 1 | const pm = [ 2 | { 3 | name: 'NPM', 4 | lockFile: 'package-lock.json', 5 | installCommand: 'npm install', 6 | uninstallCommand: 'npm uninstall', 7 | }, 8 | { 9 | name: 'Yarn', 10 | lockFile: 'yarn.lock', 11 | installCommand: 'yarn install', 12 | uninstallCommand: 'yarn remove', 13 | }, 14 | { 15 | name: 'pnpm', 16 | lockFile: 'pnpm-lock.yaml', 17 | installCommand: 'pnpm install', 18 | uninstallCommand: 'pnpm remove', 19 | }, 20 | { 21 | name: 'Bun', 22 | lockFile: 'bun.lockb', 23 | installCommand: 'bun install', 24 | uninstallCommand: 'bun remove', 25 | } 26 | ] 27 | 28 | export default pm 29 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, commands } from 'vscode'; 2 | import { activateExtension, publicCommands } from './extension' 3 | import { isNuxtProject, isNuxtTwo } from './utils'; 4 | 5 | export async function activate (context: ExtensionContext) { 6 | const isNuxt = await isNuxtProject(); 7 | 8 | 9 | commands.executeCommand('setContext', 'nuxtr.isNuxtProject', isNuxt); 10 | 11 | 12 | if (isNuxt) { 13 | const nuxtTwo = isNuxtTwo(); 14 | commands.executeCommand('setContext', 'nuxtr.isNuxtTwo', nuxtTwo); 15 | 16 | activateExtension(context); 17 | } else { 18 | for (const { command, function: commandFunction } of publicCommands) { 19 | context.subscriptions.push(commands.registerCommand(command, commandFunction)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/intellisense/completionProviders/nuxtIgnore.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, readdirSync } from 'node:fs'; 2 | import { dirname, extname, join, sep } from 'pathe'; 3 | import * as vscode from 'vscode'; 4 | 5 | const EXTENSIONS = new Set(['.vue', '.js', '.ts']); 6 | const DIR_SEPARATOR = sep; 7 | const SCOPED_DIRECTORIES = new Set(['components', 'layouts', 'pages', 'composables', 'middleware']); 8 | 9 | export class NuxtIgnoreCompletionProvider implements vscode.CompletionItemProvider { 10 | provideCompletionItems ( 11 | document: vscode.TextDocument, 12 | position: vscode.Position, 13 | ): vscode.ProviderResult { 14 | try { 15 | const lineText = document.lineAt(position).text; 16 | const currentDirectory = dirname(document.fileName); 17 | 18 | if (position.character === lineText.length) { 19 | const textBeforeCursor = lineText.slice(0, Math.max(0, position.character)); 20 | const pathSeparatorIndex = textBeforeCursor.lastIndexOf(DIR_SEPARATOR); 21 | 22 | if (pathSeparatorIndex !== -1) { 23 | const userTypedPath = textBeforeCursor.slice(0, Math.max(0, pathSeparatorIndex)); 24 | const targetDirectory = join(currentDirectory, userTypedPath); 25 | const subDirectories = this.getSubDirectories(targetDirectory); 26 | 27 | return this.createCompletionItems(subDirectories); 28 | } else if (textBeforeCursor.endsWith('!')) { 29 | const parts = textBeforeCursor.split('!'); 30 | const userTypedPath = parts[0]; 31 | // @ts-ignore 32 | const targetDirectory = join(currentDirectory, userTypedPath); 33 | const subDirectories = this.getSubDirectories(targetDirectory); 34 | 35 | return this.createCompletionItems(subDirectories); 36 | } else { 37 | const topLevelSubDirectories = this.getTopLevelSubDirectories(currentDirectory); 38 | return this.createCompletionItems(topLevelSubDirectories); 39 | } 40 | } 41 | } catch (error) { 42 | this.logError(error); 43 | } 44 | 45 | return []; 46 | } 47 | 48 | private createCompletionItems (directories: string[]): vscode.CompletionItem[] { 49 | return directories.map(subDir => { 50 | const type = this.getCompletionItemType(subDir); 51 | const item = new vscode.CompletionItem(subDir, type); 52 | 53 | if (type === vscode.CompletionItemKind.Folder) { 54 | item.insertText = new vscode.SnippetString(subDir); 55 | } 56 | 57 | return item; 58 | }); 59 | } 60 | 61 | private getCompletionItemType (name: string): vscode.CompletionItemKind { 62 | const extension = extname(name); 63 | return EXTENSIONS.has(extension) ? vscode.CompletionItemKind.File : vscode.CompletionItemKind.Folder; 64 | } 65 | 66 | private getSubDirectories (directory: string): string[] { 67 | directory = directory.replace(/!/g, ''); 68 | 69 | try { 70 | if (existsSync(directory)) { 71 | return readdirSync(directory, { withFileTypes: true }) 72 | .filter(dirent => dirent.isDirectory() || EXTENSIONS.has(extname(dirent.name))) 73 | .map(dirent => dirent.name); 74 | } 75 | } catch (error) { 76 | this.logError(error); 77 | } 78 | 79 | return []; 80 | } 81 | 82 | private getTopLevelSubDirectories (directory: string): string[] { 83 | return this.getSubDirectories(directory).filter(subDir => SCOPED_DIRECTORIES.has(subDir)); 84 | } 85 | 86 | private logError (error: any): void { 87 | console.error(error); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/intellisense/completionProviders/nuxtRC.ts: -------------------------------------------------------------------------------- 1 | import * as nuxtRcSchemaJson from '@nuxt/schema/schema/config.schema.json'; 2 | import * as vscode from 'vscode'; 3 | import type { ConfigurationProperty } from '../../types'; 4 | 5 | const nuxtRcSchema = nuxtRcSchemaJson as { properties: { [key: string]: ConfigurationProperty } }; 6 | 7 | const autoImportProperty: ConfigurationProperty = { 8 | type: 'boolean', 9 | id: '#imports/autoImport', 10 | default: true, 11 | title: 'Enable implicit auto import from Vue, Nuxt, and module contributed utilities. Generate global TypeScript definitions.', 12 | markdownType: 'SrcTypesImportsImportsOptions' 13 | }; 14 | 15 | const transformProperty: ConfigurationProperty = { 16 | type: 'object', 17 | id: '#imports/transform', 18 | properties: { 19 | exclude: { 20 | type: 'array', 21 | }, 22 | include: { 23 | type: 'array', 24 | } 25 | } 26 | }; 27 | 28 | function createCompletionItem (propertyName: string, property: ConfigurationProperty) { 29 | const snippet = new vscode.CompletionItem(propertyName); 30 | snippet.detail = property.title || ''; 31 | snippet.label = propertyName; 32 | snippet.kind = vscode.CompletionItemKind.Property; 33 | 34 | snippet.documentation = new vscode.MarkdownString( 35 | `[Nuxt Documentation](https://nuxt.com/docs/api/configuration/nuxt-config#${propertyName})` 36 | ); 37 | 38 | snippet.preselect = true; 39 | 40 | if (property.type === 'boolean') { 41 | snippet.insertText = new vscode.SnippetString(`${propertyName}=\${1|true,false|}`); 42 | } else if (property.type === 'string') { 43 | snippet.insertText = new vscode.SnippetString(`${propertyName}=\${1:}`); 44 | } else if (property.type === 'object' && property.properties) { 45 | snippet.insertText = new vscode.SnippetString(`${propertyName}`); 46 | } else if (property.type === 'any' && !property.properties) { 47 | snippet.insertText = new vscode.SnippetString(`${propertyName}=\${1:}`); 48 | } 49 | 50 | return snippet; 51 | } 52 | 53 | 54 | export class CustomCompletionProvider implements vscode.CompletionItemProvider { 55 | provideCompletionItems (document: vscode.TextDocument, position: vscode.Position) { 56 | const linePrefix = document.lineAt(position).text.slice(0, position.character); 57 | 58 | const completionItems = Object.keys(nuxtRcSchema.properties) 59 | .filter(propertyName => propertyName.startsWith(linePrefix) && !propertyName.startsWith('_')) 60 | .map(propertyName => { 61 | const property = nuxtRcSchema.properties[propertyName] as ConfigurationProperty; 62 | 63 | return createCompletionItem(propertyName, property); 64 | }); 65 | 66 | 67 | if (linePrefix.endsWith('.')) { 68 | const propertyPath = linePrefix.slice(0, - 1); 69 | 70 | const property = nuxtRcSchema.properties[propertyPath]; 71 | if (property && property.type === 'object' && property.properties) { 72 | const nestedProperties = property.properties; 73 | const nestedCompletionItems = Object.keys(nestedProperties).map(propertyName => { 74 | const nestedProperty = nestedProperties[propertyName] as ConfigurationProperty; 75 | return createCompletionItem(`${propertyName}`, nestedProperty); 76 | }); 77 | 78 | if (propertyPath === 'imports') { 79 | nestedCompletionItems.push(createCompletionItem('autoImport', autoImportProperty)); 80 | nestedCompletionItems.push(createCompletionItem('transform', transformProperty)); 81 | } 82 | 83 | return nestedCompletionItems; 84 | } 85 | return []; 86 | } else { 87 | return completionItems; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/intellisense/index.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, languages } from 'vscode'; 2 | import { NuxtIgnoreCompletionProvider } from './completionProviders/nuxtIgnore'; 3 | import { CustomCompletionProvider } from './completionProviders/nuxtRC' 4 | import { NuxtPagesCompletionProvider, PublicDirCompletionProvider } from './completionProviders/vueFiles'; 5 | import { languageSelector, nuxtrConfiguration, patternSelector } from '../utils'; 6 | 7 | const intelliSenseConfig = nuxtrConfiguration().intellisense; 8 | 9 | 10 | export function activateIntellisense (context: ExtensionContext) { 11 | if (intelliSenseConfig.nuxtignore) { 12 | const nuxtIgnoreProvider = languages.registerCompletionItemProvider( 13 | patternSelector('**/.nuxtignore'), 14 | new NuxtIgnoreCompletionProvider(), 15 | '/', 16 | ) 17 | 18 | context.subscriptions.push(nuxtIgnoreProvider); 19 | } 20 | 21 | if (intelliSenseConfig.vueFiles) { 22 | const nuxtPagesProvider = languages.registerCompletionItemProvider( 23 | languageSelector('vue'), 24 | new NuxtPagesCompletionProvider(), 25 | '/', '\\' 26 | ) 27 | 28 | const publicDirProvider = languages.registerCompletionItemProvider( 29 | languageSelector('vue'), 30 | new PublicDirCompletionProvider(), 31 | '/', '\\' 32 | ) 33 | 34 | 35 | context.subscriptions.push(nuxtPagesProvider, publicDirProvider); 36 | } 37 | 38 | if (intelliSenseConfig.nuxtrc) { 39 | const nuxtRCProvider = languages.registerCompletionItemProvider( 40 | patternSelector('**/.nuxtrc'), 41 | new CustomCompletionProvider(), 42 | '.' 43 | ) 44 | 45 | context.subscriptions.push(nuxtRCProvider); 46 | } 47 | 48 | 49 | } -------------------------------------------------------------------------------- /src/nuxi/index.ts: -------------------------------------------------------------------------------- 1 | import { tryImport } from '../utils'; 2 | 3 | export async function tryImportNuxi () { 4 | return ((await tryImport('nuxi')) || (await tryImport('nuxi-nightly'))) as undefined | typeof import('nuxi-nightly'); 5 | } 6 | -------------------------------------------------------------------------------- /src/sideBar/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "arrowParens": "always", 4 | "printWidth": 80, 5 | "singleQuote": false, 6 | "jsxSingleQuote": false, 7 | "semi": true, 8 | "trailingComma": "all", 9 | "tabWidth": 2 10 | } -------------------------------------------------------------------------------- /src/sideBar/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /src/sideBar/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | Vite + Vue + TS 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/sideBar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@vscode/webview-ui-toolkit": "^1.4.0", 13 | "ofetch": "^1.4.0", 14 | "vue": "^3.5.8", 15 | "vue-router": "4.4.5" 16 | }, 17 | "devDependencies": { 18 | "@types/vscode-webview": "^1.57.5", 19 | "@vitejs/plugin-vue": "^5.1.4", 20 | "autoprefixer": "^10.4.20", 21 | "postcss": "^8.4.47", 22 | "prettier": "^3.3.3", 23 | "prettier-plugin-tailwindcss": "^0.6.6", 24 | "tailwindcss": "^3.4.12", 25 | "typescript": "^5.6.2", 26 | "vite": "^5.4.7", 27 | "vue-tsc": "^2.1.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/sideBar/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/sideBar/prettier.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require('prettier-plugin-tailwindcss')], 3 | tailwindConfig: './tailwind.config.js', 4 | pluginSearchDirs: false, 5 | }; 6 | -------------------------------------------------------------------------------- /src/sideBar/public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/public/icon.png -------------------------------------------------------------------------------- /src/sideBar/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Black.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Black.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Bold.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-ExtraBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-ExtraBold.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-ExtraLight.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-ExtraLight.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Light.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Medium.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Regular.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-SemiBold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-SemiBold.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/inter/Inter-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nuxtrdev/nuxtr-vscode/cc48b77b9eb5066a11e1ae4a48a9ef09c9458e23/src/sideBar/src/assets/inter/Inter-Thin.woff -------------------------------------------------------------------------------- /src/sideBar/src/assets/main.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | /* @layer base { 6 | @font-face { 7 | font-family: Inter; 8 | font-weight: 100; 9 | src: url("./inter/Inter-Thin.woff") format("woff"); 10 | } 11 | 12 | @font-face { 13 | font-family: Inter; 14 | font-weight: 200; 15 | src: url("./inter/Inter-ExtraLight.woff") format("woff"); 16 | } 17 | 18 | @font-face { 19 | font-family: Inter; 20 | font-weight: 300; 21 | src: url("./inter/Inter-Light.woff") format("woff"); 22 | } 23 | 24 | @font-face { 25 | font-family: Inter; 26 | font-weight: 400; 27 | src: url("./inter/Inter-Regular.woff") format("woff"); 28 | } 29 | 30 | @font-face { 31 | font-family: Inter; 32 | font-weight: 500; 33 | src: url("./inter/Inter-Medium.woff") format("woff"); 34 | } 35 | 36 | @font-face { 37 | font-family: Inter; 38 | font-weight: 600; 39 | src: url("./inter/Inter-SemiBold.woff") format("woff"); 40 | } 41 | 42 | @font-face { 43 | font-family: Inter; 44 | font-weight: 700; 45 | src: url("./inter/Inter-Bold.woff") format("woff"); 46 | } 47 | 48 | @font-face { 49 | font-family: Inter; 50 | font-weight: 800; 51 | src: url("./inter/Inter-ExtraLight.woff") format("woff"); 52 | } 53 | 54 | @font-face { 55 | font-family: Inter; 56 | font-weight: 900; 57 | src: url("./inter/Inter-Black.woff") format("woff"); 58 | } 59 | } */ 60 | .slide-fade-enter-active, 61 | .slide-fade-leave-active { 62 | transition: all 0.3s ease-out; 63 | } 64 | 65 | .slide-fade-enter-from, 66 | .slide-fade-leave-to { 67 | transform: translateY(-10px); 68 | opacity: 0; 69 | } 70 | 71 | .slide-down-fade-enter-active, 72 | .slide-down-fade-leave-active { 73 | transition: all 0.1s ease-out; 74 | } 75 | 76 | .slide-down-fade-enter-from, 77 | .slide-down-fade-leave-to { 78 | transform: translateY(-10px); 79 | opacity: 0; 80 | } 81 | -------------------------------------------------------------------------------- /src/sideBar/src/components/DepndenciesDropdown.vue: -------------------------------------------------------------------------------- 1 | 26 | 68 | -------------------------------------------------------------------------------- /src/sideBar/src/components/DropdownItem.vue: -------------------------------------------------------------------------------- 1 | 67 | 68 | 123 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Add.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Chevron.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Code.vue: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Dots.vue: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Downloads.vue: -------------------------------------------------------------------------------- 1 | 39 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Edit.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Feedback.vue: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Filter.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Flash.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Message.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Module.vue: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/NPM.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Nuxt.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Placeholder.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Play.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/SnippetItem.vue: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Snippets.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Star.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Template.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Trash.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Upgrade.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Icons/Vue.vue: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /src/sideBar/src/components/ModuleCard.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 110 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Modules.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 142 | -------------------------------------------------------------------------------- /src/sideBar/src/components/ModulesFilter.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 142 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 51 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project/Section/Actions.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 51 | 52 | 65 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project/Section/Dependencies.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 105 | 106 | 119 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project/Section/Feedback.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 81 | 82 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project/Section/FileTemplates.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 104 | -------------------------------------------------------------------------------- /src/sideBar/src/components/Project/Section/Snippets.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 91 | -------------------------------------------------------------------------------- /src/sideBar/src/composables/modules.ts: -------------------------------------------------------------------------------- 1 | import content from './modules.json'; 2 | const layers = new Set(['seo-kit', 'umami']); 3 | 4 | export interface Module { 5 | name: string; 6 | description: string; 7 | repo: string; 8 | npm: string; 9 | icon: string; 10 | github: string; 11 | website: string; 12 | learn_more: string; 13 | category: string; 14 | type: string; 15 | maintainers: Maintainer[]; 16 | compatibility: Compatibility; 17 | stats: Stats; 18 | contributors: Contributor[]; 19 | isLayer?: boolean; // Add the isLayer property if it's applicable 20 | } 21 | 22 | export interface Maintainer { 23 | name: string; 24 | github: string; 25 | avatar: string; 26 | } 27 | 28 | export interface Compatibility { 29 | nuxt: string; 30 | requires: Record; 31 | } 32 | 33 | export interface Stats { 34 | downloads: number; 35 | stars: number; 36 | watchers: number; 37 | forks: number; 38 | defaultBranch: string; 39 | publishedAt: number; 40 | createdAt: number; 41 | } 42 | 43 | export interface Contributor { 44 | id: number; 45 | username: string; 46 | contributions: number; 47 | } 48 | 49 | 50 | export function useModules () { 51 | const modules = content.modules as unknown as Module[]; 52 | modules.forEach((module: Module) => { 53 | module.isLayer = layers.has(module.name) ? true : false; 54 | }); 55 | 56 | return modules 57 | } 58 | -------------------------------------------------------------------------------- /src/sideBar/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import './assets/main.css'; 3 | import App from './App.vue'; 4 | import { provideVSCodeDesignSystem } from '@vscode/webview-ui-toolkit'; 5 | import { createRouter, createWebHistory } from 'vue-router'; 6 | import routes from './router'; 7 | 8 | const router = createRouter({ 9 | history: createWebHistory(), 10 | routes, 11 | }); 12 | 13 | provideVSCodeDesignSystem().register(); 14 | createApp(App).use(router).mount('#app'); 15 | -------------------------------------------------------------------------------- /src/sideBar/src/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /src/sideBar/src/pages/Project.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /src/sideBar/src/plugins/globals.cjs: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | export default { 4 | install (app) { 5 | const componentFiles = import.meta.globEager('./components/Icon/*.vue'); 6 | 7 | for (const [path, m] of Object.entries(componentFiles)) { 8 | const componentName = _.upperFirst( 9 | _.camelCase( 10 | path 11 | .split('/') 12 | .pop() 13 | .replace(/\.\w+$/, ''), 14 | ), 15 | ); 16 | 17 | app.component(`Icon${componentName}`, m.default); 18 | } 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /src/sideBar/src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Home from '../pages/Home.vue'; 2 | import Project from '../pages/Project.vue'; 3 | 4 | const routes = [ 5 | { path: '/', component: Home }, 6 | { path: '/project', component: Project }, 7 | ]; 8 | 9 | export default routes; 10 | -------------------------------------------------------------------------------- /src/sideBar/src/utilities/vscode.ts: -------------------------------------------------------------------------------- 1 | import type { WebviewApi } from 'vscode-webview'; 2 | 3 | /** 4 | * A utility wrapper around the acquireVsCodeApi() function, which enables 5 | * message passing and state management between the webview and extension 6 | * contexts. 7 | * 8 | * This utility also enables webview code to be run in a web browser-based 9 | * dev server by using native web browser features that mock the functionality 10 | * enabled by acquireVsCodeApi. 11 | */ 12 | class VSCodeAPIWrapper { 13 | private readonly vsCodeApi: WebviewApi | undefined; 14 | 15 | constructor () { 16 | // Check if the acquireVsCodeApi function exists in the current development 17 | // context (i.e. VS Code development window or web browser) 18 | if (typeof acquireVsCodeApi === 'function') { 19 | this.vsCodeApi = acquireVsCodeApi(); 20 | } 21 | } 22 | 23 | /** 24 | * Post a message (i.e. send arbitrary data) to the owner of the webview. 25 | * 26 | * @remarks When running webview code inside a web browser, postMessage will instead 27 | * log the given message to the console. 28 | * 29 | * @param message Abitrary data (must be JSON serializable) to send to the extension context. 30 | */ 31 | public postMessage (message: unknown) { 32 | if (this.vsCodeApi) { 33 | this.vsCodeApi.postMessage(message); 34 | } else { 35 | console.log(message); 36 | } 37 | } 38 | 39 | /** 40 | * Get the persistent state stored for this webview. 41 | * 42 | * @remarks When running webview source code inside a web browser, getState will retrieve state 43 | * from local storage (https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage). 44 | * 45 | * @return The current state or `undefined` if no state has been set. 46 | */ 47 | public getState (): unknown | undefined { 48 | if (this.vsCodeApi) { 49 | return this.vsCodeApi.getState(); 50 | } else { 51 | const state = localStorage.getItem('vscodeState'); 52 | return state ? JSON.parse(state) : undefined; 53 | } 54 | } 55 | 56 | /** 57 | * Set the persistent state stored for this webview. 58 | * 59 | * @remarks When running webview source code inside a web browser, setState will set the given 60 | * state using local storage (https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage). 61 | * 62 | * @param newState New persisted state. This must be a JSON serializable object. Can be retrieved 63 | * using {@link getState}. 64 | * 65 | * @return The new state. 66 | */ 67 | public setState (newState: T): T { 68 | if (this.vsCodeApi) { 69 | return this.vsCodeApi.setState(newState); 70 | } else { 71 | localStorage.setItem('vscodeState', JSON.stringify(newState)); 72 | return newState; 73 | } 74 | } 75 | } 76 | 77 | // Exports class singleton to prevent multiple invocations of acquireVsCodeApi. 78 | export const vscode = new VSCodeAPIWrapper(); 79 | -------------------------------------------------------------------------------- /src/sideBar/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/sideBar/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./index.html', './src/**/*.{js,jsx,ts,tsx,vue}'], 4 | theme: { 5 | extend: { 6 | colors: { 7 | green: { 8 | 50: '#D6FFEE', 9 | 100: '#ACFFDD', 10 | 200: '#83FFCC', 11 | 300: '#30FFAA', 12 | 500: '#00BD6F', 13 | 400: '#00dc82', 14 | 600: '#009D5D', 15 | 700: '#007E4A', 16 | 800: '#005E38', 17 | 900: '#003F25', 18 | }, 19 | gray: { 20 | 50: '#FAFAFA', 21 | 100: '#F4F4F5', 22 | 200: '#E4E4E7', 23 | 300: '#D4D4D8', 24 | 400: '#A1A1AA', 25 | 500: '#71717A', 26 | 600: '#52525B', 27 | 700: '#3F3F46', 28 | 800: '#27272A', 29 | }, 30 | base: { 31 | 1: '#ffffff', 32 | 2: '#F0F0F0', 33 | 3: '#E7E7E7', 34 | 4: '#E5E5E5', 35 | 5: '#D4D4D4', 36 | 6: '#CCCCCC', 37 | 7: '#C6C6C6', 38 | 8: '#BBBBBB', 39 | 9: '#A0A0A0', 40 | 10: '#808080', 41 | 11: '#7F7F7F', 42 | 12: '#606060', 43 | 13: '#454545', 44 | 14: '#3C3C3C', 45 | 15: '#3A3D41', 46 | 16: '#333333', 47 | 17: '#303031', 48 | 18: '#292929', 49 | 19: '#252526', 50 | 20: '#1E1E1E', 51 | 21: '#000000', 52 | }, 53 | }, 54 | fontFamily: { 55 | inter: ['Inter', 'sans-serif'], 56 | }, 57 | }, 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /src/sideBar/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": [ 13 | "ESNext", 14 | "DOM" 15 | ], 16 | "skipLibCheck": true, 17 | "noEmit": true 18 | }, 19 | "include": [ 20 | "src/**/*.ts", 21 | "src/**/*.d.ts", 22 | "src/**/*.tsx", 23 | "src/**/*.vue" 24 | ], 25 | "references": [ 26 | { 27 | "path": "./tsconfig.node.json" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /src/sideBar/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": [ 9 | "vite.config.ts" 10 | ] 11 | } -------------------------------------------------------------------------------- /src/sideBar/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | 4 | export default defineConfig({ 5 | plugins: [vue()], 6 | build: { 7 | outDir: 'build', 8 | cssMinify: 'esbuild', 9 | minify: 'terser', 10 | rollupOptions: { 11 | output: { 12 | entryFileNames: 'assets/[name].js', 13 | chunkFileNames: 'assets/[name].js', 14 | assetFileNames: 'assets/[name].[ext]', 15 | }, 16 | }, 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/snippets/index.ts: -------------------------------------------------------------------------------- 1 | import { homedir } from 'node:os'; 2 | import { resolve } from 'pathe'; 3 | import { readPackageJSON, writePackageJSON } from 'pkg-types'; 4 | import { CompletionItem, CompletionItemKind, MarkdownString, extensions, languages } from 'vscode'; 5 | import { generateVueFileTemplate } from '../utils'; 6 | 7 | interface Snippet { 8 | language: string; 9 | path: string; 10 | } 11 | 12 | 13 | export async function toggleSnippets (source: 'Nuxt' | 'Nitro', moveToDisabled: boolean) { 14 | const homeDir = homedir(); 15 | const extensionName = 'nuxtr.nuxtr-vscode'; 16 | const nuxtrVersion = await extensions.getExtension(extensionName)?.packageJSON.version; 17 | 18 | const extensionDir = resolve(homeDir, '.vscode', 'extensions', `${extensionName}-${nuxtrVersion}`); 19 | const pkgJsonPath = resolve(extensionDir, 'package.json'); 20 | 21 | const pkgJSON = await readPackageJSON(extensionDir); 22 | 23 | let snippets: Snippet[] = pkgJSON?.contributes?.snippets || []; 24 | let disabledSnippets: Snippet[] = pkgJSON?.contributes?.disabled_snippets || []; 25 | 26 | const filteredSnippets = snippets.filter(snippet => snippet.path.includes(source.toLowerCase())); 27 | const filteredDisabledSnippets = disabledSnippets.filter(snippet => snippet.path.includes(source.toLowerCase())); 28 | 29 | if (moveToDisabled) { 30 | snippets = [...new Set([...snippets, ...filteredDisabledSnippets])]; 31 | disabledSnippets = disabledSnippets.filter(snippet => !filteredDisabledSnippets.includes(snippet)); 32 | } else { 33 | disabledSnippets = [...new Set([...disabledSnippets, ...filteredSnippets])]; 34 | snippets = snippets.filter(snippet => !filteredSnippets.includes(snippet)); 35 | 36 | } 37 | 38 | pkgJSON.contributes.snippets = snippets; 39 | pkgJSON.contributes.disabled_snippets = disabledSnippets; 40 | 41 | try { 42 | await writePackageJSON(pkgJsonPath, pkgJSON); 43 | } catch (error) { 44 | console.log('error updating tsConfig', error); 45 | } 46 | } 47 | 48 | 49 | languages.registerCompletionItemProvider( 50 | { language: 'vue' }, 51 | { 52 | provideCompletionItems () { 53 | const completionItem = new CompletionItem('nuxtBaseLayout', CompletionItemKind.Snippet); 54 | completionItem.detail = 'Generate a Nuxt Layout template'; 55 | 56 | const template = generateVueFileTemplate('layout'); 57 | 58 | const documentation = new MarkdownString(); 59 | documentation.appendMarkdown('Generate a Nuxt layout template according to your Nuxtr configuration.\n\n'); 60 | documentation.appendCodeblock(template, 'vue'); 61 | 62 | completionItem.documentation = documentation; 63 | completionItem.kind = CompletionItemKind.Snippet; 64 | completionItem.insertText = template; 65 | 66 | return [completionItem]; 67 | } 68 | } 69 | ); 70 | 71 | 72 | languages.registerCompletionItemProvider( 73 | { language: 'vue' }, 74 | { 75 | provideCompletionItems () { 76 | const completionItem = new CompletionItem('vueBase', CompletionItemKind.Snippet); 77 | completionItem.detail = 'Generate a Vue file template'; 78 | 79 | const template = generateVueFileTemplate('page'); 80 | 81 | const documentation = new MarkdownString(); 82 | documentation.appendMarkdown('Generate a Vue file template according to your Nuxtr configuration.\n\n'); 83 | documentation.appendCodeblock(template, 'vue'); 84 | 85 | completionItem.documentation = documentation; 86 | completionItem.kind = CompletionItemKind.Snippet; 87 | completionItem.insertText = template; 88 | 89 | return [completionItem]; 90 | } 91 | } 92 | ); -------------------------------------------------------------------------------- /src/statusBar/index.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, StatusBarAlignment, StatusBarItem, ThemeColor, commands, window } from 'vscode'; 2 | import { directToggleDevTools, nuxtDevToolsHandler } from '../commands/devtools'; 3 | import { dependenciesUpdatesHandler } from '../utils'; 4 | 5 | export const statusBars: NuxtrStatusBars = { 6 | devToolsStatusBar: window.createStatusBarItem(StatusBarAlignment.Right, 100), 7 | updatesStatusBar: window.createStatusBarItem(StatusBarAlignment.Right, 200), 8 | }; 9 | interface NuxtrStatusBars { 10 | devToolsStatusBar: StatusBarItem; 11 | updatesStatusBar: StatusBarItem; 12 | } 13 | 14 | interface DevtoolsStatusBar { 15 | command: string; 16 | tooltip: string; 17 | text: string; 18 | color?: ThemeColor; 19 | } 20 | 21 | export function activateStatusBarIcons (context: ExtensionContext): NuxtrStatusBars { 22 | statusBars.devToolsStatusBar.name = 'Nuxtr'; 23 | statusBars.devToolsStatusBar.text = '$(loading~spin)'; 24 | statusBars.devToolsStatusBar.show(); 25 | 26 | nuxtDevToolsHandler(); 27 | 28 | statusBars.updatesStatusBar.name = 'Nuxtr'; 29 | statusBars.updatesStatusBar.text = '$(loading~spin)'; 30 | statusBars.updatesStatusBar.show(); 31 | dependenciesUpdatesHandler(statusBars.updatesStatusBar); 32 | 33 | // directToggleDevTools 34 | context.subscriptions.push( 35 | commands.registerCommand('nuxtr.directToggleDevTools', async () => { 36 | await directToggleDevTools(); 37 | }) 38 | ); 39 | return statusBars; 40 | } 41 | 42 | export function updateDevtoolsStatusBar (statusBar: DevtoolsStatusBar) { 43 | statusBars.devToolsStatusBar.command = statusBar.command; 44 | statusBars.devToolsStatusBar.tooltip = statusBar.tooltip; 45 | statusBars.devToolsStatusBar.text = statusBar.text; 46 | statusBars.devToolsStatusBar.color = statusBar.color; 47 | } 48 | 49 | export function hideDevtoolsStatusBar () { 50 | statusBars.devToolsStatusBar.hide() 51 | } -------------------------------------------------------------------------------- /src/templates/css.ts: -------------------------------------------------------------------------------- 1 | const unoCSSConfig = `import { defineConfig } from 'unocss' 2 | 3 | export default defineConfig({ 4 | 5 | }) 6 | ` 7 | const tailwindCSSJSConfig = `/** @type {import('tailwindcss').Config} */ 8 | module.exports = { 9 | content: [], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [], 14 | } 15 | ` 16 | 17 | const tailwindCSSTSConfig = `import type { Config } from 'tailwindcss' 18 | 19 | export default > { 20 | content: [], 21 | theme: { 22 | extend: { 23 | } 24 | }, 25 | plugins: [] 26 | } 27 | ` 28 | 29 | const tailwindCSSFile = `@tailwind base; 30 | @tailwind components; 31 | @tailwind utilities;` 32 | 33 | const vuetifyConfigFile = `import { defineVuetifyConfiguration } from 'vuetify-nuxt-module/custom-configuration' 34 | 35 | export default defineVuetifyConfiguration({ 36 | 37 | })` 38 | 39 | export { 40 | unoCSSConfig, 41 | tailwindCSSJSConfig, 42 | tailwindCSSTSConfig, 43 | tailwindCSSFile, 44 | vuetifyConfigFile 45 | } -------------------------------------------------------------------------------- /src/templates/index.ts: -------------------------------------------------------------------------------- 1 | export { eslintConfig, stylelintConfig, stylelintIgnore } from './linters' 2 | 3 | export { tailwindCSSFile, tailwindCSSJSConfig, tailwindCSSTSConfig, unoCSSConfig, vuetifyConfigFile } from './css' 4 | 5 | export { 6 | appConfigContent, 7 | composableTemplate, 8 | nitroDefaultTemplate, 9 | nitroPluginTemplate, 10 | nitroUtilTemplate, 11 | nuxtMiddlewareTemplate, 12 | nuxtPluginTemplate, 13 | nuxtUtilTemplate 14 | } from './typeScriptFiles' 15 | 16 | export { generateScriptTag, generateStyleTag, templateTag } from './vueFiles' 17 | 18 | export { piniaOptionsContent, piniaSetupContent, vuexContent } from './stores' 19 | -------------------------------------------------------------------------------- /src/templates/linters.ts: -------------------------------------------------------------------------------- 1 | const eslintConfig = `// @ts-check 2 | import withNuxt from './.nuxt/eslint.config.mjs' 3 | 4 | export default withNuxt( 5 | // Your custom configs here 6 | ) 7 | ` 8 | 9 | const stylelintConfig = `{ 10 | "extends": "stylelint-config-recommended-vue" 11 | } 12 | ` 13 | 14 | const stylelintIgnore = 'node_modules' 15 | 16 | 17 | export { eslintConfig, stylelintConfig, stylelintIgnore } -------------------------------------------------------------------------------- /src/templates/stores.ts: -------------------------------------------------------------------------------- 1 | import { normalizeName } from '../utils' 2 | 3 | const piniaOptionsContent = (name: string): string => { 4 | return `export const use${normalizeName(name)}Store = defineStore({ 5 | id: '${normalizeName(name)}Store', 6 | state: () => ({ }), 7 | actions: {} 8 | }) 9 | `} 10 | 11 | const piniaSetupContent = (name: string): string => { 12 | return `export const use${normalizeName(name)}Store = defineStore('${normalizeName(name)}', () => { 13 | return {} 14 | }) 15 | `} 16 | 17 | 18 | const vuexContent = `export const state = () => ({ }) 19 | 20 | export const mutations = {} 21 | 22 | export const actions = { } 23 | ` 24 | 25 | // piniaContent, 26 | 27 | export { 28 | piniaOptionsContent, 29 | piniaSetupContent, 30 | vuexContent, 31 | } -------------------------------------------------------------------------------- /src/templates/typeScriptFiles.ts: -------------------------------------------------------------------------------- 1 | import { normalizeName } from '../utils' 2 | 3 | const nitroDefaultTemplate = `export default defineEventHandler(async (event) => { 4 | return 'Hello Nitro' 5 | }) 6 | ` 7 | 8 | const nuxtMiddlewareTemplate = `export default defineNuxtRouteMiddleware((to, from) => { 9 | 10 | }) 11 | ` 12 | 13 | const composableTemplate = (name: string) => `export const use${normalizeName(name)} = () => { 14 | return ref() 15 | } 16 | ` 17 | 18 | const nuxtPluginTemplate = `export default defineNuxtPlugin((nuxtApp) => { 19 | 20 | }) 21 | ` 22 | 23 | const nitroPluginTemplate = `export default defineNitroPlugin((nitroApp) => { 24 | 25 | }) 26 | ` 27 | 28 | const nuxtUtilTemplate = () => `export default () => { 29 | return 'Hello Util' 30 | } 31 | ` 32 | 33 | const nitroUtilTemplate = `export default () => { 34 | return 'Hello Util' 35 | }` 36 | 37 | const appConfigContent = `export default defineAppConfig({ 38 | 39 | }) 40 | ` 41 | 42 | 43 | export { 44 | nitroDefaultTemplate, 45 | nuxtMiddlewareTemplate, 46 | composableTemplate, 47 | nuxtPluginTemplate, 48 | nitroPluginTemplate, 49 | nuxtUtilTemplate, 50 | nitroUtilTemplate, 51 | appConfigContent 52 | } -------------------------------------------------------------------------------- /src/templates/vueFiles.ts: -------------------------------------------------------------------------------- 1 | const templateTag = (type: string, language: string) => { 2 | return language === 'html' ? `` : ``; 10 | } 11 | 12 | function generateStyleTag (lang: string, scoped: boolean) { 13 | return ` 14 | 15 | ` 16 | } 17 | 18 | function generateScriptTag (scriptType: string, lang: string) { 19 | return ` 20 | 21 | ` 22 | } 23 | 24 | export { generateStyleTag, generateScriptTag, templateTag } 25 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { TSConfig } from 'pkg-types' 2 | 3 | export interface ConfigurationProperty { 4 | title?: string; 5 | description?: string; 6 | tags?: string[]; 7 | tsType?: string; 8 | markdownType?: string; 9 | id?: string; 10 | properties?: { [key: string]: ConfigurationProperty }; 11 | default?: any; 12 | type: string; 13 | } 14 | 15 | export interface NuxtrConfiguration { 16 | openItemsAfterCreation: boolean; 17 | defaultPackageManager: 'null' | 'Yarn' | 'NPM' | 'pnpm' | 'Bun'; 18 | monorepoMode: { 19 | DirectoryName: string | null; 20 | }; 21 | vueFiles: { 22 | template: { 23 | defaultLanguage: 'html' | 'pug'; 24 | } 25 | firstTag: 'template' | 'script'; 26 | script: { 27 | type: 'setup' | 'options'; 28 | defaultLanguage: 'js' | 'ts'; 29 | }; 30 | style: { 31 | addStyleTag: boolean; 32 | alwaysScoped: boolean; 33 | defaultLanguage: 34 | | 'css' 35 | | 'scss' 36 | | 'sass' 37 | | 'less' 38 | | 'stylus' 39 | | 'postcss'; 40 | }; 41 | pages: { 42 | defaultTemplate: string; 43 | }; 44 | layouts: { 45 | defaultTemplate: string; 46 | }; 47 | }; 48 | intellisense: { 49 | vueFiles: boolean; 50 | nuxtignore: boolean; 51 | nuxtrc: boolean; 52 | }; 53 | snippets: { 54 | nuxt: boolean; 55 | nitro: boolean; 56 | } 57 | 58 | piniaFiles: { 59 | defaultTemplate: 'options' | 'setup'; 60 | } 61 | 62 | projectTemplates: UserProjectTemplate[]; 63 | } 64 | 65 | export interface TSConfigNuxt extends TSConfig { 66 | vueCompilerOptions?: { 67 | plugins?: string[] | undefined; 68 | } 69 | } 70 | 71 | export type nuxtModule = { 72 | name: string; 73 | description: string; 74 | repo: string; 75 | npm: string; 76 | github: string; 77 | category: string; 78 | type: string; 79 | compatibility: { 80 | nuxt: string; 81 | requires: Record; 82 | }; 83 | tags: string[]; 84 | }; 85 | 86 | export type NuxtOfficialTemplate = { 87 | name: string; 88 | repo: string; 89 | branch: string; 90 | docs: string; 91 | description: string; 92 | }; 93 | 94 | 95 | export type UserProjectTemplate = { 96 | name: string; 97 | description: string; 98 | repoURL: string; 99 | branch?: string; 100 | } -------------------------------------------------------------------------------- /src/utils/commands.ts: -------------------------------------------------------------------------------- 1 | import { hasServerDir } from '../utils'; 2 | 3 | export const getCommandType = async (commandType: string) => { 4 | let type = { 5 | name: '', 6 | path: '', 7 | extension: '', 8 | }; 9 | switch (commandType) { 10 | case 'components': { 11 | type = { 12 | name: 'Components', 13 | path: 'components', 14 | extension: '.vue', 15 | }; 16 | break; 17 | } 18 | case 'composables': { 19 | type = { 20 | name: 'Composables', 21 | path: 'composables', 22 | extension: '.ts', 23 | }; 24 | break; 25 | } 26 | case 'pages': { 27 | type = { 28 | name: 'Pages', 29 | path: 'pages', 30 | extension: '.vue', 31 | }; 32 | break; 33 | } 34 | case 'layouts': { 35 | type = { 36 | name: 'Layouts', 37 | path: 'layouts', 38 | extension: '.vue', 39 | }; 40 | break; 41 | } 42 | case 'store': { 43 | type = { 44 | name: 'Store', 45 | path: 'store', 46 | extension: '.ts', 47 | }; 48 | break; 49 | } 50 | case 'middleware': { 51 | type = { 52 | name: 'Middleware', 53 | path: 'middleware', 54 | extension: '.ts', 55 | }; 56 | break; 57 | } 58 | case 'plugins': { 59 | type = { 60 | name: 'Plugins', 61 | path: 'plugins', 62 | extension: '.ts', 63 | }; 64 | break; 65 | } 66 | case 'api': { 67 | type = { 68 | name: 'APIs', 69 | path: `${await hasServerDir()}/api`, 70 | extension: '.ts', 71 | }; 72 | break; 73 | } 74 | case 'route': { 75 | type = { 76 | name: 'Routes', 77 | path: `${await hasServerDir()}/routes`, 78 | extension: '.ts', 79 | }; 80 | break; 81 | } 82 | case 'nitroPlugin': { 83 | type = { 84 | name: 'Plugins', 85 | path: `${await hasServerDir()}/plugins`, 86 | extension: '.ts', 87 | }; 88 | break; 89 | } 90 | case 'nitroMiddleware': { 91 | type = { 92 | name: 'Middleware', 93 | path: `${await hasServerDir()}/middleware`, 94 | extension: '.ts', 95 | }; 96 | break; 97 | } 98 | case 'nuxtUtil': { 99 | type = { 100 | name: 'Utilities', 101 | path: 'utils', 102 | extension: '.ts', 103 | }; 104 | break; 105 | } 106 | case 'nitroUtil': { 107 | type = { 108 | name: 'Utility', 109 | path: `${await hasServerDir()}/utils`, 110 | extension: '.ts', 111 | }; 112 | break; 113 | } 114 | default: { 115 | // show error message 116 | type = { 117 | name: '', 118 | path: '', 119 | extension: '', 120 | }; 121 | break; 122 | } 123 | } 124 | 125 | return { type }; 126 | 127 | }; 128 | -------------------------------------------------------------------------------- /src/utils/files.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync } from 'node:fs'; 2 | import { nuxtrConfiguration, projectRootDirectory, vscodeConfiguration } from '.'; 3 | import { generateScriptTag, generateStyleTag, piniaOptionsContent, piniaSetupContent, templateTag } from '../templates'; 4 | 5 | const eolConfiguration = vscodeConfiguration().files.eol 6 | 7 | export function generateVueFileTemplate (type: 'page' | 'layout', template?: string) { 8 | const userDefaultTemplate = template || (type === 'page' 9 | ? nuxtrConfiguration().vueFiles.pages.defaultTemplate 10 | : nuxtrConfiguration().vueFiles.layouts.defaultTemplate); 11 | 12 | const templatePath = `${projectRootDirectory()}/.vscode/${userDefaultTemplate}`; 13 | try { 14 | return readFileSync(templatePath).toString(); 15 | } catch { 16 | return generateVueFileBasicTemplate(type); 17 | } 18 | } 19 | 20 | export function generateVueFileBasicTemplate (type: string) { 21 | let fileTemplate = '' 22 | const templateLang = nuxtrConfiguration().vueFiles.template.defaultLanguage 23 | const firstTag = nuxtrConfiguration().vueFiles.firstTag 24 | const scriptType = nuxtrConfiguration().vueFiles.script.type 25 | const addStyleTag = nuxtrConfiguration().vueFiles.style.addStyleTag 26 | const styleLang = nuxtrConfiguration().vueFiles.style.defaultLanguage 27 | const isScoped = nuxtrConfiguration().vueFiles.style.alwaysScoped 28 | const lang = nuxtrConfiguration().vueFiles.script.defaultLanguage 29 | 30 | const scriptTag = generateScriptTag(scriptType, lang) 31 | 32 | const eol = eolConfiguration === '\n' ? '\n\n' : '\n\n'; 33 | 34 | if (firstTag === 'template') { 35 | fileTemplate = templateTag(type, templateLang); 36 | fileTemplate += eol; 37 | fileTemplate += scriptTag; 38 | } else { 39 | fileTemplate = scriptTag; 40 | fileTemplate += eol; 41 | fileTemplate += templateTag(type, templateLang); 42 | } 43 | 44 | if (addStyleTag) { 45 | fileTemplate += eol; 46 | fileTemplate += generateStyleTag(styleLang, isScoped); 47 | } 48 | 49 | // eolConfiguration !== '\n' ? normalizeLFToCRLF(fileTemplate) : 50 | return fileTemplate 51 | } 52 | 53 | export function generatePiniaTemplates (name: string) { 54 | return nuxtrConfiguration().piniaFiles.defaultTemplate === 'options' ? piniaOptionsContent(name) : piniaSetupContent(name) 55 | } -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | getNonce, 3 | getUri, 4 | newTerminal, 5 | nuxtrConfiguration, 6 | openExternalLink, 7 | projectRootDirectory, 8 | projectSrcDirectory, 9 | runCommand, 10 | tryImport, 11 | vscodeConfiguration 12 | } from './global' 13 | 14 | export { 15 | findNuxtConfig, 16 | getNuxtVersion, 17 | hasServerDir, 18 | isModuleConfigured, 19 | isNuxtProject, 20 | isNuxtTwo, 21 | removeNuxtModule, 22 | updateNuxtConfig 23 | } from './nuxt' 24 | 25 | export { 26 | dependenciesUpdatesHandler, 27 | detectPackageManagerByName, 28 | getInstallationCommand, 29 | getOutdatedPackages, 30 | getProjectDependencies, 31 | getProjectScripts, 32 | isDependencyInstalled, 33 | managePackageVersion, 34 | removePackage, 35 | updateDependencies 36 | } from './dependency' 37 | 38 | export { 39 | createDir, 40 | createFile, 41 | createSubFolders, 42 | createVueTemplate, 43 | normalizeFileExtension, 44 | normalizeName, 45 | showSubFolderQuickPick 46 | } from './file' 47 | 48 | export { logger } from './outputChannel' 49 | export { injectPkgJSONScript } from './pkgJSON' 50 | export { createConfigWatcher } from './watchers' 51 | 52 | export { isDirectory, languageSelector, openFolder, patternSelector, quickOpenButtons } from './vscode' 53 | 54 | export { generatePiniaTemplates, generateVueFileBasicTemplate, generateVueFileTemplate } from './files' 55 | -------------------------------------------------------------------------------- /src/utils/navigation.ts: -------------------------------------------------------------------------------- 1 | import { commands } from 'vscode' 2 | 3 | export function openSettings () { 4 | commands.executeCommand('workbench.action.openSettings', 'nuxtr') 5 | } -------------------------------------------------------------------------------- /src/utils/outputChannel.ts: -------------------------------------------------------------------------------- 1 | import { OutputChannel, window } from 'vscode' 2 | class Logger { 3 | private static _instance: Logger 4 | private _outputChannel: OutputChannel 5 | 6 | private constructor () { 7 | this._outputChannel = window.createOutputChannel('Nuxtr', { log: true }) 8 | } 9 | 10 | public static getInstance (): Logger { 11 | if (!Logger._instance) { 12 | Logger._instance = new Logger() 13 | } 14 | return Logger._instance 15 | } 16 | 17 | public log (message: string): void { 18 | this._outputChannel.appendLine(message) 19 | } 20 | 21 | public show (): void { 22 | this._outputChannel.show() 23 | } 24 | } 25 | 26 | export const logger = Logger.getInstance() -------------------------------------------------------------------------------- /src/utils/pkgJSON.ts: -------------------------------------------------------------------------------- 1 | 2 | import { readPackageJSON, writePackageJSON } from 'pkg-types' 3 | import { projectRootDirectory } from './' 4 | 5 | export const injectPkgJSONScript = async (scriptName: string, script: string) => { 6 | const packageJsonPath = `${projectRootDirectory()}/package.json` 7 | const packageJson = await readPackageJSON(packageJsonPath) 8 | if (packageJson && packageJson.scripts && !packageJson.scripts[scriptName]) { 9 | packageJson.scripts[scriptName] = script 10 | await writePackageJSON(packageJsonPath, packageJson) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/vscode.ts: -------------------------------------------------------------------------------- 1 | import { DocumentSelector, FileType, QuickInputButton, ThemeIcon, Uri, commands, window, workspace } from 'vscode'; 2 | 3 | export const languageSelector = (language: string): DocumentSelector => ({ scheme: 'file', language, } as const); 4 | 5 | export const patternSelector = (pattern: string): DocumentSelector => ({ scheme: 'file', pattern, } as const); 6 | 7 | 8 | export async function isDirectory (filePath: string): Promise { 9 | try { 10 | const stat = await workspace.fs.stat(Uri.file(filePath)); 11 | return stat.type === FileType.Directory; 12 | } catch { 13 | return false; 14 | } 15 | } 16 | 17 | 18 | export async function openFolder (path: Uri, folderName: string, newWindow: boolean) { 19 | try { 20 | await commands.executeCommand('vscode.openFolder', path, { 21 | forceNewWindow: newWindow, 22 | }); 23 | } catch { 24 | window.showErrorMessage(`Failed to open Nuxt ${folderName} template`) 25 | } 26 | } 27 | 28 | 29 | const github: QuickInputButton = { 30 | iconPath: new ThemeIcon('github'), 31 | tooltip: 'Template Github Repo', 32 | }; 33 | 34 | const docs: QuickInputButton = { 35 | iconPath: new ThemeIcon('book'), 36 | tooltip: 'Template Docs/Reference', 37 | }; 38 | 39 | 40 | export const quickOpenButtons = { 41 | github, 42 | docs, 43 | } -------------------------------------------------------------------------------- /src/utils/watchers.ts: -------------------------------------------------------------------------------- 1 | import { ConfigurationChangeEvent, Disposable, commands, window, workspace } from 'vscode'; 2 | 3 | export function createConfigWatcher (configKey: string, callback?: () => Promise, defaultBehavior?: boolean): Disposable { 4 | const watcher = workspace.onDidChangeConfiguration(async (event: ConfigurationChangeEvent) => { 5 | if (event.affectsConfiguration(configKey)) { 6 | if (callback && typeof callback === 'function') { 7 | await callback(); 8 | } 9 | 10 | if (defaultBehavior) { 11 | const question = window.showInformationMessage( 12 | 'Nuxtr configuration updated.', 13 | 'Reload Window' 14 | ); 15 | 16 | question.then((answer) => { 17 | if (answer === 'Reload Window') { 18 | commands.executeCommand('workbench.action.reloadWindow'); 19 | 20 | } 21 | }); 22 | } 23 | } 24 | }); 25 | 26 | return watcher; 27 | } -------------------------------------------------------------------------------- /src/watchers/config.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'node:fs'; 2 | import { readTSConfig } from 'pkg-types'; 3 | import { Disposable, commands, window } from 'vscode'; 4 | import nuxtrCommands from '../commands'; 5 | import { PugConfigurationSteps } from '../commands/templates'; 6 | import { toggleSnippets } from '../snippets'; 7 | import { TSConfigNuxt } from '../types'; 8 | import { createConfigWatcher, getProjectDependencies, nuxtrConfiguration, projectRootDirectory } from '../utils'; 9 | 10 | const reloatWindowProps = () => { 11 | window.showInformationMessage('Configuration has been modified.', 'Reload Window').then((answer) => { 12 | if (answer === 'Reload Window') { 13 | commands.executeCommand('workbench.action.reloadWindow'); 14 | return; 15 | } 16 | }); 17 | } 18 | 19 | export const nuxtSnippetsConfigWatcher: Disposable = 20 | createConfigWatcher('nuxtr.snippets.nuxt', async () => { 21 | await toggleSnippets('Nuxt', nuxtrConfiguration().snippets.nuxt) 22 | reloatWindowProps(); 23 | }); 24 | 25 | export const nitroSnippetsConfigWatcher: Disposable = 26 | createConfigWatcher('nuxtr.snippets.nitro', async () => { 27 | await toggleSnippets('Nitro', nuxtrConfiguration().snippets.nitro) 28 | reloatWindowProps(); 29 | }) 30 | 31 | export const templatesConfigWatcher: Disposable = createConfigWatcher('nuxtr.vueFiles.template.defaultLanguage', async () => { 32 | const options: string[] = []; 33 | 34 | const dependencies = await getProjectDependencies(); 35 | 36 | if (nuxtrConfiguration().vueFiles.template.defaultLanguage === 'pug') { 37 | const pugDependency = dependencies.find(dep => dep.name === 'pug'); 38 | 39 | if (!pugDependency) { 40 | options.push(PugConfigurationSteps.installPug); 41 | } 42 | 43 | const languagePluginPugDependency = dependencies.find(dep => dep.name === '@vue/language-plugin-pug'); 44 | 45 | if (!languagePluginPugDependency) { 46 | options.push(PugConfigurationSteps.installLanguagePlugin); 47 | } 48 | 49 | const path = `${projectRootDirectory()}/tsconfig.json`; 50 | const tsconfig: TSConfigNuxt = await readTSConfig(path); 51 | 52 | if (!existsSync(path)) { 53 | return; 54 | } 55 | 56 | const vueCompilerPlugins: string[] = tsconfig.vueCompilerOptions?.plugins ?? []; 57 | if (!vueCompilerPlugins.includes('@vue/language-plugin-pug')) { 58 | options.push(PugConfigurationSteps.addPluginToTSConfig) 59 | } 60 | 61 | 62 | if (options.length > 0) { 63 | const question = window.showInformationMessage('Pug is not configured properly', 'Configure Pug') 64 | question.then((answer) => { 65 | if (answer === 'Configure Pug') { 66 | nuxtrCommands.configurePug(options); 67 | } 68 | }) 69 | 70 | 71 | } 72 | } 73 | }); 74 | 75 | export const intellisenseConfigWatcher: Disposable = createConfigWatcher('nuxtr.intellisense', (): Promise => { 76 | reloatWindowProps(); 77 | return Promise.resolve(); 78 | }) -------------------------------------------------------------------------------- /src/watchers/files.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, StatusBarItem, commands, workspace } from 'vscode'; 2 | import { nuxtConfigWatcher } from '../commands/devtools'; 3 | import { findNuxtConfig, projectRootDirectory } from '../utils'; 4 | class FileWatchers { 5 | 6 | public sidebarProvider: any 7 | public statusBar: StatusBarItem 8 | 9 | constructor (sidebarProvider: any, context: ExtensionContext, statusBar: StatusBarItem) { 10 | this.sidebarProvider = sidebarProvider 11 | this.statusBar = statusBar 12 | } 13 | 14 | public nuxtConfigFileWatcher = workspace 15 | .createFileSystemWatcher(findNuxtConfig() as string) 16 | .onDidChange(() => { 17 | nuxtConfigWatcher() 18 | }) 19 | 20 | public packageJsonFileWatcher = workspace 21 | .createFileSystemWatcher(`${projectRootDirectory()}/package.json`) 22 | .onDidCreate(async () => { 23 | this.sidebarProvider.updateModules() 24 | await this.sidebarProvider.getDependencies() 25 | 26 | const outdatedDependencies: any = await commands.executeCommand('nuxtr.globalState', { name: 'outdatedDependencies' }) 27 | 28 | this.statusBar.text = `$(nuxtr-npm) ${outdatedDependencies.length}` 29 | 30 | if (outdatedDependencies.length === 0) { 31 | this.statusBar.color = undefined 32 | this.statusBar.command = undefined 33 | } else { 34 | this.statusBar.command = 'nuxtr.updateDependencies' 35 | } 36 | }) 37 | 38 | public packageJsonFileChangedWatcher = workspace 39 | .createFileSystemWatcher(`${projectRootDirectory()}/package.json`) 40 | .onDidChange(async () => { 41 | this.sidebarProvider.updateModules() 42 | await this.sidebarProvider.getDependencies() 43 | 44 | const outdatedDependencies: any = await commands.executeCommand('nuxtr.globalState', { name: 'outdatedDependencies' }) 45 | 46 | this.statusBar.text = `$(nuxtr-npm) ${outdatedDependencies.length}` 47 | 48 | if (outdatedDependencies.length === 0) { 49 | this.statusBar.color = undefined 50 | this.statusBar.command = undefined 51 | } else { 52 | this.statusBar.command = 'nuxtr.updateDependencies' 53 | 54 | } 55 | }) 56 | 57 | public dotvscodeFileWatcher = workspace 58 | .createFileSystemWatcher(`${projectRootDirectory()}/.vscode/**/*`) 59 | .onDidCreate(() => { 60 | this.sidebarProvider.getDependencies() 61 | }) 62 | 63 | public dotvscodeFileWatcherOnDelete = workspace 64 | .createFileSystemWatcher(`${projectRootDirectory()}/.vscode/**/*`) 65 | .onDidDelete(() => { 66 | this.sidebarProvider.getDependencies() 67 | }) 68 | 69 | } 70 | 71 | export default FileWatchers -------------------------------------------------------------------------------- /src/watchers/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | intellisenseConfigWatcher, 3 | nitroSnippetsConfigWatcher, 4 | nuxtSnippetsConfigWatcher, 5 | templatesConfigWatcher 6 | } from './config' 7 | export { default as filesWatcher } from './files' 8 | -------------------------------------------------------------------------------- /syntaxes/log.langConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "brackets": [ 3 | ["{", "}"], 4 | ["[", "]"], 5 | ["(", ")"], 6 | ["<", ">"] 7 | ], 8 | "autoClosingPairs": [ 9 | ["{", "}"], 10 | ["[", "]"], 11 | ["(", ")"], 12 | ["\"", "\""], 13 | ["'", "'"] 14 | ], 15 | "surroundingPairs": [ 16 | ["{", "}"], 17 | ["[", "]"], 18 | ["(", ")"], 19 | ["\"", "\""], 20 | ["'", "'"] 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /syntaxes/nuxtignore.langConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "#" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /syntaxes/nuxtignore.tmConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "scopeName": "source.nuxtignore", 4 | "patterns": [ 5 | { 6 | "match": "^[^#\\s].*$", 7 | "name": "string.path.nuxtignore" 8 | }, 9 | { 10 | "match": "^\\s*#.*$", 11 | "name": "comment.line" 12 | } 13 | ], 14 | "fileTypes": [".nuxtignore"] 15 | } 16 | -------------------------------------------------------------------------------- /syntaxes/nuxtrc.langConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "#" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /syntaxes/nuxtrc.tmConfiguration.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", 3 | "fileTypes": [".nuxtrc"], 4 | "name": "nuxtrc", 5 | "scopeName": "source.nuxtrc", 6 | "patterns": [ 7 | { 8 | "match": "^(\\s*)([a-zA-Z_][\\w-]*(?:\\.[a-zA-Z_][\\w-]*)*)\\s*=\\s*([^\\n]*)$", 9 | "captures": { 10 | "2": { 11 | "name": "variable.other" 12 | }, 13 | "3": { 14 | "patterns": [ 15 | { 16 | "match": "\\b(true|false|null)\\b", 17 | "name": "constant.language" 18 | }, 19 | { 20 | "match": "\\d+", 21 | "name": "constant.numeric" 22 | }, 23 | { 24 | "match": "\"[^\"]*\"", 25 | "name": "string.quoted.double" 26 | }, 27 | { 28 | "match": "[^\\s=]+(?=\\s*$)", 29 | "name": "string.unquoted" 30 | } 31 | ] 32 | } 33 | } 34 | }, 35 | { 36 | "match": "^\\s*#.*$", 37 | "name": "comment.line" 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /taze.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'taze' 2 | 3 | export default defineConfig({ 4 | force: true, 5 | write: true, 6 | install: true, 7 | packageMode: { 8 | 'nuxi-nightly': 'latest' 9 | }, 10 | exclude: [ 11 | '@types/vscode' 12 | ], 13 | recursive: true 14 | }) 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "target": "ES2022", 7 | "isolatedModules": true, 8 | "moduleDetection": "force", 9 | "rootDir": "src", 10 | "outDir": "out", 11 | "strict": true, 12 | "skipLibCheck": true, 13 | "resolveJsonModule": true, 14 | "incremental": true, 15 | "sourceMap": true 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test", 20 | "src/sideBar/src", 21 | "src/sideBar/build", 22 | "taze.config.ts", 23 | "tsup.config.ts", 24 | "tsdown.config.ts", 25 | "rolldown.config.js", 26 | "knip.ts" 27 | ] 28 | } -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'tsup' 2 | 3 | export default defineConfig({ 4 | entry: [ 'src/index.ts', ], 5 | outDir: 'out', 6 | format: ['cjs'], 7 | shims: false, 8 | dts: false, 9 | splitting: false, 10 | clean: true, 11 | minify: 'terser', 12 | platform: 'node', 13 | sourcemap: false, 14 | treeshake: true, 15 | minifySyntax: true, 16 | minifyWhitespace: true, 17 | minifyIdentifiers: true, 18 | target: 'es2022', 19 | external: ['vscode'], 20 | noExternal: [ 21 | '@nuxt/schema', 22 | 'destr', 23 | 'fs-extra', 24 | 'jiti', 25 | 'magicast', 26 | 'ofetch', 27 | 'pathe', 28 | 'pkg-types', 29 | 'string-ts', 30 | 'giget', 31 | 'semver' 32 | ] 33 | }) 34 | --------------------------------------------------------------------------------