mirror of https://github.com/msgbyte/tailchat
chore: use source-ref
parent
2371c90f07
commit
27fe626000
@ -1 +0,0 @@
|
|||||||
lib
|
|
@ -1 +0,0 @@
|
|||||||
## source-ref for webpack loader
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "rollup-plugin-source-ref",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"files": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc",
|
|
||||||
"prepare": "tsc"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"sourcecode",
|
|
||||||
"pointer",
|
|
||||||
"webpack"
|
|
||||||
],
|
|
||||||
"author": "moonrailgun",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/babel__generator": "^7.6.4",
|
|
||||||
"@types/babel__traverse": "^7.17.1",
|
|
||||||
"@types/node": "^15.12.5",
|
|
||||||
"@types/webpack": "^5.28.0",
|
|
||||||
"rollup": "^2.75.0",
|
|
||||||
"ts-node": "^10.0.0",
|
|
||||||
"typescript": "^4.5.2",
|
|
||||||
"webpack": "^5.72.0",
|
|
||||||
"webpack-test-utils": "^1.1.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/generator": "^7.17.7",
|
|
||||||
"@babel/parser": "^7.17.7",
|
|
||||||
"@babel/traverse": "^7.17.3",
|
|
||||||
"@babel/types": "^7.17.10"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import type { Plugin } from 'rollup';
|
|
||||||
import { parse } from '@babel/parser';
|
|
||||||
import traverse from '@babel/traverse';
|
|
||||||
import generate from '@babel/generator';
|
|
||||||
import {
|
|
||||||
isJSXIdentifier,
|
|
||||||
isJSXMemberExpression,
|
|
||||||
jsxAttribute,
|
|
||||||
jsxIdentifier,
|
|
||||||
stringLiteral,
|
|
||||||
} from '@babel/types';
|
|
||||||
|
|
||||||
const TRACE_ID = 'data-source';
|
|
||||||
|
|
||||||
export default function sourceRef(): Plugin {
|
|
||||||
return {
|
|
||||||
name: 'source-ref',
|
|
||||||
transform(code, id) {
|
|
||||||
const filepath = id;
|
|
||||||
|
|
||||||
const ast = parse(code, {
|
|
||||||
sourceType: 'module',
|
|
||||||
plugins: ['jsx', 'typescript'],
|
|
||||||
});
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
JSXOpeningElement(path) {
|
|
||||||
const location = path.node.loc;
|
|
||||||
if (!location) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(location)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = path.node.name;
|
|
||||||
if (isJSXIdentifier(name) && name.name === 'Fragment') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
isJSXMemberExpression(name) &&
|
|
||||||
name.property.name === 'Fragment'
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const line = location.start.line;
|
|
||||||
const col = location.start.column;
|
|
||||||
|
|
||||||
const attrs = path.node.attributes;
|
|
||||||
for (let i = 0; i < attrs.length; i++) {
|
|
||||||
const attr = attrs[i];
|
|
||||||
if (attr.type === 'JSXAttribute' && attr.name.name === TRACE_ID) {
|
|
||||||
// existed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const traceId = `${filepath}:${line}:${col}`;
|
|
||||||
|
|
||||||
attrs.push(
|
|
||||||
jsxAttribute(jsxIdentifier(TRACE_ID), stringLiteral(traceId))
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = generate(ast);
|
|
||||||
|
|
||||||
return { code: res.code, map: res.map };
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "lib",
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "ES5",
|
|
||||||
"lib": ["ESNext"],
|
|
||||||
"declaration": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
lib
|
|
@ -1 +0,0 @@
|
|||||||
## source-ref for webpack loader
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "source-ref-loader",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "lib/index.js",
|
|
||||||
"files": [
|
|
||||||
"lib"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsc",
|
|
||||||
"prepare": "tsc",
|
|
||||||
"test": "ts-node ./test/index.ts"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"sourcecode",
|
|
||||||
"pointer",
|
|
||||||
"webpack"
|
|
||||||
],
|
|
||||||
"author": "moonrailgun",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/babel__generator": "^7.6.4",
|
|
||||||
"@types/babel__traverse": "^7.17.1",
|
|
||||||
"@types/node": "^15.12.5",
|
|
||||||
"@types/webpack": "^5.28.0",
|
|
||||||
"ts-node": "^10.0.0",
|
|
||||||
"typescript": "^4.5.2",
|
|
||||||
"webpack": "^5.72.0",
|
|
||||||
"webpack-test-utils": "^1.1.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/generator": "^7.17.7",
|
|
||||||
"@babel/parser": "^7.17.7",
|
|
||||||
"@babel/traverse": "^7.17.3",
|
|
||||||
"@babel/types": "^7.17.10"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
import type { LoaderContext } from 'webpack';
|
|
||||||
import { parse } from '@babel/parser';
|
|
||||||
import traverse from '@babel/traverse';
|
|
||||||
import generate from '@babel/generator';
|
|
||||||
import {
|
|
||||||
isJSXIdentifier,
|
|
||||||
isJSXMemberExpression,
|
|
||||||
jsxAttribute,
|
|
||||||
jsxIdentifier,
|
|
||||||
stringLiteral,
|
|
||||||
} from '@babel/types';
|
|
||||||
|
|
||||||
const TRACE_ID = 'data-source';
|
|
||||||
|
|
||||||
async function loader(this: LoaderContext<any>, source: string): Promise<void> {
|
|
||||||
const done = this.async();
|
|
||||||
|
|
||||||
const { available } = this.getOptions();
|
|
||||||
if (!available) {
|
|
||||||
// skip if not
|
|
||||||
done(null, source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ast = parse(source, {
|
|
||||||
sourceType: 'module',
|
|
||||||
plugins: ['jsx', 'typescript'],
|
|
||||||
});
|
|
||||||
const filepath = this.resourcePath;
|
|
||||||
if (filepath.includes('node_modules')) {
|
|
||||||
done(null, source);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
traverse(ast, {
|
|
||||||
JSXOpeningElement(path) {
|
|
||||||
const location = path.node.loc;
|
|
||||||
if (!location) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Array.isArray(location)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = path.node.name;
|
|
||||||
if (isJSXIdentifier(name) && name.name === 'Fragment') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isJSXMemberExpression(name) && name.property.name === 'Fragment') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const line = location.start.line;
|
|
||||||
const col = location.start.column;
|
|
||||||
|
|
||||||
const attrs = path.node.attributes;
|
|
||||||
for (let i = 0; i < attrs.length; i++) {
|
|
||||||
const attr = attrs[i];
|
|
||||||
if (attr.type === 'JSXAttribute' && attr.name.name === TRACE_ID) {
|
|
||||||
// existed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const traceId = `${filepath}:${line}:${col}`;
|
|
||||||
|
|
||||||
attrs.push(jsxAttribute(jsxIdentifier(TRACE_ID), stringLiteral(traceId)));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const code = generate(ast).code;
|
|
||||||
|
|
||||||
done(null, code);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default loader;
|
|
@ -1,20 +0,0 @@
|
|||||||
export const tsx = {
|
|
||||||
'/src/index.js': `
|
|
||||||
import Foo, { HelloWorld } from './foo.tsx'
|
|
||||||
export default [
|
|
||||||
HelloWorld,
|
|
||||||
(new Foo()).render(),
|
|
||||||
];
|
|
||||||
`,
|
|
||||||
|
|
||||||
'/src/foo.tsx': `
|
|
||||||
export const HelloWorld = <><div>hello world</div></>;
|
|
||||||
export const HelloWorld2 = <React.Fragment><div>hello world</div></React.Fragment>;
|
|
||||||
export const HelloWorld3 = <Fragment><div>hello world</div></Fragment>;
|
|
||||||
export default class Foo {
|
|
||||||
render() {
|
|
||||||
return <div className="class-name">content</div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
};
|
|
@ -1,17 +0,0 @@
|
|||||||
import { build } from 'webpack-test-utils';
|
|
||||||
import * as fixtures from './fixtures';
|
|
||||||
import { configureLoader } from './utils';
|
|
||||||
|
|
||||||
async function test() {
|
|
||||||
const built = await build(fixtures.tsx, (config) => {
|
|
||||||
configureLoader(config);
|
|
||||||
|
|
||||||
console.log('configureLoader(config);', config.module.rules);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('usage:', built.stats.endTime - built.stats.startTime, 'ms');
|
|
||||||
console.log('errors:', built.stats.compilation.errors.length);
|
|
||||||
// console.log(built.stats.compilation.errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
test();
|
|
@ -1,18 +0,0 @@
|
|||||||
import type { Configuration } from 'webpack';
|
|
||||||
import type { DefaultWebpackConfig } from 'webpack-test-utils';
|
|
||||||
|
|
||||||
const loaderPath = require.resolve('../src/index.ts');
|
|
||||||
|
|
||||||
export function configureLoader(config: DefaultWebpackConfig & Configuration) {
|
|
||||||
config.resolveLoader.alias = {
|
|
||||||
'source-ref-loader': loaderPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
config.module.rules.push({
|
|
||||||
test: /\.tsx$/,
|
|
||||||
loader: 'source-ref-loader',
|
|
||||||
options: {
|
|
||||||
available: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "lib",
|
|
||||||
"module": "commonjs",
|
|
||||||
|
|
||||||
"target": "ES2018",
|
|
||||||
"lib": ["ES2018"],
|
|
||||||
|
|
||||||
"declaration": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
|
||||||
"include": ["src"]
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
declare module 'source-ref-open-vscode' {}
|
|
@ -1,223 +0,0 @@
|
|||||||
(function (win, doc) {
|
|
||||||
const sourceMap = {};
|
|
||||||
const sourceMapReverse = {};
|
|
||||||
let cursor = 0;
|
|
||||||
let timer = null;
|
|
||||||
|
|
||||||
function openVscode(node) {
|
|
||||||
let path = null;
|
|
||||||
if (node.dataset.sid) {
|
|
||||||
path = sidToURI(node.dataset.sid);
|
|
||||||
}
|
|
||||||
if (node.dataset.source) {
|
|
||||||
path = 'vscode://file/' + node.dataset.source;
|
|
||||||
}
|
|
||||||
if (!path) {
|
|
||||||
return console.warn('Not found data-source');
|
|
||||||
}
|
|
||||||
win.location.href = path;
|
|
||||||
}
|
|
||||||
function sourceToId(node) {
|
|
||||||
if (!node.dataset.source) return;
|
|
||||||
const source = node.dataset.source;
|
|
||||||
const splits = source.split(':');
|
|
||||||
const column = splits.pop();
|
|
||||||
const row = splits.pop();
|
|
||||||
const file = splits.join(':');
|
|
||||||
|
|
||||||
if (!sourceMap[file]) {
|
|
||||||
cursor++;
|
|
||||||
sourceMap[file] = cursor;
|
|
||||||
sourceMapReverse[cursor] = file;
|
|
||||||
}
|
|
||||||
const id = sourceMap[file];
|
|
||||||
node.removeAttribute('data-source');
|
|
||||||
node.setAttribute('data-sid', `${id}:${row}:${column}`);
|
|
||||||
}
|
|
||||||
function sidToURI(sid) {
|
|
||||||
const [id, row, column] = sid.split(':');
|
|
||||||
const path =
|
|
||||||
'vscode://file/' + sourceMapReverse[id] + ':' + row + ':' + column;
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Selector {
|
|
||||||
constructor(node) {
|
|
||||||
this.sids = [];
|
|
||||||
this.containerId = '__source-ref-panel';
|
|
||||||
this.getAncestorSids = (node) => {
|
|
||||||
const sids = [];
|
|
||||||
let cur = node;
|
|
||||||
while (cur !== doc.body) {
|
|
||||||
if (cur.dataset.sid) {
|
|
||||||
sids.push(cur.dataset.sid);
|
|
||||||
}
|
|
||||||
cur = cur.parentElement;
|
|
||||||
}
|
|
||||||
return sids;
|
|
||||||
};
|
|
||||||
this.focusBlock = null;
|
|
||||||
this.setFocusBlock = (target) => {
|
|
||||||
if (target === null) {
|
|
||||||
// clear if target is null
|
|
||||||
if (this.focusBlock) {
|
|
||||||
doc.body.removeChild(this.focusBlock);
|
|
||||||
this.focusBlock = null;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.focusBlock) {
|
|
||||||
this.focusBlock = doc.createElement('div');
|
|
||||||
this.focusBlock.className = '__source-ref-mask';
|
|
||||||
this.focusBlock.style.position = 'absolute';
|
|
||||||
this.focusBlock.style.backgroundColor = 'rgba(134, 185, 242, 0.5)';
|
|
||||||
|
|
||||||
doc.body.appendChild(this.focusBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
const rect = target.getBoundingClientRect();
|
|
||||||
|
|
||||||
this.focusBlock.style.height = rect.height + 'px';
|
|
||||||
this.focusBlock.style.width = rect.width + 'px';
|
|
||||||
this.focusBlock.style.left = rect.x + 'px';
|
|
||||||
this.focusBlock.style.top = rect.y + 'px';
|
|
||||||
};
|
|
||||||
this.getContainer = () => {
|
|
||||||
const container = doc.getElementById(this.containerId);
|
|
||||||
if (!container) {
|
|
||||||
const div = doc.createElement('div');
|
|
||||||
div.id = this.containerId;
|
|
||||||
doc.body.appendChild(div);
|
|
||||||
|
|
||||||
// Add dom red border on hover
|
|
||||||
div.addEventListener('mouseover', (e) => {
|
|
||||||
const node = e.target;
|
|
||||||
if (node.dataset.tid) {
|
|
||||||
const target = doc.querySelector(
|
|
||||||
`[data-sid="${node.dataset.tid}"]`
|
|
||||||
);
|
|
||||||
if (target) {
|
|
||||||
target.classList.add('__source-ref-selected');
|
|
||||||
this.setFocusBlock(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove dom red border when leave
|
|
||||||
div.addEventListener('mouseout', (e) => {
|
|
||||||
const node = e.target;
|
|
||||||
if (node.dataset.tid) {
|
|
||||||
const target = doc.querySelector(
|
|
||||||
`[data-sid="${node.dataset.tid}"]`
|
|
||||||
);
|
|
||||||
if (target) {
|
|
||||||
target.classList.remove('__source-ref-selected');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const close = () => {
|
|
||||||
this.setFocusBlock(null);
|
|
||||||
doc.body.removeChild(div);
|
|
||||||
};
|
|
||||||
|
|
||||||
// click event
|
|
||||||
div.addEventListener('click', (e) => {
|
|
||||||
const node = e.target;
|
|
||||||
const command = node.dataset.command;
|
|
||||||
switch (command) {
|
|
||||||
case 'close': {
|
|
||||||
e.stopPropagation();
|
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
console.warn('Unknown command', command);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// keyboard event
|
|
||||||
function escKeyHandler(e) {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
e.stopPropagation();
|
|
||||||
close();
|
|
||||||
doc.removeEventListener('keydown', escKeyHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doc.addEventListener('keydown', escKeyHandler);
|
|
||||||
|
|
||||||
return div;
|
|
||||||
}
|
|
||||||
return container;
|
|
||||||
};
|
|
||||||
this.renderHTML = () => {
|
|
||||||
const html = `
|
|
||||||
<div style="
|
|
||||||
position: fixed;
|
|
||||||
background: white;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 9999;
|
|
||||||
opacity: 0.6;
|
|
||||||
border-radius: 0 10px 0 0;
|
|
||||||
">
|
|
||||||
<div style="cursor:pointer;margin:10px;text-align:right;font-size:18px;" data-command="close">X</div>
|
|
||||||
${this.sids
|
|
||||||
.map((sid) => {
|
|
||||||
const uri = sidToURI(sid);
|
|
||||||
// 这里加了一个左向省略,暂时没用上,先放着
|
|
||||||
return `<a href="${uri}" style="
|
|
||||||
display: block;
|
|
||||||
margin: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
direction: rtl;
|
|
||||||
text-align: left;
|
|
||||||
" data-tid="${sid}">source-ref: ${uri}</a>`;
|
|
||||||
})
|
|
||||||
.join('')}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
const container = this.getContainer();
|
|
||||||
container.innerHTML = html;
|
|
||||||
};
|
|
||||||
this.sids = this.getAncestorSids(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function init() {
|
|
||||||
win.vscode = (node = win.$0) => {
|
|
||||||
openVscode(node);
|
|
||||||
};
|
|
||||||
doc.body.addEventListener(
|
|
||||||
'click',
|
|
||||||
(e) => {
|
|
||||||
if (e.altKey) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
const selector = new Selector(e.target);
|
|
||||||
selector.renderHTML();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
true
|
|
||||||
);
|
|
||||||
const mo = new MutationObserver(() => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
// recal sid
|
|
||||||
doc
|
|
||||||
.querySelectorAll('[data-source]')
|
|
||||||
.forEach((node) => sourceToId(node));
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
mo.observe(doc.body, {
|
|
||||||
attributes: true,
|
|
||||||
childList: true,
|
|
||||||
subtree: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
})(window, document);
|
|
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "source-ref-open-vscode",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"types": "index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"keywords": [],
|
|
||||||
"author": "moonrailgun",
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
Loading…
Reference in New Issue