Match-id-bc7ac7f1ed9550d223c8b89b50f26ea697888b4b
This commit is contained in:
commit
9f6078ecd8
|
@ -0,0 +1,15 @@
|
||||||
|
module.exports = api => {
|
||||||
|
const isTest = api.env('test');
|
||||||
|
console.log('isTest', isTest);
|
||||||
|
return {
|
||||||
|
presets: [
|
||||||
|
'@babel/preset-env',
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
['@babel/preset-react', {
|
||||||
|
runtime: 'classic',
|
||||||
|
'pragma': 'Horizon.createElement',
|
||||||
|
'pragmaFrag': 'Horizon.Fragment',
|
||||||
|
}]],
|
||||||
|
plugins: ['@babel/plugin-proposal-class-properties'],
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"name": "extension",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "",
|
||||||
|
"scripts": {
|
||||||
|
"build": "webpack --config ./webpack.config.js",
|
||||||
|
"watch": "webpack --config ./webpack.config.js --watch",
|
||||||
|
"build-dev": "webpack --config ./webpack.dev.js",
|
||||||
|
"start": "webpack serve --config ./webpack.dev.js ",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "7.12.3",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||||
|
"@babel/preset-env": "7.12.1",
|
||||||
|
"@babel/preset-react": "7.12.1",
|
||||||
|
"@babel/preset-typescript": "^7.16.7",
|
||||||
|
"@types/jest": "^27.4.1",
|
||||||
|
"babel-loader": "8.1.0",
|
||||||
|
"css-loader": "^6.7.1",
|
||||||
|
"html-webpack-plugin": "^5.5.0",
|
||||||
|
"jest": "^27.5.1",
|
||||||
|
"less": "^4.1.2",
|
||||||
|
"less-loader": "^10.2.0",
|
||||||
|
"style-loader": "^3.3.1",
|
||||||
|
"ts-jest": "^27.1.4",
|
||||||
|
"webpack": "^5.70.0",
|
||||||
|
"webpack-cli": "^4.9.2",
|
||||||
|
"webpack-dev-server": "^4.7.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@
|
||||||
devtools_page: devtool主页面
|
devtools_page: devtool主页面
|
||||||
default_popup: 拓展图标点击时弹窗页面
|
default_popup: 拓展图标点击时弹窗页面
|
||||||
content_scripts: 内容脚本,在项目中负责在页面初始化时调用注入全局变量代码和消息传递
|
content_scripts: 内容脚本,在项目中负责在页面初始化时调用注入全局变量代码和消息传递
|
||||||
web_accessible_resources: 注入全局变量代码
|
|
||||||
|
|
||||||
## 打开 panel 页面调试面板的方式
|
## 打开 panel 页面调试面板的方式
|
||||||
|
|
||||||
|
@ -53,5 +52,12 @@ sequenceDiagram
|
||||||
- 每个vNode有唯一的 path 属性,可以作为标识使用
|
- 每个vNode有唯一的 path 属性,可以作为标识使用
|
||||||
- 通过解析 path 值可以分析出组件树的结构
|
- 通过解析 path 值可以分析出组件树的结构
|
||||||
|
|
||||||
|
## 组件props/state/hook等数据的传输和解析
|
||||||
|
将数据格式进行转换后进行传递。对于 props 和 类组件的 state,他们都是对象,可以将对象进行解析然后以 k-v 的形式,树的结构显示。函数组件的 Hooks 是以数组的形式存储在 vNode 的属性中的,每个 hook 的唯一标识符是 hIndex 属性值,在对象展示的时候不能展示该属性值,需要根据 hook 类型展示一个 state/ref/effect 等值。hook 中存储的值也可能不是对象,只是一个简单的字符串,他们的解析和 props/state 的解析同样存在差异。
|
||||||
|
|
||||||
|
|
||||||
## 滚动动态渲染 Tree
|
## 滚动动态渲染 Tree
|
||||||
考虑到组件树可能很大,所以并不适合一次性全部渲染出来,可以通过滚动渲染的方式减少页面 dom 的数量。我们可以把树看成有不同缩进长度的列表,动态渲染滚动列表的实现可以参考谷歌的这篇文章:https://developers.google.com/web/updates/2016/07/infinite-scroller 这样,我们需要的组件树数据可以由树结构转变为数组,可以减少动态渲染时对树结构进行解析时的计算工作。
|
考虑到组件树可能很大,所以并不适合一次性全部渲染出来,可以通过滚动渲染的方式减少页面 dom 的数量。我们可以把树看成有不同缩进长度的列表,动态渲染滚动列表的实现可以参考谷歌的这篇文章:https://developers.google.com/web/updates/2016/07/infinite-scroller 这样,我们需要的组件树数据可以由树结构转变为数组,可以减少动态渲染时对树结构进行解析时的计算工作。
|
||||||
|
|
||||||
|
## 测试框架
|
||||||
|
jest测试框架不提供浏览器插件的相关 api,我们在封装好相关 api 后需要模拟这些 api 的行为从而展开测试工作。
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// 多个页面、tab页共享一个 background,需要建立连接池,给每个tab建立连接
|
||||||
|
const connections = {};
|
||||||
|
|
||||||
|
// panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数
|
||||||
|
chrome.runtime.onConnect.addListener(function (port) {
|
||||||
|
|
||||||
|
// The original connection event doesn't include the tab ID of the
|
||||||
|
// DevTools page, so we need to send it explicitly.
|
||||||
|
function extensionListener(message, sender, sendResponse) {
|
||||||
|
// 在backgroundPageConnection创建后会发送初始化请求,这样就可以获取tabId,给连接编号
|
||||||
|
if (message.name === 'init') {
|
||||||
|
// 获取 panel 所在 tab 页的tabId
|
||||||
|
connections[message.tabId] = port;
|
||||||
|
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
|
||||||
|
chrome.tabs.sendMessage(tabs[0].id, {tag: 'init horizon info'}, function(response) {
|
||||||
|
console.log(response.farewell);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.name === 'update') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// other message handling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listen to messages sent from the DevTools page
|
||||||
|
port.onMessage.addListener(extensionListener);
|
||||||
|
|
||||||
|
port.onDisconnect.addListener(function (port) {
|
||||||
|
port.onMessage.removeListener(extensionListener);
|
||||||
|
|
||||||
|
const tabs = Object.keys(connections);
|
||||||
|
for (let i = 0, len = tabs.length; i < len; i++) {
|
||||||
|
if (connections[tabs[i]] == port) {
|
||||||
|
delete connections[tabs[i]];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听来自 content script 的消息,并将消息发送给对应的 devTools page,也就是 panel
|
||||||
|
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
|
||||||
|
// Messages from content scripts should have sender.tab set
|
||||||
|
if (sender.tab) {
|
||||||
|
const tabId = sender.tab.id;
|
||||||
|
if (tabId in connections) {
|
||||||
|
connections[tabId].postMessage(request);
|
||||||
|
} else {
|
||||||
|
console.log('Tab not found in connection list.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('sender.tab not defined.');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
|
@ -18,7 +18,7 @@ type IComponentInfo = {
|
||||||
onClickParent: (item: IData) => void;
|
onClickParent: (item: IData) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type IAttr = {
|
export type IAttr = {
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
value: string | boolean;
|
value: string | boolean;
|
||||||
|
|
|
@ -36,13 +36,8 @@
|
||||||
border-bottom: unset;
|
border-bottom: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
>:first-child {
|
|
||||||
padding: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
>div {
|
>div {
|
||||||
border-bottom: @divider-style;
|
border-bottom: @divider-style;
|
||||||
padding: 0.5rem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.attrContainer {
|
.attrContainer {
|
||||||
|
|
|
@ -60,7 +60,7 @@ export function addResizeListener(element: any, fn: any) {
|
||||||
observer.data = 'about:blank';
|
observer.data = 'about:blank';
|
||||||
observer.onload = loadObserver;
|
observer.onload = loadObserver;
|
||||||
observer.type = 'text/html';
|
observer.type = 'text/html';
|
||||||
observer.__observeElement__ = element;
|
observer['__observeElement__'] = element;
|
||||||
element.__observer__ = observer;
|
element.__observer__ = observer;
|
||||||
element.appendChild(observer);
|
element.appendChild(observer);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { addResizeListener, removeResizeListener } from './ResizeEvent';
|
||||||
|
|
||||||
export function SizeObserver(props) {
|
export function SizeObserver(props) {
|
||||||
const { children, ...rest } = props;
|
const { children, ...rest } = props;
|
||||||
const containerRef = useRef();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
const [size, setSize] = useState();
|
const [size, setSize] = useState<{width: number, height: number}>();
|
||||||
const notifyChild = (element) => {
|
const notifyChild = (element) => {
|
||||||
setSize({
|
setSize({
|
||||||
width: element.offsetWidth,
|
width: element.offsetWidth,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// TODO:当前的 item 渲染效果较差,每次滚动所有项在数组中的位置都会发生变更。
|
||||||
|
// 建议修改成选项增加减少时,未变更项在原数组中位置不变更
|
||||||
|
|
||||||
import { useState, useRef, useEffect } from 'horizon';
|
import { useState, useRef, useEffect } from 'horizon';
|
||||||
import styles from './VList.less';
|
import styles from './VList.less';
|
||||||
|
@ -30,7 +32,7 @@ export function VList<T extends { id: string }>(props: IProps<T>) {
|
||||||
} = props;
|
} = props;
|
||||||
const [scrollTop, setScrollTop] = useState(data.indexOf(scrollToItem) * itemHeight);
|
const [scrollTop, setScrollTop] = useState(data.indexOf(scrollToItem) * itemHeight);
|
||||||
const renderInfoRef: { current: renderInfoType<T> } = useRef({ visibleItems: [], skipItemCountBeforeScrollItem: 0 });
|
const renderInfoRef: { current: renderInfoType<T> } = useRef({ visibleItems: [], skipItemCountBeforeScrollItem: 0 });
|
||||||
const containerRef = useRef();
|
const containerRef = useRef<HTMLDivElement>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onRendered(renderInfoRef.current);
|
onRendered(renderInfoRef.current);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useState, useEffect } from 'horizon';
|
import { useState, useEffect } from 'horizon';
|
||||||
import styles from './VTree.less';
|
import styles from './VTree.less';
|
||||||
import Triangle from '../svgs/Triangle';
|
import Triangle from '../svgs/Triangle';
|
||||||
import { createRegExp } from './../utils';
|
import { createRegExp } from '../utils/regExpUtils';
|
||||||
import { SizeObserver } from './SizeObserver';
|
import { SizeObserver } from './SizeObserver';
|
||||||
import { renderInfoType, VList } from './VList';
|
import { renderInfoType, VList } from './VList';
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ function VTree(props: {
|
||||||
onRendered: (renderInfo: renderInfoType<IData>) => void,
|
onRendered: (renderInfo: renderInfoType<IData>) => void,
|
||||||
collapsedNodes?: IData[],
|
collapsedNodes?: IData[],
|
||||||
onCollapseNode?: (item: IData[]) => void,
|
onCollapseNode?: (item: IData[]) => void,
|
||||||
selectItem: IData[],
|
selectItem: IData,
|
||||||
onSelectItem: (item: IData) => void,
|
onSelectItem: (item: IData) => void,
|
||||||
}) {
|
}) {
|
||||||
const { data, highlightValue, scrollToItem, onRendered, onCollapseNode, onSelectItem } = props;
|
const { data, highlightValue, scrollToItem, onRendered, onCollapseNode, onSelectItem } = props;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { injectCode } from '../utils/injectUtils';
|
||||||
|
|
||||||
|
// 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook
|
||||||
|
injectCode(chrome.runtime.getURL('/injector.js'));
|
||||||
|
|
||||||
|
// 监听来自页面的信息
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
// 只监听来自本页面的消息
|
||||||
|
if (event.source !== window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS')) {
|
||||||
|
console.log('Content script received: ' + JSON.stringify(event.data.vNode));
|
||||||
|
// 传递给background
|
||||||
|
chrome.runtime.sendMessage(event.data.vNode, function (response) {
|
||||||
|
console.log(response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 监听来自background的消息
|
||||||
|
chrome.runtime.onMessage.addListener(
|
||||||
|
function (request, sender, sendResponse) {
|
||||||
|
console.log(sender.tab ?
|
||||||
|
'from a content script:' + sender.tab.url :
|
||||||
|
'from the extension');
|
||||||
|
if (request.tag === 'init horizon info') {
|
||||||
|
// 传递消息给页面
|
||||||
|
console.log('start pass info to webpage');
|
||||||
|
window.postMessage({type: 'HORIZON_DEV_TOOLS', id: 1}, '*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Component } from 'horizon';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
name: 'jenny',
|
||||||
|
boolean: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class MockClassComponent extends Component<{fruit: string}, typeof defaultState> {
|
||||||
|
|
||||||
|
state = defaultState;
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={() => (this.setState({name: 'pika'}))} >update state</button>
|
||||||
|
{this.state.name}
|
||||||
|
{this.props?.fruit}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { useState, useEffect, useRef, createContext } from 'horizon';
|
||||||
|
|
||||||
|
const Ctx = createContext();
|
||||||
|
|
||||||
|
export default function MockFunctionComponent(props) {
|
||||||
|
const [age, setAge] = useState(0);
|
||||||
|
const domRef = useRef<HTMLDivElement>();
|
||||||
|
const objRef = useRef({ str: 'string' });
|
||||||
|
|
||||||
|
useEffect(() => { }, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
age: {age}
|
||||||
|
<button onClick={() => setAge(age + 1)} >update age</button>
|
||||||
|
count: {props.count}
|
||||||
|
<div ref={domRef} />
|
||||||
|
<div>{objRef.current.str}</div>
|
||||||
|
<Ctx.Provider value={{ctx: 'I am ctx'}}></Ctx.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { render } from 'horizon';
|
||||||
|
import MockClassComponent from './MockClassComponent';
|
||||||
|
import MockFunctionComponent from './MockFunctionComponent';
|
||||||
|
|
||||||
|
const root = document.createElement('div');
|
||||||
|
document.body.append(root);
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
abc
|
||||||
|
<MockClassComponent fruit={'apple'}/>
|
||||||
|
<MockFunctionComponent />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<App/>, root);
|
|
@ -0,0 +1,28 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf8">
|
||||||
|
<title>Horizon Mock Page</title>
|
||||||
|
<script src="horizon.production.js"></script>
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -13,7 +13,7 @@
|
||||||
// 找到该节点的缩进值,和index值,在data中向上遍历,通过缩进值判断父节点
|
// 找到该节点的缩进值,和index值,在data中向上遍历,通过缩进值判断父节点
|
||||||
|
|
||||||
import { useState, useRef } from 'horizon';
|
import { useState, useRef } from 'horizon';
|
||||||
import { createRegExp } from '../utils';
|
import { createRegExp } from '../utils/regExpUtils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 把节点的父节点从收起节点数组中删除,并返回新的收起节点数组
|
* 把节点的父节点从收起节点数组中删除,并返回新的收起节点数组
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import parseTreeRoot from "../parser/parseVNode";
|
||||||
|
|
||||||
|
function injectHook() {
|
||||||
|
if (window.__HORIZON_DEV_HOOK__) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object.defineProperty(window, '__HORIZON_DEV_HOOK__', {
|
||||||
|
enumerable: false,
|
||||||
|
value: {
|
||||||
|
roots: [],
|
||||||
|
send: function (vNode: any) {
|
||||||
|
const result = parseTreeRoot(vNode);
|
||||||
|
window.postMessage({
|
||||||
|
type: 'HORIZON_DEV_TOOLS', vNode: result
|
||||||
|
}, '*');
|
||||||
|
},
|
||||||
|
listen: function (id: number) {
|
||||||
|
window.addEventListener('message', function(event) {
|
||||||
|
// We only accept messages from ourselves
|
||||||
|
if (event.source !== window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS') && event.data.id === id) {
|
||||||
|
console.log('todo');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
injectHook();
|
|
@ -0,0 +1,7 @@
|
||||||
|
chrome.devtools.panels.create('Horizon',
|
||||||
|
'',
|
||||||
|
'panel.html',
|
||||||
|
function(panel) {
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p>Horizon dev tools!</p>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script src="main.js"></script>
|
||||||
|
</html>
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "Horizon dev tool",
|
||||||
|
"description": "Horizon chrome dev extension",
|
||||||
|
"version": "1.0",
|
||||||
|
"minimum_chrome_version": "10.0",
|
||||||
|
"manifest_version": 3,
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"permissions": ["storage", "activeTab", "scripting"],
|
||||||
|
|
||||||
|
"devtools_page": "main.html",
|
||||||
|
"action": {},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["<all_urls>"],
|
||||||
|
"js": ["contentScript.js"],
|
||||||
|
"run_at": "document_start"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"web_accessible_resources": []
|
||||||
|
}
|
|
@ -1,12 +1,8 @@
|
||||||
|
import { IAttr } from "../components/ComponentInfo";
|
||||||
|
|
||||||
// 将状态的值解析成固定格式
|
// 将状态的值解析成固定格式
|
||||||
export function parseAttr(rootAttr: any) {
|
export function parseAttr(rootAttr: any) {
|
||||||
const result: {
|
const result: IAttr[] = [];
|
||||||
name: string,
|
|
||||||
type: string,
|
|
||||||
value: string,
|
|
||||||
indentation: number
|
|
||||||
}[] = [];
|
|
||||||
const indentation = 0;
|
const indentation = 0;
|
||||||
const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => {
|
const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => {
|
||||||
const stateType = typeof attr;
|
const stateType = typeof attr;
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
function ifNullThrows(v) {
|
||||||
|
if (v === null) {
|
||||||
|
throw new Error('received a null');
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用于向页面注入脚本
|
||||||
|
export function injectCode(src) {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = src;
|
||||||
|
script.onload = function () {
|
||||||
|
// 加载完毕后需要移除
|
||||||
|
script.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
ifNullThrows(document.head || document.documentElement).appendChild(script);
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"module": "es6",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "preserve",
|
||||||
|
"allowJs": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"*": ["types/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"includes": [
|
||||||
|
"./src/index.d.ts", "./src/*/*.ts", "./src/*/*.tsx"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
entry: {
|
||||||
|
background: './src/background/index.ts',
|
||||||
|
main: './src/main/index.ts',
|
||||||
|
injector: './src/injector/index.ts',
|
||||||
|
contentScript: './src/contentScript/index.ts',
|
||||||
|
panel: './src/panel/index.tsx',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, './build'),
|
||||||
|
filename: '[name].js',
|
||||||
|
},
|
||||||
|
mode: 'development',
|
||||||
|
devtool: 'inline-source-map',
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.tsx?$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'babel-loader',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.less/i,
|
||||||
|
use: [
|
||||||
|
'style-loader',
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
modules: true,
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'less-loader'],
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.ts', '.tsx'],
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
'horizon': 'Horizon',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = config;
|
|
@ -1,5 +1,4 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
|
|
||||||
// 用于 panel 页面开发
|
// 用于 panel 页面开发
|
||||||
|
@ -8,6 +7,7 @@ module.exports = {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
entry: {
|
entry: {
|
||||||
panel: path.join(__dirname, './src/panel/index.tsx'),
|
panel: path.join(__dirname, './src/panel/index.tsx'),
|
||||||
|
mockPage: path.join(__dirname, './src/devtools/mockPage/index.tsx'),
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'dist'),
|
path: path.join(__dirname, 'dist'),
|
||||||
|
@ -15,7 +15,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.tsx', '.js']
|
extensions: ['.ts', '.tsx', '.js'],
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
@ -25,16 +25,6 @@ module.exports = {
|
||||||
use: [
|
use: [
|
||||||
{
|
{
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
|
||||||
presets: ['@babel/preset-env',
|
|
||||||
'@babel/preset-typescript',
|
|
||||||
['@babel/preset-react', {
|
|
||||||
runtime: 'classic',
|
|
||||||
'pragma': 'Horizon.createElement',
|
|
||||||
'pragmaFrag': 'Horizon.Fragment',
|
|
||||||
}]],
|
|
||||||
plugins: ['@babel/plugin-proposal-class-properties'],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -64,10 +54,6 @@ module.exports = {
|
||||||
magicHtml: true,
|
magicHtml: true,
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
new HtmlWebpackPlugin({
|
|
||||||
filename: 'panel.html',
|
|
||||||
template: './src/panel/panel.html'
|
|
||||||
}),
|
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env.NODE_ENV': '"development"',
|
'process.env.NODE_ENV': '"development"',
|
||||||
isDev: 'true',
|
isDev: 'true',
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"horizon"
|
"horizon"
|
||||||
],
|
],
|
||||||
"version": "1.0.0",
|
"version": "0.0.6",
|
||||||
"homepage": "",
|
"homepage": "",
|
||||||
"bugs": "",
|
"bugs": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
|
|
|
@ -23,7 +23,7 @@ const readLib = (lib) => {
|
||||||
ejs.renderFile(path.resolve(__dirname, './template.ejs'), {
|
ejs.renderFile(path.resolve(__dirname, './template.ejs'), {
|
||||||
Horizon: readLib(`horizon.${suffix}`),
|
Horizon: readLib(`horizon.${suffix}`),
|
||||||
}, null, function(err, result) {
|
}, null, function(err, result) {
|
||||||
const common3rdLibPath = path.resolve(__dirname, `${libPathPrefix}/common3rdlib.min.js`)
|
const common3rdLibPath = path.resolve(__dirname, `${libPathPrefix}/horizonCommon3rdlib.min.js`)
|
||||||
rimRaf(common3rdLibPath, e => {
|
rimRaf(common3rdLibPath, e => {
|
||||||
if (e) {
|
if (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
|
|
1406
scripts/template.ejs
1406
scripts/template.ejs
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue