jamdesk 1.1.69 → 1.1.71

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 (75) hide show
  1. package/dist/__tests__/integration/validate.integration.test.js +21 -0
  2. package/dist/__tests__/integration/validate.integration.test.js.map +1 -1
  3. package/dist/__tests__/unit/extract-hooks.test.js +102 -1
  4. package/dist/__tests__/unit/extract-hooks.test.js.map +1 -1
  5. package/dist/__tests__/unit/mdx-validator.test.js +22 -0
  6. package/dist/__tests__/unit/mdx-validator.test.js.map +1 -1
  7. package/dist/__tests__/unit/migrate-mdx.test.js +121 -1
  8. package/dist/__tests__/unit/migrate-mdx.test.js.map +1 -1
  9. package/dist/__tests__/unit/relative-mdx-imports.test.d.ts +2 -0
  10. package/dist/__tests__/unit/relative-mdx-imports.test.d.ts.map +1 -0
  11. package/dist/__tests__/unit/relative-mdx-imports.test.js +452 -0
  12. package/dist/__tests__/unit/relative-mdx-imports.test.js.map +1 -0
  13. package/dist/__tests__/unit/relocate-snippets.test.d.ts +2 -0
  14. package/dist/__tests__/unit/relocate-snippets.test.d.ts.map +1 -0
  15. package/dist/__tests__/unit/relocate-snippets.test.js +542 -0
  16. package/dist/__tests__/unit/relocate-snippets.test.js.map +1 -0
  17. package/dist/__tests__/unit/run-build-script.test.js +23 -1
  18. package/dist/__tests__/unit/run-build-script.test.js.map +1 -1
  19. package/dist/__tests__/unit/warn-relative-imports.test.d.ts +2 -0
  20. package/dist/__tests__/unit/warn-relative-imports.test.d.ts.map +1 -0
  21. package/dist/__tests__/unit/warn-relative-imports.test.js +44 -0
  22. package/dist/__tests__/unit/warn-relative-imports.test.js.map +1 -0
  23. package/dist/commands/dev.d.ts.map +1 -1
  24. package/dist/commands/dev.js +22 -8
  25. package/dist/commands/dev.js.map +1 -1
  26. package/dist/commands/migrate/convert-mdx.d.ts +5 -1
  27. package/dist/commands/migrate/convert-mdx.d.ts.map +1 -1
  28. package/dist/commands/migrate/convert-mdx.js +19 -6
  29. package/dist/commands/migrate/convert-mdx.js.map +1 -1
  30. package/dist/commands/migrate/extract-hooks.d.ts +26 -0
  31. package/dist/commands/migrate/extract-hooks.d.ts.map +1 -1
  32. package/dist/commands/migrate/extract-hooks.js +71 -12
  33. package/dist/commands/migrate/extract-hooks.js.map +1 -1
  34. package/dist/commands/migrate/fix-mdx-syntax.d.ts +38 -0
  35. package/dist/commands/migrate/fix-mdx-syntax.d.ts.map +1 -0
  36. package/dist/commands/migrate/fix-mdx-syntax.js +80 -0
  37. package/dist/commands/migrate/fix-mdx-syntax.js.map +1 -0
  38. package/dist/commands/migrate/index.d.ts.map +1 -1
  39. package/dist/commands/migrate/index.js +165 -3
  40. package/dist/commands/migrate/index.js.map +1 -1
  41. package/dist/commands/migrate/relocate-snippets.d.ts +30 -0
  42. package/dist/commands/migrate/relocate-snippets.d.ts.map +1 -0
  43. package/dist/commands/migrate/relocate-snippets.js +357 -0
  44. package/dist/commands/migrate/relocate-snippets.js.map +1 -0
  45. package/dist/commands/validate.d.ts.map +1 -1
  46. package/dist/commands/validate.js +31 -1
  47. package/dist/commands/validate.js.map +1 -1
  48. package/dist/lib/mdx-syntax-fixes.d.ts +14 -0
  49. package/dist/lib/mdx-syntax-fixes.d.ts.map +1 -0
  50. package/dist/lib/mdx-syntax-fixes.js +38 -0
  51. package/dist/lib/mdx-syntax-fixes.js.map +1 -0
  52. package/dist/lib/mdx-validator.d.ts +11 -2
  53. package/dist/lib/mdx-validator.d.ts.map +1 -1
  54. package/dist/lib/mdx-validator.js +41 -4
  55. package/dist/lib/mdx-validator.js.map +1 -1
  56. package/dist/lib/relative-mdx-imports.d.ts +44 -0
  57. package/dist/lib/relative-mdx-imports.d.ts.map +1 -0
  58. package/dist/lib/relative-mdx-imports.js +145 -0
  59. package/dist/lib/relative-mdx-imports.js.map +1 -0
  60. package/dist/lib/run-build-script.d.ts.map +1 -1
  61. package/dist/lib/run-build-script.js +7 -0
  62. package/dist/lib/run-build-script.js.map +1 -1
  63. package/dist/lib/warn-relative-imports.d.ts +2 -0
  64. package/dist/lib/warn-relative-imports.d.ts.map +1 -0
  65. package/dist/lib/warn-relative-imports.js +29 -0
  66. package/dist/lib/warn-relative-imports.js.map +1 -0
  67. package/package.json +1 -1
  68. package/vendored/app/(unlock)/jd/unlock/page.tsx +32 -0
  69. package/vendored/app/[[...slug]]/page.tsx +63 -15
  70. package/vendored/components/navigation/SocialFooter.tsx +4 -18
  71. package/vendored/lib/branding-url.ts +9 -0
  72. package/vendored/lib/layout-helpers.tsx +7 -9
  73. package/vendored/lib/preprocess-mdx.ts +42 -0
  74. package/vendored/scripts/compile-snippets.cjs +50 -7
  75. package/vendored/workspace-package-lock.json +3 -3
@@ -0,0 +1,9 @@
1
+ // `NEXT_PUBLIC_PROJECT_SLUG` is read inline so Next.js inlines the value at build
2
+ // time for client components (SocialFooter). Hoisting the read would defeat the
3
+ // inline. Server components (the unlock page) just see it as runtime env.
4
+ export function getBrandingUrl(projectSlug?: string | null): string {
5
+ const slug = encodeURIComponent(
6
+ projectSlug || process.env.NEXT_PUBLIC_PROJECT_SLUG || 'docs'
7
+ );
8
+ return `https://www.jamdesk.com?utm_campaign=poweredBy&utm_medium=referral&utm_source=${slug}`;
9
+ }
@@ -234,7 +234,9 @@ export async function DocsChrome({
234
234
 
235
235
  const linkPrefix = config.hostAtDocs ? '/docs' : '';
236
236
 
237
- const analyticsScript = config.analytics?.enabled !== false
237
+ // Skip building the IIFE string in dev — both the script render below and
238
+ // the IIFE itself self-gate to non-localhost (analytics-script.ts:49).
239
+ const analyticsScript = process.env.NODE_ENV === 'production' && config.analytics?.enabled !== false
238
240
  ? getAnalyticsScript(resolvedProjectSlug)
239
241
  : null;
240
242
 
@@ -449,14 +451,10 @@ export async function DocsChrome({
449
451
  <HtmlLangSync defaultLanguage={projectDefaultLang} />
450
452
  </ThemeProvider>
451
453
  {/*
452
- Inline scripts gated to production: avoids React 19's "scripts inside
453
- React components are never executed when rendering on the client"
454
- warning in `jamdesk dev`. All three are no-ops on localhost anyway —
455
- Crisp won't load on dev domains, customJs is typically tracking code,
456
- and analyticsScript self-gates to non-localhost (analytics-script.ts:49).
457
- Same pattern as the project-subdomain hydration suppressor in <head>.
458
- (Crisp websiteId pre-validated by regex; customJs/analyticsScript are
459
- owner-controlled — same trust model as before this gate was added.)
454
+ Inline scripts gated to production avoids React 19's "scripts
455
+ inside React components are never executed when rendering on the
456
+ client" warning in `jamdesk dev`. Same pattern as the
457
+ project-subdomain hydration suppressor in <head>.
460
458
  */}
461
459
  {process.env.NODE_ENV === 'production' && config.integrations?.crisp?.websiteId &&
462
460
  /^[a-f0-9-]{36}$/.test(config.integrations.crisp.websiteId) && (
@@ -518,6 +518,42 @@ export function normalizeJsxIndentation(content: string): string {
518
518
  return result.join('\n');
519
519
  }
520
520
 
521
+ /**
522
+ * Un-indent a closing JSX tag absorbed into the previous markdown list item.
523
+ *
524
+ * Distinct from `unindentJsxAfterListItems` below: that one rewrites indented
525
+ * *opening* tags inside list context. Here the opening tag is at root level
526
+ * and only the closing tag is indented after the list — markdown reads it as
527
+ * listItem continuation, so MDX compiles see the parent component as unclosed
528
+ * ("Expected the closing tag `</Card>` either after the end of `listItem`...").
529
+ *
530
+ * CLI mirror: `builder/cli/src/lib/mdx-syntax-fixes.ts` — keep regex in sync.
531
+ */
532
+ export function unindentClosingTagAfterList(content: string): string {
533
+ // Hot-path prefilter — preprocessMdx runs on every page render, and most
534
+ // MDX has no indented closing tags at all. Skip the split+walk in that case.
535
+ if (!/^\s+<\/[A-Z]/m.test(content)) return content;
536
+
537
+ const closingTagPattern = /^(\s+)(<\/[A-Z][\w-]*>)\s*$/;
538
+ const listItemPattern = /^\s*(?:[-*+]\s|\d+\.\s)/;
539
+ const lines = content.split('\n');
540
+ const out: string[] = [];
541
+ let lastNonBlank = '';
542
+
543
+ for (const line of lines) {
544
+ const closing = line.match(closingTagPattern);
545
+ if (closing && listItemPattern.test(lastNonBlank)) {
546
+ out.push(closing[2]);
547
+ lastNonBlank = closing[2];
548
+ continue;
549
+ }
550
+ out.push(line);
551
+ if (line.trim() !== '') lastNonBlank = line;
552
+ }
553
+
554
+ return out.join('\n');
555
+ }
556
+
521
557
  /**
522
558
  * Un-indent JSX blocks that follow markdown list items.
523
559
  *
@@ -997,6 +1033,12 @@ export function preprocessMdx(content: string, options?: { assetVersion?: string
997
1033
  // Convert noStyle to data-no-style for React compatibility
998
1034
  processed = convertNoStyleToDataAttr(processed);
999
1035
 
1036
+ // Un-indent absorbed closing tags FIRST — handles the case where the
1037
+ // opening tag is at root level but the closing tag got indented after a
1038
+ // list. unindentJsxAfterListItems below only catches cases where the
1039
+ // opening tag itself is indented inside list context.
1040
+ processed = unindentClosingTagAfterList(processed);
1041
+
1000
1042
  // Un-indent JSX blocks that follow list items to prevent
1001
1043
  // "Expected closing tag before end of listItem" errors
1002
1044
  // This must run BEFORE ensureBlankLinesInJsx since it changes block structure
@@ -321,6 +321,22 @@ export const SnippetComponents: Record<string, React.ComponentType<any>> = {};
321
321
 
322
322
  const snippetFiles = findSnippetFiles(snippetsDir);
323
323
 
324
+ // Deterministic order: within a (dir, baseName) collision, process .mdx
325
+ // first so the .tsx/.jsx code file overwrites the wrapped-MDX output on
326
+ // disk. Without this, fs.readdir order varies by platform and the
327
+ // surviving file content is non-deterministic. Outside of collisions,
328
+ // the order doesn't matter — sort makes it stable.
329
+ const EXT_PRIORITY = { '.mdx': 0, '.md': 1, '.ts': 2, '.js': 3, '.tsx': 4, '.jsx': 5 };
330
+ snippetFiles.sort((a, b) => {
331
+ const aDir = path.join(a.dir, '');
332
+ const bDir = path.join(b.dir, '');
333
+ if (aDir !== bDir) return aDir < bDir ? -1 : 1;
334
+ const aBase = a.file.replace(/\.[^.]+$/, '');
335
+ const bBase = b.file.replace(/\.[^.]+$/, '');
336
+ if (aBase !== bBase) return aBase < bBase ? -1 : 1;
337
+ return (EXT_PRIORITY[path.extname(a.file)] ?? 99) - (EXT_PRIORITY[path.extname(b.file)] ?? 99);
338
+ });
339
+
324
340
  if (snippetFiles.length === 0) {
325
341
  console.log(' No snippet files found');
326
342
 
@@ -376,7 +392,8 @@ export const SnippetComponents: Record<string, React.ComponentType<any>> = {};
376
392
  name: exportName,
377
393
  dir: dir,
378
394
  fileName,
379
- needsClient
395
+ needsClient,
396
+ sourceFile: dir ? path.join(dir, file) : file,
380
397
  });
381
398
 
382
399
  const status = written ? '(updated)' : '(unchanged)';
@@ -399,7 +416,8 @@ export const SnippetComponents: Record<string, React.ComponentType<any>> = {};
399
416
  name: baseName,
400
417
  dir: dir,
401
418
  fileName,
402
- needsClient
419
+ needsClient,
420
+ sourceFile: dir ? path.join(dir, file) : file,
403
421
  });
404
422
 
405
423
  const status = written ? '(updated)' : '(unchanged)';
@@ -414,8 +432,33 @@ export const SnippetComponents: Record<string, React.ComponentType<any>> = {};
414
432
  // For locale-specific snippets (e.g., es/Counter), we export under prefixed name (EsCounter).
415
433
  // The buildSnippetAliasMap function handles mapping import aliases to the correct component.
416
434
 
435
+ // Dedupe by (dir, name): two source files with the same base name (e.g.
436
+ // `vercel-json-generator.mdx` + `vercel-json-generator.tsx`) compile to the
437
+ // same generated fileName. The on-disk file is correctly overwritten by
438
+ // the last writer (sorted above so code files win), but componentInfo
439
+ // would otherwise carry both entries and produce duplicate import/export
440
+ // lines in ProjectSnippets.tsx. Keep the last (= the code file).
441
+ const seenComponentKeys = new Map();
442
+ for (const c of componentInfo) {
443
+ const key = `${c.dir}|${c.name}`;
444
+ if (seenComponentKeys.has(key)) {
445
+ const prior = seenComponentKeys.get(key);
446
+ // stderr (not stdout) so `runBuildScript` surfaces this in non-verbose
447
+ // mode — without it, the `jamdesk dev` user sees the collision is
448
+ // silently resolved and the next collision (a different name) crashes
449
+ // Turbopack with no breadcrumb.
450
+ console.warn(
451
+ ` ⚠ Snippet name collision: "${c.name}" defined in both ` +
452
+ `${prior.sourceFile} and ${c.sourceFile} — using ${c.sourceFile}. ` +
453
+ `Remove or rename one of these files to clear this warning.`,
454
+ );
455
+ }
456
+ seenComponentKeys.set(key, c);
457
+ }
458
+ const dedupedComponentInfo = [...seenComponentKeys.values()];
459
+
417
460
  // Build unique import names for each component (prefixed with dir if needed)
418
- const importInfo = componentInfo.map(c => {
461
+ const importInfo = dedupedComponentInfo.map(c => {
419
462
  const prefix = c.dir ? toPascalCase(c.dir.replace(/\//g, '-')) : '';
420
463
  const uniqueName = prefix + c.name.charAt(0).toUpperCase() + c.name.slice(1);
421
464
  // Strip the .tsx extension from fileName to get the import path
@@ -468,17 +511,17 @@ ${importInfo.map(c => ` ${c.uniqueName},`).join('\n')}
468
511
  }
469
512
  }
470
513
 
471
- const clientCount = componentInfo.filter(c => c.needsClient).length;
472
- const serverCount = componentInfo.length - clientCount;
514
+ const clientCount = dedupedComponentInfo.filter(c => c.needsClient).length;
515
+ const serverCount = dedupedComponentInfo.length - clientCount;
473
516
 
474
517
  console.log(`\n✅ Generated snippets:`);
475
518
  console.log(` Index: ${outputPath} ${indexWritten ? '(updated)' : '(unchanged)'}`);
476
- console.log(` Components: ${generatedDir}/ (${componentInfo.length} files)`);
519
+ console.log(` Components: ${generatedDir}/ (${dedupedComponentInfo.length} files)`);
477
520
  if (removedCount > 0) {
478
521
  console.log(` Removed: ${removedCount} stale file(s)`);
479
522
  }
480
523
  console.log(` Client components: ${clientCount}, Server components: ${serverCount}`);
481
- console.log(` Exports: ${componentInfo.map(c => c.name).join(', ')}\n`);
524
+ console.log(` Exports: ${dedupedComponentInfo.map(c => c.name).join(', ')}\n`);
482
525
  }
483
526
 
484
527
  // Run the compilation
@@ -2153,9 +2153,9 @@
2153
2153
  }
2154
2154
  },
2155
2155
  "node_modules/baseline-browser-mapping": {
2156
- "version": "2.10.25",
2157
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.25.tgz",
2158
- "integrity": "sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA==",
2156
+ "version": "2.10.27",
2157
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.27.tgz",
2158
+ "integrity": "sha512-zEs/ufmZoUd7WftKpKyXaT6RFxpQ5Qm9xytKRHvJfxFV9DFJkZph9RvJ1LcOUi0Z1ZVijMte65JbILeV+8QQEA==",
2159
2159
  "license": "Apache-2.0",
2160
2160
  "bin": {
2161
2161
  "baseline-browser-mapping": "dist/cli.cjs"