@knighted/jsx 1.11.0 → 1.12.0

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.
@@ -239,6 +239,46 @@ const collectTopLevelDeclarationMetadata = (body) => {
239
239
  }
240
240
  return declarations;
241
241
  };
242
+ const unwrapExpressionNode = (value) => {
243
+ let current = value;
244
+ while (isObjectRecord(current) && typeof current.type === 'string') {
245
+ if (current.type === 'ParenthesizedExpression') {
246
+ current = current.expression;
247
+ continue;
248
+ }
249
+ if (current.type === 'TSAsExpression' ||
250
+ current.type === 'TSSatisfiesExpression' ||
251
+ current.type === 'TSInstantiationExpression' ||
252
+ current.type === 'TSNonNullExpression' ||
253
+ current.type === 'TSTypeAssertion') {
254
+ current = current.expression;
255
+ continue;
256
+ }
257
+ break;
258
+ }
259
+ return current;
260
+ };
261
+ const isJsxExpressionNode = (value) => {
262
+ const unwrapped = unwrapExpressionNode(value);
263
+ if (!isObjectRecord(unwrapped) || typeof unwrapped.type !== 'string') {
264
+ return false;
265
+ }
266
+ return unwrapped.type === 'JSXElement' || unwrapped.type === 'JSXFragment';
267
+ };
268
+ const collectTopLevelJsxExpressionMetadata = (body) => {
269
+ if (!Array.isArray(body)) {
270
+ return false;
271
+ }
272
+ for (const statement of body) {
273
+ if (!isObjectRecord(statement) || statement.type !== 'ExpressionStatement') {
274
+ continue;
275
+ }
276
+ if (isJsxExpressionNode(statement.expression)) {
277
+ return true;
278
+ }
279
+ }
280
+ return false;
281
+ };
242
282
  const ensureSupportedOptions = (options) => {
243
283
  if (options.sourceType !== undefined &&
244
284
  options.sourceType !== 'module' &&
@@ -259,6 +299,10 @@ const ensureSupportedOptions = (options) => {
259
299
  typeof options.collectTopLevelDeclarations !== 'boolean') {
260
300
  throw new Error(`[jsx] Unsupported collectTopLevelDeclarations value "${String(options.collectTopLevelDeclarations)}". Use true or false.`);
261
301
  }
302
+ if (options.collectTopLevelJsxExpression !== undefined &&
303
+ typeof options.collectTopLevelJsxExpression !== 'boolean') {
304
+ throw new Error(`[jsx] Unsupported collectTopLevelJsxExpression value "${String(options.collectTopLevelJsxExpression)}". Use true or false.`);
305
+ }
262
306
  };
263
307
  function transformJsxSource(source, options = {}) {
264
308
  const internalOptions = options;
@@ -272,6 +316,9 @@ function transformJsxSource(source, options = {}) {
272
316
  const declarations = internalOptions.collectTopLevelDeclarations
273
317
  ? collectTopLevelDeclarationMetadata(parsed.program.body)
274
318
  : undefined;
319
+ const hasTopLevelJsxExpression = internalOptions.collectTopLevelJsxExpression
320
+ ? collectTopLevelJsxExpressionMetadata(parsed.program.body)
321
+ : undefined;
275
322
  if (parserDiagnostics.length) {
276
323
  return {
277
324
  code: source,
@@ -279,6 +326,7 @@ function transformJsxSource(source, options = {}) {
279
326
  imports,
280
327
  diagnostics: parserDiagnostics,
281
328
  declarations,
329
+ hasTopLevelJsxExpression,
282
330
  };
283
331
  }
284
332
  const transpileBaseOptions = {
@@ -295,6 +343,7 @@ function transformJsxSource(source, options = {}) {
295
343
  imports,
296
344
  diagnostics: parserDiagnostics,
297
345
  declarations,
346
+ hasTopLevelJsxExpression,
298
347
  };
299
348
  }
300
349
  if (typescriptStripBackend === 'transpile-manual') {
@@ -308,6 +357,7 @@ function transformJsxSource(source, options = {}) {
308
357
  imports,
309
358
  diagnostics: parserDiagnostics,
310
359
  declarations,
360
+ hasTopLevelJsxExpression,
311
361
  };
312
362
  }
313
363
  const transformed = (0, oxc_transform_1.transformSync)('transform-jsx-source.tsx', source, {
@@ -326,6 +376,7 @@ function transformJsxSource(source, options = {}) {
326
376
  imports,
327
377
  diagnostics,
328
378
  declarations,
379
+ hasTopLevelJsxExpression,
329
380
  };
330
381
  }
331
382
  const jsxResult = (0, transpile_js_1.transpileJsxSource)(transformed.code, transpileBaseOptions);
@@ -335,5 +386,6 @@ function transformJsxSource(source, options = {}) {
335
386
  imports,
336
387
  diagnostics,
337
388
  declarations,
389
+ hasTopLevelJsxExpression,
338
390
  };
339
391
  }
@@ -36,6 +36,7 @@ export type TransformTopLevelDeclaration = {
36
36
  };
37
37
  export type TransformJsxSourceOptions = TranspileJsxSourceOptions & {
38
38
  collectTopLevelDeclarations?: boolean;
39
+ collectTopLevelJsxExpression?: boolean;
39
40
  };
40
41
  export type TransformJsxSourceResult = {
41
42
  code: string;
@@ -43,6 +44,7 @@ export type TransformJsxSourceResult = {
43
44
  imports: TransformImport[];
44
45
  diagnostics: TransformDiagnostic[];
45
46
  declarations?: TransformTopLevelDeclaration[];
47
+ hasTopLevelJsxExpression?: boolean;
46
48
  };
47
49
  export declare function transformJsxSource(source: string, options?: TransformJsxSourceOptions): TransformJsxSourceResult;
48
50
  export {};
@@ -36,6 +36,7 @@ export type TransformTopLevelDeclaration = {
36
36
  };
37
37
  export type TransformJsxSourceOptions = TranspileJsxSourceOptions & {
38
38
  collectTopLevelDeclarations?: boolean;
39
+ collectTopLevelJsxExpression?: boolean;
39
40
  };
40
41
  export type TransformJsxSourceResult = {
41
42
  code: string;
@@ -43,6 +44,7 @@ export type TransformJsxSourceResult = {
43
44
  imports: TransformImport[];
44
45
  diagnostics: TransformDiagnostic[];
45
46
  declarations?: TransformTopLevelDeclaration[];
47
+ hasTopLevelJsxExpression?: boolean;
46
48
  };
47
49
  export declare function transformJsxSource(source: string, options?: TransformJsxSourceOptions): TransformJsxSourceResult;
48
50
  export {};
package/dist/transform.js CHANGED
@@ -236,6 +236,46 @@ const collectTopLevelDeclarationMetadata = (body) => {
236
236
  }
237
237
  return declarations;
238
238
  };
239
+ const unwrapExpressionNode = (value) => {
240
+ let current = value;
241
+ while (isObjectRecord(current) && typeof current.type === 'string') {
242
+ if (current.type === 'ParenthesizedExpression') {
243
+ current = current.expression;
244
+ continue;
245
+ }
246
+ if (current.type === 'TSAsExpression' ||
247
+ current.type === 'TSSatisfiesExpression' ||
248
+ current.type === 'TSInstantiationExpression' ||
249
+ current.type === 'TSNonNullExpression' ||
250
+ current.type === 'TSTypeAssertion') {
251
+ current = current.expression;
252
+ continue;
253
+ }
254
+ break;
255
+ }
256
+ return current;
257
+ };
258
+ const isJsxExpressionNode = (value) => {
259
+ const unwrapped = unwrapExpressionNode(value);
260
+ if (!isObjectRecord(unwrapped) || typeof unwrapped.type !== 'string') {
261
+ return false;
262
+ }
263
+ return unwrapped.type === 'JSXElement' || unwrapped.type === 'JSXFragment';
264
+ };
265
+ const collectTopLevelJsxExpressionMetadata = (body) => {
266
+ if (!Array.isArray(body)) {
267
+ return false;
268
+ }
269
+ for (const statement of body) {
270
+ if (!isObjectRecord(statement) || statement.type !== 'ExpressionStatement') {
271
+ continue;
272
+ }
273
+ if (isJsxExpressionNode(statement.expression)) {
274
+ return true;
275
+ }
276
+ }
277
+ return false;
278
+ };
239
279
  const ensureSupportedOptions = (options) => {
240
280
  if (options.sourceType !== undefined &&
241
281
  options.sourceType !== 'module' &&
@@ -256,6 +296,10 @@ const ensureSupportedOptions = (options) => {
256
296
  typeof options.collectTopLevelDeclarations !== 'boolean') {
257
297
  throw new Error(`[jsx] Unsupported collectTopLevelDeclarations value "${String(options.collectTopLevelDeclarations)}". Use true or false.`);
258
298
  }
299
+ if (options.collectTopLevelJsxExpression !== undefined &&
300
+ typeof options.collectTopLevelJsxExpression !== 'boolean') {
301
+ throw new Error(`[jsx] Unsupported collectTopLevelJsxExpression value "${String(options.collectTopLevelJsxExpression)}". Use true or false.`);
302
+ }
259
303
  };
260
304
  export function transformJsxSource(source, options = {}) {
261
305
  const internalOptions = options;
@@ -269,6 +313,9 @@ export function transformJsxSource(source, options = {}) {
269
313
  const declarations = internalOptions.collectTopLevelDeclarations
270
314
  ? collectTopLevelDeclarationMetadata(parsed.program.body)
271
315
  : undefined;
316
+ const hasTopLevelJsxExpression = internalOptions.collectTopLevelJsxExpression
317
+ ? collectTopLevelJsxExpressionMetadata(parsed.program.body)
318
+ : undefined;
272
319
  if (parserDiagnostics.length) {
273
320
  return {
274
321
  code: source,
@@ -276,6 +323,7 @@ export function transformJsxSource(source, options = {}) {
276
323
  imports,
277
324
  diagnostics: parserDiagnostics,
278
325
  declarations,
326
+ hasTopLevelJsxExpression,
279
327
  };
280
328
  }
281
329
  const transpileBaseOptions = {
@@ -292,6 +340,7 @@ export function transformJsxSource(source, options = {}) {
292
340
  imports,
293
341
  diagnostics: parserDiagnostics,
294
342
  declarations,
343
+ hasTopLevelJsxExpression,
295
344
  };
296
345
  }
297
346
  if (typescriptStripBackend === 'transpile-manual') {
@@ -305,6 +354,7 @@ export function transformJsxSource(source, options = {}) {
305
354
  imports,
306
355
  diagnostics: parserDiagnostics,
307
356
  declarations,
357
+ hasTopLevelJsxExpression,
308
358
  };
309
359
  }
310
360
  const transformed = transformSync('transform-jsx-source.tsx', source, {
@@ -323,6 +373,7 @@ export function transformJsxSource(source, options = {}) {
323
373
  imports,
324
374
  diagnostics,
325
375
  declarations,
376
+ hasTopLevelJsxExpression,
326
377
  };
327
378
  }
328
379
  const jsxResult = transpileJsxSource(transformed.code, transpileBaseOptions);
@@ -332,5 +383,6 @@ export function transformJsxSource(source, options = {}) {
332
383
  imports,
333
384
  diagnostics,
334
385
  declarations,
386
+ hasTopLevelJsxExpression,
335
387
  };
336
388
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/jsx",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Runtime JSX tagged template that renders DOM or React trees anywhere with or without a build step.",
5
5
  "keywords": [
6
6
  "jsx runtime",
@@ -173,7 +173,7 @@
173
173
  "jsdom": "^27.2.0",
174
174
  "lint-staged": "^16.2.7",
175
175
  "lit": "^3.2.1",
176
- "next": "^16.1.6",
176
+ "next": "^16.1.7",
177
177
  "oxlint": "^1.51.0",
178
178
  "prettier": "^3.7.3",
179
179
  "react": "^19.0.0",