@ktjs/vite-plugin-ktjsx 0.1.2 → 0.1.3

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.
Files changed (2) hide show
  1. package/dist/index.js +237 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -68959,9 +68959,6 @@ var libExports$1 = requireLib();
68959
68959
 
68960
68960
  var libExports = requireLib$n();
68961
68961
 
68962
- const SVG_ATTR_FLAG = "__kt_svg__";
68963
- const MATHML_ATTR_FLAG = "__kt_mathml__";
68964
-
68965
68962
  function isSvgTag(tag) {
68966
68963
  return tag === "svg" || typeof tag === "string" && tag.startsWith("svg:");
68967
68964
  }
@@ -69029,7 +69026,7 @@ function addFlagToSvgMathMLElement(path) {
69029
69026
  return;
69030
69027
  }
69031
69028
  const attrs = opening.attributes || [];
69032
- const flag = inSvgContext ? SVG_ATTR_FLAG : MATHML_ATTR_FLAG;
69029
+ const flag = inSvgContext ? "__svg" : "__mathml";
69033
69030
  const hasFlag = attrs.some((a) => libExports.isJSXAttribute(a) && libExports.isJSXIdentifier(a.name) && a.name.name === flag);
69034
69031
  if (!hasFlag) {
69035
69032
  attrs.push(libExports.jsxAttribute(libExports.jsxIdentifier(flag)));
@@ -69038,66 +69035,67 @@ function addFlagToSvgMathMLElement(path) {
69038
69035
  }
69039
69036
 
69040
69037
  function hasConditionalDirective(element) {
69041
- const openingElement = element.openingElement;
69042
- const attributes = openingElement.attributes || [];
69038
+ const attributes = element.openingElement.attributes || [];
69043
69039
  return attributes.some((attr) => {
69044
- if (libExports$1.types.isJSXAttribute(attr)) {
69045
- const name = attr.name;
69046
- if (libExports$1.types.isJSXIdentifier(name)) {
69047
- return name.name === "k-if" || name.name === "k-else-if" || name.name === "k-else";
69048
- }
69040
+ if (!libExports.isJSXAttribute(attr)) {
69041
+ return false;
69049
69042
  }
69050
- return false;
69043
+ if (!libExports.isJSXIdentifier(attr.name)) {
69044
+ return false;
69045
+ }
69046
+ return attr.name.name === "k-if" || attr.name.name === "k-else-if" || attr.name.name === "k-else";
69051
69047
  });
69052
69048
  }
69053
69049
  function getConditionalDirective(element) {
69054
- const openingElement = element.openingElement;
69055
- const attributes = openingElement.attributes || [];
69050
+ const attributes = element.openingElement.attributes || [];
69056
69051
  for (const attr of attributes) {
69057
- if (libExports$1.types.isJSXAttribute(attr)) {
69058
- const name = attr.name;
69059
- if (libExports$1.types.isJSXIdentifier(name)) {
69060
- if (name.name === "k-if" || name.name === "k-else-if") {
69061
- let condition = null;
69062
- if (attr.value) {
69063
- if (libExports$1.types.isJSXExpressionContainer(attr.value)) {
69064
- condition = attr.value.expression;
69065
- } else {
69066
- condition = attr.value;
69067
- }
69068
- }
69069
- return {
69070
- type: name.name,
69071
- condition
69072
- };
69073
- } else if (name.name === "k-else") {
69074
- return { type: "k-else", condition: null };
69052
+ if (!libExports.isJSXAttribute(attr) || !libExports.isJSXIdentifier(attr.name)) {
69053
+ continue;
69054
+ }
69055
+ if (attr.name.name === "k-if" || attr.name.name === "k-else-if") {
69056
+ let condition = null;
69057
+ if (attr.value) {
69058
+ if (libExports.isJSXExpressionContainer(attr.value)) {
69059
+ condition = attr.value.expression;
69060
+ } else if (libExports.isStringLiteral(attr.value)) {
69061
+ condition = attr.value;
69075
69062
  }
69076
69063
  }
69064
+ return { type: attr.name.name, condition };
69065
+ }
69066
+ if (attr.name.name === "k-else") {
69067
+ return { type: "k-else", condition: null };
69077
69068
  }
69078
69069
  }
69079
69070
  return null;
69080
69071
  }
69081
69072
  function removeConditionalDirectives(attributes) {
69082
69073
  return attributes.filter((attr) => {
69083
- if (!libExports$1.types.isJSXAttribute(attr)) return true;
69084
- const name = attr.name;
69085
- if (!libExports$1.types.isJSXIdentifier(name)) return true;
69086
- return name.name !== "k-if" && name.name !== "k-else-if" && name.name !== "k-else";
69074
+ if (!libExports.isJSXAttribute(attr)) {
69075
+ return true;
69076
+ }
69077
+ if (!libExports.isJSXIdentifier(attr.name)) {
69078
+ return true;
69079
+ }
69080
+ return attr.name.name !== "k-if" && attr.name.name !== "k-else-if" && attr.name.name !== "k-else";
69087
69081
  });
69088
69082
  }
69089
69083
  function createCompoundCondition(prevConditions, currentCondition = null) {
69090
69084
  if (prevConditions.length === 0) {
69091
- return currentCondition || libExports$1.types.booleanLiteral(true);
69085
+ if (currentCondition && libExports.isExpression(currentCondition)) {
69086
+ return currentCondition;
69087
+ }
69088
+ return libExports.booleanLiteral(true);
69092
69089
  }
69093
- let compound = libExports$1.types.unaryExpression("!", prevConditions[0] || libExports$1.types.booleanLiteral(false));
69090
+ const first = prevConditions[0];
69091
+ let compound = libExports.unaryExpression("!", libExports.isExpression(first) ? first : libExports.booleanLiteral(false));
69094
69092
  for (let i = 1; i < prevConditions.length; i++) {
69095
- const condition = prevConditions[i] || libExports$1.types.booleanLiteral(false);
69096
- const negated = libExports$1.types.unaryExpression("!", condition);
69097
- compound = libExports$1.types.logicalExpression("&&", compound, negated);
69093
+ const condition = prevConditions[i];
69094
+ const negated = libExports.unaryExpression("!", libExports.isExpression(condition) ? condition : libExports.booleanLiteral(false));
69095
+ compound = libExports.logicalExpression("&&", compound, negated);
69098
69096
  }
69099
- if (currentCondition) {
69100
- compound = libExports$1.types.logicalExpression("&&", compound, currentCondition);
69097
+ if (currentCondition && libExports.isExpression(currentCondition)) {
69098
+ compound = libExports.logicalExpression("&&", compound, currentCondition);
69101
69099
  }
69102
69100
  return compound;
69103
69101
  }
@@ -69128,42 +69126,226 @@ function transformConditionalChains(path) {
69128
69126
  const elementPath = chain[i];
69129
69127
  const element = elementPath.node;
69130
69128
  const directive = getConditionalDirective(element);
69131
- if (!directive) continue;
69129
+ if (!directive) {
69130
+ continue;
69131
+ }
69132
69132
  const openingElement = element.openingElement;
69133
69133
  const newAttributes = removeConditionalDirectives(openingElement.attributes || []);
69134
69134
  let newCondition;
69135
69135
  if (directive.type === "k-if") {
69136
- newCondition = directive.condition || libExports$1.types.booleanLiteral(true);
69136
+ newCondition = libExports.isExpression(directive.condition) ? directive.condition : libExports.booleanLiteral(true);
69137
69137
  prevConditions.push(directive.condition);
69138
69138
  } else if (directive.type === "k-else-if") {
69139
69139
  if (prevConditions.length === 0) {
69140
- newCondition = directive.condition || libExports$1.types.booleanLiteral(true);
69140
+ newCondition = libExports.isExpression(directive.condition) ? directive.condition : libExports.booleanLiteral(true);
69141
69141
  } else {
69142
- const currentCondition = directive.condition || libExports$1.types.booleanLiteral(true);
69143
- newCondition = createCompoundCondition(prevConditions, currentCondition);
69142
+ newCondition = createCompoundCondition(prevConditions, directive.condition);
69144
69143
  }
69145
69144
  prevConditions.push(directive.condition);
69146
- } else if (directive.type === "k-else") {
69145
+ } else {
69147
69146
  if (prevConditions.length === 0) {
69148
- newCondition = libExports$1.types.booleanLiteral(true);
69147
+ newCondition = libExports.booleanLiteral(true);
69149
69148
  } else {
69150
69149
  newCondition = createCompoundCondition(prevConditions);
69151
69150
  }
69152
69151
  }
69153
- const kIfAttribute = libExports$1.types.jsxAttribute(libExports$1.types.jsxIdentifier("k-if"), libExports$1.types.jsxExpressionContainer(newCondition));
69154
- newAttributes.push(kIfAttribute);
69152
+ newAttributes.push(libExports.jsxAttribute(libExports.jsxIdentifier("k-if"), libExports.jsxExpressionContainer(newCondition)));
69155
69153
  openingElement.attributes = newAttributes;
69156
69154
  elementPath.skip();
69157
69155
  }
69158
69156
  }
69159
69157
 
69158
+ const KFOR_SINGLE_PATTERN = /^([A-Za-z_$][A-Za-z0-9_$]*)\s+(in|of)\s+([\s\S]+)$/;
69159
+ const KFOR_TUPLE_PATTERN = /^\(\s*([A-Za-z_$][A-Za-z0-9_$]*)(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?(?:\s*,\s*([A-Za-z_$][A-Za-z0-9_$]*))?\s*\)\s+(in|of)\s+([\s\S]+)$/;
69160
+ function validateDirectiveCombinations(path) {
69161
+ const opening = path.node.openingElement;
69162
+ const hasFor = hasAttribute(opening, "k-for");
69163
+ const hasIf = hasAttribute(opening, "k-if");
69164
+ const hasElse = hasAttribute(opening, "k-else");
69165
+ const hasElseIf = hasAttribute(opening, "k-else-if");
69166
+ if (hasIf && hasElse) {
69167
+ throw path.buildCodeFrameError(
69168
+ "Invalid directive usage: `k-if` and `k-else` cannot be used on the same element."
69169
+ );
69170
+ }
69171
+ if (hasFor && (hasIf || hasElseIf || hasElse)) {
69172
+ throw path.buildCodeFrameError(
69173
+ "Invalid directive usage: `k-for` cannot be combined with `k-if` / `k-else-if` / `k-else` on the same element."
69174
+ );
69175
+ }
69176
+ }
69177
+ function transformKFor(path) {
69178
+ const opening = path.node.openingElement;
69179
+ const forAttr = getAttribute(opening, "k-for");
69180
+ if (!forAttr) {
69181
+ return false;
69182
+ }
69183
+ const parsedForText = readAttributeStringValue(forAttr, path, "k-for");
69184
+ const parsedFor = parseKForExpression(parsedForText);
69185
+ if (!parsedFor) {
69186
+ throw path.buildCodeFrameError(
69187
+ "Invalid `k-for` expression. Expected `item in list` or `(item, index[, array]) in list`."
69188
+ );
69189
+ }
69190
+ const sourceExpression = parseTextAsExpression(path, parsedFor.source, "k-for source");
69191
+ const keyAttr = getAttribute(opening, "k-key");
69192
+ if (keyAttr && hasStringLikeValue(keyAttr)) {
69193
+ const keyText = readAttributeStringValue(keyAttr, path, "k-key");
69194
+ parseTextAsExpression(path, keyText, "k-key");
69195
+ }
69196
+ const renderNode = libExports.cloneNode(path.node, true);
69197
+ const renderOpening = renderNode.openingElement;
69198
+ renderOpening.attributes = removeAttributes(renderOpening.attributes, ["k-for", "k-key"]);
69199
+ const callbackParams = parsedFor.aliases.map((alias) => libExports.identifier(alias));
69200
+ const callback = libExports.arrowFunctionExpression(callbackParams, renderNode);
69201
+ const mapCall = libExports.callExpression(libExports.memberExpression(sourceExpression, libExports.identifier("map")), [callback]);
69202
+ if (isInsideJSXChildren(path)) {
69203
+ path.replaceWith(libExports.jsxExpressionContainer(mapCall));
69204
+ } else {
69205
+ path.replaceWith(mapCall);
69206
+ }
69207
+ return true;
69208
+ }
69209
+ function parseKForExpression(raw) {
69210
+ const value = raw.trim();
69211
+ if (!value) {
69212
+ return null;
69213
+ }
69214
+ const tupleMatch = KFOR_TUPLE_PATTERN.exec(value);
69215
+ if (tupleMatch) {
69216
+ const source = tupleMatch[5]?.trim();
69217
+ if (!source) {
69218
+ return null;
69219
+ }
69220
+ const aliases = [tupleMatch[1], tupleMatch[2], tupleMatch[3]].filter(
69221
+ (item) => typeof item === "string" && item.length > 0
69222
+ );
69223
+ return {
69224
+ aliases,
69225
+ source
69226
+ };
69227
+ }
69228
+ const singleMatch = KFOR_SINGLE_PATTERN.exec(value);
69229
+ if (singleMatch) {
69230
+ const source = singleMatch[3]?.trim();
69231
+ if (!source) {
69232
+ return null;
69233
+ }
69234
+ return {
69235
+ aliases: [singleMatch[1]],
69236
+ source
69237
+ };
69238
+ }
69239
+ return null;
69240
+ }
69241
+ function parseTextAsExpression(path, text, label) {
69242
+ try {
69243
+ const ast = libExports$1.parseSync(`(${text});`, {
69244
+ configFile: false,
69245
+ babelrc: false,
69246
+ sourceType: "module",
69247
+ parserOpts: {
69248
+ plugins: ["typescript", "jsx"]
69249
+ }
69250
+ });
69251
+ if (!ast) {
69252
+ throw new Error("Babel returned an empty AST.");
69253
+ }
69254
+ const statement = ast.program.body[0];
69255
+ if (!statement || !libExports.isExpressionStatement(statement)) {
69256
+ throw new Error("Expected an expression statement.");
69257
+ }
69258
+ return statement.expression;
69259
+ } catch (error) {
69260
+ const message = error instanceof Error ? error.message : String(error);
69261
+ throw path.buildCodeFrameError(`Failed to parse ${label}: ${message}`);
69262
+ }
69263
+ }
69264
+ function getAttribute(opening, name) {
69265
+ const attributes = opening.attributes || [];
69266
+ for (let i = 0; i < attributes.length; i++) {
69267
+ const attr = attributes[i];
69268
+ if (!libExports.isJSXAttribute(attr) || !libExports.isJSXIdentifier(attr.name)) {
69269
+ continue;
69270
+ }
69271
+ if (attr.name.name === name) {
69272
+ return attr;
69273
+ }
69274
+ }
69275
+ return void 0;
69276
+ }
69277
+ function hasAttribute(opening, name) {
69278
+ return !!getAttribute(opening, name);
69279
+ }
69280
+ function readAttributeStringValue(attr, path, attrName) {
69281
+ if (!attr.value) {
69282
+ throw path.buildCodeFrameError(`Directive \`${attrName}\` requires a string value.`);
69283
+ }
69284
+ if (libExports.isStringLiteral(attr.value)) {
69285
+ return attr.value.value;
69286
+ }
69287
+ if (!libExports.isJSXExpressionContainer(attr.value)) {
69288
+ throw path.buildCodeFrameError(`Directive \`${attrName}\` must be a string literal.`);
69289
+ }
69290
+ if (libExports.isJSXEmptyExpression(attr.value.expression)) {
69291
+ throw path.buildCodeFrameError(`Directive \`${attrName}\` cannot be empty.`);
69292
+ }
69293
+ if (libExports.isStringLiteral(attr.value.expression)) {
69294
+ return attr.value.expression.value;
69295
+ }
69296
+ if (libExports.isTemplateLiteral(attr.value.expression) && attr.value.expression.expressions.length === 0) {
69297
+ return attr.value.expression.quasis[0]?.value.cooked ?? "";
69298
+ }
69299
+ throw path.buildCodeFrameError(`Directive \`${attrName}\` must be a string literal.`);
69300
+ }
69301
+ function hasStringLikeValue(attr) {
69302
+ if (!attr.value) {
69303
+ return false;
69304
+ }
69305
+ if (libExports.isStringLiteral(attr.value)) {
69306
+ return true;
69307
+ }
69308
+ if (!libExports.isJSXExpressionContainer(attr.value)) {
69309
+ return false;
69310
+ }
69311
+ if (libExports.isStringLiteral(attr.value.expression)) {
69312
+ return true;
69313
+ }
69314
+ return libExports.isTemplateLiteral(attr.value.expression) && attr.value.expression.expressions.length === 0;
69315
+ }
69316
+ function removeAttributes(attributes, names) {
69317
+ const set = new Set(names);
69318
+ if (!attributes || attributes.length === 0) {
69319
+ return [];
69320
+ }
69321
+ return attributes.filter((attr) => {
69322
+ if (!libExports.isJSXAttribute(attr) || !libExports.isJSXIdentifier(attr.name)) {
69323
+ return true;
69324
+ }
69325
+ return !set.has(attr.name.name);
69326
+ });
69327
+ }
69328
+ function isInsideJSXChildren(path) {
69329
+ if (!path.inList || path.listKey !== "children") {
69330
+ return false;
69331
+ }
69332
+ const parent = path.parentPath;
69333
+ return !!parent && (parent.isJSXElement() || parent.isJSXFragment());
69334
+ }
69335
+
69160
69336
  function babelKTjsx() {
69161
69337
  return {
69162
69338
  name: "babel-plugin-ktjsx",
69163
69339
  visitor: {
69164
- JSXElement(path) {
69165
- transformConditionalChains(path);
69166
- addFlagToSvgMathMLElement(path);
69340
+ JSXElement: {
69341
+ enter(path) {
69342
+ validateDirectiveCombinations(path);
69343
+ transformConditionalChains(path);
69344
+ addFlagToSvgMathMLElement(path);
69345
+ },
69346
+ exit(path) {
69347
+ transformKFor(path);
69348
+ }
69167
69349
  }
69168
69350
  }
69169
69351
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ktjs/vite-plugin-ktjsx",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",