vite-plugin-enter-dev 0.0.4 → 0.0.6
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/index.d.ts +22 -0
- package/dist/index.js +282 -0
- package/package.json +1 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
|
|
3
|
+
interface DevOptions {
|
|
4
|
+
/**
|
|
5
|
+
* 是否注入消息监听器脚本(默认:true)
|
|
6
|
+
*/
|
|
7
|
+
injectMessageListener?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare function enterDevPlugin(options?: DevOptions): Plugin[];
|
|
10
|
+
interface ProdOptions {
|
|
11
|
+
/**
|
|
12
|
+
* 是否注入 Google Fonts(默认:true)
|
|
13
|
+
*/
|
|
14
|
+
injectGoogleFonts?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Google Fonts 链接(可自定义)
|
|
17
|
+
*/
|
|
18
|
+
googleFontsUrl?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function enterProdPlugin(options?: ProdOptions): Plugin[];
|
|
21
|
+
|
|
22
|
+
export { enterDevPlugin, enterProdPlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import react from '@vitejs/plugin-react';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
|
|
5
|
+
// src/sourceInfoInjectPlugin.ts
|
|
6
|
+
function injectSourcePlugin({ types: t }) {
|
|
7
|
+
return {
|
|
8
|
+
name: "inject-source-location-enhanced",
|
|
9
|
+
visitor: {
|
|
10
|
+
JSXElement(path, state) {
|
|
11
|
+
const { node } = path;
|
|
12
|
+
const filename = state.filename || state.file.opts.filename || "unknown";
|
|
13
|
+
const start = node.loc?.start;
|
|
14
|
+
const end = node.loc?.end;
|
|
15
|
+
if (!start) return;
|
|
16
|
+
const relativePath = filename.replace(/^.*\/src\//, "src/").replace(/\\/g, "/");
|
|
17
|
+
const sourceLocation = `${relativePath}:${start.line}:${start.column + 1}`;
|
|
18
|
+
const elementType = getElementType(node.openingElement.name);
|
|
19
|
+
const elementInfo = {
|
|
20
|
+
fileName: relativePath,
|
|
21
|
+
lineNumber: start.line,
|
|
22
|
+
columnNumber: start.column + 1,
|
|
23
|
+
endLine: end?.line || start.line,
|
|
24
|
+
endColumn: end?.column ? end.column + 1 : start.column + 1,
|
|
25
|
+
elementType,
|
|
26
|
+
tagName: elementType.toLowerCase(),
|
|
27
|
+
// 获取元素的属性信息
|
|
28
|
+
attributes: node.openingElement.attributes.map((attr) => {
|
|
29
|
+
if (attr.type === "JSXAttribute" && attr.name) {
|
|
30
|
+
return {
|
|
31
|
+
name: attr.name.name,
|
|
32
|
+
hasValue: !!attr.value,
|
|
33
|
+
line: attr.loc?.start?.line,
|
|
34
|
+
column: attr.loc?.start?.column
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}).filter(Boolean),
|
|
39
|
+
// 记录是否有子元素
|
|
40
|
+
hasChildren: node.children && node.children.length > 0,
|
|
41
|
+
// 记录父元素信息(如果可获得)
|
|
42
|
+
parentElement: getParentElementType(path.parent)
|
|
43
|
+
};
|
|
44
|
+
const sourceLocationAttr = t.jsxAttribute(
|
|
45
|
+
t.jsxIdentifier("data-source-location"),
|
|
46
|
+
t.stringLiteral(sourceLocation)
|
|
47
|
+
);
|
|
48
|
+
const sourceStackAttr = t.jsxAttribute(
|
|
49
|
+
t.jsxIdentifier("data-source-stack"),
|
|
50
|
+
t.stringLiteral(JSON.stringify(elementInfo))
|
|
51
|
+
);
|
|
52
|
+
const elementTypeAttr = t.jsxAttribute(
|
|
53
|
+
t.jsxIdentifier("data-element-type"),
|
|
54
|
+
t.stringLiteral(elementType)
|
|
55
|
+
);
|
|
56
|
+
const lineNumberAttr = t.jsxAttribute(
|
|
57
|
+
t.jsxIdentifier("data-line-number"),
|
|
58
|
+
t.stringLiteral(start.line.toString())
|
|
59
|
+
);
|
|
60
|
+
const runtimeFileAttr = t.jsxAttribute(
|
|
61
|
+
t.jsxIdentifier("data-real-file"),
|
|
62
|
+
t.stringLiteral(relativePath)
|
|
63
|
+
);
|
|
64
|
+
const runtimeLineAttr = t.jsxAttribute(
|
|
65
|
+
t.jsxIdentifier("data-real-line"),
|
|
66
|
+
t.stringLiteral(start.line.toString())
|
|
67
|
+
);
|
|
68
|
+
const runtimeColumnAttr = t.jsxAttribute(
|
|
69
|
+
t.jsxIdentifier("data-real-column"),
|
|
70
|
+
t.stringLiteral((start.column + 1).toString())
|
|
71
|
+
);
|
|
72
|
+
const injectionMethodAttr = t.jsxAttribute(
|
|
73
|
+
t.jsxIdentifier("data-injected-source"),
|
|
74
|
+
t.stringLiteral("babel")
|
|
75
|
+
);
|
|
76
|
+
node.openingElement.attributes.push(
|
|
77
|
+
sourceLocationAttr,
|
|
78
|
+
sourceStackAttr,
|
|
79
|
+
elementTypeAttr,
|
|
80
|
+
lineNumberAttr,
|
|
81
|
+
runtimeFileAttr,
|
|
82
|
+
runtimeLineAttr,
|
|
83
|
+
runtimeColumnAttr,
|
|
84
|
+
injectionMethodAttr
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function getElementType(nameNode) {
|
|
91
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
92
|
+
return nameNode.name;
|
|
93
|
+
} else if (nameNode.type === "JSXMemberExpression") {
|
|
94
|
+
return `${nameNode.object.name}.${nameNode.property.name}`;
|
|
95
|
+
}
|
|
96
|
+
return "unknown";
|
|
97
|
+
}
|
|
98
|
+
function getParentElementType(parentNode) {
|
|
99
|
+
if (parentNode && parentNode.type === "JSXElement" && parentNode.openingElement) {
|
|
100
|
+
return getElementType(parentNode.openingElement.name);
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/index.ts
|
|
106
|
+
function enterDevPlugin(options = {}) {
|
|
107
|
+
const {
|
|
108
|
+
injectMessageListener = true
|
|
109
|
+
} = options;
|
|
110
|
+
const reactPlugin = react({
|
|
111
|
+
// 确保在开发模式下启用JSX源码映射
|
|
112
|
+
jsxImportSource: void 0,
|
|
113
|
+
jsxRuntime: "automatic",
|
|
114
|
+
// 启用构建时注入源码位置信息
|
|
115
|
+
babel: {
|
|
116
|
+
plugins: [
|
|
117
|
+
// 我们的自定义插件:在构建时注入源码位置
|
|
118
|
+
[injectSourcePlugin],
|
|
119
|
+
// 标准的React JSX转换
|
|
120
|
+
[
|
|
121
|
+
"@babel/plugin-transform-react-jsx",
|
|
122
|
+
{
|
|
123
|
+
runtime: "automatic",
|
|
124
|
+
importSource: "react",
|
|
125
|
+
development: true
|
|
126
|
+
// 保持开发模式以获得更好的调试体验
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
const htmlInjectPlugin = {
|
|
133
|
+
name: "enter-dev-message-listener",
|
|
134
|
+
enforce: "pre",
|
|
135
|
+
transformIndexHtml(html) {
|
|
136
|
+
if (!injectMessageListener) {
|
|
137
|
+
return html;
|
|
138
|
+
}
|
|
139
|
+
const messageListenerScript = `
|
|
140
|
+
<script>
|
|
141
|
+
(function() {
|
|
142
|
+
window.__earlyErrors__ = [];
|
|
143
|
+
window.__earlyConsoleLogs__ = [];
|
|
144
|
+
|
|
145
|
+
const earlyErrorHandler = function(event) {
|
|
146
|
+
window.__earlyErrors__.push({
|
|
147
|
+
type: 'error',
|
|
148
|
+
message: event.message,
|
|
149
|
+
filename: event.filename,
|
|
150
|
+
lineno: event.lineno,
|
|
151
|
+
colno: event.colno,
|
|
152
|
+
error: event.error ? {
|
|
153
|
+
name: event.error.name,
|
|
154
|
+
message: event.error.message,
|
|
155
|
+
stack: event.error.stack
|
|
156
|
+
} : null,
|
|
157
|
+
timestamp: Date.now()
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
window.addEventListener('error', earlyErrorHandler, true);
|
|
161
|
+
|
|
162
|
+
const earlyRejectionHandler = function(event) {
|
|
163
|
+
const reason = event.reason;
|
|
164
|
+
window.__earlyErrors__.push({
|
|
165
|
+
type: 'unhandledrejection',
|
|
166
|
+
reason: reason instanceof Error ? {
|
|
167
|
+
name: reason.name,
|
|
168
|
+
message: reason.message,
|
|
169
|
+
stack: reason.stack
|
|
170
|
+
} : String(reason),
|
|
171
|
+
timestamp: Date.now()
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
window.addEventListener('unhandledrejection', earlyRejectionHandler, true);
|
|
175
|
+
|
|
176
|
+
const originalConsoleError = console.error;
|
|
177
|
+
const originalConsoleWarn = console.warn;
|
|
178
|
+
|
|
179
|
+
console.error = function(...args) {
|
|
180
|
+
originalConsoleError.apply(console, args);
|
|
181
|
+
|
|
182
|
+
window.__earlyConsoleLogs__.push({
|
|
183
|
+
type: 'error',
|
|
184
|
+
args: args.map(arg => {
|
|
185
|
+
if (arg instanceof Error) {
|
|
186
|
+
return {
|
|
187
|
+
name: arg.name,
|
|
188
|
+
message: arg.message,
|
|
189
|
+
stack: arg.stack
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
return String(arg);
|
|
194
|
+
} catch {
|
|
195
|
+
return '[Unstringifiable]';
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
timestamp: Date.now()
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
console.warn = function(...args) {
|
|
203
|
+
originalConsoleWarn.apply(console, args);
|
|
204
|
+
|
|
205
|
+
window.__earlyConsoleLogs__.push({
|
|
206
|
+
type: 'warn',
|
|
207
|
+
args: args.map(arg => {
|
|
208
|
+
try {
|
|
209
|
+
return String(arg);
|
|
210
|
+
} catch {
|
|
211
|
+
return '[Unstringifiable]';
|
|
212
|
+
}
|
|
213
|
+
}),
|
|
214
|
+
timestamp: Date.now()
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
window.__cleanupEarlyErrorHandlers__ = function() {
|
|
219
|
+
window.removeEventListener('error', earlyErrorHandler, true);
|
|
220
|
+
window.removeEventListener('unhandledrejection', earlyRejectionHandler, true);
|
|
221
|
+
|
|
222
|
+
console.error = originalConsoleError;
|
|
223
|
+
console.warn = originalConsoleWarn;
|
|
224
|
+
|
|
225
|
+
delete window.__cleanupEarlyErrorHandlers__;
|
|
226
|
+
};
|
|
227
|
+
})();
|
|
228
|
+
|
|
229
|
+
window.addEventListener('message', (event) => {
|
|
230
|
+
if (event.data.type === 'inject-script') {
|
|
231
|
+
try {
|
|
232
|
+
console.log('Executing injector script in sandbox...');
|
|
233
|
+
|
|
234
|
+
const script = document.createElement('script');
|
|
235
|
+
script.textContent = event.data.script;
|
|
236
|
+
document.head.appendChild(script);
|
|
237
|
+
console.log('Injector script executed successfully');
|
|
238
|
+
} catch (error) {
|
|
239
|
+
console.error('Failed to execute injector script:', error);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
</script>`;
|
|
245
|
+
if (html.includes("</head>")) {
|
|
246
|
+
return html.replace("</head>", `${messageListenerScript}
|
|
247
|
+
</head>`);
|
|
248
|
+
} else if (html.includes("</title>")) {
|
|
249
|
+
return html.replace("</title>", `</title>${messageListenerScript}`);
|
|
250
|
+
}
|
|
251
|
+
return html;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
return [...reactPlugin, htmlInjectPlugin];
|
|
255
|
+
}
|
|
256
|
+
function enterProdPlugin(options = {}) {
|
|
257
|
+
const {
|
|
258
|
+
injectGoogleFonts = true,
|
|
259
|
+
googleFontsUrl = "https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900;950&family=Poppins:wght@100;200;300;400;500;600;700;800;900;950&family=Roboto:wght@100;200;300;400;500;600;700;800;900;950&display=swap"
|
|
260
|
+
} = options;
|
|
261
|
+
const fontsInjectPlugin = {
|
|
262
|
+
name: "enter-prod-fonts-inject",
|
|
263
|
+
enforce: "pre",
|
|
264
|
+
transformIndexHtml(html) {
|
|
265
|
+
if (!injectGoogleFonts) {
|
|
266
|
+
return html;
|
|
267
|
+
}
|
|
268
|
+
const googleFontsLink = `<link href="${googleFontsUrl}" rel="stylesheet">`;
|
|
269
|
+
if (html.includes(googleFontsUrl)) {
|
|
270
|
+
return html;
|
|
271
|
+
}
|
|
272
|
+
if (html.includes("</head>")) {
|
|
273
|
+
return html.replace("</head>", ` ${googleFontsLink}
|
|
274
|
+
</head>`);
|
|
275
|
+
}
|
|
276
|
+
return html;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
return [fontsInjectPlugin];
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export { enterDevPlugin, enterProdPlugin };
|