@dimina-kit/devtools 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/index.bundle.js +2765 -0
- package/dist/main/services/default-adapter.js +1 -3
- package/dist/main/services/projects/project-repository.js +6 -0
- package/dist/main/services/simulator/dir.d.ts +1 -1
- package/dist/main/services/simulator/dir.js +1 -4
- package/dist/main/services/simulator-storage/index.js +40 -1
- package/dist/main/services/workspace/workspace-service.js +12 -0
- package/dist/main/utils/paths.d.ts +2 -0
- package/dist/main/utils/paths.js +27 -4
- package/dist/preload/instrumentation/wxml-extract.js +134 -3
- package/dist/preload/runtime/bridge.d.ts +3 -1
- package/dist/preload/runtime/bridge.js +55 -4
- package/dist/preload/shared/types.d.ts +2 -1
- package/dist/preload/windows/simulator.js +100 -7
- package/dist/renderer/assets/index-D1p6crtf.js +46 -0
- package/dist/renderer/assets/{input-VLotwer2.js → input-DxfYnzX_.js} +2 -2
- package/dist/renderer/assets/{ipc-transport-COKIC6Ny.js → ipc-transport-ClIvDcYY.js} +6 -6
- package/dist/renderer/assets/ipc-transport-EiWxu_hG.css +1 -0
- package/dist/renderer/assets/{popover-Dn0RwMLI.js → popover-CtXcBJtM.js} +2 -2
- package/dist/renderer/assets/{select-DzIsLaKX.js → select-DMfp49f1.js} +2 -2
- package/dist/renderer/assets/{settings-CD-dacWB.js → settings-DteGraEU.js} +2 -2
- package/dist/renderer/assets/{settings-api-BV9MPckG.js → settings-api-CY84UMpN.js} +2 -2
- package/dist/renderer/assets/{workbenchSettings-ClwgsvJL.js → workbenchSettings-DjELLnSJ.js} +2 -2
- package/dist/renderer/entries/main/index.html +5 -5
- package/dist/renderer/entries/popover/index.html +5 -5
- package/dist/renderer/entries/settings/index.html +5 -5
- package/dist/renderer/entries/workbench-settings/index.html +4 -4
- package/dist/shared/ipc-channels.d.ts +23 -0
- package/dist/shared/ipc-channels.js +5 -0
- package/package.json +21 -19
- package/dist/main/api.d.ts.map +0 -1
- package/dist/main/api.js.map +0 -1
- package/dist/main/app/app.d.ts.map +0 -1
- package/dist/main/app/app.js.map +0 -1
- package/dist/main/app/bootstrap.d.ts.map +0 -1
- package/dist/main/app/bootstrap.js.map +0 -1
- package/dist/main/app/launch.d.ts.map +0 -1
- package/dist/main/app/launch.js.map +0 -1
- package/dist/main/app/lifecycle.d.ts.map +0 -1
- package/dist/main/app/lifecycle.js.map +0 -1
- package/dist/main/index.d.ts.map +0 -1
- package/dist/main/index.js.map +0 -1
- package/dist/main/ipc/app.d.ts.map +0 -1
- package/dist/main/ipc/app.js.map +0 -1
- package/dist/main/ipc/index.d.ts.map +0 -1
- package/dist/main/ipc/index.js.map +0 -1
- package/dist/main/ipc/panels.d.ts.map +0 -1
- package/dist/main/ipc/panels.js.map +0 -1
- package/dist/main/ipc/popover.d.ts.map +0 -1
- package/dist/main/ipc/popover.js.map +0 -1
- package/dist/main/ipc/projects.d.ts.map +0 -1
- package/dist/main/ipc/projects.js.map +0 -1
- package/dist/main/ipc/session.d.ts.map +0 -1
- package/dist/main/ipc/session.js.map +0 -1
- package/dist/main/ipc/settings.d.ts.map +0 -1
- package/dist/main/ipc/settings.js.map +0 -1
- package/dist/main/ipc/simulator.d.ts.map +0 -1
- package/dist/main/ipc/simulator.js.map +0 -1
- package/dist/main/ipc/toolbar.d.ts.map +0 -1
- package/dist/main/ipc/toolbar.js.map +0 -1
- package/dist/main/menu/index.d.ts.map +0 -1
- package/dist/main/menu/index.js.map +0 -1
- package/dist/main/services/automation/exec.d.ts.map +0 -1
- package/dist/main/services/automation/exec.js.map +0 -1
- package/dist/main/services/automation/handlers/app.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/app.js.map +0 -1
- package/dist/main/services/automation/handlers/element.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/element.js.map +0 -1
- package/dist/main/services/automation/handlers/page.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/page.js.map +0 -1
- package/dist/main/services/automation/handlers/tool.d.ts.map +0 -1
- package/dist/main/services/automation/handlers/tool.js.map +0 -1
- package/dist/main/services/automation/index.d.ts.map +0 -1
- package/dist/main/services/automation/index.js.map +0 -1
- package/dist/main/services/automation/registry.d.ts.map +0 -1
- package/dist/main/services/automation/registry.js.map +0 -1
- package/dist/main/services/automation/shared.d.ts.map +0 -1
- package/dist/main/services/automation/shared.js.map +0 -1
- package/dist/main/services/default-adapter.d.ts.map +0 -1
- package/dist/main/services/default-adapter.js.map +0 -1
- package/dist/main/services/layout/index.d.ts.map +0 -1
- package/dist/main/services/layout/index.js.map +0 -1
- package/dist/main/services/mcp/index.d.ts.map +0 -1
- package/dist/main/services/mcp/index.js.map +0 -1
- package/dist/main/services/mcp/server.d.ts.map +0 -1
- package/dist/main/services/mcp/server.js.map +0 -1
- package/dist/main/services/mcp/target-manager.d.ts.map +0 -1
- package/dist/main/services/mcp/target-manager.js.map +0 -1
- package/dist/main/services/mcp/tool-registry.d.ts.map +0 -1
- package/dist/main/services/mcp/tool-registry.js.map +0 -1
- package/dist/main/services/mcp/tools/context-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/context-tools.js.map +0 -1
- package/dist/main/services/mcp/tools/simulator-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/simulator-tools.js.map +0 -1
- package/dist/main/services/mcp/tools/workbench-tools.d.ts.map +0 -1
- package/dist/main/services/mcp/tools/workbench-tools.js.map +0 -1
- package/dist/main/services/notifications/renderer-notifier.d.ts.map +0 -1
- package/dist/main/services/notifications/renderer-notifier.js.map +0 -1
- package/dist/main/services/projects/index.d.ts.map +0 -1
- package/dist/main/services/projects/index.js.map +0 -1
- package/dist/main/services/projects/project-repository.d.ts.map +0 -1
- package/dist/main/services/projects/project-repository.js.map +0 -1
- package/dist/main/services/settings/index.d.ts.map +0 -1
- package/dist/main/services/settings/index.js.map +0 -1
- package/dist/main/services/simulator/dir.d.ts.map +0 -1
- package/dist/main/services/simulator/dir.js.map +0 -1
- package/dist/main/services/simulator/referer.d.ts.map +0 -1
- package/dist/main/services/simulator/referer.js.map +0 -1
- package/dist/main/services/simulator-storage/index.d.ts.map +0 -1
- package/dist/main/services/simulator-storage/index.js.map +0 -1
- package/dist/main/services/update/github-release-checker.d.ts.map +0 -1
- package/dist/main/services/update/github-release-checker.js.map +0 -1
- package/dist/main/services/update/index.d.ts.map +0 -1
- package/dist/main/services/update/index.js.map +0 -1
- package/dist/main/services/update/update-manager.d.ts.map +0 -1
- package/dist/main/services/update/update-manager.js.map +0 -1
- package/dist/main/services/views/view-manager.d.ts.map +0 -1
- package/dist/main/services/views/view-manager.js.map +0 -1
- package/dist/main/services/workbench-context.d.ts.map +0 -1
- package/dist/main/services/workbench-context.js.map +0 -1
- package/dist/main/services/workspace/index.d.ts.map +0 -1
- package/dist/main/services/workspace/index.js.map +0 -1
- package/dist/main/services/workspace/workspace-service.d.ts.map +0 -1
- package/dist/main/services/workspace/workspace-service.js.map +0 -1
- package/dist/main/utils/logger.d.ts.map +0 -1
- package/dist/main/utils/logger.js.map +0 -1
- package/dist/main/utils/paths.d.ts.map +0 -1
- package/dist/main/utils/paths.js.map +0 -1
- package/dist/main/utils/theme.d.ts.map +0 -1
- package/dist/main/utils/theme.js.map +0 -1
- package/dist/main/windows/main-window/create.d.ts.map +0 -1
- package/dist/main/windows/main-window/create.js.map +0 -1
- package/dist/main/windows/main-window/events.d.ts.map +0 -1
- package/dist/main/windows/main-window/events.js.map +0 -1
- package/dist/main/windows/main-window/index.d.ts.map +0 -1
- package/dist/main/windows/main-window/index.js.map +0 -1
- package/dist/main/windows/settings-window/create.d.ts.map +0 -1
- package/dist/main/windows/settings-window/create.js.map +0 -1
- package/dist/main/windows/settings-window/events.d.ts.map +0 -1
- package/dist/main/windows/settings-window/events.js.map +0 -1
- package/dist/main/windows/settings-window/index.d.ts.map +0 -1
- package/dist/main/windows/settings-window/index.js.map +0 -1
- package/dist/preload/instrumentation/app-data.d.ts.map +0 -1
- package/dist/preload/instrumentation/app-data.js.map +0 -1
- package/dist/preload/instrumentation/console.d.ts.map +0 -1
- package/dist/preload/instrumentation/console.js.map +0 -1
- package/dist/preload/instrumentation/disposable.d.ts.map +0 -1
- package/dist/preload/instrumentation/disposable.js.map +0 -1
- package/dist/preload/instrumentation/index.d.ts.map +0 -1
- package/dist/preload/instrumentation/index.js.map +0 -1
- package/dist/preload/instrumentation/wxml-extract.d.ts.map +0 -1
- package/dist/preload/instrumentation/wxml-extract.js.map +0 -1
- package/dist/preload/instrumentation/wxml.d.ts.map +0 -1
- package/dist/preload/instrumentation/wxml.js.map +0 -1
- package/dist/preload/runtime/bridge.d.ts.map +0 -1
- package/dist/preload/runtime/bridge.js.map +0 -1
- package/dist/preload/runtime/host.d.ts.map +0 -1
- package/dist/preload/runtime/host.js.map +0 -1
- package/dist/preload/shared/api-compat.d.ts.map +0 -1
- package/dist/preload/shared/api-compat.js.map +0 -1
- package/dist/preload/shared/constants.d.ts.map +0 -1
- package/dist/preload/shared/constants.js.map +0 -1
- package/dist/preload/shared/types.d.ts.map +0 -1
- package/dist/preload/shared/types.js.map +0 -1
- package/dist/preload/windows/main.d.ts.map +0 -1
- package/dist/preload/windows/main.js.map +0 -1
- package/dist/preload/windows/simulator.js.map +0 -7
- package/dist/renderer/assets/index-D_HFRqZA.js +0 -46
- package/dist/renderer/assets/index-D_HFRqZA.js.map +0 -1
- package/dist/renderer/assets/input-VLotwer2.js.map +0 -1
- package/dist/renderer/assets/ipc-transport-6Gy3FXIL.css +0 -1
- package/dist/renderer/assets/ipc-transport-COKIC6Ny.js.map +0 -1
- package/dist/renderer/assets/popover-Dn0RwMLI.js.map +0 -1
- package/dist/renderer/assets/select-DzIsLaKX.js.map +0 -1
- package/dist/renderer/assets/settings-CD-dacWB.js.map +0 -1
- package/dist/renderer/assets/settings-api-BV9MPckG.js.map +0 -1
- package/dist/renderer/assets/workbenchSettings-ClwgsvJL.js.map +0 -1
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/constants.js.map +0 -1
- package/dist/shared/ipc-channels.d.ts.map +0 -1
- package/dist/shared/ipc-channels.js.map +0 -1
- package/dist/shared/types.d.ts.map +0 -1
- package/dist/shared/types.js.map +0 -1
- package/dist/simulator/assets/simulator-C1eHXnuq.js.map +0 -1
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
2
|
import { app } from 'electron';
|
|
4
|
-
|
|
5
|
-
const simulatorDir = path.resolve(__dirname, '../../simulator');
|
|
3
|
+
import { simulatorDir } from '../utils/paths.js';
|
|
6
4
|
export const defaultAdapter = {
|
|
7
5
|
async openProject(opts) {
|
|
8
6
|
const diminaKit = await import('@dimina-kit/devkit');
|
|
@@ -22,6 +22,12 @@ export function listProjects() {
|
|
|
22
22
|
return load();
|
|
23
23
|
}
|
|
24
24
|
export function validateProjectDir(dirPath) {
|
|
25
|
+
if (!dirPath) {
|
|
26
|
+
return '小程序目录路径为空,请选择包含小程序源码的目录';
|
|
27
|
+
}
|
|
28
|
+
if (!fs.existsSync(dirPath)) {
|
|
29
|
+
return `小程序目录不存在:${dirPath}`;
|
|
30
|
+
}
|
|
25
31
|
if (!fs.existsSync(path.join(dirPath, 'app.json'))) {
|
|
26
32
|
const configPath = path.join(dirPath, 'project.config.json');
|
|
27
33
|
if (fs.existsSync(configPath)) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { simulatorDir } from '../../utils/paths.js';
|
|
2
2
|
//# sourceMappingURL=dir.d.ts.map
|
|
@@ -1,5 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
4
|
-
export const simulatorDir = path.join(__dirname, '../../../simulator');
|
|
1
|
+
export { simulatorDir } from '../../utils/paths.js';
|
|
5
2
|
//# sourceMappingURL=dir.js.map
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* events stop flowing until they close it.
|
|
17
17
|
*/
|
|
18
18
|
import { app, ipcMain, webContents as wcStatic } from 'electron';
|
|
19
|
-
import { SimulatorStorageChannel, } from '../../../shared/ipc-channels.js';
|
|
19
|
+
import { SimulatorElementChannel, SimulatorStorageChannel, } from '../../../shared/ipc-channels.js';
|
|
20
20
|
let attachedWc = null;
|
|
21
21
|
let hostWc = null;
|
|
22
22
|
function isSimulatorWebview(wc) {
|
|
@@ -108,6 +108,43 @@ async function getSnapshot() {
|
|
|
108
108
|
return [];
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
// Element inspection delegates to the simulator preload's __simulatorData
|
|
112
|
+
// bridge via executeJavaScript. Looked up independently from `attachedWc` so
|
|
113
|
+
// it keeps working when the user opens Chrome DevTools and the storage
|
|
114
|
+
// debugger detaches (debugger.attach is mutually exclusive with F12).
|
|
115
|
+
function findSimulatorWebContents() {
|
|
116
|
+
for (const wc of wcStatic.getAllWebContents()) {
|
|
117
|
+
if (isSimulatorWebview(wc))
|
|
118
|
+
return wc;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
async function inspectElement(sid) {
|
|
123
|
+
if (!sid)
|
|
124
|
+
return null;
|
|
125
|
+
const sim = findSimulatorWebContents();
|
|
126
|
+
if (!sim)
|
|
127
|
+
return null;
|
|
128
|
+
try {
|
|
129
|
+
const result = (await sim.executeJavaScript(`window.__simulatorData && window.__simulatorData.highlightElement ? window.__simulatorData.highlightElement(${JSON.stringify(sid)}) : null`));
|
|
130
|
+
return result ?? null;
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
console.warn('[simulator-element] inspect failed:', e.message);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async function clearElementInspection() {
|
|
138
|
+
const sim = findSimulatorWebContents();
|
|
139
|
+
if (!sim)
|
|
140
|
+
return;
|
|
141
|
+
try {
|
|
142
|
+
await sim.executeJavaScript('window.__simulatorData && window.__simulatorData.unhighlightElement && window.__simulatorData.unhighlightElement()');
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// best-effort
|
|
146
|
+
}
|
|
147
|
+
}
|
|
111
148
|
export function setupSimulatorStorage(host) {
|
|
112
149
|
hostWc = host;
|
|
113
150
|
// Attach to any simulator webview that already exists (project may have
|
|
@@ -132,5 +169,7 @@ export function setupSimulatorStorage(host) {
|
|
|
132
169
|
});
|
|
133
170
|
});
|
|
134
171
|
ipcMain.handle(SimulatorStorageChannel.GetSnapshot, getSnapshot);
|
|
172
|
+
ipcMain.handle(SimulatorElementChannel.Inspect, (_event, sid) => inspectElement(sid));
|
|
173
|
+
ipcMain.handle(SimulatorElementChannel.Clear, clearElementInspection);
|
|
135
174
|
}
|
|
136
175
|
//# sourceMappingURL=index.js.map
|
|
@@ -45,6 +45,18 @@ export function createWorkspaceService(ctx) {
|
|
|
45
45
|
clearSimulatorServicewechatReferer();
|
|
46
46
|
await disposeSession();
|
|
47
47
|
currentProjectPath = '';
|
|
48
|
+
// Reject obviously broken projects up front so we never spin up a
|
|
49
|
+
// compile/server that the simulator will then fetch asset-by-asset.
|
|
50
|
+
// Without this guard, deleted recent-list entries and typo'd CLI paths
|
|
51
|
+
// ride all the way through to the container runtime, whose
|
|
52
|
+
// `fetch(…).then(r=>r.text()).then(JSON.parse)` pipeline dies on the
|
|
53
|
+
// dev server's HTML SPA fallback with
|
|
54
|
+
// `SyntaxError: Unexpected token '<', "<!doctype "…`.
|
|
55
|
+
const dirError = repo.validateProjectDir(projectPath);
|
|
56
|
+
if (dirError) {
|
|
57
|
+
sendStatus('error', dirError);
|
|
58
|
+
return { success: false, error: dirError };
|
|
59
|
+
}
|
|
48
60
|
sendStatus('compiling', '正在编译...');
|
|
49
61
|
let session;
|
|
50
62
|
try {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export declare const devtoolsPackageRoot: string;
|
|
1
2
|
export declare const rendererDir: string;
|
|
2
3
|
export declare const defaultPreloadPath: string;
|
|
4
|
+
export declare const simulatorDir: string;
|
|
3
5
|
export declare function getRendererDir(): string;
|
|
4
6
|
export declare function getPreloadDir(): string;
|
|
5
7
|
export declare function getRendererHtml(filename: string): string;
|
package/dist/main/utils/paths.js
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import { fileURLToPath } from 'url';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Hardcoded relative jumps like '../../dist/renderer' break under the
|
|
5
|
+
// esbuild single-file bundle (dist/main/index.bundle.js): every inlined
|
|
6
|
+
// module ends up at the bundle's depth instead of its tsc location, so
|
|
7
|
+
// the math goes off by a directory. Walk up to this package's own
|
|
8
|
+
// package.json so the resolver works for tsc multi-file, the bundle,
|
|
9
|
+
// npm/pnpm node_modules, and asar packaging alike.
|
|
10
|
+
function resolveDevtoolsPackageRoot() {
|
|
11
|
+
let dir = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
const root = path.parse(dir).root;
|
|
13
|
+
while (dir !== root) {
|
|
14
|
+
const pkgPath = path.join(dir, 'package.json');
|
|
15
|
+
try {
|
|
16
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
17
|
+
if (pkg.name === '@dimina-kit/devtools')
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
catch { }
|
|
21
|
+
dir = path.dirname(dir);
|
|
22
|
+
}
|
|
23
|
+
throw new Error('@dimina-kit/devtools package root not found from ' + fileURLToPath(import.meta.url));
|
|
24
|
+
}
|
|
25
|
+
export const devtoolsPackageRoot = resolveDevtoolsPackageRoot();
|
|
26
|
+
export const rendererDir = path.join(devtoolsPackageRoot, 'dist/renderer');
|
|
27
|
+
export const defaultPreloadPath = path.join(devtoolsPackageRoot, 'dist/preload/windows/simulator.js');
|
|
28
|
+
export const simulatorDir = path.join(devtoolsPackageRoot, 'dist/simulator');
|
|
6
29
|
export function getRendererDir() {
|
|
7
30
|
return rendererDir;
|
|
8
31
|
}
|
|
9
32
|
export function getPreloadDir() {
|
|
10
|
-
return path.join(
|
|
33
|
+
return path.join(devtoolsPackageRoot, 'dist/preload');
|
|
11
34
|
}
|
|
12
35
|
export function getRendererHtml(filename) {
|
|
13
36
|
return path.join(rendererDir, filename);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerSyntheticSid } from '../runtime/bridge.js';
|
|
1
2
|
const INTERNAL_CLASSES = new Set([
|
|
2
3
|
'dd-swiper-wrapper', 'dd-swiper-slides', 'dd-swiper-slide-frame', 'dd-swiper-dots',
|
|
3
4
|
'dd-picker-overlay', 'dd-picker-container', 'dd-picker-header', 'dd-picker-body',
|
|
@@ -24,10 +25,44 @@ function resolveTemplateNameFromParent(instance) {
|
|
|
24
25
|
}
|
|
25
26
|
return null;
|
|
26
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Convert a PascalCase / camelCase identifier to kebab-case. Matches the
|
|
30
|
+
* upstream `camelCaseToUnderscore` (dimina/fe/packages/common) so a name
|
|
31
|
+
* already normalized by `withInstall` round-trips identically.
|
|
32
|
+
* `View` -> `view`, `ScrollView` -> `scroll-view`, `CoverImage` -> `cover-image`.
|
|
33
|
+
*/
|
|
34
|
+
function pascalToKebab(name) {
|
|
35
|
+
return name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the page's source path off Vue's provide/inject. dimina runtime.js 在
|
|
39
|
+
* dd-page 的 setup() 里调用 `provide('path', path)`,Vue 把它存到
|
|
40
|
+
* `instance.provides`。我们利用这个把页面节点的 tagName 从硬编码 `page`
|
|
41
|
+
* 升级为页面全路径(如 `pages/index/index`),对齐微信开发者工具。
|
|
42
|
+
*/
|
|
43
|
+
function resolvePagePath(instance) {
|
|
44
|
+
// dimina runtime 在 dd-page 的 setup 里 `instance.proxy.__page__ = true` 并
|
|
45
|
+
// `provide('path', path)`。两个标记同时具备才视为页面层级,避免 dd-page
|
|
46
|
+
// 的子节点继承 provides.path 后被误判(Vue 用 Object.create 链式 provides)。
|
|
47
|
+
const proxy = instance.proxy;
|
|
48
|
+
if (proxy?.__page__ !== true)
|
|
49
|
+
return null;
|
|
50
|
+
const provides = instance.provides;
|
|
51
|
+
const path = provides?.path;
|
|
52
|
+
if (typeof path !== 'string' || !path)
|
|
53
|
+
return null;
|
|
54
|
+
return path.startsWith('/') ? path.slice(1) : path;
|
|
55
|
+
}
|
|
27
56
|
function resolveTagName(instance) {
|
|
28
57
|
const type = instance.type;
|
|
29
58
|
if (!type)
|
|
30
59
|
return 'unknown';
|
|
60
|
+
// 页面层级优先:dd-page 没有 __tagName/__name,且 home 页等无 usingComponents
|
|
61
|
+
// 的页面 type.components 为 undefined,会落到 resolveTemplateNameFromParent
|
|
62
|
+
// 回到 'page'。在那之前直接用 provide('path') 的路径升级 tag 名。
|
|
63
|
+
const pagePath = resolvePagePath(instance);
|
|
64
|
+
if (pagePath)
|
|
65
|
+
return pagePath;
|
|
31
66
|
if (typeof type.__tagName === 'string')
|
|
32
67
|
return type.__tagName;
|
|
33
68
|
const name = (type.__name || type.name);
|
|
@@ -42,8 +77,16 @@ function resolveTagName(instance) {
|
|
|
42
77
|
return 'page';
|
|
43
78
|
if (name.startsWith('dd-'))
|
|
44
79
|
return name.slice(3);
|
|
80
|
+
// Reverse-map Dimina component names back to their miniprogram tag names.
|
|
81
|
+
// The installer (`withInstall`) sets `__tagName = camelCaseToUnderscore(__name)`,
|
|
82
|
+
// but in dev builds, custom registrations, or when the installer hasn't run,
|
|
83
|
+
// we may only see the raw `__name`/`name` (e.g. `View`, `ScrollView`, `DdButton`).
|
|
84
|
+
// Without this fallback the WXML panel would surface the upstream Vue name
|
|
85
|
+
// verbatim, defeating the panel's purpose of showing source-level tags.
|
|
45
86
|
if (name.startsWith('Dd') && name.length > 2)
|
|
46
|
-
return name.slice(2)
|
|
87
|
+
return pascalToKebab(name.slice(2));
|
|
88
|
+
if (/^[A-Z]/.test(name))
|
|
89
|
+
return pascalToKebab(name);
|
|
47
90
|
return name;
|
|
48
91
|
}
|
|
49
92
|
function extractProps(instance) {
|
|
@@ -79,7 +122,12 @@ function isInternalElement(vnode) {
|
|
|
79
122
|
function getElementSid(instance) {
|
|
80
123
|
const subTree = instance.subTree;
|
|
81
124
|
const el = subTree?.el;
|
|
82
|
-
|
|
125
|
+
if (!el?.getAttribute)
|
|
126
|
+
return undefined;
|
|
127
|
+
const sid = el.getAttribute('data-sid');
|
|
128
|
+
if (sid)
|
|
129
|
+
return sid;
|
|
130
|
+
return registerSyntheticSid(el);
|
|
83
131
|
}
|
|
84
132
|
function isTransparentComponent(instance, tagName) {
|
|
85
133
|
if (tagName === 'unknown')
|
|
@@ -97,9 +145,29 @@ function isTransparentComponent(instance, tagName) {
|
|
|
97
145
|
}
|
|
98
146
|
return false;
|
|
99
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* 检测 Vue 的 Comment vnode(`v-if`/`v-else`/`v-for` 占位锚点)。
|
|
150
|
+
* Vue 在条件不成立时会留下 `<!-- v-if -->` 这样的注释 vnode 用作 DOM 锚点。
|
|
151
|
+
* 这些 vnode 不是用户内容,必须从 wxml 树里剔除。
|
|
152
|
+
*
|
|
153
|
+
* - dev build:type 是 `Symbol('Comment')`,description = 'Comment' 或 'v-cmt'
|
|
154
|
+
* - prod build:description 可能丢失,但 children 一般是 'v-if'/'v-else' 等 marker
|
|
155
|
+
*/
|
|
156
|
+
function isCommentVNode(vnode) {
|
|
157
|
+
const type = vnode.type;
|
|
158
|
+
if (typeof type !== 'symbol')
|
|
159
|
+
return false;
|
|
160
|
+
const desc = type.description;
|
|
161
|
+
if (desc === 'Comment' || desc === 'v-cmt')
|
|
162
|
+
return true;
|
|
163
|
+
const children = typeof vnode.children === 'string' ? vnode.children : '';
|
|
164
|
+
return /^v-(if|else|else-if|for)$/.test(children);
|
|
165
|
+
}
|
|
100
166
|
function extractChildrenFromVNode(vnode, depth) {
|
|
101
167
|
if (!vnode || depth > 50)
|
|
102
168
|
return [];
|
|
169
|
+
if (isCommentVNode(vnode))
|
|
170
|
+
return [];
|
|
103
171
|
if (vnode.component) {
|
|
104
172
|
const result = walkInstance(vnode.component, depth + 1);
|
|
105
173
|
return result ? (Array.isArray(result) ? result : [result]) : [];
|
|
@@ -157,6 +225,69 @@ function extractChildrenFromVNode(vnode, depth) {
|
|
|
157
225
|
}
|
|
158
226
|
return result;
|
|
159
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Reverse-map Dimina's `<wrapper name="/components/foo/foo">` (used to host
|
|
230
|
+
* every user-defined component) back to the source-level tag the user wrote
|
|
231
|
+
* in WXML (e.g. `<foo>`).
|
|
232
|
+
*
|
|
233
|
+
* The wrapper carries the registered component path in its `name` Vue prop.
|
|
234
|
+
* By convention (also followed by miniprogram tooling), the registered key is
|
|
235
|
+
* the last path segment, optionally stripping a trailing `/index`. We can't
|
|
236
|
+
* see the page's `usingComponents` map from inside the Vue instance, so this
|
|
237
|
+
* convention-based recovery is best-effort: if the user picked a different
|
|
238
|
+
* registration key than the directory name (e.g. `usingComponents:
|
|
239
|
+
* { myCounter: '/components/counter/counter' }`), the panel will show
|
|
240
|
+
* `counter`, not `myCounter`.
|
|
241
|
+
*
|
|
242
|
+
* The path heuristic also resolves a name-collision risk: a user-written
|
|
243
|
+
* `<counter name="x">` would land in the same `attrs.name` slot as the
|
|
244
|
+
* wrapper-internal path. We only unwrap when the value looks like an absolute
|
|
245
|
+
* component path (leading `/`), which dimina always emits but a user would
|
|
246
|
+
* almost never type as a literal attr.
|
|
247
|
+
*/
|
|
248
|
+
function unwrapCustomComponent(node) {
|
|
249
|
+
if (node.tagName !== 'wrapper')
|
|
250
|
+
return node;
|
|
251
|
+
const path = node.attrs?.name;
|
|
252
|
+
if (typeof path !== 'string' || !path.startsWith('/'))
|
|
253
|
+
return node;
|
|
254
|
+
// 去掉前导 `/` 后保留原路径形式(保留所有斜杠,不做 kebab/dash 转换)。
|
|
255
|
+
// 仅当路径以 `/index` 结尾且剥离后仍至少剩一段时才剥(`/index` 单独存在则
|
|
256
|
+
// 退回 wrapper,避免出现空 tagName)。
|
|
257
|
+
const stripped = path.replace(/^\//, '');
|
|
258
|
+
const withoutIndex = stripped.endsWith('/index') ? stripped.slice(0, -'/index'.length) : stripped;
|
|
259
|
+
const recovered = withoutIndex || stripped;
|
|
260
|
+
if (!recovered || recovered === 'index')
|
|
261
|
+
return node;
|
|
262
|
+
const nextAttrs = {};
|
|
263
|
+
for (const [k, v] of Object.entries(node.attrs)) {
|
|
264
|
+
if (k === 'name')
|
|
265
|
+
continue;
|
|
266
|
+
nextAttrs[k] = v;
|
|
267
|
+
}
|
|
268
|
+
return { ...node, tagName: recovered, attrs: nextAttrs };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 把"用户授权"层级(页面 / 自定义组件,tagName 以路径形式呈现,含 `/`)
|
|
272
|
+
* 的 children 包一层合成 `#shadow-root`,对齐微信开发者工具:组件本身
|
|
273
|
+
* 与内部实现之间用 shadow-root 边界视觉分隔。
|
|
274
|
+
*
|
|
275
|
+
* 内置组件(view/text/button 等)和合成节点(#text/#fragment)tagName 都
|
|
276
|
+
* 不含 `/`,自然不会被包裹。children 为空也不插入(避免空壳)。重复调用
|
|
277
|
+
* 是幂等的:若 children[0] 已经是 #shadow-root 就跳过。
|
|
278
|
+
*/
|
|
279
|
+
function wrapInShadowRoot(node) {
|
|
280
|
+
if (!node.tagName.includes('/'))
|
|
281
|
+
return node;
|
|
282
|
+
if (node.children.length === 0)
|
|
283
|
+
return node;
|
|
284
|
+
if (node.children[0]?.tagName === '#shadow-root')
|
|
285
|
+
return node;
|
|
286
|
+
return {
|
|
287
|
+
...node,
|
|
288
|
+
children: [{ tagName: '#shadow-root', attrs: {}, children: node.children }],
|
|
289
|
+
};
|
|
290
|
+
}
|
|
160
291
|
export function walkInstance(instance, depth) {
|
|
161
292
|
if (depth > 50)
|
|
162
293
|
return null;
|
|
@@ -168,6 +299,6 @@ export function walkInstance(instance, depth) {
|
|
|
168
299
|
const sid = getElementSid(instance);
|
|
169
300
|
if (sid)
|
|
170
301
|
node.sid = sid;
|
|
171
|
-
return node;
|
|
302
|
+
return wrapInShadowRoot(unwrapCustomComponent(node));
|
|
172
303
|
}
|
|
173
304
|
//# sourceMappingURL=wxml-extract.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ElementInspection } from '../../shared/ipc-channels.js';
|
|
1
2
|
export interface WxmlNode {
|
|
2
3
|
tagName: string;
|
|
3
4
|
attrs: Record<string, string>;
|
|
@@ -20,7 +21,8 @@ interface SimulatorBridgeState {
|
|
|
20
21
|
wxml: Snapshot<WxmlNode | WxmlNode[] | null>;
|
|
21
22
|
refreshHandler: ((type: RefreshTarget) => void) | null;
|
|
22
23
|
}
|
|
23
|
-
declare function
|
|
24
|
+
export declare function registerSyntheticSid(el: HTMLElement): string;
|
|
25
|
+
declare function highlightElement(sid: string): ElementInspection | null;
|
|
24
26
|
declare function unhighlightElement(): void;
|
|
25
27
|
export { highlightElement, unhighlightElement };
|
|
26
28
|
export declare const simulatorBridge: SimulatorBridgeState;
|
|
@@ -7,6 +7,38 @@ const state = {
|
|
|
7
7
|
};
|
|
8
8
|
let highlightOverlay = null;
|
|
9
9
|
let exposedApi = null;
|
|
10
|
+
// 合成 sid 注册表:用 WeakMap 把元素 ↔ sid 双向绑定,避免在源 DOM 上写
|
|
11
|
+
// `data-*` 属性(提取本应只读,且属性形式会污染用户的快照/选择器)。
|
|
12
|
+
// elBySyntheticSid 为反向查找用 WeakRef,元素被 GC 后下次 lookup 自动清理。
|
|
13
|
+
const SYNTHETIC_SID_PREFIX = 'devtools-';
|
|
14
|
+
const syntheticSidByEl = new WeakMap();
|
|
15
|
+
const elBySyntheticSid = new Map();
|
|
16
|
+
let nextSyntheticSid = 1;
|
|
17
|
+
export function registerSyntheticSid(el) {
|
|
18
|
+
const existing = syntheticSidByEl.get(el);
|
|
19
|
+
if (existing)
|
|
20
|
+
return existing;
|
|
21
|
+
const synthetic = `${SYNTHETIC_SID_PREFIX}${nextSyntheticSid++}`;
|
|
22
|
+
syntheticSidByEl.set(el, synthetic);
|
|
23
|
+
elBySyntheticSid.set(synthetic, new WeakRef(el));
|
|
24
|
+
return synthetic;
|
|
25
|
+
}
|
|
26
|
+
function findElementBySid(doc, sid) {
|
|
27
|
+
if (sid.startsWith(SYNTHETIC_SID_PREFIX)) {
|
|
28
|
+
const ref = elBySyntheticSid.get(sid);
|
|
29
|
+
if (!ref)
|
|
30
|
+
return null;
|
|
31
|
+
const el = ref.deref();
|
|
32
|
+
if (!el || !el.isConnected) {
|
|
33
|
+
elBySyntheticSid.delete(sid);
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (el.ownerDocument !== doc)
|
|
37
|
+
return null;
|
|
38
|
+
return el;
|
|
39
|
+
}
|
|
40
|
+
return doc.querySelector(`[data-sid="${CSS.escape(sid)}"]`);
|
|
41
|
+
}
|
|
10
42
|
function clone(value) {
|
|
11
43
|
try {
|
|
12
44
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -27,18 +59,20 @@ function ensureOverlay(doc) {
|
|
|
27
59
|
highlightOverlay.style.cssText =
|
|
28
60
|
'position:fixed;pointer-events:none;z-index:999999;' +
|
|
29
61
|
'border:2px solid #1a73e8;background:rgba(26,115,232,0.12);' +
|
|
30
|
-
'transition:all 0.1s ease;display:none;border-radius:2px;';
|
|
62
|
+
'transition:all 0.1s ease;display:none;border-radius:2px;box-sizing:border-box;';
|
|
31
63
|
doc.body.appendChild(highlightOverlay);
|
|
32
64
|
return highlightOverlay;
|
|
33
65
|
}
|
|
34
66
|
function highlightElement(sid) {
|
|
67
|
+
if (!sid)
|
|
68
|
+
return null;
|
|
35
69
|
const iframe = getPageIframe();
|
|
36
70
|
if (!iframe?.contentDocument)
|
|
37
|
-
return;
|
|
71
|
+
return null;
|
|
38
72
|
const doc = iframe.contentDocument;
|
|
39
|
-
const el = doc
|
|
73
|
+
const el = findElementBySid(doc, sid);
|
|
40
74
|
if (!el)
|
|
41
|
-
return;
|
|
75
|
+
return null;
|
|
42
76
|
const rect = el.getBoundingClientRect();
|
|
43
77
|
const overlay = ensureOverlay(doc);
|
|
44
78
|
overlay.style.left = `${rect.left}px`;
|
|
@@ -46,6 +80,23 @@ function highlightElement(sid) {
|
|
|
46
80
|
overlay.style.width = `${rect.width}px`;
|
|
47
81
|
overlay.style.height = `${rect.height}px`;
|
|
48
82
|
overlay.style.display = 'block';
|
|
83
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
84
|
+
if (!style)
|
|
85
|
+
return null;
|
|
86
|
+
return {
|
|
87
|
+
sid,
|
|
88
|
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
89
|
+
style: {
|
|
90
|
+
display: style.display,
|
|
91
|
+
position: style.position,
|
|
92
|
+
boxSizing: style.boxSizing,
|
|
93
|
+
margin: style.margin,
|
|
94
|
+
padding: style.padding,
|
|
95
|
+
color: style.color,
|
|
96
|
+
backgroundColor: style.backgroundColor,
|
|
97
|
+
fontSize: style.fontSize,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
49
100
|
}
|
|
50
101
|
function unhighlightElement() {
|
|
51
102
|
if (highlightOverlay)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* window by our preload/instrumentation code.
|
|
6
6
|
*/
|
|
7
7
|
import type { Snapshot, StorageSnapshot, WxmlNode } from '../runtime/bridge.js';
|
|
8
|
+
import type { ElementInspection } from '../../shared/ipc-channels.js';
|
|
8
9
|
/** The simulator data bridge exposed via contextBridge. */
|
|
9
10
|
export interface SimulatorDataBridge {
|
|
10
11
|
getAppdata: () => Record<string, unknown>;
|
|
@@ -16,7 +17,7 @@ export interface SimulatorDataBridge {
|
|
|
16
17
|
getWxmlSnapshot: () => Snapshot<WxmlNode | WxmlNode[] | null>;
|
|
17
18
|
getWxmlGen: () => number;
|
|
18
19
|
refresh: (type: 'wxml' | 'appdata' | 'storage') => void;
|
|
19
|
-
highlightElement: (sid: string) =>
|
|
20
|
+
highlightElement: (sid: string) => ElementInspection | null;
|
|
20
21
|
unhighlightElement: () => void;
|
|
21
22
|
}
|
|
22
23
|
/** Hook interface for app data interception. */
|
|
@@ -10,6 +10,32 @@ var state = {
|
|
|
10
10
|
};
|
|
11
11
|
var highlightOverlay = null;
|
|
12
12
|
var exposedApi = null;
|
|
13
|
+
var SYNTHETIC_SID_PREFIX = "devtools-";
|
|
14
|
+
var syntheticSidByEl = /* @__PURE__ */ new WeakMap();
|
|
15
|
+
var elBySyntheticSid = /* @__PURE__ */ new Map();
|
|
16
|
+
var nextSyntheticSid = 1;
|
|
17
|
+
function registerSyntheticSid(el) {
|
|
18
|
+
const existing = syntheticSidByEl.get(el);
|
|
19
|
+
if (existing) return existing;
|
|
20
|
+
const synthetic = `${SYNTHETIC_SID_PREFIX}${nextSyntheticSid++}`;
|
|
21
|
+
syntheticSidByEl.set(el, synthetic);
|
|
22
|
+
elBySyntheticSid.set(synthetic, new WeakRef(el));
|
|
23
|
+
return synthetic;
|
|
24
|
+
}
|
|
25
|
+
function findElementBySid(doc, sid) {
|
|
26
|
+
if (sid.startsWith(SYNTHETIC_SID_PREFIX)) {
|
|
27
|
+
const ref = elBySyntheticSid.get(sid);
|
|
28
|
+
if (!ref) return null;
|
|
29
|
+
const el = ref.deref();
|
|
30
|
+
if (!el || !el.isConnected) {
|
|
31
|
+
elBySyntheticSid.delete(sid);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (el.ownerDocument !== doc) return null;
|
|
35
|
+
return el;
|
|
36
|
+
}
|
|
37
|
+
return doc.querySelector(`[data-sid="${CSS.escape(sid)}"]`);
|
|
38
|
+
}
|
|
13
39
|
function clone(value) {
|
|
14
40
|
try {
|
|
15
41
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -25,16 +51,17 @@ function ensureOverlay(doc) {
|
|
|
25
51
|
if (highlightOverlay && highlightOverlay.ownerDocument === doc) return highlightOverlay;
|
|
26
52
|
highlightOverlay = doc.createElement("div");
|
|
27
53
|
highlightOverlay.id = "__simulator-highlight";
|
|
28
|
-
highlightOverlay.style.cssText = "position:fixed;pointer-events:none;z-index:999999;border:2px solid #1a73e8;background:rgba(26,115,232,0.12);transition:all 0.1s ease;display:none;border-radius:2px;";
|
|
54
|
+
highlightOverlay.style.cssText = "position:fixed;pointer-events:none;z-index:999999;border:2px solid #1a73e8;background:rgba(26,115,232,0.12);transition:all 0.1s ease;display:none;border-radius:2px;box-sizing:border-box;";
|
|
29
55
|
doc.body.appendChild(highlightOverlay);
|
|
30
56
|
return highlightOverlay;
|
|
31
57
|
}
|
|
32
58
|
function highlightElement(sid) {
|
|
59
|
+
if (!sid) return null;
|
|
33
60
|
const iframe = getPageIframe();
|
|
34
|
-
if (!iframe?.contentDocument) return;
|
|
61
|
+
if (!iframe?.contentDocument) return null;
|
|
35
62
|
const doc = iframe.contentDocument;
|
|
36
|
-
const el = doc
|
|
37
|
-
if (!el) return;
|
|
63
|
+
const el = findElementBySid(doc, sid);
|
|
64
|
+
if (!el) return null;
|
|
38
65
|
const rect = el.getBoundingClientRect();
|
|
39
66
|
const overlay = ensureOverlay(doc);
|
|
40
67
|
overlay.style.left = `${rect.left}px`;
|
|
@@ -42,6 +69,22 @@ function highlightElement(sid) {
|
|
|
42
69
|
overlay.style.width = `${rect.width}px`;
|
|
43
70
|
overlay.style.height = `${rect.height}px`;
|
|
44
71
|
overlay.style.display = "block";
|
|
72
|
+
const style = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
73
|
+
if (!style) return null;
|
|
74
|
+
return {
|
|
75
|
+
sid,
|
|
76
|
+
rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
77
|
+
style: {
|
|
78
|
+
display: style.display,
|
|
79
|
+
position: style.position,
|
|
80
|
+
boxSizing: style.boxSizing,
|
|
81
|
+
margin: style.margin,
|
|
82
|
+
padding: style.padding,
|
|
83
|
+
color: style.color,
|
|
84
|
+
backgroundColor: style.backgroundColor,
|
|
85
|
+
fontSize: style.fontSize
|
|
86
|
+
}
|
|
87
|
+
};
|
|
45
88
|
}
|
|
46
89
|
function unhighlightElement() {
|
|
47
90
|
if (highlightOverlay) highlightOverlay.style.display = "none";
|
|
@@ -356,9 +399,22 @@ function resolveTemplateNameFromParent(instance) {
|
|
|
356
399
|
}
|
|
357
400
|
return null;
|
|
358
401
|
}
|
|
402
|
+
function pascalToKebab(name) {
|
|
403
|
+
return name.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
404
|
+
}
|
|
405
|
+
function resolvePagePath(instance) {
|
|
406
|
+
const proxy = instance.proxy;
|
|
407
|
+
if (proxy?.__page__ !== true) return null;
|
|
408
|
+
const provides = instance.provides;
|
|
409
|
+
const path = provides?.path;
|
|
410
|
+
if (typeof path !== "string" || !path) return null;
|
|
411
|
+
return path.startsWith("/") ? path.slice(1) : path;
|
|
412
|
+
}
|
|
359
413
|
function resolveTagName(instance) {
|
|
360
414
|
const type = instance.type;
|
|
361
415
|
if (!type) return "unknown";
|
|
416
|
+
const pagePath = resolvePagePath(instance);
|
|
417
|
+
if (pagePath) return pagePath;
|
|
362
418
|
if (typeof type.__tagName === "string") return type.__tagName;
|
|
363
419
|
const name = type.__name || type.name;
|
|
364
420
|
if (!name) {
|
|
@@ -368,7 +424,8 @@ function resolveTagName(instance) {
|
|
|
368
424
|
}
|
|
369
425
|
if (name === "dd-page") return "page";
|
|
370
426
|
if (name.startsWith("dd-")) return name.slice(3);
|
|
371
|
-
if (name.startsWith("Dd") && name.length > 2) return name.slice(2)
|
|
427
|
+
if (name.startsWith("Dd") && name.length > 2) return pascalToKebab(name.slice(2));
|
|
428
|
+
if (/^[A-Z]/.test(name)) return pascalToKebab(name);
|
|
372
429
|
return name;
|
|
373
430
|
}
|
|
374
431
|
function extractProps(instance) {
|
|
@@ -398,7 +455,10 @@ function isInternalElement(vnode) {
|
|
|
398
455
|
function getElementSid(instance) {
|
|
399
456
|
const subTree = instance.subTree;
|
|
400
457
|
const el = subTree?.el;
|
|
401
|
-
|
|
458
|
+
if (!el?.getAttribute) return void 0;
|
|
459
|
+
const sid = el.getAttribute("data-sid");
|
|
460
|
+
if (sid) return sid;
|
|
461
|
+
return registerSyntheticSid(el);
|
|
402
462
|
}
|
|
403
463
|
function isTransparentComponent(instance, tagName) {
|
|
404
464
|
if (tagName === "unknown") return true;
|
|
@@ -413,8 +473,17 @@ function isTransparentComponent(instance, tagName) {
|
|
|
413
473
|
}
|
|
414
474
|
return false;
|
|
415
475
|
}
|
|
476
|
+
function isCommentVNode(vnode) {
|
|
477
|
+
const type = vnode.type;
|
|
478
|
+
if (typeof type !== "symbol") return false;
|
|
479
|
+
const desc = type.description;
|
|
480
|
+
if (desc === "Comment" || desc === "v-cmt") return true;
|
|
481
|
+
const children = typeof vnode.children === "string" ? vnode.children : "";
|
|
482
|
+
return /^v-(if|else|else-if|for)$/.test(children);
|
|
483
|
+
}
|
|
416
484
|
function extractChildrenFromVNode(vnode, depth) {
|
|
417
485
|
if (!vnode || depth > 50) return [];
|
|
486
|
+
if (isCommentVNode(vnode)) return [];
|
|
418
487
|
if (vnode.component) {
|
|
419
488
|
const result2 = walkInstance(vnode.component, depth + 1);
|
|
420
489
|
return result2 ? Array.isArray(result2) ? result2 : [result2] : [];
|
|
@@ -465,6 +534,30 @@ function extractChildrenFromVNode(vnode, depth) {
|
|
|
465
534
|
}
|
|
466
535
|
return result;
|
|
467
536
|
}
|
|
537
|
+
function unwrapCustomComponent(node) {
|
|
538
|
+
if (node.tagName !== "wrapper") return node;
|
|
539
|
+
const path = node.attrs?.name;
|
|
540
|
+
if (typeof path !== "string" || !path.startsWith("/")) return node;
|
|
541
|
+
const stripped = path.replace(/^\//, "");
|
|
542
|
+
const withoutIndex = stripped.endsWith("/index") ? stripped.slice(0, -"/index".length) : stripped;
|
|
543
|
+
const recovered = withoutIndex || stripped;
|
|
544
|
+
if (!recovered || recovered === "index") return node;
|
|
545
|
+
const nextAttrs = {};
|
|
546
|
+
for (const [k, v] of Object.entries(node.attrs)) {
|
|
547
|
+
if (k === "name") continue;
|
|
548
|
+
nextAttrs[k] = v;
|
|
549
|
+
}
|
|
550
|
+
return { ...node, tagName: recovered, attrs: nextAttrs };
|
|
551
|
+
}
|
|
552
|
+
function wrapInShadowRoot(node) {
|
|
553
|
+
if (!node.tagName.includes("/")) return node;
|
|
554
|
+
if (node.children.length === 0) return node;
|
|
555
|
+
if (node.children[0]?.tagName === "#shadow-root") return node;
|
|
556
|
+
return {
|
|
557
|
+
...node,
|
|
558
|
+
children: [{ tagName: "#shadow-root", attrs: {}, children: node.children }]
|
|
559
|
+
};
|
|
560
|
+
}
|
|
468
561
|
function walkInstance(instance, depth) {
|
|
469
562
|
if (depth > 50) return null;
|
|
470
563
|
const tagName = resolveTagName(instance);
|
|
@@ -473,7 +566,7 @@ function walkInstance(instance, depth) {
|
|
|
473
566
|
const node = { tagName, attrs: extractProps(instance), children };
|
|
474
567
|
const sid = getElementSid(instance);
|
|
475
568
|
if (sid) node.sid = sid;
|
|
476
|
-
return node;
|
|
569
|
+
return wrapInShadowRoot(unwrapCustomComponent(node));
|
|
477
570
|
}
|
|
478
571
|
|
|
479
572
|
// src/preload/instrumentation/wxml.ts
|