unplugin-zed-gpui 0.0.5 → 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.cjs CHANGED
@@ -50,7 +50,9 @@ const factoryElementTypes = {
50
50
  input: "HTMLInputElement",
51
51
  textarea: "HTMLTextAreaElement",
52
52
  btn: "HTMLButtonElement",
53
- select: "HTMLSelectElement"
53
+ select: "HTMLSelectElement",
54
+ svg: "SVGElement",
55
+ mathml: "MathMLElement"
54
56
  };
55
57
  const tagElementTypes = {
56
58
  a: "HTMLAnchorElement",
@@ -85,7 +87,11 @@ const zedGpuiPlugin = (0, unplugin.createUnplugin)((options = {}) => {
85
87
  return {
86
88
  name: "unplugin-zed-gpui",
87
89
  transform(code, id) {
88
- if (shouldProcessFile(id)) analyzeUsage(code, usedMethods, opts);
90
+ if (shouldProcessFile(id)) {
91
+ analyzeUsage(code, usedMethods, opts);
92
+ const result = transformForbiddenHCalls(code, opts);
93
+ if (result) return result;
94
+ }
89
95
  if (hasPrototypeEnhancement(code)) {
90
96
  if (opts.debug) {
91
97
  console.log(`[zed-gpui] Processing element file: ${id}`);
@@ -108,6 +114,44 @@ function shouldProcessFile(id) {
108
114
  if (id.includes("node_modules") || id.includes("dist")) return false;
109
115
  return /\.(ts|js|tsx|jsx)$/.test(id);
110
116
  }
117
+ function transformForbiddenHCalls(code, options) {
118
+ if (!hasZedGpuiImport(code, options) || !/\bh\s*\(\s*(['"])(svg|mathml)\1/.test(code)) return null;
119
+ try {
120
+ const ast = (0, _babel_parser.parse)(code, {
121
+ sourceType: "module",
122
+ plugins: ["typescript", "jsx"]
123
+ });
124
+ let modified = false;
125
+ visitAst(ast.program, (node) => {
126
+ if (!_babel_types.isCallExpression(node) || !_babel_types.isIdentifier(node.callee, { name: "h" })) return;
127
+ const arg = node.arguments[0];
128
+ if (!_babel_types.isStringLiteral(arg) || arg.value !== "svg" && arg.value !== "mathml") return;
129
+ node.callee = _babel_types.identifier("throws_");
130
+ node.arguments = [_babel_types.stringLiteral(arg.value === "svg" ? "Cannot use h('svg') to create SVGElement" : "Cannot use h('mathml') to create MathMLElement")];
131
+ node.typeArguments = void 0;
132
+ modified = true;
133
+ });
134
+ if (!modified) return null;
135
+ ast.program.body.push(_babel_types.functionDeclaration(_babel_types.identifier("throws_"), [_babel_types.identifier("message")], _babel_types.blockStatement([_babel_types.throwStatement(_babel_types.newExpression(_babel_types.identifier("Error"), [_babel_types.identifier("message")]))])));
136
+ return { code: (0, _babel_generator.default)(ast, {}, code).code };
137
+ } catch (error) {
138
+ if (options.debug) console.warn(`[zed-gpui] Failed to transform forbidden h() calls:`, error);
139
+ return null;
140
+ }
141
+ }
142
+ function hasZedGpuiImport(code, options) {
143
+ const packageName = options.zedGpuiPackageName.replace(/[.*+?^\${}()|[\]\\]/g, "\\$&");
144
+ return new RegExp(`from\\s+['"]${packageName}(?:\\/[^'"]*)?['"]|import\\s+['"]${packageName}(?:\\/[^'"]*)?['"]`).test(code);
145
+ }
146
+ function visitAst(node, visitor) {
147
+ visitor(node);
148
+ for (const key in node) {
149
+ if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
150
+ const child = node[key];
151
+ if (Array.isArray(child)) child.forEach((item) => item?.type && visitAst(item, visitor));
152
+ else if (child?.type) visitAst(child, visitor);
153
+ }
154
+ }
111
155
  /**
112
156
  * Analyze code to find zed-gpui method usage
113
157
  */
@@ -140,7 +184,7 @@ function hasZedGpuiUsage(code) {
140
184
  return /\.\s*([a-z_][a-zA-Z0-9_]*)\s*\(/.test(code);
141
185
  }
142
186
  function hasPrototypeEnhancement(code) {
143
- return code.includes("$_(") && /HTML[A-Za-z]*Element/.test(code);
187
+ return code.includes("$_(") && /(HTML[A-Za-z]*Element|SVGElement|MathMLElement)/.test(code);
144
188
  }
145
189
  /**
146
190
  * Recursively scan AST nodes for method calls
@@ -226,7 +270,7 @@ function getElementTypeFromTsType(node) {
226
270
  return _babel_types.isTSTypeReference(node) && _babel_types.isIdentifier(node.typeName) && isElementType(node.typeName.name) ? node.typeName.name : void 0;
227
271
  }
228
272
  function isElementType(name) {
229
- return name === "HTMLElement" || /^HTML[A-Za-z]*Element$/.test(name);
273
+ return name === "HTMLElement" || /^HTML[A-Za-z]*Element$/.test(name) || name === "SVGElement" || name === "MathMLElement";
230
274
  }
231
275
  /**
232
276
  * Transform element.ts to remove unused methods
@@ -238,21 +282,36 @@ function transformElementFile(code, usedMethods, options) {
238
282
  plugins: ["typescript"]
239
283
  });
240
284
  let modified = false;
285
+ const objectBindings = /* @__PURE__ */ new Map();
241
286
  const prototypeMethods = /* @__PURE__ */ new Map();
242
287
  const assignments = [];
288
+ for (const node of ast.program.body) {
289
+ if (!_babel_types.isVariableDeclaration(node)) continue;
290
+ for (const declaration of node.declarations) {
291
+ const init = declaration.init && unwrapExpression(declaration.init);
292
+ if (_babel_types.isIdentifier(declaration.id) && init && _babel_types.isObjectExpression(init)) objectBindings.set(declaration.id.name, init);
293
+ }
294
+ }
243
295
  for (const node of ast.program.body) {
244
296
  if (!_babel_types.isExpressionStatement(node)) continue;
245
297
  for (const expression of _babel_types.isSequenceExpression(node.expression) ? node.expression.expressions : [node.expression]) {
246
298
  if (!_babel_types.isCallExpression(expression) || !_babel_types.isIdentifier(expression.callee, { name: "$_" }) || expression.arguments.length < 2) continue;
247
299
  const targetPrototype = getEnhancedPrototype(expression.arguments[0]);
248
- const sourceArg = unwrapExpression(expression.arguments[1]);
249
- if (targetPrototype && _babel_types.isObjectExpression(sourceArg)) {
300
+ if (!targetPrototype) continue;
301
+ for (const arg of expression.arguments.slice(1)) {
302
+ const sourceArg = getObjectArgument(arg, objectBindings);
303
+ if (!sourceArg) continue;
250
304
  assignments.push({
251
305
  targetPrototype,
252
306
  sourceArg,
253
307
  call: expression
254
308
  });
255
- prototypeMethods.set(targetPrototype, new Set(sourceArg.properties.map((prop) => _babel_types.isObjectProperty(prop) || _babel_types.isObjectMethod(prop) ? getPropertyName(prop.key) : void 0).filter((methodName) => !!methodName)));
309
+ const methods = prototypeMethods.get(targetPrototype) ?? /* @__PURE__ */ new Set();
310
+ for (const prop of sourceArg.properties) {
311
+ const methodName = _babel_types.isObjectProperty(prop) || _babel_types.isObjectMethod(prop) ? getPropertyName(prop.key) : void 0;
312
+ if (methodName) methods.add(methodName);
313
+ }
314
+ prototypeMethods.set(targetPrototype, methods);
256
315
  }
257
316
  }
258
317
  }
@@ -271,15 +330,26 @@ function transformElementFile(code, usedMethods, options) {
271
330
  }
272
331
  }
273
332
  ast.program.body = ast.program.body.filter((node) => {
333
+ if (_babel_types.isVariableDeclaration(node)) {
334
+ const declarations = node.declarations.filter((declaration) => {
335
+ if (!_babel_types.isIdentifier(declaration.id)) return true;
336
+ const sourceArg = objectBindings.get(declaration.id.name);
337
+ if (!sourceArg || sourceArg.properties.length > 0) return true;
338
+ modified = true;
339
+ return false;
340
+ });
341
+ node.declarations = declarations;
342
+ return declarations.length > 0;
343
+ }
274
344
  if (!_babel_types.isExpressionStatement(node)) return true;
275
345
  if (_babel_types.isSequenceExpression(node.expression)) {
276
- const expressions = node.expression.expressions.filter((expression) => !assignments.some(({ call, sourceArg }) => call === expression && sourceArg.properties.length === 0));
346
+ const expressions = node.expression.expressions.filter((expression) => !isEmptyEnhancementCall(expression, assignments));
277
347
  if (expressions.length !== node.expression.expressions.length) modified = true;
278
348
  if (expressions.length === 0) return false;
279
349
  node.expression = expressions.length === 1 ? expressions[0] : _babel_types.sequenceExpression(expressions);
280
350
  return true;
281
351
  }
282
- const shouldRemove = assignments.some(({ call, sourceArg }) => call === node.expression && sourceArg.properties.length === 0);
352
+ const shouldRemove = isEmptyEnhancementCall(node.expression, assignments);
283
353
  if (shouldRemove) modified = true;
284
354
  return !shouldRemove;
285
355
  });
@@ -290,6 +360,14 @@ function transformElementFile(code, usedMethods, options) {
290
360
  return null;
291
361
  }
292
362
  }
363
+ function getObjectArgument(node, objectBindings) {
364
+ const unwrapped = unwrapExpression(node);
365
+ if (_babel_types.isObjectExpression(unwrapped)) return unwrapped;
366
+ return _babel_types.isIdentifier(unwrapped) ? objectBindings.get(unwrapped.name) : void 0;
367
+ }
368
+ function isEmptyEnhancementCall(expression, assignments) {
369
+ return _babel_types.isCallExpression(expression) && assignments.some(({ call, sourceArg }) => call === expression && sourceArg.properties.length === 0) && assignments.filter(({ call }) => call === expression).every(({ sourceArg }) => sourceArg.properties.length === 0);
370
+ }
293
371
  function getEnhancedPrototype(node) {
294
372
  return _babel_types.isIdentifier(node) && isElementType(node.name) ? node.name : void 0;
295
373
  }
@@ -302,6 +380,11 @@ function getPropertyName(node) {
302
380
  if (_babel_types.isStringLiteral(node)) return node.value;
303
381
  }
304
382
  function isMethodUsed(prototypeName, methodName, usedMethods, prototypeMethods) {
383
+ if ((prototypeName === "HTMLElement" || prototypeName === "SVGElement" || prototypeName === "MathMLElement") && prototypeMethods.get("HTMLElement")?.has(methodName) && prototypeMethods.get("SVGElement")?.has(methodName) && prototypeMethods.get("MathMLElement")?.has(methodName)) {
384
+ if (usedMethods.unknown.has(methodName)) return true;
385
+ for (const methods of usedMethods.byPrototype.values()) if (methods.has(methodName)) return true;
386
+ return false;
387
+ }
305
388
  if (usedMethods.unknown.has(methodName) || usedMethods.byPrototype.get(prototypeName)?.has(methodName)) return true;
306
389
  if (prototypeName !== "HTMLElement") return false;
307
390
  if (keepHTMLElementMethods.has(methodName)) return true;
package/dist/index.mjs CHANGED
@@ -22,7 +22,9 @@ const factoryElementTypes = {
22
22
  input: "HTMLInputElement",
23
23
  textarea: "HTMLTextAreaElement",
24
24
  btn: "HTMLButtonElement",
25
- select: "HTMLSelectElement"
25
+ select: "HTMLSelectElement",
26
+ svg: "SVGElement",
27
+ mathml: "MathMLElement"
26
28
  };
27
29
  const tagElementTypes = {
28
30
  a: "HTMLAnchorElement",
@@ -57,7 +59,11 @@ const zedGpuiPlugin = createUnplugin((options = {}) => {
57
59
  return {
58
60
  name: "unplugin-zed-gpui",
59
61
  transform(code, id) {
60
- if (shouldProcessFile(id)) analyzeUsage(code, usedMethods, opts);
62
+ if (shouldProcessFile(id)) {
63
+ analyzeUsage(code, usedMethods, opts);
64
+ const result = transformForbiddenHCalls(code, opts);
65
+ if (result) return result;
66
+ }
61
67
  if (hasPrototypeEnhancement(code)) {
62
68
  if (opts.debug) {
63
69
  console.log(`[zed-gpui] Processing element file: ${id}`);
@@ -80,6 +86,44 @@ function shouldProcessFile(id) {
80
86
  if (id.includes("node_modules") || id.includes("dist")) return false;
81
87
  return /\.(ts|js|tsx|jsx)$/.test(id);
82
88
  }
89
+ function transformForbiddenHCalls(code, options) {
90
+ if (!hasZedGpuiImport(code, options) || !/\bh\s*\(\s*(['"])(svg|mathml)\1/.test(code)) return null;
91
+ try {
92
+ const ast = parse(code, {
93
+ sourceType: "module",
94
+ plugins: ["typescript", "jsx"]
95
+ });
96
+ let modified = false;
97
+ visitAst(ast.program, (node) => {
98
+ if (!t.isCallExpression(node) || !t.isIdentifier(node.callee, { name: "h" })) return;
99
+ const arg = node.arguments[0];
100
+ if (!t.isStringLiteral(arg) || arg.value !== "svg" && arg.value !== "mathml") return;
101
+ node.callee = t.identifier("throws_");
102
+ node.arguments = [t.stringLiteral(arg.value === "svg" ? "Cannot use h('svg') to create SVGElement" : "Cannot use h('mathml') to create MathMLElement")];
103
+ node.typeArguments = void 0;
104
+ modified = true;
105
+ });
106
+ if (!modified) return null;
107
+ ast.program.body.push(t.functionDeclaration(t.identifier("throws_"), [t.identifier("message")], t.blockStatement([t.throwStatement(t.newExpression(t.identifier("Error"), [t.identifier("message")]))])));
108
+ return { code: generate(ast, {}, code).code };
109
+ } catch (error) {
110
+ if (options.debug) console.warn(`[zed-gpui] Failed to transform forbidden h() calls:`, error);
111
+ return null;
112
+ }
113
+ }
114
+ function hasZedGpuiImport(code, options) {
115
+ const packageName = options.zedGpuiPackageName.replace(/[.*+?^\${}()|[\]\\]/g, "\\$&");
116
+ return new RegExp(`from\\s+['"]${packageName}(?:\\/[^'"]*)?['"]|import\\s+['"]${packageName}(?:\\/[^'"]*)?['"]`).test(code);
117
+ }
118
+ function visitAst(node, visitor) {
119
+ visitor(node);
120
+ for (const key in node) {
121
+ if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
122
+ const child = node[key];
123
+ if (Array.isArray(child)) child.forEach((item) => item?.type && visitAst(item, visitor));
124
+ else if (child?.type) visitAst(child, visitor);
125
+ }
126
+ }
83
127
  /**
84
128
  * Analyze code to find zed-gpui method usage
85
129
  */
@@ -112,7 +156,7 @@ function hasZedGpuiUsage(code) {
112
156
  return /\.\s*([a-z_][a-zA-Z0-9_]*)\s*\(/.test(code);
113
157
  }
114
158
  function hasPrototypeEnhancement(code) {
115
- return code.includes("$_(") && /HTML[A-Za-z]*Element/.test(code);
159
+ return code.includes("$_(") && /(HTML[A-Za-z]*Element|SVGElement|MathMLElement)/.test(code);
116
160
  }
117
161
  /**
118
162
  * Recursively scan AST nodes for method calls
@@ -198,7 +242,7 @@ function getElementTypeFromTsType(node) {
198
242
  return t.isTSTypeReference(node) && t.isIdentifier(node.typeName) && isElementType(node.typeName.name) ? node.typeName.name : void 0;
199
243
  }
200
244
  function isElementType(name) {
201
- return name === "HTMLElement" || /^HTML[A-Za-z]*Element$/.test(name);
245
+ return name === "HTMLElement" || /^HTML[A-Za-z]*Element$/.test(name) || name === "SVGElement" || name === "MathMLElement";
202
246
  }
203
247
  /**
204
248
  * Transform element.ts to remove unused methods
@@ -210,21 +254,36 @@ function transformElementFile(code, usedMethods, options) {
210
254
  plugins: ["typescript"]
211
255
  });
212
256
  let modified = false;
257
+ const objectBindings = /* @__PURE__ */ new Map();
213
258
  const prototypeMethods = /* @__PURE__ */ new Map();
214
259
  const assignments = [];
260
+ for (const node of ast.program.body) {
261
+ if (!t.isVariableDeclaration(node)) continue;
262
+ for (const declaration of node.declarations) {
263
+ const init = declaration.init && unwrapExpression(declaration.init);
264
+ if (t.isIdentifier(declaration.id) && init && t.isObjectExpression(init)) objectBindings.set(declaration.id.name, init);
265
+ }
266
+ }
215
267
  for (const node of ast.program.body) {
216
268
  if (!t.isExpressionStatement(node)) continue;
217
269
  for (const expression of t.isSequenceExpression(node.expression) ? node.expression.expressions : [node.expression]) {
218
270
  if (!t.isCallExpression(expression) || !t.isIdentifier(expression.callee, { name: "$_" }) || expression.arguments.length < 2) continue;
219
271
  const targetPrototype = getEnhancedPrototype(expression.arguments[0]);
220
- const sourceArg = unwrapExpression(expression.arguments[1]);
221
- if (targetPrototype && t.isObjectExpression(sourceArg)) {
272
+ if (!targetPrototype) continue;
273
+ for (const arg of expression.arguments.slice(1)) {
274
+ const sourceArg = getObjectArgument(arg, objectBindings);
275
+ if (!sourceArg) continue;
222
276
  assignments.push({
223
277
  targetPrototype,
224
278
  sourceArg,
225
279
  call: expression
226
280
  });
227
- prototypeMethods.set(targetPrototype, new Set(sourceArg.properties.map((prop) => t.isObjectProperty(prop) || t.isObjectMethod(prop) ? getPropertyName(prop.key) : void 0).filter((methodName) => !!methodName)));
281
+ const methods = prototypeMethods.get(targetPrototype) ?? /* @__PURE__ */ new Set();
282
+ for (const prop of sourceArg.properties) {
283
+ const methodName = t.isObjectProperty(prop) || t.isObjectMethod(prop) ? getPropertyName(prop.key) : void 0;
284
+ if (methodName) methods.add(methodName);
285
+ }
286
+ prototypeMethods.set(targetPrototype, methods);
228
287
  }
229
288
  }
230
289
  }
@@ -243,15 +302,26 @@ function transformElementFile(code, usedMethods, options) {
243
302
  }
244
303
  }
245
304
  ast.program.body = ast.program.body.filter((node) => {
305
+ if (t.isVariableDeclaration(node)) {
306
+ const declarations = node.declarations.filter((declaration) => {
307
+ if (!t.isIdentifier(declaration.id)) return true;
308
+ const sourceArg = objectBindings.get(declaration.id.name);
309
+ if (!sourceArg || sourceArg.properties.length > 0) return true;
310
+ modified = true;
311
+ return false;
312
+ });
313
+ node.declarations = declarations;
314
+ return declarations.length > 0;
315
+ }
246
316
  if (!t.isExpressionStatement(node)) return true;
247
317
  if (t.isSequenceExpression(node.expression)) {
248
- const expressions = node.expression.expressions.filter((expression) => !assignments.some(({ call, sourceArg }) => call === expression && sourceArg.properties.length === 0));
318
+ const expressions = node.expression.expressions.filter((expression) => !isEmptyEnhancementCall(expression, assignments));
249
319
  if (expressions.length !== node.expression.expressions.length) modified = true;
250
320
  if (expressions.length === 0) return false;
251
321
  node.expression = expressions.length === 1 ? expressions[0] : t.sequenceExpression(expressions);
252
322
  return true;
253
323
  }
254
- const shouldRemove = assignments.some(({ call, sourceArg }) => call === node.expression && sourceArg.properties.length === 0);
324
+ const shouldRemove = isEmptyEnhancementCall(node.expression, assignments);
255
325
  if (shouldRemove) modified = true;
256
326
  return !shouldRemove;
257
327
  });
@@ -262,6 +332,14 @@ function transformElementFile(code, usedMethods, options) {
262
332
  return null;
263
333
  }
264
334
  }
335
+ function getObjectArgument(node, objectBindings) {
336
+ const unwrapped = unwrapExpression(node);
337
+ if (t.isObjectExpression(unwrapped)) return unwrapped;
338
+ return t.isIdentifier(unwrapped) ? objectBindings.get(unwrapped.name) : void 0;
339
+ }
340
+ function isEmptyEnhancementCall(expression, assignments) {
341
+ return t.isCallExpression(expression) && assignments.some(({ call, sourceArg }) => call === expression && sourceArg.properties.length === 0) && assignments.filter(({ call }) => call === expression).every(({ sourceArg }) => sourceArg.properties.length === 0);
342
+ }
265
343
  function getEnhancedPrototype(node) {
266
344
  return t.isIdentifier(node) && isElementType(node.name) ? node.name : void 0;
267
345
  }
@@ -274,6 +352,11 @@ function getPropertyName(node) {
274
352
  if (t.isStringLiteral(node)) return node.value;
275
353
  }
276
354
  function isMethodUsed(prototypeName, methodName, usedMethods, prototypeMethods) {
355
+ if ((prototypeName === "HTMLElement" || prototypeName === "SVGElement" || prototypeName === "MathMLElement") && prototypeMethods.get("HTMLElement")?.has(methodName) && prototypeMethods.get("SVGElement")?.has(methodName) && prototypeMethods.get("MathMLElement")?.has(methodName)) {
356
+ if (usedMethods.unknown.has(methodName)) return true;
357
+ for (const methods of usedMethods.byPrototype.values()) if (methods.has(methodName)) return true;
358
+ return false;
359
+ }
277
360
  if (usedMethods.unknown.has(methodName) || usedMethods.byPrototype.get(prototypeName)?.has(methodName)) return true;
278
361
  if (prototypeName !== "HTMLElement") return false;
279
362
  if (keepHTMLElementMethods.has(methodName)) return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unplugin-zed-gpui",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Unplugin for zed-gpui tree-shaking optimization - removes unused zed-gpui methods from bundle",
5
5
  "author": {
6
6
  "name": "Kasukabe Tsumugi",