Compare commits

..

No commits in common. "master" and "v0.22" have entirely different histories.

186 changed files with 42929 additions and 3366 deletions

31
.github/ISSUE_TEMPLATE/--bug.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: 报告Bug
about: 创建一个问题报告帮助我们做得更好
title: ''
labels: bug
assignees: ''
---
**问题描述**
请对问题做一个简单描述。
**问题复现**
复现问题需要进行的操作:
1. 打开 '...'
2. 点击 '....'
3. 看到问题
**期望的效果**
经过上述操作你希望得到的结果.
**截屏**
添加截图图片有助于解释您的问题.
**版本信息 (这是必要的):**
- OS: [e.g. iOS]
- Package [e.g. xxx_wine.tar.gz]
- Wine Version [e.g. 7]
**额外的信息**
一些关于此问题的额外信息.

View File

@ -1,41 +1,32 @@
---
name: 报告Bug
about: 如果阁下提交的 `issue` 属于bug但没有按模板提交那么将会被忽略或删除
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**预先准备**
1. 删除 `~/.config/wechat-devtools` 目录,重新打开项目(若发布 `issue` 即默认阁下执行了此操作且问题没有解决);
2. HelloWorld是否正常登录状态新建默认项目,执行复现步骤):正常/不正常
3. ***注意如果你的问题是编译器错误那么请提供可复现的demo否则会被直接关闭。***
**Describe the bug**
A clear and concise description of what the bug is.
**问题描述**
<!-- 请对问题做一个简单描述。 -->
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**问题复现**
<!-- 复现问题需要进行的操作: -->
1. 打开项目
2. 点击 '....'
3. 看到问题
**Expected behavior**
A clear and concise description of what you expected to happen.
**期望的效果**
<!-- 经过上述操作你希望得到的结果. -->
**Screenshots**
If applicable, add screenshots to help explain your problem.
**截屏**
<!-- 添加截图图片有助于解释您的问题,请尽量将整个开发工具都涵括在截图在(复制图片后直接粘贴将自动上传图片)。 -->
**Version Information (please complete the following information):**
- OS: [e.g. iOS]
- Package [e.g. xxx_wine.tar.gz]
- Wine Version [e.g. 7]
**版本信息 (这是必要的):**
- OS: e.g. manjaro
- Package: e.g. xxx_wine.tar.gz
- Wine Version: e.g. 7 可选
**代码片段**
<!-- 提供代码片段对维护者定位与修复问题有极大帮助;
若阁下不能提供,且维护者不能复现问题,那么阁下的`issue`将被搁置或者关闭。
[查看创建代码片段的教程](https://developers.weixin.qq.com/miniprogram/dev/devtools/minicode.html) -->
**额外的信息**
<!-- 一些关于此问题的额外信息。 -->
**Additional context**
Add any other context about the problem here.

View File

@ -1,66 +0,0 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build DEB Package
on:
workflow_dispatch:
workflow_call:
jobs:
build-deb:
name: Build DEB Package
runs-on: ubuntu-latest
strategy:
matrix:
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
# https://stackoverflow.com/questions/61096521/how-to-use-gpg-key-in-github-actions
# gpg --generate-key
# gpg --export-secret-keys YOUR_ID_HERE | base64 > private.key
- name: Configure GPG Key
run: |
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --import
gpg --list-secret-keys jiyecafe@gmail.com
env:
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
- name: Prepare
run: |
sudo apt update -y
sudo apt-get install -y build-essential fakeroot devscripts debhelper # debmake lintian pbuilder
- name: Build Deb Package
run: |
export BUILD_VERSION=${{ steps.tag.outputs.tag }}
ls -l
mkdir -p tmp/build
export WINE=false
tools/build-prepare.sh
env WINE=false tools/build-deepin.sh ${{ steps.tag.outputs.tag }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
# Artifact name
name: wechat-devtools-deb-${{ matrix.ARCH }}.build
path: tmp/build

View File

@ -1,88 +0,0 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build Base Packages
on:
workflow_dispatch:
workflow_call:
jobs:
build-src:
name: Build Base Packages
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
with:
submodules: 'recursive'
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
- name: Prepare
run: |
echo "$UID, $GID"
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
- name: Build
run: |
export ACTION_MODE=true
ls -l
export WINE=false
tools/build-with-docker.sh
- name: Compress Resources
run: |
ls -l
mkdir -p tmp/src
rm -rf nwjs/node nwjs/node.exe
cp node/bin/node nwjs/node
cd nwjs && ln -s node node.exe
cd ..
tar -zcf tmp/src/src-linux.tar.gz bin nwjs package.nw tools
- name: Compress nodegit
run: |
ls -l
mkdir -p tmp/build
cp -r package.nw/node_modules/nodegit .
tar -zcf nodegit.tar.gz nodegit
mv nodegit.tar.gz tmp/build
cd tmp/build
ls -l
- name: View Directory
run: |
ls -l
- name: Upload src artifact
uses: actions/upload-artifact@v4
with:
# Artifact name
name: wechat-devtools-${{ matrix.ARCH }}.src
path: tmp/src
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
# Artifact name
name: wechat-devtools-${{ matrix.ARCH }}.build
path: tmp/build

View File

@ -1,66 +0,0 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build tar.gz AppImage
on:
workflow_dispatch:
workflow_call:
jobs:
build-tar:
name: Build tar.gz AppImage
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Inspect directory after downloading artifacts
run: |
ls -alFR wechat-devtools-*.build
ls -alFR wechat-devtools-*.src
- name: Prepare
run: |
sudo apt install -y fuse
echo "$UID, $GID"
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
- name: Build
run: |
export WINE=false
export ACTION_MODE=true
# tar.gz AppImage
ls -l
mkdir -p tmp/build
tools/build-prepare.sh
tools/build-release.sh ${{ matrix.ARCH }} ${{ steps.tag.outputs.tag }}
- name: View Directory
run: |
ls -l
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
# Artifact name
name: wechat-devtools-simple-${{ matrix.ARCH }}.build
path: tmp/build

View File

@ -1,7 +1,7 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Build Packages Linux
name: Build Packages
on:
release:
@ -9,41 +9,213 @@ on:
push:
tags:
- v*
branches: [ master, dev, ci, skyline]
paths-ignore:
- 'README.MD'
- 'CHANGELOG.MD'
- 'docs/**'
branches: [ master, aur]
pull_request:
branches: [ master ]
# # Allows you to run this workflow manually from the Actions tab
# workflow_dispatch:
jobs:
build-src:
uses: ./.github/workflows/build-src.yml
secrets: inherit
build-tar:
needs:
- build-src
uses: ./.github/workflows/build-tar.yml
secrets: inherit
build:
name: Build tar.gz AppImage
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Prepare
run: |
pwd
ls -l
export ACTION_MODE=true
sudo apt-get install -y libx11-dev libxkbfile-dev p7zip-full python2 python3 libkrb5-dev gcc openssl libssh2-1-dev g++ make
npm install node-gyp nw-gyp npm -g
node-gyp install
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
- name: Build
run: |
# docker-compose up
export ACTION=true
# tar.gz AppImage
ls -l
tools/setup-wechat-devtools-bash
sudo chmod -R 755 package.nw
bash tools/build-release.sh ${{ steps.tag.outputs.tag }} ${{ matrix.ARCH }}
- name: Compress nodegit compiler
run: |
ls -l
sudo cp -r package.nw/node_modules/nodegit tmp/build/nodegit
sudo cp -r compiler tmp/build/compiler
cd tmp/build
ls -l
tar -zcf compiler.tar.gz compiler
tar -zcf nodegit.tar.gz nodegit
sudo rm -rf compiler nodegit
ls -l
- name: View Directory
run: |
ls -l
- name: Upload artifact
uses: actions/upload-artifact@v2.3.1
with:
# Artifact name
name: wechat-devtools-${{ matrix.ARCH }}.build
path: tmp/build
build-deb:
needs:
- build-src
uses: ./.github/workflows/build-deb.yml
secrets: inherit
name: Build Deb Package
runs-on: ubuntu-18.04
strategy:
matrix:
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
- name: Build Deb Package
run: |
export BUILD_VERSION=${{ steps.tag.outputs.tag }}
tools/build-deb.sh
# deb
sudo chmod -R 777 tmp
cd tmp
ls -l deb
mkdir -p build
mv deb/*.deb build
ls -l build
- name: Upload artifact
uses: actions/upload-artifact@v2.3.1
with:
# Artifact name
name: wechat-devtools-${{ matrix.ARCH }}.build
path: tmp/build
build-arch:
name: Build ArchLinux Package
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
node-version: [16.x]
ARCH: ['x86_64']
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Prepare
run: |
npm install node-gyp nw-gyp npm -g
node-gyp install
cat /etc/passwd
- name: Generate TAG
id: Tag
run: |
tag='continuous'
name='Continuous Build'
if [ 'true' == ${{ startsWith(github.ref, 'refs/tags/') }} ];then
tag='${{ github.ref_name }}'
name='${{ github.ref_name }}'
fi
echo "tag result: $tag - $name"
echo "::set-output name=tag::$tag"
echo "::set-output name=name::$name"
- name: Build ArchLinux Package
uses: countstarlight/arch-makepkg-action@master
with:
repos: >
archlinuxcn=https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch
before: "tools/build-aur.sh && sudo pacman -S --noconfirm archlinuxcn-keyring"
packages: >
gconf
p7zip
libxkbfile
python2
openssl
gcc
make
libssh2
krb5
scripts: "cd tmp/AUR && makepkg && ls -l && cd ../../"
- name: Fix Permissions
run: |
sudo chmod -R 0777 tmp
mkdir -p tmp/build
mv tmp/AUR/*.pkg.* tmp/build
cd tmp/build
for file in `ls *.pkg.*`;do mv $file `echo $file|sed 's/:/-/g'`;done;
ls -l
- name: Upload artifact
uses: actions/upload-artifact@v2.3.1
with:
# Artifact name
name: wechat-devtools-${{ matrix.ARCH }}.build
path: tmp/build
upload:
name: Create release and upload artifacts
needs:
- build-tar
- build
- build-deb
# - build-snap
# - build-arch
- build-arch
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v2
- name: Inspect directory after downloading artifacts
run: ls -alFR
@ -65,10 +237,9 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
sudo apt install -y fuse
wget -q https://github.com/TheAssassin/pyuploadtool/releases/download/continuous/pyuploadtool-x86_64.AppImage
chmod +x pyuploadtool-x86_64.AppImage
./pyuploadtool-x86_64.AppImage **build/WeChat*.AppImage **build/*.tar.gz **build/*.deb
./pyuploadtool-x86_64.AppImage **/WeChat*.AppImage **/*.tar.gz **/*.deb **/*.pkg.*
- name: Release
uses: softprops/action-gh-release@v1
@ -79,6 +250,7 @@ jobs:
name: ${{ steps.tag.outputs.name }}
tag_name: ${{ steps.tag.outputs.tag }}
files: |
**build/WeChat*.AppImage
**build/*.tar.gz
**build/*.deb
**/WeChat*.AppImage
**/*.tar.gz
**/*.deb
**/*.pkg.*

7
.gitignore vendored
View File

@ -13,9 +13,4 @@ build
*.AppImage
appimage-builder-cache
wcc.bin
wcsc.bin
temp.sh
# flatpak
.flatpak-builder
build-dir
__pycache__
wcsc.bin

6
.vscode/launch.json vendored
View File

@ -8,13 +8,13 @@
"type": "nwjs",
"request": "launch",
"name": "Launch NWjs",
"nwjsVersion": "0.55.0",
"nwjsVersion": "0.47.0",
"webRoot": "${workspaceFolder}/package.nw",
"reloadAfterAttached": true,
"runtimeArgs": [
// "--load-extension=${workspaceFolder}/tmp/data/WeappPlugin",
"--load-extension=${workspaceFolder}/tmp/data/WeappPlugin",
// "--custom-devtools-frontend=file://${workspaceFolder}/tmp/data/WeappPlugin/inspector/",
// "--user-data-dir=${workspaceFolder}/tmp/data",
"--user-data-dir=${workspaceFolder}/tmp/data",
// "--remote-debugging-port=9999"
],
"env": {

10
.vscode/settings.json vendored
View File

@ -1,10 +0,0 @@
{
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.hg/store/**": true,
".flatpak/**": true,
"_build/**": true
}
}

View File

@ -1,261 +0,0 @@
# 1.06.2504010-3 / 2025-07-
- refactor: 重构主题监听处理方案
# 1.06.2504010-2 / 2025-07-15
- fix: cli使用异常。#147
# 1.06.2504010-1 / 2025-07-05
- fix: 更新弹窗无法关闭。
- update: devtools to v1.06.2504010
# 1.06.2503290-3 / 2025-05-23
- update: compiler(Linux) v0.1.5
- fix: worker无法使用 #145
# 1.06.2503290-2 / 2025-05-02
- update: compiler(Linux) v0.1.4
# 1.06.2503290-1 / 2025-04-30
- update: node v16.11.0 (解决codebuddy无法使用。)
- perf: 替换float-pigment。
- update: devtools to v1.06.2503290
# 1.06.2412050-2 / 2025-02-17
- update: compiler(Linux) v0.1.3
# 1.06.2412050-1 / 2025-02-16
- update: devtools to v1.06.2412050
- update: compiler(Linux) v0.1.2
# 1.06.2412040-1 / 2025-01-16
- update: devtools to v1.06.2412040
# 1.06.2409140-3 / 2024-11-16
- fix: 多开实例会出现卡死的情况
# 1.06.2409140-2 / 2024-11-15
- perf: 不对Skyline插件进行初始化不会弹窗
# 1.06.2409140-1 / 2024-10-26
- update: devtools to v1.06.2409140
# 1.06.2407120-1 / 2024-09-28
- update: devtools to v1.06.2407120
# 1.06.2405020-4 / 2024-09-28
- fix: 视频无法播放的问题(#118
# 1.06.2405020-3 / 2024-08-10
- update: compiler(Linux) v0.1.1
# 1.06.2405020-2 / 2024-07-13
- fix: mock 面板按钮点击无反应
- update: compiler(Linux) v0.1.0
# 1.06.2405020-1 / 2024-07-07
- update: devtools to v1.06.2405020
- update: compiler(Linux) v0.0.9
# 1.06.2402040-1 / 2024-05-03
- update: devtools to v1.06.2402040
# 1.06.2310080-2 / 2024-01-02
- fix: AppImage的cli使用会闪退
# 1.06.2310080-1 / 2023-12-30
- fix: AppImage的cli使用会闪退
- update: devtools to v1.06.2310080
# 1.06.2308310-3 / 2023-12-24
- update: compiler(Linux) v0.0.7
# 1.06.2308310-2 / 2023-11-10
- update: compiler(Linux) v0.0.6
# 1.06.2308310-1 / 2023-09-30
- update: devtools to v1.06.2308310
# 1.06.2307260-2 / 2023-09-23
- update: compiler(Linux) v0.0.5
# 1.06.2307260-1 / 2023-09-09
- update: node-pty to v1.0.0
- update: devtools to v1.06.2307260
- update: compiler(Linux) v0.0.4
- dprecated: wine version
# 1.06.2306020-1 / 2023-06-04
- update: devtools to v1.06.2306020
# 1.06.2301040-1 / 2023-01-21
- update: devtools to v1.06.2301040
# 1.06.2209070-1 / 2022-09-09
- update: devtools to v1.06.2209070
# 1.06.2208010-1 / 2022-09-04
- update: devtools to v1.06.2208010
# 1.06.2207210-1 / 2022-09-03
- update: devtools to v1.06.2207210
- update: compiler
# 1.06.2206090-2 / 2022-06-15
- fix: deepin icon
# 1.06.2206090-1 / 2022-06-13
- update: devtools to v1.06.2206090
# 1.06.2206020-1 / 2022-06-03
- update: devtools to v1.06.2206020
# 1.05.2204250-2 / 2022-05-11
- fix: 修复wcc编译器不支持单文件编译的bug感谢 @2715851270 反馈)
- fix: 修复cli异常
# 1.05.2204250-1 / 2022-04-26
- update: devtools 1.05.2204250
- update: 跟进wcc编译器变更
# 1.05.2204180-2 / 2022-04-26
- feat: wcc,wcsc node模块懒加载template解析支持
- feat: wcc,wcsc node模块懒加载cut参数支持
# 1.05.2204180-1 / 2022-04-24
- update: devtools 1.05.2204180
- update: 跟进更新 wcc,wcsc node模块逻辑
- feat: wcc,wcsc编译器template解析支持
# 1.05.2203070-10 / 2022-04-22
- fix: 修正命令行入口
- fix: gsettings子进程未退出转为孤儿进程
- update: 优化主题监听
# 1.05.2203070-8 / 2022-04-10
- fix: 国内网络vscode-ripgrep安装失败的问题
- fix: 内部的更新检查界面无法正常使用(这个更新用处不大,仅用于让维护者知道存在更新)
- fix: 修复可能出现扩展宿主意外终止的问题
- feat: 主题深浅跟随系统变化感谢icepie的参与
- update: 更新nwjs与nodejs
# 1.05.2203070-7 / 2022-03-19
- fix: cli
- 兼容ubuntu16.04
# 1.05.2203070-6 / 2022-03-19
- feat: wcc node模块支持wxml引用
- feat: wcc node模块支持自定义函数名
# 1.05.2203070-5 / 2022-03-18
- fix: 增强wcsc编译器
# 1.05.2203070-4 / 2022-03-12
- add: 增加wine版本的deb包
# 1.05.2203070-3 / 2022-03-11
- fix: 修复wcc常量操作问题
- fix: 修复deb打包细节问题
# 1.05.2203070-2 / 2022-03-10
- feat: wcc编译器懒加载支持wxml引用
# 1.05.2203070-1 / 2022-03-08
- upgrade: devtools 1.05.2203070
- fix: 编译器异常信息显示不完全
# 1.05.2203030-2 / 2022-03-07
- refactor: 规范版本号
# 0.24 / 2022-03-05
- fix: 编译异常的问题
- 处理一些包相关细节
# 0.23 / 2022-03-05
- upgrade devtools to 1.05.2203030
# 0.22 / 2022-03-03
- refactor: 使用低版本环境构建deb,提升兼容性
# 0.21 / 2022-03-02
- add: deb aur 打包格式
# 0.20 / 2022-02-28
- fix: node模块中编译器的可执行权限
# 0.19 / 2022-02-28
- 过渡版本,存在问题
# 0.16 / 2022-02-24
- feat: wcc编译器路径异常
# 0.15 / 2022-02-23
- feat: wcc编译器支持`debugWXS`选项
# 0.14 / 2022-02-22
- feat: 正式添加node版本的编译器
# 0.13 / 2022-02-20
- feat: wine弱依赖版本
# 0.12 / 2022-02-16
- fix: 版本管理中的“在终端打开”不可用
# 0.11 / 2022-02-15
- add: appimage打包版本
# 0.10 / 2022-02-06
- fix: 二维码真机调试不可用
# 0.9 / 2022-02-05
- fix: 修复Webview组件涉及代码分析插件商店
# 0.8 / 2022-02-04
- fix: 云开发控制台不可用
# 0.7 / 2022-02-04
- add: 重新使用windows的编译器
# 0.6 / 2022-02-03
- fix: 可视化功能不可用
# 0.5 / 2022-02-02
- fix: 版本管理
- feat: node版本编译器
- remove: windows的编译器
# 0.4 / 2022-01-30
- fix: 创建终端时会崩溃的问题
# 0.3 / 2022-01-28
- 适配nwjs版本至0.53.1
# 0.2 / 2022-01-27
体验版

218
README.MD
View File

@ -1,218 +0,0 @@
<div align="center">
<img src="./res/icons/512x512.png" height="100px" width="100px"/>
<h3>微信开发者工具 Linux版</h3>
<br>
----
[![Node.js CI](https://github.com/msojocs/wechat-devtools-linux/actions/workflows/release.yml/badge.svg)](https://github.com/msojocs/wechat-devtools-linux/actions/workflows/release.yml)
[![wechat-tools](https://img.shields.io/badge/wechat--devtools-1.06.2503290-blue)](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
[![nwjs](https://img.shields.io/badge/nwjs-0.55.0-green)](https://nwjs.io/downloads/)
[![node](https://img.shields.io/badge/node-16.11.0-orange)](https://nodejs.org/en/)
这是微信开发者工具 Linux版
<br>
</div>
# 项目说明
本项目是一个完整的搭建Linux下可用的“微信开发者工具”的脚本和工具集
用于在Linux下的GNOME桌面上搭建可以持续更新和使用的“微信开发者工具”。
本项目修改自https://github.com/dragonation/wechat-devtools/
# 项目地址
* https://github.com/msojocs/wechat-web-devtools-linux
# 进度
当前工具可以在Linux上构筑最新版 `1.06.2503290`支持CLI模式。
另现在已经可以直接在设置界面里面修改字体,手工输入字体名称就可以。
# 功能测试记录
[测试记录](docs/Features.MD)
注意:
此测试仅在修复某项功能时测试通过,并不代表阁下在使用时是正常可用的;因为我没有精力在每个版本发布前进行一次测试。
在阁下遇到任何无法解决的问题时,请新建一个 [issue](https://github.com/msojocs/wechat-web-devtools-linux/issues/new/choose)
# 系统要求
* 基于Linux的桌面系统首选GNOME其他的桌面环境可能会有问题未测试
* CI自动构建的包对 glibc 和 libstdc++ 有一定的版本要求glibc 的版本要求>=2.23libstdc++ 的版本要求>=3.4.21
* ~~如果你下载的是 `wine` 版本,那么你需要安装有 `wine` `wine-binfmt` 支持建议版本在5.0以上,低版本可能会存在有问题~~
# CLI支持
在项目的 `bin` 目录中有 `wechat-devtools-cli` 脚本,是微信开发者工具的命令行支持 的Linux版本。相关资料可以在[微信CLI命令行V2](https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html)上找到。
# 使用方法
1. [在线安装](#在线安装)
2. 可以在本项目的[Release](https://github.com/msojocs/wechat-web-devtools-linux/releases)中,寻找已经构筑好了的软件包,下载使用。
如果`Release`版本存在问题,可以尝试[Continuous](https://github.com/msojocs/wechat-web-devtools-linux/releases/tag/continuous)版本,这是基于`master`分支的最新提交构建的,会及时修复一些 bug但也可能会带来新的 bug。
3. 你可以 [自行构建](#自行构建)
# 在线安装
目前支持以下平台:
1. 统信应用商店deepin
# 自行构建
> 注:
> 如果想构建 wine 版本,请添加环境变量:`export WINE=true` 。注意:不受支持。
## 方法0推荐
理论上此方法99%成功;在网络不稳定时容易失败(原因懂得都懂,构建过程已经尽量使用镜像源)
1. 如果构建 `wine` 版本请先在Linux环境中自行安装 `wine` `wine-binfmt`
2. 请安装 `docker` `docker-compose`
3. 克隆本项目:
```
git clone --recurse-submodules https://github.com/msojocs/wechat-web-devtools-linux.git
```
4. 在本地项目目录中执行如下的语句,构建开发者工具:
```
tools/build-with-docker.sh
```
5. 在本地项目目录中执行如下的语句,可以安装应用图标(非必须):
```
./tools/install-desktop-icon-node
```
之后即可通过点击应用图标启动微信开发者工具,也可以运行 `bin/wechat-devtools` 通过命令行启动
## 方法1
由于使用到 `node-gyp` `nw-gyp`,此方法会受 `python`、`node` 版本影响出现一些难以预料的异常(比如使用 `node15.0.1` 时,大部分模块构建会被忽略,但没有任何报错信息)。但是,`Docker` 构建方式会帮你处理好这些问题。
1. 如果构建 `wine` 版本请先在Linux环境中自行安装 `wine` `wine-binfmt`
2. 安装编译 `nodegit` 所需的依赖:`python2.7 python3.6及以上 libkrb5-dev gcc openssl libssh2-1-dev g++ make pkg-config`
另外 `7z` 旧版解压可能存在问题未测试最低版本号比如ubuntu16.04 自带的不行),请参考[`Dockerfile`](docker/Dockerfile)
3. 克隆本项目:
```
git clone --recurse-submodules https://github.com/msojocs/wechat-web-devtools-linux.git
```
4. 在本地项目目录中执行以下命令,构建开发者工具:
```
./tools/setup-wechat-devtools-bash
```
5. 在本地项目目录中执行如下的语句,可以安装应用图标(非必须):
```
./tools/install-desktop-icon-bash
```
之后即可通过点击应用图标启动微信开发者工具,也可以运行 `bin/wechat-devtools` 通过命令行启动
# 与其他Linux下的微信开发者工具版本区别
1. 支持最新版本并个人会持续更新在添加新的tag时actions会自动构建并上传Release
2. 核心构筑过程完全开源,可以自行修改;
3. 修复了nwjs上关于Menu的段错误确保最新版本可以正常启动 (by dragonation)
4. 在构筑过程中会重新编译node_modules确保原生模块可以在Linux上正确运行
5. 下载更新可以支持断点再续并使用了taobao国内的npm源加速下载稳定性待测试
6. 纯 Linux 支持,使用 cpp 实现模拟官方编译器。[wx-compiler](https://github.com/msojocs/wx-compiler)
# 后续计划
请参考: [处理计划](https://github.com/msojocs/wechat-devtools-linux/projects?type=beta)
# 移植相关
请参考: [移植流程记录](https://github.com/msojocs/wechat-web-devtools-linux/wiki)
# FAQ
请参考: [FAQ](docs/FAQ.MD)
# 界面截图
版本 1.05.2201240
![screenshot 1.03.2006090](res/screenshots/1.05.2201240.png)
版本 1.03.2006090
![screenshot 1.03.2006090](res/screenshots/1.03.2006090.jpg)
版本 1.02.2001191
![screenshot 1.02.2001191](res/screenshots/1.02.2001191.jpg)
# 赞赏
如果这个仓库让你感到舒适可以点个Star或者请这个 ~~菜鸡大学生~~ 打工人喝杯咖啡请尽量带上github昵称
![赞赏码](https://user-images.githubusercontent.com/20937135/154661198-93854dc1-c8ba-4c97-a7ab-9f8de26c0226.png)
# 感谢赞赏支持
时间倒序
| 赞赏人 | 赞赏 | 时间 |
|-------|---------|-----|
| returning | 10.00¥ | 2025-07-12 |
| 👍 | 10.00¥ | 2025-07-10 |
| hanwor | 20.00¥ | 2025-06-17 |
| 。。。 | 10.00¥ | 2025-03-29 |
| SakuraPuare | 20.00¥ | 2025-03-24 |
| [senseab](https://github.com/senseab) | 50.00¥ | 2024-12-21 |
| l | 10.00¥ | 2024-12-08 |
| lcurk0 | 50.00¥ | 2024-11-29 |
| [stvsl](https://github.com/stvsl) | 10.00¥ | 2024-11-26 |
| 仙人柱 | 50.00¥ | 2024-11-20 |
| [cabbage7th](https://github.com/cabbage7th) | 20.00¥ | 2024-10-06 |
| [shao4598](https://github.com/shao4598) | 50.00¥ | 2024-09-24 |
| [OWALabuy](https://github.com/OWALabuy) | 16.66¥ | 2024-08-28 |
| [wangvation](https://github.com/wangvation) | 50.00¥ | 2024-07-16 |
| 孤泉冷月 | 100.00¥ | 2024-07-12 |
| [liushuai05](https://github.com/liushuai05) | 88.00¥ | 2023-12-26 |
| LGTU | 10.00¥ | 2023-11-25 |
| [WRXinYue](https://github.com/WRXinYue) | 10 | 2023-11-09 |
| silentdoer | 50.00¥ | 2023-09-26 |
| ??? | 20 |2023-08-11 |
| Geequlim | 100 | 2023-07-12 |
| 对方正在输入 | 10 | 2023-04-28 |
| @DaqiongYang | 50 | 2023-03-29 |
| AInoob | 5 | 2023-01-30 |
| ??? | 5 | 2023-01-18 |
| 仙人柱 | 50.00¥ | 2022-08-09 |
| [guanzhengyinqin](https://github.com/guanzhengyinqin) | 20.00¥ | 2022-07-14 |
| [nsfoxer](https://github.com/nsfoxer) | 10.00¥ | 2022-06-30 |
| [chiiihc](https://github.com/chiiihc) | 5.00¥ | 2022-06-17 |
| [younland](https://github.com/younland) | 20.00¥ | 2022-06-15 |
| [chiiihc](https://github.com/chiiihc) | 5.00¥ | 2022-06-14 |
| 陈陈菌 | 10.00¥ | 2022-05-29 |
| WWW | 5.00¥ | 2022-05-26 |
| 南极の短尾猫 | 20.00¥ | 2022-05-22 |
| 猪宝的猪 | 20.00¥ | 2022-05-15 |
| finalwhy | 66.60¥ | 2022-05-09 |
| [CoryByte](https://github.com/Corybyte) | 5.00¥ | 2022-04-23 |
| [Starrah](https://github.com/Starrah) | 50.00¥ | 2022-04-12 |
| [zyk-miao](https://github.com/zyk-miao) | 5.00¥ | 2022-04-12 |
| [icepie](https://github.com/icepie) | 20.00¥ | 2022-04-08 |
| Milder | 50.00¥ | 2022-03-23 |
| . | 20.00¥ | 2022-03-21 |
| shaoxp | 5.00¥ | 2022-03-16 |
| 李喆 | 10.00¥ | 2022-03-05 |
| david | 5.00¥ | ??? |
# 免责声明
微信开发者工具版权归腾讯公司所有本项目旨在交流学习之用。如有不当之处请联系本人邮箱jiyecafe@gmail.com
## Stargazers over time
[![Stargazers over time](https://starchart.cc/msojocs/wechat-web-devtools-linux.svg)](https://starchart.cc/msojocs/wechat-web-devtools-linux)

View File

@ -1,43 +1,16 @@
#!/bin/bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
root_dir="$( cd -P "$( dirname "$SOURCE" )"/.. >/dev/null 2>&1 && pwd )"
# 数据目录处理
if [ -z $XDG_CONFIG_HOME ];then
DATA_DIR=$HOME/.config
else
DATA_DIR=$XDG_CONFIG_HOME
fi
# 获取App名称
APP_NAME=$( cat "$root_dir/package.nw/package.json" | grep -Eo 'name":".*","main' )
APP_NAME=${APP_NAME//name\":\"/}
APP_NAME=${APP_NAME//\",\"main/}
root_dir=$(cd `dirname $0`/.. && pwd -P)
export WECHAT_DEVTOOLS_DIR="$root_dir/nwjs"
export APPDATA="$DATA_DIR/$APP_NAME"
export PATH="$root_dir/node/bin:$root_dir/nwjs:$PATH"
export USERPROFILE=$APPDATA
# SNAP
# if [ -n $SNAP ];then
# if [ ! -f $DATA_DIR/nacl_irt_x86_64.nexe ];then
# # 解压
# tar -zxf $root_dir/nwjs/nacl_irt_x86_64.nexe.tar.gz -C $APPDATA/nacl_irt_x86_64.nexe
# fi
# fi
export APPDATA="$( echo ~ )/.config/wechat_devtools"
export PATH="$root_dir/wine:$root_dir/node/bin:$root_dir/nwjs:$PATH"
export USERPROFILE=$( echo ~ )
clean_cache() {
echo "清理缓存";
rm -rf "$APPDATA/WeappCache";
rm -rf "$APPDATA/WeappVendor";
rm -rf "$DATA_DIR/wechat_devtools"
}
# 检测是否有清除缓存的必要
@ -53,11 +26,11 @@ else
fi;
EXTENSION1="--load-extension=$root_dir/nwjs/package.nw/js/ideplugin"
EXTENSION2="--load-extension=$APPDATA/WeappPlugin/inspector"
EXTENSION2=--load-extension=~/.config/wechat_devtools/WeappPlugin
INSPECTOR1="--custom-devtools-frontend=file://$root_dir/package.nw/js/ideplugin/inspector" # 要加file://指明为本地文件路径
INSPECTOR2="--custom-devtools-frontend=file://$APPDATA/WeappPlugin/inspector/"
USERDATADIR="$APPDATA"
INSPECTOR2="--custom-devtools-frontend=file://$( echo ~ )/.config/wechat_devtools/WeappPlugin/inspector/"
USERDATADIR="$( echo ~ )/.config/wechat_devtools"
# "$@"参数
LANG=zh_CN.UTF-8
exec "$root_dir/nwjs/nw" $root_dir/package.nw $EXTENSION1 $INSPECTOR1 "$@"
exec "$root_dir/nwjs/nw" $root_dir/package.nw $EXTENSION2 $INSPECTOR1 "$@"

View File

@ -6,16 +6,12 @@ while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symli
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
root_dir="$( cd -P "$( dirname "$SOURCE" )"/.. >/dev/null 2>&1 && pwd )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
cd $root_dir/bin
cd $DIR/..
export WECHAT_DEVTOOLS_DIR="$root_dir/nwjs"
export APPDATA="$root_dir/nwjs"
export PATH="$root_dir/node/bin:$root_dir/nwjs:$PATH"
export WECHAT_DEVTOOLS_DIR="$DIR/../nwjs"
export APPDATA="$DIR/../nwjs"
export PATH="$DIR/../wine:$DIR/../node/bin:$DIR/../nwjs:$PATH"
node $root_dir/package.nw/js/common/cli/index.js "$@"
if [ ! -z "$APPIMAGE" ];then
# AppImage在程序退出后会删除相关程序文件导致无法启动
tail -f /etc/issue
fi
$DIR/../tools/wechat-devtools-cli "$@"

14
bin/wechat-devtools-docker Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
xhost +
docker run \
-t -i --rm \
-v /tmp/.X11-unix:/tmp/.X11-unix:rw \
-v $HOME/.Xauthority:/root/.Xauthority \
-e DISPLAY=unix:0.0 \
-e GDK_SCALE \
-e GDK_DPI_SCALE \
--privileged \
wechat-devtools /opt/wechat/devtools/bin/docker-entrypoint

84
compiler/generatemd5.js Normal file
View File

@ -0,0 +1,84 @@
const path = require('path')
const fs = require('fs')
const crypto = require('crypto')
const os = require('os')
const child_process = require('child_process')
let platform = process.argv[2]
let exeList = []
const macExeList = [
'wcc',
'wcsc',
'wcc.bin',
'wcsc.bin'
]
const winExeList = [
'wcc.exe',
'wcsc.exe'
]
if(platform === 'darwin') {
exeList = macExeList;
} else if(platform === 'win32') {
exeList= winExeList;
} else {
exeList = macExeList.concat(winExeList)
}
function generate(vendorPath) {
let dirList = fs.readdirSync(vendorPath)
let libs = {}
dirList.forEach((item) => {
if (item == 'dev' || item == 'quickstart' || item == 'beta') {
return
}
let itemPath = path.join(vendorPath, item)
let stat = fs.statSync(itemPath)
if (stat.isDirectory()) {
let fileList = fs.readdirSync(itemPath)
let md5Info = {}
fileList.forEach((file) => {
if (fs.statSync(path.join(itemPath, file)).isFile()) {
let fileData = fs.readFileSync(path.join(itemPath, file))
let md5sum = crypto.createHash('md5')
md5sum.update(fileData)
md5Info[file] = md5sum.digest('hex')
}
})
libs[item] = md5Info
}
})
let oldConfig = JSON.parse(fs.readFileSync(path.join(vendorPath, 'config.json'), 'utf8'))
for (var key in oldConfig.libs) {
// libs[key] = Object.assign({}, oldConfig.libs[key], libs[key])
oldConfig.libs[key] = Object.assign({}, oldConfig.libs[key], libs[key])
}
// let newLib = {}
// let keys = Object.keys(libs)
// keys.sort()
// for (var i = 0; i < keys.length; i++) {
// let key = keys[i]
// newLib[key] = libs[key]
// }
// 更新
oldConfig.configVersion = Date.now()
for (let key in exeList) {
let file = exeList[key]
let fileData = fs.readFileSync(path.join(vendorPath, file))
let md5sum = crypto.createHash('md5')
md5sum.update(fileData)
oldConfig[file] = md5sum.digest('hex')
}
fs.writeFileSync(path.join(vendorPath, 'config.json'), JSON.stringify(oldConfig, null, '\t'))
}
generate(__dirname)

244
compiler/nodejs/wcc Executable file

File diff suppressed because one or more lines are too long

75
compiler/nodejs/wcsc Executable file
View File

@ -0,0 +1,75 @@
#!/usr/bin/env node
const { spawn } = require("child_process");
const path = require("path");
const version = "20220222"
const args = process.argv.slice(2);
const wcscPath = path.resolve(__dirname, "./wcsc.bin")
function encode1(s) {
return encodeURI(s).replace(
/%(u[0-9A-F]{4})|(%[0-9A-F]{2})/gm,
function ($0, $1, $2) {
return ($1 && "\\" + $1.toLowerCase()) || decodeURI($2);
}
);
}
if (args.includes("-ll")) {
const wcsc = spawn(wcscPath, args, {
cwd: process.cwd(),
});
const spwanData = [],
errData = [];
wcsc.stdout.on("data", (e) => {
spwanData.push(e);
});
wcsc.stderr.on("data", (e) => {
errData.push(e);
});
wcsc.on("close", (n) => {
if (0 === n) {
const str = Buffer.concat(spwanData).toString().replace(/\\\\/g, '\\\\u005c');
const resultSplit = encode1(str).split("=");
const tempObj = {};
for (
let i = 0, total = resultSplit.length;
i < total && resultSplit[i + 1];
i += 2
) {
// a=b ---> a: b
const key = resultSplit[i];
if (key === "version") continue;
tempObj[key] = resultSplit[i + 1].replace(
/((\\x[\da-f]{2}|\\u[\da-f]{4})){1,}/gi,
function ($0, $1, $2) {
return eval('"' + $0 + '"');
}
);
}
const resultObj = {
common: tempObj.comm,
pageWxss: {},
};
for (const key in tempObj) {
if (key.endsWith(".wxss")) {
resultObj.pageWxss[key] = tempObj[key];
}
}
let result = JSON.stringify(resultObj);
String.prototype.splice = function (start, newStr) {
return this.slice(0, start) + newStr + this.slice(start + 1);
};
result = result.replace(/\\\\/g, "\\");
result = result.replace(/\\[\s\S]{1}/gi, function ($0, $1, $2) {
const c = $0 === "\\n" ? "\n" : $0[1];
return "\\u" + c.charCodeAt(0).toString(16).padStart(4, "0")
})
process.stdout.write(result);
}
});
} else {
spawn(wcscPath, args, {
cwd: process.cwd(),
stdio: "inherit",
});
}

BIN
compiler/wcc Normal file

Binary file not shown.

View File

@ -0,0 +1,93 @@
const util = require('./util')
const path = require('path')
let wcc
try {
wcc = require('./src/wcc')
} catch (err) {
console.error('wcc', err)
// wcc = require('./build/Release/wcc.node')
}
const fs = util.fs
exports = async function (options) {
if (!options) throw Error('options is required')
const lazyload = !!options.lazyloadConfig
options = Object.assign(
{
files: [], // FILES
contents: [],
replaceContent: {},
verbose: false,
debug: false, // -d
debugWXS: false, // -ds
showNewTree: false,
isPlugin: false,
addTestAttre: false,
independent: false,
genfuncname: '$gwx', // -gn
isCut: false, // --split
cwd: process.cwd,
debug: false,
lazyload, // -ll
lazyloadConfig: '',
},
options,
)
return new Promise(async (resolve, reject) => {
let st = Date.now()
// 获取文件内容
if (!options.contents.length) {
const tasks = options.files.map((file) => {
if (typeof options.replaceContent[file] === 'string') {
return options.replaceContent[file]
}
return fs.readFile(path.resolve(options.cwd, file), 'utf8')
})
options.contents = await Promise.all(tasks) || []
}
// console.warn('wcc get files', Date.now() - st, options.contents)
let result
try {
// console.warn('final options:', options);
result = wcc(options)
// console.warn('wcc result', result)
} catch(errmsg) {
reject(new Error(errmsg))
return
}
console.log('wcc get compile', Date.now() - st)
if (options.output) {
const output = path.resolve(options.cwd, options.output)
const dir = path.dirname(output)
if (lazyload) {
// lazyload 为 true时wcc 返回值是个对象, 需要序列化一下
result = JSON.stringify(result)
}
try {
await fs.stat(dir)
} catch (e) {
await fs.mkdir(dir, {
recursive: true,
})
}
await fs.writeFile(output, result, 'utf8')
}
console.warn('wcc get output', Date.now() - st)
resolve(result)
})
}
Object.defineProperty(exports, 'version', {
get() {
return wcc.version
},
})
module.exports = exports

View File

@ -0,0 +1,25 @@
{
"name": "miniprogram-wcc",
"version": "0.0.1",
"description": "WCC node C++ addon",
"main": "index.js",
"scripts": {
"install": "node-gyp-build",
"rebuild": "node-gyp rebuild",
"build:dist": "node scripts/build",
"build": "node-gyp build",
"test": "node ./test/index",
"format": "prettier *.js test/*.js scripts/*.js --write"
},
"author": "coverguo",
"license": "MIT",
"dependencies": {
"node-gyp-build": "^4.2.1"
},
"devDependencies": {
"eustia-module": "^1.21.2",
"licia": "^1.21.2",
"ncp": "^2.0.0",
"node-gyp": "^7.0.0"
}
}

View File

@ -0,0 +1,30 @@
const path = require('path')
const vm = require('vm')
const glob = require('glob')
const unescapeJs = require('unescape-js')
const wcc = require('./wcc')
module.exports = {
wxmlToJs(rootPath) {
// wcc 编译器需要完整的 wxml 文件列表
const files = glob.sync('**/*.wxml', {
cwd: rootPath,
nodir: true,
dot: true,
ignore: ['node_modules/**/*.wxml'],
})
const wxsFiles = glob.sync('**/*.wxs', {
cwd: rootPath,
nodir: true,
dot: true,
ignore: ['node_modules/**/*.wxs'],
})
const compileResult = wcc(rootPath, files.map(file => file.substring(0, file.length - 5)), wxsFiles)
return `
${compileResult};
return $gwx;
`
},
}

File diff suppressed because one or more lines are too long

1134
compiler/wcc_node/util.js Normal file

File diff suppressed because it is too large Load Diff

BIN
compiler/wcsc Normal file

Binary file not shown.

View File

@ -0,0 +1,91 @@
const util = require('./util')
const path = require('path')
const fs = util.fs
let wcsc
try {
wcsc = require('./src/wcsc')
} catch (err) {
console.error('wcsc', err)
// wcsc = require('./build/Release/wcsc.node')
}
function tranWcscResultToObject(resultStr) {
const resultArr = resultStr.split('=')
const result = {}
for (let i = 0, len = resultArr.length; i < len && resultArr[i + 1]; i += 2) {
result[resultArr[i]] = resultArr[i + 1]
}
return result
}
exports = async function (options) {
if (!options) throw Error('options is required')
// avoid undefined or null
if (typeof options.subPackage !== 'string') {
delete options.subPackage
}
if (typeof options.lazyload !== 'boolean') {
delete options.lazyload
}
options = Object.assign(
{
files: [],
contents: [],
pageCount: 0,
cwd: process.cwd,
replaceContent: {},
debug: false,
classPrefix: '',
lazyload: false,
},
options,
)
if (!options.contents.length) {
const tasks = options.files.map((file) => {
if (typeof options.replaceContent[file] === 'string') {
return options.replaceContent[file]
}
return fs.readFile(path.resolve(options.cwd, file), 'utf8')
})
options.contents = await Promise.all(tasks) || []
}
let wcscResult
try {
console.warn('wcsc options', options)
wcscResult = await wcsc(options)
console.warn('wcsc ok')
} catch (errmsg) {
throw new Error(errmsg)
}
const result = options.lazyload ? wcscResult : tranWcscResultToObject(wcscResult)
if (options.output) {
const output = path.resolve(options.cwd, options.output)
const dir = path.dirname(output)
try {
await fs.stat(dir)
} catch (e) {
await fs.mkdir(dir, {
recursive: true,
})
}
await fs.writeFile(output, JSON.stringify(result, null, 2), 'utf8')
}
return result
}
Object.defineProperty(exports, 'version', {
get() {
return wcsc.version
},
})
module.exports = exports

View File

@ -0,0 +1,30 @@
{
"name": "miniprogram-wcsc",
"version": "0.0.2",
"description": "WXSS node C++ addon",
"main": "index.js",
"scripts": {
"install": "node-gyp-build",
"rebuild": "node-gyp rebuild",
"build:dist": "node scripts/build",
"build": "node-gyp build",
"test": "node ./test/index",
"format": "prettier *.js test/*.js scripts/*.js --write"
},
"repository": {
"type": "git",
"url": "git@git.code.oa.com:redhoodsu/wxss.git"
},
"author": "redhoodsu",
"license": "MIT",
"dependencies": {
"node-gyp-build": "^4.2.1"
},
"devDependencies": {
"eustia-module": "^1.21.2",
"licia": "^1.21.2",
"ncp": "^2.0.0",
"node-gyp": "^7.0.0",
"prettier": "^2.3.1"
}
}

View File

@ -0,0 +1,58 @@
const path = require('path')
const vm = require('vm')
const glob = require('glob')
const unescapeJs = require('unescape-js')
const wcc = require('./wcc')
const wcsc = require('./wcsc')
module.exports = {
wxmlToJs(rootPath) {
// wcc 编译器需要完整的 wxml 文件列表
const files = glob.sync('**/*.wxml', {
cwd: rootPath,
nodir: true,
dot: true,
ignore: ['node_modules/**/*.wxml'],
})
const wxsFiles = glob.sync('**/*.wxs', {
cwd: rootPath,
nodir: true,
dot: true,
ignore: ['node_modules/**/*.wxs'],
})
const compileResult = wcc(rootPath, files.map(file => file.substr(0, file.length - 5)), wxsFiles)
return `
${compileResult};
return $gwx;
`
},
wxssToJs(rootPath) {
// wcsc 编译器需要完整的 wxss 文件列表
const files = glob.sync('**/*.wxss', {
cwd: rootPath,
nodir: true,
dot: true,
ignore: ['node_modules/**/*.wxss'],
})
const compileResult = wcsc(rootPath, files.map(file => file.substr(0, file.length - 5)))
// 拼装 wxss map 字符串
let wxssMap = ''
Object.keys(compileResult).forEach(key => {
if (path.extname(key) === '.wxss') {
wxssMap += `'${key}': ${unescapeJs(compileResult[key])},`
}
})
return `
${unescapeJs(compileResult.comm)};
var wxssMap = { ${wxssMap} };
return function (filePath) {
return wxssMap[filePath];
};
`
},
}

View File

@ -0,0 +1,143 @@
const { spawn, spawnSync } = require('child_process')
const fs = require('fs')
const path = require('path')
const os = require('os')
const { throws } = require('assert')
/**
* 获取 wxss 编译器路径
*/
let wxssParserPath = ''
function getWXSSParsePath() {
if (wxssParserPath) return wxssParserPath
const fileName = process.platform === 'darwin' ? '../bin/mac/wcsc' : process.platform === 'linux' ? '../bin/linux/wcsc' : '../bin/windows/wcsc.exe'
wxssParserPath = path.join(__dirname, fileName)
// 尝试修改权限
try {
fs.chmodSync(wxssParserPath, 0o777)
} catch (err) {
// ignore
}
return wxssParserPath
}
/**
* 获取完整文件列表
*/
function getAllFiles(rootPath, files) {
const ret = []
let compWxssNum = 0
for (let i = 0, len = files.length; i < len; i++) {
const file = files[i]
let fileJson = null
try {
fileJson = require(path.join(rootPath, `${file}.json`))
} catch(err) {
// ignore
}
if (fileJson) {
// 组件 wxss
compWxssNum++
ret.unshift(`${file}.wxss`)
} else {
ret.push(`${file}.wxss`)
}
}
return {
list: ret,
compWxssNum,
}
}
/**
* 编译 wxss js
*/
async function wxssToJS(options) {
// 创建临时目录
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'wcsc_'))
// 判断是否replace,是写入replace,否则拷贝文件到临时目录
for(let file of options.files){
if (typeof options.replaceContent[file] === 'string') {
// 写入替换内容
fs.mkdirSync(path.dirname(path.resolve(tmp, file)), {recursive:true})
fs.writeFileSync(path.resolve(tmp, file), options.replaceContent[file])
}else{
// 复制原文件
fs.mkdirSync(path.dirname(path.resolve(tmp, file)), {recursive:true})
fs.copyFileSync(path.resolve(options.cwd, file), path.resolve(tmp, file))
}
}
// 使用临时目录执行wcc
options.cwd = tmp
let rootPath = options.cwd, files=options.files
// files = getAllFiles(rootPath, files)
const args = ['-db', '-pc', String(options.pageCount)].concat(files)
const wxssParserPath = getWXSSParsePath()
// console.warn('wcsc args: ', args)
// const wcsc = spawnSync(wxssParserPath, args, { cwd: rootPath })
return new Promise((resolve, reject)=>{
const wcsc = spawn(wxssParserPath, args, {
cwd: rootPath,
});
const spwanData = [],
errData = [];
wcsc.stdout.on("data", (e) => {
spwanData.push(e);
});
wcsc.stderr.on("data", (e) => {
errData.push(e);
});
wcsc.on("close", (code) => {
console.warn('close', new Date().getTime()/1000)
if (code === 0) {
let result = Buffer.concat(spwanData).toString();
if(options.lazyload){
result = result.split('=')
let funcList = {}
for (let i = 0, len = result.length; i < len && result[i + 1]; i += 2) {
funcList[result[i]] = result[i + 1]
.replace(
/[^\\]((\\x[\da-f]{2}|\\u[\da-f]{4})){1,}/gi,
function ($0, $1, $2) {
return eval('"' + $0 + '"');
}
)
.replace(/\\[\s\S]{1}/gi, function ($0, $1, $2) {
// console.log($0, $1)
const c = $0 === "\\n" ? "\n" : $0[1];
return c
})
}
const t = funcList
funcList = {
common: t.comm,
pageWxss: {}
}
for(let key in t){
if(key.endsWith('.wxss')){
funcList.pageWxss[key] = t[key]
}
}
result = funcList
}
resolve(result)
} else {
throw new Error(`编译 .wxss 文件错误(${wcsc.status})${wcsc.stderr.toString()}`)
}
})
})
}
module.exports = wxssToJS

1134
compiler/wcsc_node/util.js Normal file

File diff suppressed because it is too large Load Diff

15
compiler/wine/wcc Normal file
View File

@ -0,0 +1,15 @@
#!/bin/sh
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [[ $@ =~ -ll ]];then
wine "$DIR/wcc.exe" "$@"
else
"$DIR/wcc.bin" "$@"
fi

16
compiler/wine/wcsc Normal file
View File

@ -0,0 +1,16 @@
#!/bin/sh
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [[ $@ =~ -ll ]];then
wine "$DIR/wcsc.exe" "$@"
else
"$DIR/wcsc.bin" "$@"
fi

View File

@ -1 +0,0 @@
WX_COMPILER_VERSION=v0.1.5

View File

@ -1 +0,0 @@
1.06.2504010,6a579ac93961fbfe04774b26f0c85b78

6
conf/node.json Normal file
View File

@ -0,0 +1,6 @@
{
"description": "此处版本应与nwjs使用的node版本一致",
"version": "16.1.0",
"url": "https://npm.taobao.org/mirrors/node/v${version}/node-v${version}-linux-x64.tar.gz",
"url-global": "https://nodejs.org/download/release/v${version}/node-v${version}-linux-x64.tar.gz"
}

View File

@ -1,6 +0,0 @@
# 此处版本应与nwjs使用的node版本一致
if [ -z $NODE_VERSION ];then
NODE_VERSION=16.11.0
fi
NODE_URL_CN=https://npmmirror.com/mirrors/node/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz
NODE_URL_GLOBAL=https://nodejs.org/download/release/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz

View File

@ -1,9 +1,7 @@
{
"version": "0.55.0",
"version": "0.53.1",
"url": "https://oss.npmmirror.com/dist/nwjs/v${version}/nwjs-sdk-v${version}-linux-x64.tar.gz",
"url-global": "https://dl.nwjs.io/v${version}/nwjs-sdk-v${version}-linux-x64.tar.gz",
"nw2node": {
"0.61.0": "17.3.0",
"0.60.0": "17.3.0",
"0.59.1": "17.1.0",
"0.59.0": "17.1.0",

17
docker-compose.yml Normal file
View File

@ -0,0 +1,17 @@
version: "3"
services:
wechat_devtools:
# image: node:16.13.1
build:
context: ./docker
dockerfile: Dockerfile
volumes:
- .:/workspace
- ./cache/.npm:/root/.npm
environment:
- ACTION=${ACTION_MODE:-false}
# - https_proxy=${https_proxy:-}
- NO_WINE=${NO_WINE:-true}
- LOCAL_USER_ID=${UID:-1000}
entrypoint: /workspace/docker/docker-entrypoint
userns_mode: "host"

View File

@ -1,42 +1,20 @@
FROM ubuntu:16.04
FROM node:16.13.1
WORKDIR /workspace
RUN echo "\
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\
# deb-src https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free
"> /etc/apt/sources.list && apt-get clean && \
apt-get update && set -eux; \
apt-get update; \
apt-get install -y gosu libx11-dev libxkbfile-dev p7zip-full; \
rm -rf /var/lib/apt/lists/*; \
# verify that the binary works
gosu nobody true;
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
mkdir -p /build_temp/python36 /build_temp/nodejs && \
apt update && \
apt install -y binutils software-properties-common gcc g++ \
gconf2 libxkbfile-dev p7zip-full make libssh2-1-dev libkrb5-dev wget curl \
openssl pkg-config build-essential && \
cd /build_temp/python36 && \
apt-get install -y aptitude &&\
aptitude -y install gcc make zlib1g-dev libffi-dev libssl-dev &&\
mkdir -p test && cd test &&\
wget http://npmmirror.com/mirrors/python/3.6.5/Python-3.6.5.tgz &&\
tar -xvf Python-3.6.5.tgz &&\
chmod -R +x Python-3.6.5 &&\
cd Python-3.6.5/ &&\
./configure &&\
aptitude -y install libffi-dev libssl-dev &&\
make && make install &&\
cd /build_temp/nodejs &&\
wget https://deb.nodesource.com/setup_16.x &&\
chmod +x setup_16.x &&\
./setup_16.x &&\
apt-get install -y nodejs &&\
rm -rf /build_temp && \
apt install -y gosu unzip python && \
gosu nobody true && \
useradd -s /bin/bash -m user
RUN apt remove -y p7zip p7zip-full p7zip-rar &&\
rm -rf /opt/7z && \
mkdir -p /opt/7z && \
cd /opt/7z && \
wget https://www.7-zip.org/a/7z2107-linux-x64.tar.xz && \
tar -xJf 7z2107-linux-x64.tar.xz && \
ln -s 7zz 7z
ENV PATH=/opt/7z:$PATH
WORKDIR /workspace

33
docker/docker-entrypoint Executable file
View File

@ -0,0 +1,33 @@
#!/bin/bash
if [ "$(id -u)" -eq '0' ]
then
USER_ID=${LOCAL_USER_ID:-9001}
usermod -u ${USER_ID} -g ${USER_ID} node > /dev/null 2>&1
chown -R ${USER_ID}:${USER_ID} /workspace > /dev/null 2>&1
ACTION_MODE=$( export ACTION_MODE )
if [ $ACTION_MODE!='true' ]; then
npm set registry https://r.npm.taobao.org # 注册模块镜像
npm set disturl https://npm.taobao.org/dist # node-gyp 编译依赖的 node 源码镜像
## 以下选择添加
npm set sass_binary_site https://npm.taobao.org/mirrors/node-sass # node-sass 二进制包镜像
npm set electron_mirror https://npm.taobao.org/mirrors/electron/ # electron 二进制包镜像
npm set puppeteer_download_host https://npm.taobao.org/mirrors # puppeteer 二进制包镜像
npm set chromedriver_cdnurl https://npm.taobao.org/mirrors/chromedriver # chromedriver 二进制包镜像
npm set operadriver_cdnurl https://npm.taobao.org/mirrors/operadriver # operadriver 二进制包镜像
npm set phantomjs_cdnurl https://npm.taobao.org/mirrors/phantomjs # phantomjs 二进制包镜像
npm set selenium_cdnurl https://npm.taobao.org/mirrors/selenium # selenium 二进制包镜像
npm set node_inspector_cdnurl https://npm.taobao.org/mirrors/node-inspector # node-inspector 二进制包镜像
# npm cache clean --force # 清空缓存
fi
# ls -l
# cat /etc/passwd
exec gosu node docker/entrypoint
fi
echo "nothing"
# exec /usr/local/bin/gosu user entrypoint

View File

@ -1,7 +1,7 @@
#!/bin/bash
set -e # 命令出错就退出
trap 'catchError $LINENO $BASH_COMMAND' ERR # 捕获错误情况
trap 'catchError $LINENO $BASH_COMMAND' SIGHUP SIGINT SIGQUIT EXIT # 捕获错误情况
catchError() {
exit_code=$?
@ -24,11 +24,7 @@ python --version
echo "docker node version: $( node --version )"
cd /workspace
export HOME="/tmp/home"
mkdir -p /tmp/home
rm -rf node nwjs package.nw
#
# exec ./tools/rebuild-node-modules 0.53.1
./tools/setup-wechat-devtools.sh
# chmod -R 777 tmp node nwjs package.nw
# ls -l package.nw
exec ./tools/setup-wechat-devtools-bash

View File

@ -13,7 +13,7 @@
文件路径: `package.nw/core.wxvpkg.ext/284af385b4ef6206861fea66a2452277.js`
定位字符串:`nw.Window.open`
在回调函数中添加:
```javascript
```
Object.keys(window).forEach(key=>{
if(!e.window[key]){
/*没有就添加*/

View File

@ -15,7 +15,7 @@
3. 为什么自己构筑或更新后的运行时编辑器和调试器是一片空白?
检查一下`conf/node_info`和`conf/nwjs.json`里面定义的版本是否与最新微信官方开发者工具的一致。如果有修改版本号的情况则在更新完这两个JSON文件后执行`tools/update-node`和`tools/update-nwjs`命令。然后删除`package.nw`目录重新执行`tools/setup-wechat-devtools`命令感谢ReggieCai31的issue和解决方案
检查一下`conf/node.json`和`conf/nwjs.json`里面定义的版本是否与最新微信官方开发者工具的一致。如果有修改版本号的情况则在更新完这两个JSON文件后执行`tools/update-node`和`tools/update-nwjs`命令。然后删除`package.nw`目录重新执行`tools/setup-wechat-devtools`命令感谢ReggieCai31的issue和解决方案
4. 为什么自己构筑的运行时加载项目时会卡住?

View File

@ -1,5 +1,5 @@
NodeJS v16.x下安装nodegit
1. `apt-get install -y python2 python3 libkrb5-dev gcc openssl libssh2-1-dev g++ make pkg-config`
1. `apt-get install -y python2 python3 libkrb5-dev gcc openssl libssh2-1-dev g++ make`
2. `npm install nodegit`
python >= 3.6
@ -23,7 +23,7 @@ apt update
apt-get install -y python2 python3 libkrb5-dev gcc openssl libssh2-1-dev g++ make
echo "start"
npm install nodegit --registry=http://registry.npmmirror.com/
npm install nodegit --registry=https://registry.npm.taobao.org
npm uninstall nodegit
```

View File

@ -1,3 +0,0 @@
## 需要额外审查的接口
block-devices

View File

@ -1,160 +0,0 @@
## 设置位置
菜单栏 -> 设置 -> 外观设置 -> 主题
## 定位点
1. F12打开调试器
2. 进入Elements标签
3. 变更设置,发现`body`标签的`class`属性发生变化
4. 变化:深色添加`dark`类,浅色则移除
5. 完整class: `class="with-vscode-font-patch --with-theming dark"`
可以将这三个`class`作为定位点入手处理
## 定位具体代码
使用`--with-theming`
在解压后的`core.wxvpkg`中搜索 `grep -lr '\-\-with\-theming' .`
得到两个位置:
1. (exports.ThemingEnabledClass = "--with-theming");
2. t.body.classList.add("--with-theming"),
第二个大概率是目标了;
正好第二个后面紧接着`this.themeChange()`,好了,看命名可以确定是这个了
```javascript
this.setup().finally(() => {
var e, t;
document.body.classList.add("with-vscode-font-patch"),
null === (e = this.ourDocumentRef) ||
void 0 === e ||
e.body.classList.add("with-vscode-font-patch"),
null === (t = this.ourDocumentRef) ||
void 0 === t ||
t.body.classList.add("--with-theming"),
this.themeChange();
});
```
## 分析实现
`themeChange` 函数:
```javascript
themeChange(e) {
var t, n, i, s, o;
"dark" ===
(null !==
(n =
null === (t = (e = e || this.props).settings.appearance) ||
void 0 === t
? void 0
: t.theme) && void 0 !== n
? n
: "dark")
? null === (i = this.ourDocumentRef) ||
void 0 === i ||
i.body.classList.add("dark")
: null === (s = this.ourDocumentRef) ||
void 0 === s ||
s.body.classList.remove("dark"),
null === (o = this.ourDocumentRef) ||
void 0 === o ||
o.body.classList.add("with-vscode-font-patch");
}
```
命名处理:
```javascript
themeChange(config) {
var appearance, theme, docRef1, docRef2, docRef3;
console.warn('themeChange', config);
"dark" ===
(null !==
(theme =
null === (appearance = (config = config || this.props).settings.appearance) ||
void 0 === appearance
? void 0
: appearance.theme) && void 0 !== theme
? theme
: "dark")
? null === (docRef1 = this.ourDocumentRef) ||
void 0 === docRef1 ||
docRef1.body.classList.add("dark")
: null === (docRef2 = this.ourDocumentRef) ||
void 0 === docRef2 ||
docRef2.body.classList.remove("dark"),
null === (docRef3 = this.ourDocumentRef) ||
void 0 === docRef3 ||
docRef3.body.classList.add("with-vscode-font-patch");
}
```
审查代码,得出大致逻辑:
1. 有传入配置使用配置;没有传入,则使用当前配置
2. 获取主题配置
3. 有主题配置则直接使用,否则默认`dark`
4. 主题配置是`dark`添加`dark`类,否则移除
很明显,`theme`的值只有`dark`和`非dark`的区别(根据`console.warn`的结果看,另一个值是`white`,当然这不重要)
此处没有跟随系统的判断,应该在别处。
另外,根据`console.warn`可以知道跟随系统也会触发`themeChange`。
好的,我们卡住了,需要另辟蹊径。
## 另辟蹊径
简述: 通过字符串定位触发位置
1. 搜索`应用(除调试器外)的主题设置`,即`grep -lr '应用(除调试器外)的主题设置' .`
2. 获取对应标志`SETTING_THEME_DESC`
3. 搜索`SETTING_THEME_DESC`,即`grep -lr 'SETTING_THEME_DESC' .`
4. 三个结果,两个翻译,确定了
关键代码:
```javascript
t.createElement(
"div",
{ className: "uiv2-padding" },
t.createElement(
"label",
{
className: "uiv2-radio",
onClick: this.handleAutoDetectThemeClick,
},
t.createElement("i", {
className: r
? "uiv2-radio-icon-o"
: "uiv2-radio-icon",
}),
t.createElement(
"span",
{ className: "uiv2-radio-text" },
o.config.FOLLOW_SYSTEM
)
)
)
```
可以看到处理“跟随系统”的函数是`handleAutoDetectThemeClick`
函数内执行`this.props.updateIDESetting("appearance", "autoDetectTheme", !0);`
通过搜索`autoDetectTheme`,找到一个跟主题控制相关的实现
```javascript
constructor() {
(this.onDidOSThemeChange = () => {
const o = s.OSThemeController.shared.getCurrentTheme();
if (o === s.OSTheme.Unknown) return;
const n = o === s.OSTheme.Dark ? "dark" : "white";
this._enabledThemeAutoDetect &&
e.dispatch(t.updateIDESetting("appearance", "theme", n)),
this._enabledDevtoolsThemeAutoDetect &&
e.dispatch(
t.updateIDESetting("appearance", "devtoolsTheme", n)
);
}),
(this._enabledThemeAutoDetect = !1),
(this._enabledDevtoolsThemeAutoDetect = !1),
this.registerListeners();
}
```
好了,去看看`OSThemeController`就知道怎么回事了

View File

@ -1,15 +0,0 @@
#!/bin/bash
# 由于var目录空间大小不够故修改multipass镜像的存储位置
# https://github.com/canonical/multipass/pull/1789#issuecomment-704991752
# as root
snap stop multipass
snap connect multipass:removable-media # for /mnt /media https://snapcraft.io/docs/removable-media-interface
snap connect multipass:all-home # for /home/* https://snapcraft.io/docs/home-interface
mkdir -p /mnt/disk2/multipass
mkdir /etc/systemd/system/snap.multipass.multipassd.service.d/
tee /etc/systemd/system/snap.multipass.multipassd.service.d/override.conf <<EOF
[Service]
Environment=MULTIPASS_STORAGE=/mnt/disk2/multipass
EOF
systemctl daemon-reload
snap start multipass

6
package-lock.json generated Normal file
View File

@ -0,0 +1,6 @@
{
"name": "wechat-devtools",
"lockfileVersion": 2,
"requires": true,
"packages": {}
}

1
package.json Normal file
View File

@ -0,0 +1 @@
{}

144
readme.md Normal file
View File

@ -0,0 +1,144 @@
<div align="center">
<img src="./res/icons/wechat-devtools.png" height="100px" width="100px"/>
<h3>微信开发者工具 Linux版</h3>
<br>
----
[![Node.js CI](https://github.com/msojocs/wechat-devtools-linux/actions/workflows/release.yml/badge.svg)](https://github.com/msojocs/wechat-devtools-linux/actions/workflows/release.yml)
[![wechat-tools](https://img.shields.io/badge/wechat--devtools-1.05.2201240-yellow)](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
[![nwjs](https://img.shields.io/badge/nwjs-0.53.1-green)](https://nwjs.io/downloads/)
[![node](https://img.shields.io/badge/node-16.1.0-orange)](https://nodejs.org/en/)
这是微信开发者工具 Linux版
图标作者[dragonation](https://github.com/dragonation), 由于是基于其项目修改,未经授权。
<br>
</div>
# 项目说明
本项目是一个完整的搭建Linux下可用的“微信开发者工具”的脚本和工具集
用于在Linux下的GNOME桌面上搭建可以持续更新和使用的“微信开发者工具”。
本项目修改自https://github.com/dragonation/wechat-devtools/
# 项目地址
* https://github.com/msojocs/wechat-devtools-linux
# 进度
当前工具可以在Linux上构筑最新版1.05.2201240截止时间2022年1月26日支持CLI模式现已经有Docker支持仅用于测试
另现在已经可以直接在设置界面里面修改字体,手工输入字体名称就可以。
# 功能测试记录
[测试记录](docs/Features.MD)
注意:
此测试仅在修复某项功能时测试通过,并不代表阁下在使用时是正常可用的;因为我没有精力在每个版本发布前进行一次测试。
在阁下遇到任何无法解决的问题时,请新建一个 [issue](https://github.com/msojocs/wechat-web-devtools-linux/issues/new/choose)
# 使用方法
可以在本项目的[Release](https://github.com/msojocs/wechat-devtools-linux/releases)中,寻找已经构筑好了的`.tar.gz`包,下载解压后,运行其中的`bin/wechat-devtools`即可运行;也可以下载`AppImage`授予可执行权限后,直接运行。
# 系统要求
* 基于Linux的桌面系统首选GNOME其他的桌面环境可能会有问题未测试
* 非兼容版对glibc和libstdc++有一定的版本要求glibc的版本要求2.3libstdc++的版本要求3.4.26发布包里已经预编译了的Linux原生Node模块有这个依赖。兼容版本对这两个系统库的要求则较低如果发现非兼容版本运行有问题可以考虑尝试切换到兼容版本
* 如果你下载的是`wine`版本,那么你需要安装有`wine` `wine-binfmt`支持建议版本在5.0以上,低版本可能会存在有问题
# CLI支持
在项目的`bin`目录中有`wechat-devtools-cli`命令是微信开发者工具的命令行支持Linux版本。相关资料可以在[微信CLI命令行V2](https://developers.weixin.qq.com/miniprogram/dev/devtools/cli.html)上找到。
# 自行构建
> 注:
> 如果不想使用`wine`,请添加环境变量:`export NO_WINE=true`,但是稳定性未测试
## 方法0推荐
理论上此方法99%成功;在网络不稳定时容易失败(原因懂得都懂,构建过程已经尽量使用镜像源)
1. 如果构建`wine`版本请先在Linux环境中自行安装`wine` `wine-binfmt`
2. 请安装`docker` `docker-compose`
3. 克隆本项目:
```
git clone https://github.com/msojocs/wechat-devtools-linux.git
```
4. 在本地项目目录中执行如下的语句,构筑开发者工具:
```
docker-compose up
```
5. 在本地项目目录中执行如下的语句,可以安装应用图标(非必须):
```
./tools/install-desktop-icon-node
```
之后即可通过点击应用图标启动微信开发者工具,也可以运行`bin/wechat-devtools`通过命令行启动
## 方法1
由于使用到`node-gyp` `nw-gyp`,此方法会受`python``node`版本影响出现一些难以预料的异常(比如使用`node15.0.1`时,大部分模块构建会被忽略,但没有任何报错信息)。但是,`Docker`构建方式会帮你处理好这些问题。
1. 如果构建`wine`版本请先在Linux环境中自行安装`wine` `wine-binfmt`
2. 安装编译`nodegit`所需的依赖:`python2 libkrb5-dev gcc openssl libssh2-1-dev g++ make`
3. 请安装nodejs并配置到PATH环境变量中版本不限;
4. 克隆本项目:
```
git clone https://github.com/msojocs/wechat-devtools-linux.git
```
4. 在本地项目目录中执行如下的语句,构筑开发者工具:
```
./tools/setup-wechat-devtools-bash
```
5. 在本地项目目录中执行如下的语句,可以安装应用图标(非必须):
```
./tools/install-desktop-icon-node
```
之后即可通过点击应用图标启动微信开发者工具,也可以运行`bin/wechat-devtools`通过命令行启动
# 与其他Linux下的微信开发者工具版本区别
1. 支持最新版本并个人会持续更新在添加新的tag时actions会自动构建并上传Release
2. 核心构筑过程完全开源,可以自行修改;
3. 修复了nwjs上关于Menu的段错误确保最新版本可以正常启动 (by dragonation)
4. 在构筑过程中会重新编译node_modules确保原生模块可以在Linux上正确运行
5. 下载更新可以支持断点再续并使用了taobao国内的npm源加速下载稳定性待测试
6. 可以不需要`wine`环境的支持
# 后续计划
1. [处理计划](https://github.com/msojocs/wechat-devtools-linux/projects?type=beta)
# FAQ
[GO](docs/FAQ.MD)
# 界面截图
版本 1.05.2201240
![screenshot 1.03.2006090](res/screenshots/1.05.2201240.png)
版本 1.03.2006090
![screenshot 1.03.2006090](res/screenshots/1.03.2006090.jpg)
版本 1.02.2001191
![screenshot 1.02.2001191](res/screenshots/1.02.2001191.jpg)
# 免责声明
微信开发者工具版权归腾讯公司所有本项目旨在交流学习之用。如有不当之处请联系本人邮箱jiyecafe@gmail.com
# 赞赏
如果这个仓库让你感到舒适,可以请我喝杯咖啡:
![赞赏码](https://user-images.githubusercontent.com/20937135/154661198-93854dc1-c8ba-4c97-a7ab-9f8de26c0226.png)

View File

@ -10,5 +10,4 @@ Type=Application
Terminal=false
StartupWMClass=wechat_devtools
Actions=
MimeType=x-scheme-handler/wechatide
X-AppImage-Version=v0
MimeType=x-scheme-handler/wechatide

51
res/aur/PKGBUILD Normal file → Executable file
View File

@ -3,40 +3,36 @@
# Contributor: bruceutut <zttt183525594@gmail.com>
# 方法参考
# https://github.com/msojocs/wechat-web-devtools-linux
# https://github.com/jiyeme/wechat-devtools
# https://github.com/dragonation/wechat-devtools
# https://github.com/cytle/wechat_web_devtools
_wechat_devtools_ver="1.06.2208010"
# https://servicewechat.com/wxa-dev-logic/download_redirect?type=x64&from=mpwiki&download_version=1052203030&version_type=1
_wechat_devtools_url="https://servicewechat.com/wxa-dev-logic/download_redirect?type=x64&from=mpwiki&download_version=${_wechat_devtools_ver//\./}&version_type=1"
# _wechat_devtools_url="https://dldir1.qq.com/WechatWebDev/release/p-ae42ee2cde4d42ee80ac60b35f183a99/wechat_devtools_1.05.2201240_x64.exe"
_wechat_devtools_md5="2785d569b88d72a8e238d438d92faf44"
_wechat_devtools_ver="1.05.2201240"
_wechat_devtools_url="https://dldir1.qq.com/WechatWebDev/release/p-ae42ee2cde4d42ee80ac60b35f183a99/wechat_devtools_1.05.2201240_x64.exe"
_wechat_devtools_md5="85552bae33e98eb186c5068419efce03"
_wechat_devtools_exe="wechat_devtools_${_wechat_devtools_ver}_x64.exe"
_nwjs_ver="0.55.0"
_nwjs_ver="0.53.1"
_install_dir="/opt/wechat-devtools"
_node_version="16.11.0"
_node_version="16.1.0"
pkgname=wechat-devtools
pkgver="${_wechat_devtools_ver}" # 主版本号
pkgrel=1 # 修订版本号release
pkgrel=15 # 次版本号release
epoch=2 # 大版本迭代强制更新(维护者变更,尽量不用)
pkgdesc="WeChat Devtools For Linux. "
arch=("x86_64")
url="https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html"
license=('unknown')
depends=('gconf' 'libxkbfile')
makedepends=('p7zip' 'python2' 'openssl' 'gcc' 'make' 'libssh2' 'krb5' 'wget')
makedepends=('p7zip' 'python2' 'openssl' 'gcc' 'make' 'libssh2' 'krb5')
# compiler 用于可视化,以及编译
source=("nwjs-v${_nwjs_ver}.tar.gz::https://npm.taobao.org/mirrors/nwjs/v${_nwjs_ver}/nwjs-sdk-v${_nwjs_ver}-linux-x64.tar.gz"
"${_wechat_devtools_exe}::${_wechat_devtools_url}"
"node-v${_node_version}.tar.gz::https://npm.taobao.org/mirrors/node/v${_node_version}/node-v${_node_version}-linux-x64.tar.gz"
"compiler.tar.gz::https://github.rc1844.workers.dev/msojocs/wechat-web-devtools-linux/releases/download/v1.05.2203070-6/compiler.tar.gz"
"compiler.tar.gz::https://download.fastgit.org/msojocs/wechat-devtools-linux/releases/download/v0.19/compiler.tar.gz"
"wechat-devtools.desktop"
"logo.svg"
"wechat-devtools"
"wechat-devtools-cli"
"fix-cli.sh"
"fix-menu.sh"
"fix-core.sh"
@ -45,11 +41,11 @@ source=("nwjs-v${_nwjs_ver}.tar.gz::https://npm.taobao.org/mirrors/nwjs/v${_nwjs
"wxvpkg_pack.js"
"wxvpkg_unpack.js"
"fix-other.sh")
md5sums=(ac7680788544c457daee11aaf69798fe # nwjs
md5sums=(b6f49803c51d0abacca2d1e566c7fe19 # nwjs
"${_wechat_devtools_md5}"
4d14589085ebbf79ce504dc27330d33b # nodejs
7d78f10d04fff0b525df493d95847b37 # compiler
4d3f5273be80a74741c841fcfa4185d3 # desktop
2280bfbbf29981fd5adce334f40146ff # nodejs
a9e061c97afbbc295b5664a2d8065492 # compiler
1abd6b4ebbbb918f601a6c5dbad55a05 # desktop
0f4353664123320280ea4d6bb295dce2 # svg
"SKIP"
"SKIP"
@ -58,8 +54,6 @@ md5sums=(ac7680788544c457daee11aaf69798fe # nwjs
"SKIP"
"SKIP"
"SKIP"
"SKIP"
"SKIP"
"SKIP")
options=('!strip')
@ -87,9 +81,8 @@ build() {
# prepare nw-gyp
_log "prepare nw-gyp"
node --version
npm uninstall node-gyp -g
npm install nw-gyp@3.6.6 node-gyp -g
npm install nw-gyp node-gyp -g
# node bin
_log "copy node exectuable"
@ -98,9 +91,8 @@ build() {
# run fix scripts
export NW_PACKAGE_DIR="${srcdir}/package.nw"
export NW_VERSION=$_nwjs_ver
# fix-package-name.js使用
export srcdir=$srcdir
export WINE=false
export NO_WINE=true
for script in fix-package-name.js fix-cli.sh fix-other.sh fix-menu.sh fix-core.sh rebuild-node-modules.sh; do
_log "run ${script}"
@ -115,18 +107,13 @@ package() {
mkdir -p "${pkgdir}${_install_dir}"
cd "${pkgdir}${_install_dir}"
cp -r "${srcdir}/nwjs" .
cp -r "${srcdir}/package.nw" .
cp -r "${srcdir}/nwjs/"* ./
cp -r "${srcdir}/package.nw" ./package.nw
find ./package.nw -type d | xargs -I {} chmod -R a+rx {}
cp ${srcdir}/node.${_node_version} nwjs/node
cd nwjs && ln -s node node.exe && ln -s ../package.nw package.nw
cp ${srcdir}/node.${_node_version} node
ln -s node node.exe
install -Dm755 "${srcdir}/wechat-devtools" "${pkgdir}${_install_dir}/bin/wechat-devtools"
install -Dm755 "${srcdir}/wechat-devtools-cli" "${pkgdir}${_install_dir}/bin/wechat-devtools-cli"
install -Dm644 "${srcdir}/wechat-devtools.desktop" "${pkgdir}/usr/share/applications/wechat-devtools.desktop"
install -Dm644 "${srcdir}/logo.svg" "${pkgdir}/usr/share/icons/hicolor/scalable/apps/wechat-devtools.svg"
mkdir -p "${pkgdir}/usr/bin"
ln -s "${_install_dir}/bin/wechat-devtools" "${pkgdir}/usr/bin/wechat-devtools"
ln -s "${_install_dir}/bin/wechat-devtools-cli" "${pkgdir}/usr/bin/wechat-devtools-cli"
}

View File

@ -3,7 +3,7 @@ Name=WeChat Devtools
Name[zh_CN]=web
Comment=The development tools for wechat web develop
Categories=Development;WebDevelopment;IDE;
Exec=/opt/wechat-devtools/bin/wechat-devtools %U
Exec=env APPDATA=~/.config/wechat_devtools USERPROFILE=~ bash -c ' clean_cache(){ echo ""; rm -rf "$APPDATA/WeappCache"; rm -rf "$APPDATA/WeappVendor";}; if [[ -f "$APPDATA/.build_time" ]];then diff "$APPDATA/.build_time" "/opt/wechat-devtools/package.nw/.build_time" >/dev/null; if [ ! "$?" == "0" ];then \cp -f "/opt/wechat-devtools/package.nw/.build_time" "$APPDATA/.build_time";clean_cache; fi; else \cp -f "/opt/wechat-devtools/package.nw/.build_time" "$APPDATA/.build_time"; clean_cache; fi; /opt/wechat-devtools/nw --load-extension=~/.config/wechat_devtools/WeappPlugin --custom-devtools-frontend=file:///opt/wechat-devtools/package.nw/js/ideplugin/inspector %U'
Path=/opt/wechat-devtools
Icon=wechat-devtools
Type=Application

2
res/deb.desktop Executable file → Normal file
View File

@ -8,6 +8,6 @@ Exec=/opt/wechat-devtools/bin/wechat-devtools
Icon=wechat-devtools
Type=Application
Terminal=false
StartupWMClass=wechat-devtools
StartupWMClass=wechat_devtools
Actions=
MimeType=x-scheme-handler/wechatide

View File

@ -0,0 +1,5 @@
Package: wechat-devtools
Version: BUILD_VERSION
Maintainer: msojocs
Architecture: amd64
Description: WeChat Devtools For Linux.

View File

@ -0,0 +1,13 @@
version: "3"
services:
wechat_devtools:
image: jiyecafe/wechat-devtools-build:v1
# build:
# context: ./docker
# dockerfile: Dockerfile
environment:
- NO_WINE=${NO_WINE:-true}
- BUILD_VERSION=${BUILD_VERSION:-v0}
volumes:
- ./../../:/workspace
entrypoint: /workspace/res/deb/docker/docker-entrypoint

30
res/deb/docker/Dockerfile Normal file
View File

@ -0,0 +1,30 @@
FROM debian:9.13-slim
WORKDIR /workspace
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
mkdir -p /build_temp/python36 /build_temp/nodejs && \
apt update && \
apt install -y binutils software-properties-common gcc g++ \
gconf2 libxkbfile-dev p7zip-full make libssh2-1-dev libkrb5-dev wget \
openssl pkg-config build-essential && \
cd /build_temp/python36 && \
apt-get install -y aptitude &&\
aptitude -y install gcc make zlib1g-dev libffi-dev libssl-dev &&\
mkdir -p test && cd test &&\
wget http://npm.taobao.org/mirrors/python/3.6.5/Python-3.6.5.tgz &&\
tar -xvf Python-3.6.5.tgz &&\
chmod -R +x Python-3.6.5 &&\
cd Python-3.6.5/ &&\
./configure &&\
aptitude -y install libffi-dev libssl-dev &&\
make && make install &&\
cd /build_temp/nodejs &&\
wget https://deb.nodesource.com/setup_16.x &&\
chmod +x setup_16.x &&\
./setup_16.x &&\
apt-get install -y nodejs &&\
rm -rf /build_temp && \
apt install -y gosu

View File

@ -0,0 +1,59 @@
#!/bin/bash
# set -e
notice() {
echo -e "\033[36m $1 \033[0m "
}
strings /usr/lib/x86_64-linux-gnu/libstdc++.so.6 | grep GLIBC
strings /lib/x86_64-linux-gnu/libc.so.6 | grep GLIBC_2.2
# gcc -v
# g++ -v
# cc -v
# node --version
# python --version
# python3 --version
echo "=============="
cd /workspace
# mkdir -p tmp/test/node_modules
# cd tmp/test
# npm install nodegit
# tail -f /etc/issue
./tools/setup-wechat-devtools-bash
############ 构建deb包 ################
root_dir=$(cd `dirname $0`/../../.. && pwd -P)
echo "root_dir: $root_dir"
tmp_dir="$root_dir/tmp"
app_dir="$root_dir/tmp/deb"
# Remove any previous build
rm -rf $app_dir
# Make usr and icons dirs
notice "Make Dirs"
mkdir -p $app_dir/opt/wechat-devtools/bin
mkdir -p $app_dir/usr/{src,bin}
mkdir -p $app_dir/usr/share/{metainfo,icons,applications}
notice "COPY Files"
cp -r "$root_dir/res/deb/data"/* $app_dir
cp "$root_dir/bin/wechat-devtools" "$app_dir/opt/wechat-devtools/bin"
cp "$root_dir/res/deb.desktop" "$app_dir/usr/share/applications/wechat-devtools.desktop"
if [[ ! $BUILD_VERSION -eq 'continuous' ]];then
sed -i "s/BUILD_VERSION/${BUILD_VERSION//v/}/" "$app_dir/DEBIAN/control"
else
sed -i "s/BUILD_VERSION/0/" "$app_dir/DEBIAN/control"
fi
cp "$root_dir/res/icons/wechat-devtools.png" "$app_dir/usr/share/icons/wechat-devtools.png"
cp -r "$root_dir/package.nw" "$app_dir/opt/wechat-devtools/package.nw"
cp -r "$root_dir/nwjs" "$app_dir/opt/wechat-devtools/nwjs"
rm -rf "$app_dir/opt/wechat-devtools/nwjs"/{node,node.exe}
cp "$root_dir/node/bin/node" "$app_dir/opt/wechat-devtools/nwjs/node"
cd "$app_dir/opt/wechat-devtools/nwjs/" && ln -s "node" "node.exe"
notice "BUILD DEB Package"
cd "$root_dir/tmp/deb"
ls -l "$root_dir/tmp/deb"
dpkg-deb -b . "WeChat_Dev_Tools_$BUILD_VERSION.deb"

View File

@ -1,6 +0,0 @@
io.github.msojocs.wechat-devtools (BUILD_VERSION) unstable; urgency=medium
* update:
- devtools 1.06.2206090
-- msojocs <jiyecafe@gmail.com> Mon, 11 Mar 2022 14:56:04 +0800

View File

@ -1 +0,0 @@
10

View File

@ -1,13 +0,0 @@
Source: io.github.msojocs.wechat-devtools
Maintainer: msojocs
Standards-Version: BUILD_VERSION
Priority: optional
Section: application
Package: io.github.msojocs.wechat-devtools
Architecture: amd64
Description: WeChat Devtools For Linux.
To help developers develop and debug Weixin Mini Programs more simply and efficiently,
Tencent have launched the new Weixin DevTools based on the original Official Account
web debugging tools. Therefore, we now have two developer modes (Official Account web
debugging) and (Mini Program debugging). This version is unofficial.

View File

@ -1,2 +0,0 @@
opt .
usr .

View File

@ -1,28 +0,0 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
DH_VERBOSE = 1
# see FEATURE AREAS in dpkg-buildflags(1)
#export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# see ENVIRONMENT in dpkg-buildflags(1)
# package maintainers to append CFLAGS
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
# package maintainers to append LDFLAGS
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
# 1.20.9ubuntu1 -> 1.20.9ubuntu2修改了默认打包工具为zstd导致统信平台不能识别强制设定为xz
# changelog: https://launchpad.net/ubuntu/+source/dpkg/1.20.9ubuntu2
override_dh_builddeb:
dh_builddeb -- -Zxz
override_dh_strip_nondeterminism:
override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info -Xswc.linux-x64-musl.node
override_dh_strip:
dh_strip --no-automatic-dbgsym
%:
dh "$@"

View File

@ -1,21 +0,0 @@
{
"appid": "io.github.msojocs.wechat-devtools",
"name": "WeChat Dev Tools",
"version": "BUILD_VERSION",
"arch": ["amd64"],
"permissions": {
"autostart": false,
"notification": false,
"trayicon": true,
"clipboard": true,
"account": false,
"bluetooth": false,
"camera": false,
"audio_record": false,
"installed_apps": false
},
"support-plugins": [
],
"plugins": [
]
}

View File

@ -1,4 +0,0 @@
#!/bin/sh
export PATH="/var/run/host/usr/bin:$PATH"
export LD_LIBRARY_PATH="/app/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH"
exec /app/opt/bin/wechat-devtools

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

View File

@ -1,123 +1,71 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve"> <image id="image0" width="256" height="256" x="0" y="0"
href="data:image/png;base64,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
rkJggg==" />
</svg>
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1024px" height="1024px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
<title>wechat-devtools</title>
<desc>Created with Sketch.</desc>
<defs>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
<stop stop-color="#0E142C" offset="0%"></stop>
<stop stop-color="#323C67" offset="100%"></stop>
</linearGradient>
<linearGradient x1="50%" y1="0%" x2="50%" y2="98.8835515%" id="linearGradient-2">
<stop stop-color="#EEEEEE" offset="0%"></stop>
<stop stop-color="#D7D7D7" offset="100%"></stop>
</linearGradient>
<text id="text-3" font-family="Menlo-Bold, Menlo" font-size="230" font-weight="bold">
<tspan x="212" y="688">&lt;/&gt;</tspan>
</text>
<filter x="-3.4%" y="-4.4%" width="106.9%" height="112.4%" filterUnits="objectBoundingBox" id="filter-4">
<feOffset dx="0" dy="5" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.761991914 0" type="matrix" in="shadowBlurOuter1"></feColorMatrix>
</filter>
<filter x="-9.7%" y="-11.8%" width="123.0%" height="128.1%" filterUnits="objectBoundingBox" id="filter-5">
<feOffset dx="0" dy="10" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="10" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.272536058 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-6">
<stop stop-color="#9EEE69" offset="0%"></stop>
<stop stop-color="#74CD30" offset="100%"></stop>
</linearGradient>
<filter x="-8.1%" y="-9.1%" width="116.2%" height="118.2%" filterUnits="objectBoundingBox" id="filter-7">
<feOffset dx="0" dy="5" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="5" in="shadowOffsetOuter1" result="shadowBlurOuter1"></feGaussianBlur>
<feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0" type="matrix" in="shadowBlurOuter1" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<linearGradient x1="50%" y1="0.269214527%" x2="50%" y2="100%" id="linearGradient-8">
<stop stop-color="#FFFFFF" offset="0%"></stop>
<stop stop-color="#E5E9EA" offset="100%"></stop>
</linearGradient>
</defs>
<g id="wechat-devtools" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" fill="url(#linearGradient-1)" x="95" y="157" width="832" height="710" rx="34"></rect>
<path d="M898,153 C916.777681,153 932,168.222319 932,187 L932,837 C932,855.777681 916.777681,871 898,871 L125,871 C106.222319,871 91,855.777681 91,837 L91,187 C91,168.222319 106.222319,153 125,153 L898,153 Z M877,199 L148,199 C141.924868,199 137,203.924868 137,210 L137,210 L137,816 C137,822.075132 141.924868,827 148,827 L148,827 L877,827 C883.075132,827 888,822.075132 888,816 L888,816 L888,210 C888,203.924868 883.075132,199 877,199 L877,199 Z" id="Combined-Shape" fill="url(#linearGradient-2)"></path>
<g id="&lt;/&gt;" opacity="0.0783110119" fill="#FFFFFF">
<use fill-opacity="1" filter="url(#filter-4)" xlink:href="#text-3"></use>
<use fill-rule="evenodd" xlink:href="#text-3"></use>
<use fill-opacity="1" xlink:href="#text-3"></use>
</g>
<g id="Group-3" filter="url(#filter-5)" transform="translate(211.000000, 266.000000)">
<g id="Group-4">
<path d="M216.5,364 C336.069648,364 433,282.515824 433,182 C433,81.4841755 336.069648,-2.84217094e-14 216.5,-2.84217094e-14 C96.9303517,-2.84217094e-14 0,81.4841755 0,182 C0,234.824118 26.7704343,282.392034 69.5285421,315.639256 C93.3413194,334.155246 58.2521249,377.054783 69.5285421,386.023056 C80.8049593,394.99133 128.568238,347 151.295389,355.600627 C171.060829,363.080455 193.777604,364 216.5,364 Z" id="Oval-Copy" fill="url(#linearGradient-6)"></path>
<circle id="Oval" fill="#126F20" cx="143.5" cy="126.5" r="29.5"></circle>
<circle id="Oval-Copy-2" fill="#126F20" cx="289.5" cy="126.5" r="29.5"></circle>
</g>
<g id="Group-5" filter="url(#filter-7)" transform="translate(237.000000, 167.000000)">
<path d="M182,305 C200.898516,305 228.587306,302.288693 236.262178,298.107116 C255.747671,287.490639 287.208639,328.334705 303.2061,323 C319.203561,317.665295 280.639295,283.352792 303.2061,265.082731 C337.702449,237.154525 364,195.852984 364,152.5 C364,68.2765757 282.515824,2.84217094e-14 182,2.84217094e-14 C81.4841755,2.84217094e-14 0,68.2765757 0,152.5 C0,236.723424 81.4841755,305 182,305 Z" id="Oval" fill="url(#linearGradient-8)"></path>
<circle id="Oval-Copy-3" fill="#7B7F7F" cx="244.5" cy="107.5" r="24.5"></circle>
<circle id="Oval-Copy-4" fill="#7B7F7F" cx="119.5" cy="107.5" r="24.5"></circle>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,29 +0,0 @@
(() => {
process.argv[0] = 'wechat-devtools-cli'
process.env.USERPROFILE = require('os').homedir()
Object.defineProperty(global, 'userDirPath', {
set: function(value) {
if (value.includes('AppData/Local')) {
value = value.replace('AppData/Local', '.config')
value = value.replace(/User Data\/.*?\//, '')
// value = value.replace('~', require('os').homedir())
}
this._userDirPath = value
},
get: function() {
return this._userDirPath
}
})
// appPath
Object.defineProperty(global, 'appPath', {
set: function(value) {
if (value.includes('.exe')) {
value = value.replace('.exe', '')
}
this._appPath = value
},
get: function() {
return this._appPath
}
})
})();

View File

@ -1,11 +0,0 @@
(() => {
const http = require('http')
const originaleListen = http.Server.prototype.listen
http.Server.prototype.listen = function(...args) {
if (args[0] == 33233) {
console.warn('block port of http server:', args[0])
return
}
return originaleListen.apply(this, args)
}
})();

View File

@ -1,347 +0,0 @@
/* patch wechat devtools begin */
(() => {
try {
{
// 修正编译器路径
const originalSpawn = require("child_process").spawn;
require("child_process").spawn = function (command, args, options) {
if (command.includes("wcc.exe")) {
command = command.replace("code/package.nw", "package.nw");
command = command.replace("wcc.exe", "wcc");
} else if (command.includes("wcsc.exe")) {
command = command.replace("code/package.nw", "package.nw");
command = command.replace("wcsc.exe", "wcsc");
}
return originalSpawn.apply(this, [command, args, options]);
};
}
{
// 修正路径错误
const originalResolve = require("path").resolve;
require("path").resolve = function (...paths) {
if (paths.length === 2 && paths[1].includes("code/package.nw")) {
paths[1] = paths[1].replace("code/package.nw", "package.nw");
}
return originalResolve.apply(this, paths);
};
}
if (typeof nw === "undefined") {
return;
}
let log = function (content) {
process.stderr.write(content + "\n");
};
let originMenuItem = nw.MenuItem;
nw.MenuItem = function MenuItem(options) {
options = Object.assign({}, options);
delete options.shortcutName;
delete options.shouldEnabled;
if (options.label && (typeof options.label === "string")) {
if (options.label.indexOf("[") !== -1) {
let rest = options.label.split("[").slice(1).join("[").trim();
if (rest[rest.length - 1] === "]") {
rest = rest.slice(0, -1).split("+").map((x) => {
if (!x) { return "+" }
switch (x) {
case "↓": { return "Down"; }
case "↑": { return "Up"; }
case "PAGE↓": { return "PageDown"; }
case "PAGE↑": { return "PageUp"; }
case "←": { return "Left"; }
case "→": { return "Right"; }
default: { return x; }
}
});
if (rest.length > 1) {
options.key = rest[rest.length - 1];
options.modifiers = rest.slice(0, -1).join("+");
} else {
options.key = rest[0];
}
}
options.label = options.label.split("[")[0];
}
if (options.label.indexOf("(&") !== -1) {
options.label = options.label.split("(&")[0];
}
options.label = options.label.replace("&", "").trim();
switch (options.label) {
case "Go to Declaration": { options.label = "转到声明"; break; }
case "Go to References": { options.label = "转到引用"; break; }
case "Find All References": { options.label = "查找所有引用"; break; }
case "Find All Implementations": { options.label = "查找所有实现"; break; }
}
}
return new originMenuItem(options);
};
let originAppend = nw.Menu.prototype.append;
nw.Menu.prototype.append = function (item) {
if (item.parentMenu) {
item.parentMenu.remove(item);
}
item.parentMenu = this;
if ((this.items.length > 0) &&
(this.items[this.items.length - 1].type === "separator") &&
(item.type === "separator")) {
originInsert.call(this, item, this.items.length);
return;
}
if ((this.items.length === 0) && (item.type === "separator")) {
originInsert.call(this, item, this.items.length);
return;
}
return originAppend.call(this, item);
};
let originInsert = nw.Menu.prototype.insert;
nw.Menu.prototype.insert = function (item, index) {
if (item.parentMenu) {
item.parentMenu.remove(item);
}
item.parentMenu = this;
return originInsert.call(this, item, index);
};
{
// 修正新窗口数据丢失的问题
const originalOpen = nw.Window.open
nw.Window.open = function (url, options, callback) {
console.warn('[wechat-devtools] nw.Window.open is called, url:', url, 'options:', options);
let cb = callback
if (options.title === '版本更新提示') {
options.inject_js_start = 'js/unpack/hackrequire/index.js';
cb = (...args) => {
const keys = [
"shareData",
"windowMap",
"isSimple",
"masterProxyPort",
"proxyPort",
"masterH2ProxyPort",
"h2ProxyPort"
];
for(let k of keys)
args[0].window.global[k] = global[k];
callback(...args)
}
}
else if (options.title === '云开发控制台') {
cb = (...args) => {
const keys = [
"shareData",
"windowMap",
"isSimple",
"masterProxyPort",
"proxyPort",
"masterH2ProxyPort",
"h2ProxyPort",
'tokenData',
];
for(let k of keys)
args[0].window.global[k] = global[k];
callback(...args)
}
}
else if (options.title.includes('微信开发者工具')) {
cb = (...args) => {
const keys = [
"shareData",
"windowMap",
"isSimple",
"masterProxyPort",
"proxyPort",
"masterH2ProxyPort",
"h2ProxyPort",
'tokenData',
];
for(let k of keys)
args[0].window.global[k] = global[k];
callback(...args)
}
}
return originalOpen.apply(this, [url, options, cb])
}
}
{
// 修正打开外部Terminal的功能
const originalExec = require('child_process').exec;
require('child_process').exec = function (command, options, callback) {
if (command.includes('open -a Terminal')) {
command = 'gnome-terminal'
}
return originalExec.apply(this, [command, options, callback])
}
}
{
// 修正 暗色/亮色 自动跟随系统
const {spawn, execSync} = require('child_process')
let isDark = (function () {
try {
const { DESKTOP_SESSION } = process.env;
console.log(DESKTOP_SESSION);
let theme = "";
switch (DESKTOP_SESSION) {
case "deepin":
theme = execSync(
`gsettings get com.deepin.dde.appearance gtk-theme`
);
break;
case "gnome":
case "gnome-classic":
theme = execSync(
`gsettings get org.gnome.desktop.interface ${this.gnomeScheme}`
);
break;
case "plasma":
theme = execSync(
`gsettings get org.gnome.desktop.interface color-scheme`,
);
break;
default:
console.warn(
`NOT SUPPORTED !!! DESKTOP_SESSION: ${DESKTOP_SESSION}`
);
break;
}
console.log(theme.toString());
return theme.toString().toLowerCase().includes("dark");
} catch (error) {
console.error("尝试获取主题信息失败,使用默认暗色");
return true;
}
})()
const originalMatchMedia = window.matchMedia
window.matchMedia = function (...args) {
console.warn('----------------> matchMedia:', ...args)
const mm = originalMatchMedia.apply(this, args)
Object.defineProperty(mm, 'matches', {
get(){
return isDark
}
})
return mm
}
class CheckDark {
// 监听gsettings monitor org.gnome.desktop.interface gtk-theme
monitorTheme(callback) {
try {
if (this.callback) {
this.callback = callback
return;
}
this.callback = callback
let monitor = null;
const { DESKTOP_SESSION } = process.env;
switch (DESKTOP_SESSION) {
case "deepin":
monitor = spawn("gsettings", [
"monitor",
"com.deepin.dde.appearance",
"gtk-theme",
]);
break;
case "gnome":
case "gnome-classic":
monitor = spawn("gsettings", [
"monitor",
"org.gnome.desktop.interface",
this.gnomeScheme,
]);
break;
case "plasma":
monitor = spawn("gsettings", [
"monitor",
"org.gnome.desktop.interface",
'color-scheme',
]);
break;
default:
console.warn(
`NOT SUPPORTED !!! DESKTOP_SESSION: ${DESKTOP_SESSION}`
);
break;
}
monitor &&
monitor.on("error", (err) => {
console.error("monitorTheme", err);
});
monitor &&
monitor.stdout.on("data", (chunk) => {
const data = chunk.toString();
console.warn('Theme changed:', data)
isDark = data.toLowerCase().includes("dark");
this.callback(isDark)
});
process.on("SIGTERM", (signal) => {
monitor.kill(signal);
});
} catch (err) {
console.error("尝试监听主题失败!", err);
}
}
get gnomeScheme() {
try {
// 判断 Gnome-Shell 版本 from @icepie
const gnomeVersion = execSync(`gnome-shell --version`)
.toString()
.replace(/[\r\n]/g, "")
.split(" ");
const gnomeVersionNum =
gnomeVersion.length == 3 ? Number(gnomeVersion[2]) : 0;
return gnomeVersionNum >= 42 ? "color-scheme" : "gtk-theme";
} catch (err) {
console.error("检查gnome版本失败, 使用gtk-theme", err);
return "gtk-theme";
}
}
}
const checkDark = new CheckDark()
const original = MediaQueryList.prototype.addEventListener
MediaQueryList.prototype.addEventListener = function (...args) {
console.warn('----------> MediaQueryList.addEventListener:', ...args)
checkDark.monitorTheme((isDark) => {
args[1]({
matches: isDark
})
})
return original.apply(this, args)
}
}
{
// 修复云开发控制台
const originalBind = Function.prototype.bind
Function.prototype.bind = function(...args) {
if (args[0]?._tokenMap) {
console.warn('---------set tokenData')
if (window.tokenData) {
args[0]._sessionToken = window.tokenData._sessionToken
args[0]._tokenMap = window.tokenData._tokenMap
}
else
window.tokenData = args[0]
}
return originalBind.apply(this, args)
}
}
} catch (error) {
process.stderr.write(error.message);
process.stderr.write(error.stack);
}
})();
/* patch wechat devtools end */

2
res/template.desktop Executable file → Normal file
View File

@ -8,6 +8,6 @@ Exec=dir/bin/wechat-devtools
Icon=dir/res/icons/wechat-devtools.svg
Type=Application
Terminal=false
StartupWMClass=wechat-devtools
StartupWMClass=wechat_devtools
Actions=
MimeType=x-scheme-handler/wechatide

View File

@ -1,11 +0,0 @@
#!/bin/bash
root_dir=$(cd `dirname $0`/.. && pwd -P)
cd $root_dir
export WINE=true
docker-compose up
$root_dir/tools/build-deepin.sh $@
export WINE=false
$root_dir/tools/fix-core.sh
$root_dir/tools/fix-other.sh
$root_dir/tools/build-deepin.sh $@

View File

@ -1,17 +0,0 @@
#!/bin/bash
# 此脚本用于使修改 core.wxvpkg 内的代码及时生效
set -e
root_dir=$(cd `dirname $0`/.. && pwd -P)
find_result="$root_dir/package.nw/core.wxvpkg.ext/19c6ae7eff08f795d1f2124f7b0248ad.js"
return_exp_wcc=$(cat $find_result | grep -P 'return [a-z]+\("wcc"\)' -o) # return ?("wcc")
echo "return exp: $return_exp_wcc"
return_exp_wcc_replace="${return_exp_wcc//wcc/wcc.bin}" # return ?("wcc.bin")
echo "replace exp1: $return_exp_wcc_replace"
return_exp_wcc_replace="${return_exp_wcc//return /${return_exp_wcc_replace},}" # return ?("wcc.bin")
echo "replace exp2: $return_exp_wcc_replace"
return_exp_wcsc=$(cat $find_result | grep -P 'return [a-z]+\("wcsc"\)' -o) # return ?("wcc")
return_exp_wcsc_replace="${return_exp_wcc_replace//wcc/wcsc}"
sed -i "s#$return_exp_wcc#$return_exp_wcc_replace#g" "$find_result"
sed -i "s#$return_exp_wcsc#$return_exp_wcsc_replace#g" "$find_result"

9
test/core-test Executable file → Normal file
View File

@ -1,15 +1,12 @@
#!/bin/bash
# 此脚本用于使修改 core.wxvpkg 内的代码及时生效
set -e
root_dir=$(cd `dirname $0`/.. && pwd -P)
a=$1
if [ "$a" == '1' ];then
rm -rf "$root_dir/package.nw/core.wxvpkg.ext"
node tools/wxvpkg_unpack.js "$root_dir/package.nw/core.wxvpkg" "$root_dir/package.nw/core.wxvpkg.ext"
mv "$root_dir/package.nw/core.wxvpkg" "$root_dir/package.nw/core.wxvpkg.orgi"
node tools/wxvpkg/unpack "$root_dir/package.nw/core.wxvpkg" "$root_dir/package.nw/core.wxvpkg.ext"
else
node tools/wxvpkg_pack.js package.nw/core.wxvpkg.ext package.nw/core.wxvpkg
rm -rf ~/.config/wechat-devtools/WeappCache
rm -rf ~/.config/wechat-devtools/WeappVendor
node tools/wxvpkg/pack package.nw/core.wxvpkg.ext package.nw/core.wxvpkg
rm -rf ~/.config/wechat_devtools/WeappCache
bin/wechat-devtools
fi

2
test/env Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env node
console.log(process.env['action']==='true')

View File

@ -1,16 +0,0 @@
#!/bin/bash
root_dir=$(cd `dirname $0`/.. && pwd -P)
package_dir="$root_dir/package.nw"
tmp_dir="$root_dir/package.nw"
mkdir -p $tmp_dir
unpack_script="$root_dir/tools/wxvpkg_unpack.js"
pack_script="$root_dir/tools/wxvpkg_pack.js"
find_result=$( grep -lr "OSThemeController=" "$tmp_dir/core.wxvpkg.ext" )
echo "theme: $find_result"
sed -i 's/"use strict";O/"use strict";const {execSync,spawn}=require("child_process");O/' $find_result
sed -i 's/this.registerListeners()/this.monitorTheme()/' $find_result
sed -i 's/}registerListeners/}monitorTheme(){let monitor=null;const{DESKTOP_SESSION}=process.env;switch(DESKTOP_SESSION){case"deepin":monitor=spawn("gsettings",["monitor","com.deepin.dde.appearance","gtk-theme",]);break;case"gnome":case"gnome-classic":monitor=spawn("gsettings",["monitor","org.gnome.desktop.interface","gtk-theme",]);break;default:console.warn(`NOT SUPPORTED!!!DESKTOP_SESSION:${DESKTOP_SESSION}`);break}monitor\&\&monitor.on("error",(err)=>{console.error("monitorTheme",err)});monitor\&\&monitor.stdout.on("data",e.debounce((chunk)=>{const data=chunk.toString();const t=data.includes("dark");console.warn(data);console.warn("dark",t);(this._theme=t?i.Dark:i.Light),this._onDidThemeChange.fire(this._theme)},400))}registerListeners/' $find_result
sed -i 's/mediaQuery.matches/isDark/' $find_result
sed -i 's/}getDefaultTheme/}get isDark(){const{DESKTOP_SESSION}=process.env;console.log(DESKTOP_SESSION);let theme="";switch(DESKTOP_SESSION){case"deepin":theme=execSync(`gsettings get com.deepin.dde.appearance gtk-theme`);break;case"gnome":case"gnome-classic":theme=execSync(`gsettings get org.gnome.desktop.interface gtk-theme`);break;default:break}return theme.includes("dark")}getDefaultTheme/' $find_result

View File

@ -1,12 +0,0 @@
#!/bin/bash
root_dir=$(cd `dirname $0`/.. && pwd -P)
export PATH="$root_dir/node/bin:$PATH"
module="spdlog"
cd "$root_dir/tmp"
mkdir -p node_test/node_modules
cd node_test
npm install $module --save
rm -rf "$root_dir/package.nw/node_modules/$module"
cp -rf "$root_dir/tmp/node_test/node_modules/$module" "$root_dir/package.nw/node_modules"

View File

@ -1,10 +0,0 @@
#!/usr/bin/env python3
#-- coding: UTF-8 --
import minium
# print(minium)
mini = minium.Minium({
"project_path": r"/home/msojocs/Documents/we1", #小程序项目路径
"dev_tool_path": r"/mnt/disk2/wechat-web-devtools-linux/bin/wechat-devtools-cli",#开发者工具的命令行工具路径
"test_port": 9420 #minitest自动化端口
})
print(mini.get_system_info())#输出开发者工具模拟器的信息

View File

@ -1,9 +0,0 @@
#!/bin/bash
root_dir=$(cd `dirname $0`/.. && pwd -P)
export PATH="$root_dir/node/bin:$PATH"
ln -s "$( which python2 )" "$root_dir/node/bin/python"
cd "$root_dir/tmp/node_test/node_modules/spdlog"
# node-gyp rebuild
nw-gyp rebuild --arch=x64 "--target=0.55.0" --dist-url=https://registry.npmmirror.com/-/binary/nwjs

View File

@ -1,9 +0,0 @@
#!/bin/bash
root_dir=$(cd `dirname $0`/.. && pwd -P)
NW_VERSION=0.53.0
ln -s $( which python2 ) "$root_dir/tmp/python"
export PATH="$root_dir/tmp:$PATH"
export all_proxy="sock5://127.0.0.1:7890"
cd /mnt/disk1/GitHub/wechat-devtools-linux/node_modules/node-pty
nw-gyp rebuild --arch=x64 "--target=$NW_VERSION" --dist-url=https://registry.npmmirror.com/-/binary/nwjs

View File

@ -1,9 +0,0 @@
<!-- <video src="https://vjs.zencdn.net/v/oceans.mp4"></video> -->
<hr />
<video autoplay width="320" height="240" controls>
<source src="https://media.w3.org/2010/05/sintel/trailer.mp4" type="video/mp4">
您的浏览器不支持 HTML5 video 标签。
</video>
<script>
// window.open('chrome://media-internals/')
</script>

View File

@ -1,4 +0,0 @@
{
"name": "test",
"main": "index.html"
}

View File

@ -1,5 +1,5 @@
#!/bin/bash
export WINE=fasle
export NO_WINE=true
root_dir=$(cd `dirname $0`/.. && pwd -P)
$root_dir/tools/fix-core.sh

View File

@ -1,7 +0,0 @@
#!/bin/bash
set -e
root_dir=$(cd `dirname $0`/.. && pwd -P)
cd $root_dir
snapcraft #--http-proxy="http://192.168.1.232:7890" --https-proxy="http://192.168.1.232:7890"
snap install wechat-devtools_*_amd64.snap --devmode
# snap run wechat-devtools

30
test/test-bash Normal file
View File

@ -0,0 +1,30 @@
#!/bin/bash
BUILD_VERSION='v1'
if [[ ! $BUILD_VERSION -eq 'continuous' ]];then
BUILD_VERSION=v0
fi
echo $BUILD_VERSION
sed -i "s/BUILD_VERSION/${BUILD_VERSION//v/}/" "/mnt/disk2/wechat-web-devtools-linux/tmp/deb/DEBIAN/control"
exit
if [[ $NO_WINE == 'true' ]];then
echo "not wine"
fi
if [[ $@ =~ -ll ]];then
echo "include"
fi
exit
echo "$@ $0 $1 $2"
root_dir=$(cd `dirname $0`/.. && pwd -P)
export PATH="$root_dir/node/bin:$PATH"
PY_VERSION=`python -V 2>&1|awk '{print $2}'|awk -F '.' '{print $1}'`
echo $PY_VERSION
if [ "$PY_VERSION" == "Python 3.10.1" ]; then
echo ok
fi

Some files were not shown because too many files have changed in this diff Show More