@knighted/jsx 1.11.0 → 1.13.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,57 @@ 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 toJsxExpressionNode = (value) => {
262
+ const unwrapped = unwrapExpressionNode(value);
263
+ if (!isObjectRecord(unwrapped) || typeof unwrapped.type !== 'string') {
264
+ return null;
265
+ }
266
+ if (unwrapped.type === 'JSXElement' || unwrapped.type === 'JSXFragment') {
267
+ return unwrapped;
268
+ }
269
+ return null;
270
+ };
271
+ const createEmptyTopLevelJsxExpressionMetadata = () => ({
272
+ hasTopLevelJsxExpression: false,
273
+ topLevelJsxExpressionRange: null,
274
+ });
275
+ const collectTopLevelJsxExpressionMetadata = (body) => {
276
+ if (!Array.isArray(body)) {
277
+ return createEmptyTopLevelJsxExpressionMetadata();
278
+ }
279
+ for (const statement of body) {
280
+ if (!isObjectRecord(statement) || statement.type !== 'ExpressionStatement') {
281
+ continue;
282
+ }
283
+ const jsxNode = toJsxExpressionNode(statement.expression);
284
+ if (jsxNode) {
285
+ return {
286
+ hasTopLevelJsxExpression: true,
287
+ topLevelJsxExpressionRange: toSourceRange(jsxNode),
288
+ };
289
+ }
290
+ }
291
+ return createEmptyTopLevelJsxExpressionMetadata();
292
+ };
242
293
  const ensureSupportedOptions = (options) => {
243
294
  if (options.sourceType !== undefined &&
244
295
  options.sourceType !== 'module' &&
@@ -259,6 +310,10 @@ const ensureSupportedOptions = (options) => {
259
310
  typeof options.collectTopLevelDeclarations !== 'boolean') {
260
311
  throw new Error(`[jsx] Unsupported collectTopLevelDeclarations value "${String(options.collectTopLevelDeclarations)}". Use true or false.`);
261
312
  }
313
+ if (options.collectTopLevelJsxExpression !== undefined &&
314
+ typeof options.collectTopLevelJsxExpression !== 'boolean') {
315
+ throw new Error(`[jsx] Unsupported collectTopLevelJsxExpression value "${String(options.collectTopLevelJsxExpression)}". Use true or false.`);
316
+ }
262
317
  };
263
318
  function transformJsxSource(source, options = {}) {
264
319
  const internalOptions = options;
@@ -272,6 +327,11 @@ function transformJsxSource(source, options = {}) {
272
327
  const declarations = internalOptions.collectTopLevelDeclarations
273
328
  ? collectTopLevelDeclarationMetadata(parsed.program.body)
274
329
  : undefined;
330
+ const topLevelJsxExpressionMetadata = internalOptions.collectTopLevelJsxExpression
331
+ ? collectTopLevelJsxExpressionMetadata(parsed.program.body)
332
+ : undefined;
333
+ const hasTopLevelJsxExpression = topLevelJsxExpressionMetadata?.hasTopLevelJsxExpression;
334
+ const topLevelJsxExpressionRange = topLevelJsxExpressionMetadata?.topLevelJsxExpressionRange;
275
335
  if (parserDiagnostics.length) {
276
336
  return {
277
337
  code: source,
@@ -279,6 +339,8 @@ function transformJsxSource(source, options = {}) {
279
339
  imports,
280
340
  diagnostics: parserDiagnostics,
281
341
  declarations,
342
+ hasTopLevelJsxExpression,
343
+ topLevelJsxExpressionRange,
282
344
  };
283
345
  }
284
346
  const transpileBaseOptions = {
@@ -295,6 +357,8 @@ function transformJsxSource(source, options = {}) {
295
357
  imports,
296
358
  diagnostics: parserDiagnostics,
297
359
  declarations,
360
+ hasTopLevelJsxExpression,
361
+ topLevelJsxExpressionRange,
298
362
  };
299
363
  }
300
364
  if (typescriptStripBackend === 'transpile-manual') {
@@ -308,6 +372,8 @@ function transformJsxSource(source, options = {}) {
308
372
  imports,
309
373
  diagnostics: parserDiagnostics,
310
374
  declarations,
375
+ hasTopLevelJsxExpression,
376
+ topLevelJsxExpressionRange,
311
377
  };
312
378
  }
313
379
  const transformed = (0, oxc_transform_1.transformSync)('transform-jsx-source.tsx', source, {
@@ -326,6 +392,8 @@ function transformJsxSource(source, options = {}) {
326
392
  imports,
327
393
  diagnostics,
328
394
  declarations,
395
+ hasTopLevelJsxExpression,
396
+ topLevelJsxExpressionRange,
329
397
  };
330
398
  }
331
399
  const jsxResult = (0, transpile_js_1.transpileJsxSource)(transformed.code, transpileBaseOptions);
@@ -335,5 +403,7 @@ function transformJsxSource(source, options = {}) {
335
403
  imports,
336
404
  diagnostics,
337
405
  declarations,
406
+ hasTopLevelJsxExpression,
407
+ topLevelJsxExpressionRange,
338
408
  };
339
409
  }
@@ -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,8 @@ export type TransformJsxSourceResult = {
43
44
  imports: TransformImport[];
44
45
  diagnostics: TransformDiagnostic[];
45
46
  declarations?: TransformTopLevelDeclaration[];
47
+ hasTopLevelJsxExpression?: boolean;
48
+ topLevelJsxExpressionRange?: SourceRange | null;
46
49
  };
47
50
  export declare function transformJsxSource(source: string, options?: TransformJsxSourceOptions): TransformJsxSourceResult;
48
51
  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,8 @@ export type TransformJsxSourceResult = {
43
44
  imports: TransformImport[];
44
45
  diagnostics: TransformDiagnostic[];
45
46
  declarations?: TransformTopLevelDeclaration[];
47
+ hasTopLevelJsxExpression?: boolean;
48
+ topLevelJsxExpressionRange?: SourceRange | null;
46
49
  };
47
50
  export declare function transformJsxSource(source: string, options?: TransformJsxSourceOptions): TransformJsxSourceResult;
48
51
  export {};
package/dist/transform.js CHANGED
@@ -236,6 +236,57 @@ 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 toJsxExpressionNode = (value) => {
259
+ const unwrapped = unwrapExpressionNode(value);
260
+ if (!isObjectRecord(unwrapped) || typeof unwrapped.type !== 'string') {
261
+ return null;
262
+ }
263
+ if (unwrapped.type === 'JSXElement' || unwrapped.type === 'JSXFragment') {
264
+ return unwrapped;
265
+ }
266
+ return null;
267
+ };
268
+ const createEmptyTopLevelJsxExpressionMetadata = () => ({
269
+ hasTopLevelJsxExpression: false,
270
+ topLevelJsxExpressionRange: null,
271
+ });
272
+ const collectTopLevelJsxExpressionMetadata = (body) => {
273
+ if (!Array.isArray(body)) {
274
+ return createEmptyTopLevelJsxExpressionMetadata();
275
+ }
276
+ for (const statement of body) {
277
+ if (!isObjectRecord(statement) || statement.type !== 'ExpressionStatement') {
278
+ continue;
279
+ }
280
+ const jsxNode = toJsxExpressionNode(statement.expression);
281
+ if (jsxNode) {
282
+ return {
283
+ hasTopLevelJsxExpression: true,
284
+ topLevelJsxExpressionRange: toSourceRange(jsxNode),
285
+ };
286
+ }
287
+ }
288
+ return createEmptyTopLevelJsxExpressionMetadata();
289
+ };
239
290
  const ensureSupportedOptions = (options) => {
240
291
  if (options.sourceType !== undefined &&
241
292
  options.sourceType !== 'module' &&
@@ -256,6 +307,10 @@ const ensureSupportedOptions = (options) => {
256
307
  typeof options.collectTopLevelDeclarations !== 'boolean') {
257
308
  throw new Error(`[jsx] Unsupported collectTopLevelDeclarations value "${String(options.collectTopLevelDeclarations)}". Use true or false.`);
258
309
  }
310
+ if (options.collectTopLevelJsxExpression !== undefined &&
311
+ typeof options.collectTopLevelJsxExpression !== 'boolean') {
312
+ throw new Error(`[jsx] Unsupported collectTopLevelJsxExpression value "${String(options.collectTopLevelJsxExpression)}". Use true or false.`);
313
+ }
259
314
  };
260
315
  export function transformJsxSource(source, options = {}) {
261
316
  const internalOptions = options;
@@ -269,6 +324,11 @@ export function transformJsxSource(source, options = {}) {
269
324
  const declarations = internalOptions.collectTopLevelDeclarations
270
325
  ? collectTopLevelDeclarationMetadata(parsed.program.body)
271
326
  : undefined;
327
+ const topLevelJsxExpressionMetadata = internalOptions.collectTopLevelJsxExpression
328
+ ? collectTopLevelJsxExpressionMetadata(parsed.program.body)
329
+ : undefined;
330
+ const hasTopLevelJsxExpression = topLevelJsxExpressionMetadata?.hasTopLevelJsxExpression;
331
+ const topLevelJsxExpressionRange = topLevelJsxExpressionMetadata?.topLevelJsxExpressionRange;
272
332
  if (parserDiagnostics.length) {
273
333
  return {
274
334
  code: source,
@@ -276,6 +336,8 @@ export function transformJsxSource(source, options = {}) {
276
336
  imports,
277
337
  diagnostics: parserDiagnostics,
278
338
  declarations,
339
+ hasTopLevelJsxExpression,
340
+ topLevelJsxExpressionRange,
279
341
  };
280
342
  }
281
343
  const transpileBaseOptions = {
@@ -292,6 +354,8 @@ export function transformJsxSource(source, options = {}) {
292
354
  imports,
293
355
  diagnostics: parserDiagnostics,
294
356
  declarations,
357
+ hasTopLevelJsxExpression,
358
+ topLevelJsxExpressionRange,
295
359
  };
296
360
  }
297
361
  if (typescriptStripBackend === 'transpile-manual') {
@@ -305,6 +369,8 @@ export function transformJsxSource(source, options = {}) {
305
369
  imports,
306
370
  diagnostics: parserDiagnostics,
307
371
  declarations,
372
+ hasTopLevelJsxExpression,
373
+ topLevelJsxExpressionRange,
308
374
  };
309
375
  }
310
376
  const transformed = transformSync('transform-jsx-source.tsx', source, {
@@ -323,6 +389,8 @@ export function transformJsxSource(source, options = {}) {
323
389
  imports,
324
390
  diagnostics,
325
391
  declarations,
392
+ hasTopLevelJsxExpression,
393
+ topLevelJsxExpressionRange,
326
394
  };
327
395
  }
328
396
  const jsxResult = transpileJsxSource(transformed.code, transpileBaseOptions);
@@ -332,5 +400,7 @@ export function transformJsxSource(source, options = {}) {
332
400
  imports,
333
401
  diagnostics,
334
402
  declarations,
403
+ hasTopLevelJsxExpression,
404
+ topLevelJsxExpressionRange,
335
405
  };
336
406
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knighted/jsx",
3
- "version": "1.11.0",
3
+ "version": "1.13.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",