jamdesk 1.1.129 → 1.1.131

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 (91) hide show
  1. package/dist/__tests__/unit/deps-sync.test.js +6 -1
  2. package/dist/__tests__/unit/deps-sync.test.js.map +1 -1
  3. package/dist/__tests__/unit/migrate-mdx.test.js +54 -0
  4. package/dist/__tests__/unit/migrate-mdx.test.js.map +1 -1
  5. package/dist/__tests__/unit/migrate-noindex-warning.test.d.ts +2 -0
  6. package/dist/__tests__/unit/migrate-noindex-warning.test.d.ts.map +1 -0
  7. package/dist/__tests__/unit/migrate-noindex-warning.test.js +115 -0
  8. package/dist/__tests__/unit/migrate-noindex-warning.test.js.map +1 -0
  9. package/dist/__tests__/unit/risky-expression-scanner-sync.test.d.ts +2 -0
  10. package/dist/__tests__/unit/risky-expression-scanner-sync.test.d.ts.map +1 -0
  11. package/dist/__tests__/unit/risky-expression-scanner-sync.test.js +43 -0
  12. package/dist/__tests__/unit/risky-expression-scanner-sync.test.js.map +1 -0
  13. package/dist/__tests__/unit/snippet-component-convert.test.d.ts +2 -0
  14. package/dist/__tests__/unit/snippet-component-convert.test.d.ts.map +1 -0
  15. package/dist/__tests__/unit/snippet-component-convert.test.js +105 -0
  16. package/dist/__tests__/unit/snippet-component-convert.test.js.map +1 -0
  17. package/dist/__tests__/unit/snippet-scanner-sync.test.d.ts +2 -0
  18. package/dist/__tests__/unit/snippet-scanner-sync.test.d.ts.map +1 -0
  19. package/dist/__tests__/unit/snippet-scanner-sync.test.js +43 -0
  20. package/dist/__tests__/unit/snippet-scanner-sync.test.js.map +1 -0
  21. package/dist/__tests__/unit/validate-risky-expressions.test.d.ts +17 -0
  22. package/dist/__tests__/unit/validate-risky-expressions.test.d.ts.map +1 -0
  23. package/dist/__tests__/unit/validate-risky-expressions.test.js +111 -0
  24. package/dist/__tests__/unit/validate-risky-expressions.test.js.map +1 -0
  25. package/dist/__tests__/unit/validate-snippets.test.d.ts +19 -0
  26. package/dist/__tests__/unit/validate-snippets.test.d.ts.map +1 -0
  27. package/dist/__tests__/unit/validate-snippets.test.js +190 -0
  28. package/dist/__tests__/unit/validate-snippets.test.js.map +1 -0
  29. package/dist/commands/migrate/convert-mdx.d.ts +12 -0
  30. package/dist/commands/migrate/convert-mdx.d.ts.map +1 -1
  31. package/dist/commands/migrate/convert-mdx.js +72 -2
  32. package/dist/commands/migrate/convert-mdx.js.map +1 -1
  33. package/dist/commands/migrate/convert.d.ts +11 -0
  34. package/dist/commands/migrate/convert.d.ts.map +1 -1
  35. package/dist/commands/migrate/convert.js +22 -0
  36. package/dist/commands/migrate/convert.js.map +1 -1
  37. package/dist/commands/migrate/index.d.ts.map +1 -1
  38. package/dist/commands/migrate/index.js +12 -8
  39. package/dist/commands/migrate/index.js.map +1 -1
  40. package/dist/commands/migrate/snippet-component-convert.d.ts +45 -0
  41. package/dist/commands/migrate/snippet-component-convert.d.ts.map +1 -0
  42. package/dist/commands/migrate/snippet-component-convert.js +222 -0
  43. package/dist/commands/migrate/snippet-component-convert.js.map +1 -0
  44. package/dist/commands/validate.d.ts.map +1 -1
  45. package/dist/commands/validate.js +39 -0
  46. package/dist/commands/validate.js.map +1 -1
  47. package/dist/lib/deps.d.ts.map +1 -1
  48. package/dist/lib/deps.js +4 -1
  49. package/dist/lib/deps.js.map +1 -1
  50. package/dist/lib/risky-expression-scanner.d.ts +32 -0
  51. package/dist/lib/risky-expression-scanner.d.ts.map +1 -0
  52. package/dist/lib/risky-expression-scanner.js +195 -0
  53. package/dist/lib/risky-expression-scanner.js.map +1 -0
  54. package/dist/lib/snippet-scanner.d.ts +78 -0
  55. package/dist/lib/snippet-scanner.d.ts.map +1 -0
  56. package/dist/lib/snippet-scanner.js +198 -0
  57. package/dist/lib/snippet-scanner.js.map +1 -0
  58. package/dist/lib/validate-risky-expressions.d.ts +34 -0
  59. package/dist/lib/validate-risky-expressions.d.ts.map +1 -0
  60. package/dist/lib/validate-risky-expressions.js +54 -0
  61. package/dist/lib/validate-risky-expressions.js.map +1 -0
  62. package/dist/lib/validate-snippets.d.ts +37 -0
  63. package/dist/lib/validate-snippets.d.ts.map +1 -0
  64. package/dist/lib/validate-snippets.js +71 -0
  65. package/dist/lib/validate-snippets.js.map +1 -0
  66. package/package.json +7 -2
  67. package/vendored/components/errors/MdxRenderBoundary.tsx +52 -0
  68. package/vendored/components/mdx/MDXComponents.tsx +3 -0
  69. package/vendored/components/mdx/MdxErrorBlock.tsx +42 -0
  70. package/vendored/lib/isr-build-executor.ts +13 -0
  71. package/vendored/lib/recma-collect-missing-refs.ts +51 -0
  72. package/vendored/lib/recma-guard-expressions.ts +230 -0
  73. package/vendored/lib/render-doc-page.tsx +121 -30
  74. package/vendored/lib/risky-expression-scanner.ts +225 -0
  75. package/vendored/lib/snippet-scanner.ts +237 -0
  76. package/vendored/lib/static-artifacts.ts +22 -10
  77. package/vendored/lib/static-file-route.ts +19 -8
  78. package/vendored/shared/status-reporter.ts +1 -1
  79. package/vendored/workspace-package-lock.json +11 -10
  80. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts +0 -2
  81. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts.map +0 -1
  82. package/dist/__tests__/unit/dev-workspace-symlinks.test.js +0 -112
  83. package/dist/__tests__/unit/dev-workspace-symlinks.test.js.map +0 -1
  84. package/dist/__tests__/unit/language-filter.test.d.ts +0 -2
  85. package/dist/__tests__/unit/language-filter.test.d.ts.map +0 -1
  86. package/dist/__tests__/unit/language-filter.test.js +0 -166
  87. package/dist/__tests__/unit/language-filter.test.js.map +0 -1
  88. package/dist/lib/language-filter.d.ts +0 -31
  89. package/dist/lib/language-filter.d.ts.map +0 -1
  90. package/dist/lib/language-filter.js +0 -14
  91. package/dist/lib/language-filter.js.map +0 -1
@@ -157,7 +157,12 @@ export interface LlmsTxtOptions {
157
157
  pages: PageMetadata[];
158
158
  /** Whether docs are hosted at /docs path */
159
159
  hostAtDocs?: boolean;
160
- /** Block all crawlers - generates empty llms.txt */
160
+ /**
161
+ * Site-level noindex flag (from seo.metatags.robots). Passed through for
162
+ * interface parity with other artifact options but NOT used to blank the
163
+ * output — AI agents fetch llms.txt directly and do not honor robots
164
+ * directives. Per-page filtering (hidden / noindex frontmatter) still applies.
165
+ */
161
166
  noindex?: boolean;
162
167
  /**
163
168
  * Visibility inputs from lib/visibility.ts. When provided, filtering
@@ -177,11 +182,12 @@ export interface LlmsTxtOptions {
177
182
  * @returns Plain text string
178
183
  */
179
184
  export function generateLlmsTxt(options: LlmsTxtOptions): string {
180
- const { name, description, baseUrl, pages, hostAtDocs = false, noindex = false, visibility } = options;
185
+ const { name, description, baseUrl, pages, hostAtDocs = false, visibility } = options;
181
186
 
182
- if (noindex) {
183
- return '';
184
- }
187
+ // NOTE: noindex is intentionally NOT respected here. AI agents fetch llms.txt
188
+ // directly and do not honor robots directives, so this file is generated
189
+ // regardless of the site's indexing state. Per-page hidden/noindex filtering
190
+ // (via isPageMetadataIncluded below) still applies.
185
191
 
186
192
  const urlPrefix = hostAtDocs ? '/docs' : '';
187
193
 
@@ -354,7 +360,12 @@ export interface LlmsFullTxtOptions {
354
360
  name: string;
355
361
  /** Pages with full content */
356
362
  pages: LlmsFullPageInfo[];
357
- /** Block all crawlers - generates empty file */
363
+ /**
364
+ * Site-level noindex flag (from seo.metatags.robots). Passed through for
365
+ * interface parity with other artifact options but NOT used to blank the
366
+ * output — AI agents fetch llms-full.txt directly and do not honor robots
367
+ * directives. Per-page filtering (hidden / noindex frontmatter) still applies.
368
+ */
358
369
  noindex?: boolean;
359
370
  /**
360
371
  * Visibility inputs from lib/visibility.ts. When provided, filtering
@@ -372,11 +383,12 @@ export interface LlmsFullTxtOptions {
372
383
  * giving consistent hidden/orphan/searchable handling across all artifacts.
373
384
  */
374
385
  export function generateLlmsFullTxt(options: LlmsFullTxtOptions): string {
375
- const { name, pages, noindex = false, visibility } = options;
386
+ const { name, pages, visibility } = options;
376
387
 
377
- if (noindex) {
378
- return '';
379
- }
388
+ // NOTE: noindex is intentionally NOT respected here. AI agents fetch
389
+ // llms-full.txt directly and do not honor robots directives, so this file is
390
+ // generated regardless of the site's indexing state. Per-page hidden/noindex
391
+ // filtering (via computePageVisibility/frontmatter checks below) still applies.
380
392
 
381
393
  const visiblePages = pages.filter(p => {
382
394
  const path = p.path.replace(/\.mdx?$/, '');
@@ -37,6 +37,12 @@ const CONTENT_TYPES: Record<string, string> = {
37
37
  * (`x-jd-noindex: true`). These advertise the project's URL inventory and
38
38
  * have no business being served from a non-canonical host.
39
39
  *
40
+ * `x-jd-noindex: true` is set by `proxy.ts` (`projectHeaderOptsForCanonical`)
41
+ * when a project uses `hostAtDocs` WITHOUT a custom domain — i.e. its public
42
+ * face is the raw `<slug>.jamdesk.app` subdomain with no canonical elsewhere.
43
+ * Once a custom domain is registered, the header is replaced by a canonical-
44
+ * host override and noindex is no longer set.
45
+ *
40
46
  * `robots.txt` is handled separately — it returns a Disallow-all directive,
41
47
  * not a 404, so crawlers get an explicit "don't crawl" signal.
42
48
  *
@@ -45,20 +51,25 @@ const CONTENT_TYPES: Record<string, string> = {
45
51
  *
46
52
  * `changelog.json` is intentionally NOT in this list either, and must stay out:
47
53
  * the embeddable widget always fetches it from `<slug>.jamdesk.app` (custom
48
- * domains can't serve the `?embed=1` render), and that subdomain carries
49
- * `x-jd-noindex: true` whenever the project has a custom domain. Suppressing it
50
- * here would 404 the metadata fetch and permanently break the widget's unread
51
- * dot for every custom-domain customer. It's a deliberately public artifact
52
- * (served with `Access-Control-Allow-Origin: *`), not a URL-inventory file.
54
+ * domains can't serve the `?embed=1` render), so suppressing it here would 404
55
+ * the metadata fetch and permanently break the widget's unread dot for every
56
+ * custom-domain customer. It's a deliberately public artifact (served with
57
+ * `Access-Control-Allow-Origin: *`), not a URL-inventory file.
58
+ *
59
+ * `llms.txt` and `llms-full.txt` are intentionally NOT in this list: AI agents
60
+ * (ChatGPT, Perplexity, Claude, etc.) fetch these files directly and do not
61
+ * honor robots directives. Suppressing them would silently exclude noindex
62
+ * projects from AI-agent discovery entirely, with no benefit to crawl hygiene.
53
63
  */
54
64
  const NOINDEX_SUPPRESSED_FILES = new Set([
55
- 'sitemap.xml', 'llms.txt', 'llms-full.txt', 'feed.xml',
65
+ 'sitemap.xml', 'feed.xml',
56
66
  ]);
57
67
 
58
68
  /**
59
69
  * Build a no-store synthetic response for direct upstream subdomain hits, or
60
70
  * return null if this filename should fall through to the R2 fetch even when
61
- * `x-jd-noindex: true` is set (i.e., `search-data.json`).
71
+ * `x-jd-noindex: true` is set (i.e., `search-data.json`, `changelog.json`,
72
+ * `llms.txt`, `llms-full.txt` — see `NOINDEX_SUPPRESSED_FILES` for the why).
62
73
  *
63
74
  * Cache-Control: no-store — the noindex flag flips off the moment a project
64
75
  * registers a customDomain; CDN edge caching would lock new customers out
@@ -168,7 +179,7 @@ export function createStaticFileHandler(
168
179
  try {
169
180
  const content = await fetchStaticFile(projectSlug, filename);
170
181
 
171
- if (!content) {
182
+ if (content === null) {
172
183
  log('warn', `${label} not found in R2`, { projectSlug });
173
184
  return new NextResponse(`${label} not found`, { status: 404 });
174
185
  }
@@ -22,7 +22,7 @@ export interface ProgressUpdate {
22
22
  }
23
23
 
24
24
  /** Warning types that can occur during builds (non-blocking) */
25
- export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page' | 'missing_openapi_ref' | 'inline_code_on_api_page' | 'invalid_openapi_spec';
25
+ export type BuildWarningType = 'broken_link' | 'auto_migrate' | 'missing_asset' | 'missing_page' | 'missing_openapi_ref' | 'inline_code_on_api_page' | 'invalid_openapi_spec' | 'missing_snippet' | 'risky_expression';
26
26
 
27
27
  /** Build warning structure */
28
28
  export interface BuildWarning {
@@ -29,6 +29,7 @@
29
29
  "@upstash/redis": "^1.37.0",
30
30
  "ajv": "^8.17.1",
31
31
  "ajv-formats": "^3.0.1",
32
+ "astring": "^1.9.0",
32
33
  "autoprefixer": "^10.4.24",
33
34
  "estree-util-visit": "^2.0.0",
34
35
  "fflate": "^0.8.2",
@@ -61,7 +62,7 @@
61
62
  "shiki": "^4.0.1",
62
63
  "tailwindcss": "^4.2.4",
63
64
  "typescript": "^6.0.3",
64
- "unified": "^11.0.0",
65
+ "unified": "^11.0.5",
65
66
  "unist-util-visit": "^5.1.0"
66
67
  }
67
68
  },
@@ -2938,15 +2939,15 @@
2938
2939
  "license": "MIT"
2939
2940
  },
2940
2941
  "node_modules/electron-to-chromium": {
2941
- "version": "1.5.366",
2942
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.366.tgz",
2943
- "integrity": "sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg==",
2942
+ "version": "1.5.367",
2943
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.367.tgz",
2944
+ "integrity": "sha512-4Mk/mrynCNQ+atY40D3UpmhLWB6AHMbYMlIrPhHcMF6x0L7O0b052FCAsxw1LlaR++UFuNg3D/A6XCuGDa0guQ==",
2944
2945
  "license": "ISC"
2945
2946
  },
2946
2947
  "node_modules/enhanced-resolve": {
2947
- "version": "5.22.1",
2948
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz",
2949
- "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==",
2948
+ "version": "5.22.2",
2949
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.2.tgz",
2950
+ "integrity": "sha512-0rxICaFZ7NQho/sHely2bvOPRP0Eu2B0NZ9zM54YvRvWMn7jfz3DmnOZDR9LlXDdDcqntAVc6Hfy4gr/tdH/Ag==",
2950
2951
  "license": "MIT",
2951
2952
  "dependencies": {
2952
2953
  "graceful-fs": "^4.2.4",
@@ -6078,9 +6079,9 @@
6078
6079
  }
6079
6080
  },
6080
6081
  "node_modules/semver": {
6081
- "version": "7.8.1",
6082
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
6083
- "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
6082
+ "version": "7.8.2",
6083
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz",
6084
+ "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==",
6084
6085
  "license": "ISC",
6085
6086
  "optional": true,
6086
6087
  "bin": {
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=dev-workspace-symlinks.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev-workspace-symlinks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/dev-workspace-symlinks.test.ts"],"names":[],"mappings":""}
@@ -1,112 +0,0 @@
1
- /**
2
- * @vitest-environment node
3
- *
4
- * Tests prepareProjectWorkspaceLinks — replaces the single
5
- * <workspace>/projects/<name> -> <projectDir> symlink with per-entry
6
- * symlinks that skip non-active language directories. This is what
7
- * actually reduces Turbopack's filesystem scan from 403 MDX files to
8
- * 135 on jamdesk-docs.
9
- */
10
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
11
- import fs from 'fs-extra';
12
- import path from 'path';
13
- import { tmpdir } from 'os';
14
- import { prepareProjectWorkspaceLinks } from '../../commands/dev.js';
15
- import { output } from '../../lib/output.js';
16
- describe('prepareProjectWorkspaceLinks', () => {
17
- let tmpRoot;
18
- let projectDir;
19
- let workspaceProjectDir;
20
- beforeEach(() => {
21
- tmpRoot = fs.mkdtempSync(path.join(tmpdir(), 'jam-ws-'));
22
- projectDir = path.join(tmpRoot, 'project');
23
- workspaceProjectDir = path.join(tmpRoot, 'ws', 'projects', 'project');
24
- // Set up a project layout that mirrors a multi-language docs project:
25
- // project/
26
- // ai/intro.mdx (en at root)
27
- // development/foo.mdx (en at root)
28
- // es/ai/intro.mdx (spanish)
29
- // fr/ai/intro.mdx (french)
30
- // docs.json
31
- // images/logo.png
32
- fs.mkdirpSync(path.join(projectDir, 'ai'));
33
- fs.writeFileSync(path.join(projectDir, 'ai', 'intro.mdx'), '# en');
34
- fs.mkdirpSync(path.join(projectDir, 'development'));
35
- fs.writeFileSync(path.join(projectDir, 'development', 'foo.mdx'), '# en');
36
- fs.mkdirpSync(path.join(projectDir, 'es', 'ai'));
37
- fs.writeFileSync(path.join(projectDir, 'es', 'ai', 'intro.mdx'), '# es');
38
- fs.mkdirpSync(path.join(projectDir, 'fr', 'ai'));
39
- fs.writeFileSync(path.join(projectDir, 'fr', 'ai', 'intro.mdx'), '# fr');
40
- fs.writeFileSync(path.join(projectDir, 'docs.json'), '{}');
41
- fs.mkdirpSync(path.join(projectDir, 'images'));
42
- fs.writeFileSync(path.join(projectDir, 'images', 'logo.png'), '');
43
- });
44
- afterEach(() => {
45
- fs.removeSync(tmpRoot);
46
- });
47
- it('symlinks every top-level entry when skip set is empty', async () => {
48
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set());
49
- expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
50
- expect(fs.existsSync(path.join(workspaceProjectDir, 'es', 'ai', 'intro.mdx'))).toBe(true);
51
- expect(fs.existsSync(path.join(workspaceProjectDir, 'fr', 'ai', 'intro.mdx'))).toBe(true);
52
- });
53
- it('does not symlink docs.json (caller writes a filtered copy)', async () => {
54
- // docs.json must not be symlinked — the caller writes a per-language
55
- // filtered copy, and fs.writeFile through a symlink would clobber the
56
- // user's source docs.json.
57
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set());
58
- expect(fs.existsSync(path.join(workspaceProjectDir, 'docs.json'))).toBe(false);
59
- });
60
- it('skips entries whose names are in the skip set', async () => {
61
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es', 'fr']));
62
- expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
63
- expect(fs.existsSync(path.join(workspaceProjectDir, 'development', 'foo.mdx'))).toBe(true);
64
- expect(fs.existsSync(path.join(workspaceProjectDir, 'images', 'logo.png'))).toBe(true);
65
- // Skipped:
66
- expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
67
- expect(fs.existsSync(path.join(workspaceProjectDir, 'fr'))).toBe(false);
68
- // docs.json not symlinked — caller writes a filtered copy.
69
- expect(fs.existsSync(path.join(workspaceProjectDir, 'docs.json'))).toBe(false);
70
- });
71
- it('rebuilds the workspace links from scratch on subsequent calls', async () => {
72
- // First call: no skip
73
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set());
74
- expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(true);
75
- // Second call: skip es
76
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es']));
77
- expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
78
- expect(fs.existsSync(path.join(workspaceProjectDir, 'fr'))).toBe(true);
79
- expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
80
- });
81
- it('handles a pre-existing single symlink at workspaceProjectDir (legacy layout)', async () => {
82
- // Pre-create the legacy single-symlink layout
83
- fs.mkdirpSync(path.dirname(workspaceProjectDir));
84
- fs.symlinkSync(projectDir, workspaceProjectDir, 'junction');
85
- await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es', 'fr']));
86
- const lstat = fs.lstatSync(workspaceProjectDir);
87
- expect(lstat.isDirectory()).toBe(true);
88
- expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
89
- expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
90
- });
91
- it('surfaces friendly error (not raw stack trace) when fs.rm throws ENOTEMPTY', async () => {
92
- // Regression: before safeRemoveCache, fs.remove raised an unfriendly stack
93
- // trace when Turbopack still held open files. Now safeRemoveCache detects
94
- // the race and calls process.exit(1) with a human-readable message.
95
- const rmSpy = vi.spyOn(fs, 'rm');
96
- const exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code) => {
97
- throw new Error(`process.exit:${code}`);
98
- }));
99
- const errorSpy = vi.spyOn(output, 'error').mockImplementation(() => undefined);
100
- const enotempty = Object.assign(new Error('ENOTEMPTY'), { code: 'ENOTEMPTY' });
101
- // fs.rm will be called by safeRemoveCache; make it fail with ENOTEMPTY
102
- // even after the internal maxRetries — simulate persistent race condition.
103
- rmSpy.mockRejectedValue(enotempty);
104
- await expect(prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set())).rejects.toThrow('process.exit:1');
105
- const msg = errorSpy.mock.calls[0]?.[0] ?? '';
106
- expect(msg).toContain('Another `jamdesk dev` instance');
107
- expect(msg).toContain('pkill -f');
108
- expect(exitSpy).toHaveBeenCalledWith(1);
109
- vi.restoreAllMocks();
110
- });
111
- });
112
- //# sourceMappingURL=dev-workspace-symlinks.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dev-workspace-symlinks.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/dev-workspace-symlinks.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=language-filter.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-filter.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/language-filter.test.ts"],"names":[],"mappings":""}
@@ -1,166 +0,0 @@
1
- /**
2
- * @vitest-environment node
3
- *
4
- * Tests for getActiveLanguageFilter — pure helper that decides which
5
- * top-level language directories to skip when symlinking project content
6
- * into the dev workspace. Multi-language sites' non-default languages
7
- * inflate Turbopack's filesystem scan and balloon cold compile time
8
- * (jamdesk-docs: 67s with 3 langs vs. 12.5s with 1 lang).
9
- */
10
- import { describe, it, expect } from 'vitest';
11
- import { getActiveLanguageFilter, isPageInSkippedLanguage, filterConfigByActiveLanguage, } from '../../lib/language-filter.js';
12
- const config = (langs) => ({
13
- navigation: { languages: langs.map(l => ({ language: l.language, default: l.default })) },
14
- });
15
- describe('getActiveLanguageFilter', () => {
16
- it('returns null filter when project has no languages array', () => {
17
- const result = getActiveLanguageFilter({ navigation: {} }, undefined, false);
18
- expect(result).toEqual({ active: null, skip: new Set() });
19
- });
20
- it('returns null filter when project has only one language', () => {
21
- const result = getActiveLanguageFilter(config([{ language: 'en', default: true }]), undefined, false);
22
- expect(result).toEqual({ active: 'en', skip: new Set() });
23
- });
24
- it('skips non-default languages when active is the default', () => {
25
- const result = getActiveLanguageFilter(config([
26
- { language: 'en', default: true },
27
- { language: 'es' },
28
- { language: 'fr' },
29
- ]), undefined, false);
30
- expect(result.active).toBe('en');
31
- expect(result.skip).toEqual(new Set(['es', 'fr']));
32
- });
33
- it('falls back to first language when none is marked default', () => {
34
- const result = getActiveLanguageFilter(config([{ language: 'en' }, { language: 'es' }, { language: 'fr' }]), undefined, false);
35
- expect(result.active).toBe('en');
36
- expect(result.skip).toEqual(new Set(['es', 'fr']));
37
- });
38
- it('accepts --lang matching the default language (no-op equivalence)', () => {
39
- const result = getActiveLanguageFilter(config([
40
- { language: 'en', default: true },
41
- { language: 'es' },
42
- ]), 'en', false);
43
- expect(result.active).toBe('en');
44
- expect(result.skip).toEqual(new Set(['es']));
45
- });
46
- it('throws on --lang for a non-default language (initial release scope)', () => {
47
- // Non-default-language layouts mix root-level default-lang content with
48
- // <lang>/ subdirs for translations; correctly stripping the default
49
- // content while keeping <lang>/ takes more work and is deferred to a
50
- // follow-up. For now, surface the workaround clearly.
51
- expect(() => getActiveLanguageFilter(config([
52
- { language: 'en', default: true },
53
- { language: 'es' },
54
- { language: 'fr' },
55
- ]), 'es', false)).toThrow(/--lang es: previewing non-default languages.*--all-langs/i);
56
- });
57
- it('returns empty skip set when --all-langs is set', () => {
58
- const result = getActiveLanguageFilter(config([
59
- { language: 'en', default: true },
60
- { language: 'es' },
61
- ]), undefined, true);
62
- expect(result.active).toBe('en');
63
- expect(result.skip).toEqual(new Set());
64
- });
65
- it('throws on --lang code that does not exist in docs.json', () => {
66
- expect(() => getActiveLanguageFilter(config([{ language: 'en', default: true }, { language: 'es' }]), 'de', false)).toThrow(/--lang de.*not.*docs\.json.*Available: en, es/);
67
- });
68
- it('handles malformed languages array (entries missing language field) by ignoring them', () => {
69
- const malformed = {
70
- navigation: {
71
- languages: [
72
- { language: 'en', default: true },
73
- { default: false }, // missing language: ignored
74
- { language: 'es' },
75
- { language: 42 }, // wrong type: ignored
76
- ],
77
- },
78
- };
79
- const result = getActiveLanguageFilter(malformed, undefined, false);
80
- expect(result.active).toBe('en');
81
- expect(result.skip).toEqual(new Set(['es']));
82
- });
83
- it('returns null active when no valid language entries exist', () => {
84
- const result = getActiveLanguageFilter({ navigation: { languages: [{ default: true }] } }, undefined, false);
85
- expect(result).toEqual({ active: null, skip: new Set() });
86
- });
87
- it('rejects empty-string --lang as an invalid code (commander passes "" through)', () => {
88
- // commander.js treats `--lang ""` as a value, not as missing — so
89
- // langOption is "" (defined, but empty). Empty string is never a valid
90
- // language code; surface it as a clear error rather than silently
91
- // falling back to the default.
92
- expect(() => getActiveLanguageFilter(config([{ language: 'en', default: true }, { language: 'es' }]), '', false)).toThrow(/--lang.*not.*docs\.json/);
93
- });
94
- });
95
- describe('isPageInSkippedLanguage', () => {
96
- it('returns false when skip set is empty', () => {
97
- expect(isPageInSkippedLanguage('fr/introduction', new Set())).toBe(false);
98
- });
99
- it('returns true when first path segment is a skipped language', () => {
100
- expect(isPageInSkippedLanguage('fr/introduction', new Set(['fr']))).toBe(true);
101
- });
102
- it('returns true for nested paths inside a skipped language', () => {
103
- expect(isPageInSkippedLanguage('fr/setup/connecting-github', new Set(['fr', 'de']))).toBe(true);
104
- });
105
- it('returns false when first segment is the active language', () => {
106
- expect(isPageInSkippedLanguage('en/introduction', new Set(['fr']))).toBe(false);
107
- });
108
- it('returns false for unprefixed root pages', () => {
109
- expect(isPageInSkippedLanguage('introduction', new Set(['fr']))).toBe(false);
110
- });
111
- it('does not match when the skip code is a prefix-substring of a different segment', () => {
112
- // Guard against startsWith('fr/') matching e.g. 'fr-something/foo'
113
- expect(isPageInSkippedLanguage('fr-something/foo', new Set(['fr']))).toBe(false);
114
- });
115
- it('returns true for a path with a fragment anchor in a skipped language', () => {
116
- // Broken-anchor warnings from validate-links.cjs surface as link values
117
- // like `fr/introduction#missing-section` — the fragment lives past the
118
- // first segment so the language check still works.
119
- expect(isPageInSkippedLanguage('fr/introduction#setup', new Set(['fr']))).toBe(true);
120
- });
121
- });
122
- describe('filterConfigByActiveLanguage', () => {
123
- it('returns the input unchanged (same reference) when skip is empty', () => {
124
- const config = { name: 'foo', navigation: { languages: [{ language: 'en' }] } };
125
- const result = filterConfigByActiveLanguage(config, { active: 'en', skip: new Set() });
126
- expect(result).toBe(config);
127
- });
128
- it('drops skipped languages from navigation.languages', () => {
129
- const config = {
130
- name: 'jamdesk-docs',
131
- navigation: {
132
- languages: [
133
- { language: 'en', default: true, tabs: [{ tab: 'Guide' }] },
134
- { language: 'fr', tabs: [{ tab: 'Guide' }] },
135
- { language: 'de', tabs: [{ tab: 'Guide' }] },
136
- ],
137
- },
138
- };
139
- const result = filterConfigByActiveLanguage(config, {
140
- active: 'en',
141
- skip: new Set(['fr', 'de']),
142
- });
143
- expect(result.navigation.languages).toEqual([
144
- { language: 'en', default: true, tabs: [{ tab: 'Guide' }] },
145
- ]);
146
- // Top-level fields preserved.
147
- expect(result.name).toBe('jamdesk-docs');
148
- // Original config untouched.
149
- expect(config.navigation.languages).toHaveLength(3);
150
- });
151
- it('preserves other navigation fields (tabs, global, anchors, etc.)', () => {
152
- const config = {
153
- navigation: {
154
- languages: [{ language: 'en' }, { language: 'fr' }],
155
- global: { anchors: [{ anchor: 'Support' }] },
156
- },
157
- };
158
- const result = filterConfigByActiveLanguage(config, {
159
- active: 'en',
160
- skip: new Set(['fr']),
161
- });
162
- expect(result.navigation.global).toEqual({ anchors: [{ anchor: 'Support' }] });
163
- expect(result.navigation.languages).toHaveLength(1);
164
- });
165
- });
166
- //# sourceMappingURL=language-filter.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-filter.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/language-filter.test.ts"],"names":[],"mappings":"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"}
@@ -1,31 +0,0 @@
1
- /**
2
- * Picks which `<lang>/` directories under projectDir to keep as workspace
3
- * symlinks and which to skip. Skipping non-active language directories
4
- * shrinks Turbopack's filesystem scan and dropped cold compile from ~67s
5
- * to ~12s on jamdesk-docs (commit 90d781b4).
6
- *
7
- * The dev server still reads content from the user's source tree via
8
- * JAMDESK_PROJECTS_DIR, so the language picker keeps showing every
9
- * language and clicks always 200 OK — the workspace symlinks are a
10
- * Turbopack-only performance layer.
11
- *
12
- * Default rules:
13
- * 1. Language with `default: true`
14
- * 2. First language in `navigation.languages[]`
15
- */
16
- export interface LanguageFilter {
17
- active: string | null;
18
- skip: Set<string>;
19
- }
20
- interface NavigationLanguageEntry {
21
- language?: string;
22
- default?: boolean;
23
- }
24
- interface MinimalConfig {
25
- navigation?: {
26
- languages?: NavigationLanguageEntry[];
27
- };
28
- }
29
- export declare function getActiveLanguageFilter(config: MinimalConfig): LanguageFilter;
30
- export {};
31
- //# sourceMappingURL=language-filter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-filter.d.ts","sourceRoot":"","sources":["../../src/lib/language-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB;AAED,UAAU,uBAAuB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE;QACX,SAAS,CAAC,EAAE,uBAAuB,EAAE,CAAC;KACvC,CAAC;CACH;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,GAAG,cAAc,CAc7E"}
@@ -1,14 +0,0 @@
1
- export function getActiveLanguageFilter(config) {
2
- const validEntries = (config.navigation?.languages ?? []).filter((l) => typeof l.language === 'string');
3
- if (validEntries.length === 0) {
4
- return { active: null, skip: new Set() };
5
- }
6
- const codes = validEntries.map((l) => l.language);
7
- const active = validEntries.find((l) => l.default)?.language ?? validEntries[0].language;
8
- if (codes.length === 1) {
9
- return { active, skip: new Set() };
10
- }
11
- const skip = new Set(codes.filter((c) => c !== active));
12
- return { active, skip };
13
- }
14
- //# sourceMappingURL=language-filter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"language-filter.js","sourceRoot":"","sources":["../../src/lib/language-filter.ts"],"names":[],"mappings":"AA+BA,MAAM,UAAU,uBAAuB,CAAC,MAAqB;IAC3D,MAAM,YAAY,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC,MAAM,CAC9D,CAAC,CAAC,EAAgD,EAAE,CAAC,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CACpF,CAAC;IACF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,IAAI,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;IAC7C,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC1B,CAAC"}