Skip to content

Commit

Permalink
feat: click to component
Browse files Browse the repository at this point in the history
  • Loading branch information
zjffun committed Jun 9, 2023
1 parent 7647911 commit 511df94
Show file tree
Hide file tree
Showing 48 changed files with 12,202 additions and 1 deletion.
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.DS_Store
node_modules
/dist

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
156 changes: 155 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,156 @@
# vue-click-to-component
Option+Click Vue components in your browser to instantly open the source in VS Code.

[![npm](https://img.shields.io/npm/v/vue-click-to-component)](https://www.npmjs.com/package/vue-click-to-component)

<kbd>Option+Click</kbd>(<kbd>Alt+Click</kbd>) a Component in the browser to **instantly** goto the source in your editor.

![Vite Demo](./images/vite.webp)

## Features

- <kbd>Option+Click</kbd>(<kbd>Alt+Click</kbd>) opens the immediate Component's source
- Supports `vscode` & `vscode-insiders`' [URL handling](https://code.visualstudio.com/docs/editor/command-line#_opening-vs-code-with-urls)
- Automatically **tree-shaken** from `production` builds

## Installation

<details>
<summary>npm</summary>

```shell
npm install vue-click-to-component
```

</details>

<details>
<summary>pnpm</summary>

```shell
pnpm add vue-click-to-component
```

</details>

<details>
<summary>yarn</summary>

```shell
yarn add vue-click-to-component
```

</details>

Even though `vue-click-to-component` is added to `dependencies`, [tree-shaking](https://esbuild.github.io/api/#tree-shaking) will remove `vue-click-to-component` from `production` builds.

## Usage

<details>
<summary>Vite</summary>

[`vite.config.ts`](https://github.com/zjffun/vue-click-to-component/blob/main/examples/vite/vite.config.ts#L7)

```diff
+import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueClickToComponent from 'vue-click-to-component/vite-plugin';

// https://vitejs.dev/config/
export default defineConfig({
- plugins: [vue()],
+ plugins: [vueClickToComponent(), vue()],
})
```

[`main.ts`](https://github.com/zjffun/vue-click-to-component/blob/main/examples/vite/src/main.ts#L4)

```diff
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
+import 'vue-click-to-component/client';

createApp(App).mount('#app')
```

</details>

<details>
<summary>Vue CLI</summary>

[`vue.config.js`](./examples/vue-cli/vue.config.js)

```diff
const { defineConfig } = require("@vue/cli-service");
+const vueClickToComponent = require("vue-click-to-component/vue-cli-plugin");

module.exports = defineConfig({
transpileDependencies: true,
+ chainWebpack: (config) => {
+ vueClickToComponent(config);
+ },
});
```

[`main.js`](examples/vue-cli/src/main.js)

```diff
import Vue from 'vue'
import App from './App.vue'
+import 'vue-click-to-component/client.js'

Vue.config.productionTip = false

new Vue({
render: h => h(App),
}).$mount('#app')
```

</details>

<details>
<summary>webpack</summary>

```diff
module: {
rules: [
+ {
+ test: /\.vue$/,
+ enforce: 'pre',
+ loader: 'vue-click-to-component/loader',
+ },
{
test: /\.vue$/,
loader: 'vue-loader',
},
],
},
```

```diff
import { createApp } from "vue";
import App from "./App.vue";
+import "vue-click-to-component/client.js";

createApp(App).mount("#app");
```

```diff
"scripts": {
- "serve": "webpack serve"
+ "serve": "NODE_ENV=development webpack serve"
},
```

</details>

### `editor`

By default, clicking will default `editor` to [`vscode`](https://code.visualstudio.com/).

If, like me, you use [`vscode-insiders`](https://code.visualstudio.com/insiders/), you can set `editor` explicitly:

```diff
import 'vue-click-to-component/client';
+window.__VUE_CLICK_TO_COMPONENT_EDITOR__ = 'vscode-insiders';
```
119 changes: 119 additions & 0 deletions client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
if (process.env.NODE_ENV === "development") {
console.warn("vue-click-to-component enabled in development mode");

function cleanTarget() {
document
.querySelectorAll("[data-click-to-component-target]")
.forEach((el) => {
el.removeAttribute("data-click-to-component-target");
});
}

function getElWithSourceCodeLocation(el) {
try {
while (el && !el.dataset.__sourceCodeLocation) {
el = el.parentElement;
}
} catch (error) {
return null;
}

return el;
}

function getSourceCodeLocation(el) {
const _el = getElWithSourceCodeLocation(el);
if (!_el) {
return null;
}
return _el.dataset.__sourceCodeLocation;
}

function setTarget(el) {
let _el = getElWithSourceCodeLocation(el);

if (!_el) {
return;
}

_el.setAttribute("data-click-to-component-target", "");
}

document.head.insertAdjacentHTML(
"beforeend",
`
<style type="text/css" key="click-to-component-style">
[data-click-to-component] * {
pointer-events: auto !important;
}
[data-click-to-component-target] {
cursor: var(--click-to-component-cursor, context-menu) !important;
outline: auto 1px;
outline: var(
--click-to-component-outline,
-webkit-focus-ring-color auto 1px
) !important;
}
</style>`
);

/* --- open --- */
window.addEventListener(
"click",
(e) => {
if (e.altKey && e.button === 0) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();

const sourceCodeLocation = getSourceCodeLocation(e.target);
if (!sourceCodeLocation) {
return;
}

let editor = "vscode";
if (typeof window.__VUE_CLICK_TO_COMPONENT_EDITOR__ === "string") {
editor = window.__VUE_CLICK_TO_COMPONENT_EDITOR__;
}

window.open(`${editor}://file/${sourceCodeLocation}`);

cleanTarget();
}
},
true
);

/* --- set target --- */
window.addEventListener(
"mousemove",
(e) => {
cleanTarget();

if (e.altKey) {
setTarget(e.target);
}
},
true
);

/* --- clean target --- */
window.addEventListener(
"keyup",
(e) => {
if (e.key === "Alt") {
cleanTarget();
}
},
true
);

window.addEventListener(
"blur",
() => {
cleanTarget();
},
true
);
}
24 changes: 24 additions & 0 deletions examples/vite/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
18 changes: 18 additions & 0 deletions examples/vite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Vue 3 + TypeScript + Vite

This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.

## Recommended IDE Setup

- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

## Type Support For `.vue` Imports in TS

TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.

If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:

1. Disable the built-in TypeScript Extension
1. Run `Extensions: Show Built-in Extensions` from VSCode's command palette
2. Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
13 changes: 13 additions & 0 deletions examples/vite/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
19 changes: 19 additions & 0 deletions examples/vite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.2.47"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.1.0",
"typescript": "^5.0.2",
"vite": "^4.3.9",
"vue-tsc": "^1.4.2"
}
}
1 change: 1 addition & 0 deletions examples/vite/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 511df94

Please sign in to comment.