jamdesk 1.1.41 → 1.1.43

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 (48) hide show
  1. package/README.md +22 -0
  2. package/dist/__tests__/unit/dev-validate-cache.test.d.ts +2 -0
  3. package/dist/__tests__/unit/dev-validate-cache.test.d.ts.map +1 -0
  4. package/dist/__tests__/unit/dev-validate-cache.test.js +116 -0
  5. package/dist/__tests__/unit/dev-validate-cache.test.js.map +1 -0
  6. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts +2 -0
  7. package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts.map +1 -0
  8. package/dist/__tests__/unit/dev-workspace-symlinks.test.js +105 -0
  9. package/dist/__tests__/unit/dev-workspace-symlinks.test.js.map +1 -0
  10. package/dist/__tests__/unit/language-filter.test.d.ts +2 -0
  11. package/dist/__tests__/unit/language-filter.test.d.ts.map +1 -0
  12. package/dist/__tests__/unit/language-filter.test.js +95 -0
  13. package/dist/__tests__/unit/language-filter.test.js.map +1 -0
  14. package/dist/__tests__/unit/vendored-sync.test.js +4 -0
  15. package/dist/__tests__/unit/vendored-sync.test.js.map +1 -1
  16. package/dist/commands/dev.d.ts +48 -3
  17. package/dist/commands/dev.d.ts.map +1 -1
  18. package/dist/commands/dev.js +134 -41
  19. package/dist/commands/dev.js.map +1 -1
  20. package/dist/index.js +15 -2
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/language-filter.d.ts +32 -0
  23. package/dist/lib/language-filter.d.ts.map +1 -0
  24. package/dist/lib/language-filter.js +28 -0
  25. package/dist/lib/language-filter.js.map +1 -0
  26. package/package.json +1 -1
  27. package/vendored/app/[[...slug]]/page.tsx +41 -28
  28. package/vendored/app/api/isr-health/route.ts +6 -4
  29. package/vendored/components/mdx/StepSlugContext.tsx +57 -0
  30. package/vendored/components/mdx/Steps.tsx +2 -2
  31. package/vendored/components/navigation/TableOfContents.tsx +77 -5
  32. package/vendored/lib/cache-tags.ts +25 -0
  33. package/vendored/lib/cache-utils.ts +19 -0
  34. package/vendored/lib/heading-extractor.ts +25 -6
  35. package/vendored/lib/indexnow.ts +1 -1
  36. package/vendored/lib/middleware-helpers.ts +103 -15
  37. package/vendored/lib/navigation-resolver.ts +1 -1
  38. package/vendored/lib/openapi-isr.ts +13 -8
  39. package/vendored/lib/r2-cleanup.ts +70 -0
  40. package/vendored/lib/r2-content.ts +0 -24
  41. package/vendored/lib/r2-manifest.ts +13 -3
  42. package/vendored/lib/revalidation-helpers.ts +41 -11
  43. package/vendored/lib/revalidation-trigger.ts +104 -28
  44. package/vendored/lib/scanner-blocklist.ts +265 -0
  45. package/vendored/lib/snippet-compiler-isr.ts +5 -2
  46. package/vendored/scripts/validate-links.cjs +17 -6
  47. package/vendored/workspace-package-lock.json +12 -12
  48. package/vendored/lib/cache-keys.ts +0 -117
package/README.md CHANGED
@@ -105,6 +105,8 @@ npx jamdesk dev
105
105
  | `jamdesk dev --webpack` | Use Webpack instead of Turbopack |
106
106
  | `jamdesk dev --clean` | Clear cache before starting |
107
107
  | `jamdesk dev --port 3001` | Custom port |
108
+ | `jamdesk dev --lang en` | Explicitly compile the default language (multi-lang sites) |
109
+ | `jamdesk dev --all-langs` | Compile every language (slower; legacy behavior) |
108
110
  | `jamdesk login` | Log in to Jamdesk |
109
111
  | `jamdesk logout` | Log out |
110
112
  | `jamdesk whoami` | Show current user |
@@ -138,8 +140,28 @@ jamdesk dev --port 3001 # Custom port
138
140
 
139
141
  The dev server auto-validates on startup, auto-recovers from corrupted Turbopack cache, and auto-increments the port if yours is taken. Full search, all themes, and all components work locally.
140
142
 
143
+ - `--clean` clears the project's workspace cache (`.next/` and version markers). For deeper "weird import error after CLI upgrade" recovery, use `jamdesk clean --all`, which also wipes `~/.jamdesk/node_modules/`.
144
+
141
145
  Set a default port in [`~/.jamdeskrc`](#cli-defaults).
142
146
 
147
+ ### Multi-language sites
148
+
149
+ When `docs.json` has a `navigation.languages` array with multiple entries, `jamdesk dev` compiles only the default language by default — Turbopack's cold compile is roughly 5x faster on a single-language tree.
150
+
151
+ ```bash
152
+ jamdesk dev # Default language only (fast — recommended)
153
+ jamdesk dev --lang en # Explicitly select the default language (no-op equivalent)
154
+ jamdesk dev --all-langs # Compile every language (slow; matches CLI ≤1.1.42)
155
+ ```
156
+
157
+ **Tip:** Run `jamdesk dev -v` to see cache-decision logs (e.g. "reusing Turbopack cache" or "Next.js version changed, clearing").
158
+
159
+ **Previewing a non-default language is not supported in this CLI version.** The per-entry symlink layer can skip non-default `<lang>/` subdirs cleanly, but cannot strip root-level default-language content while keeping a `<lang>/` subdir. Use `--all-langs` to view translated pages until follow-up support lands. Running e.g. `jamdesk dev --lang es` against a project whose default is `en` errors out with a message pointing at `--all-langs`.
160
+
161
+ Cross-language internal links (e.g. `/es/foo`) won't resolve when the active language is filtered out. Restart with `--all-langs` to preview translations.
162
+
163
+ **Known limitation:** Adding a NEW top-level directory to your project (e.g. `tutorials/`) while `jamdesk dev` is running won't be picked up automatically. Restart the dev server to see it.
164
+
143
165
  ## Authentication
144
166
 
145
167
  ### `jamdesk login`
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dev-validate-cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-validate-cache.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/dev-validate-cache.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @vitest-environment node
3
+ *
4
+ * validateAndCleanCache must distinguish between:
5
+ * - CLI version bump only (preserve cache; Turbopack content-addresses
6
+ * vendored files via syncVendoredFiles, so a wipe is unnecessary churn —
7
+ * and 22 CLI releases per 90 days made warm cache nearly unreachable)
8
+ * - Next.js version bump (wipe; Turbopack's persistent-cache encoding
9
+ * can change between Next.js minor versions, and stale cache may
10
+ * produce broken builds)
11
+ * - Workspace corruption (turbopack cache present but app/ deleted) → wipe
12
+ * - Stale per-run lockfile under .next/dev/lock → remove
13
+ *
14
+ * See docs/plans/2026-04-29-jamdesk-dev-compile-perf.md for the empirical
15
+ * validation (warm restart 1.9s with cache preserved across vendored-file
16
+ * change vs. 67s wiped).
17
+ */
18
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
19
+ import fs from 'fs-extra';
20
+ import path from 'path';
21
+ import { tmpdir } from 'os';
22
+ import { validateAndCleanCache } from '../../commands/dev.js';
23
+ describe('validateAndCleanCache', () => {
24
+ let tmpRoot;
25
+ let workspace;
26
+ let nextCache;
27
+ let cliVersionFile;
28
+ let nextVersionFile;
29
+ let blobPath;
30
+ beforeEach(() => {
31
+ tmpRoot = fs.mkdtempSync(path.join(tmpdir(), 'jam-cache-'));
32
+ workspace = path.join(tmpRoot, 'ws');
33
+ nextCache = path.join(workspace, '.next');
34
+ cliVersionFile = path.join(nextCache, '.jamdesk-cli-version');
35
+ nextVersionFile = path.join(nextCache, '.jamdesk-next-version');
36
+ fs.mkdirpSync(path.join(workspace, 'app'));
37
+ fs.mkdirpSync(path.join(nextCache, 'dev', 'cache', 'turbopack'));
38
+ blobPath = path.join(nextCache, 'dev', 'cache', 'turbopack', 'fake-cache-blob');
39
+ fs.writeFileSync(blobPath, 'x');
40
+ });
41
+ afterEach(() => {
42
+ fs.removeSync(tmpRoot);
43
+ });
44
+ it('preserves cache when both CLI and Next.js versions match', async () => {
45
+ fs.writeFileSync(cliVersionFile, '1.1.42');
46
+ fs.writeFileSync(nextVersionFile, '^16.2.4');
47
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
48
+ expect(result).toBe(true);
49
+ expect(fs.existsSync(blobPath)).toBe(true);
50
+ });
51
+ it('preserves cache when only the CLI version changes (Next.js still matches)', async () => {
52
+ fs.writeFileSync(cliVersionFile, '1.1.40');
53
+ fs.writeFileSync(nextVersionFile, '^16.2.4');
54
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
55
+ expect(result).toBe(true);
56
+ expect(fs.existsSync(blobPath)).toBe(true);
57
+ });
58
+ it('wipes cache when the Next.js version changes (Turbopack format compatibility)', async () => {
59
+ fs.writeFileSync(cliVersionFile, '1.1.42');
60
+ fs.writeFileSync(nextVersionFile, '16.1.7');
61
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
62
+ expect(result).toBe(false);
63
+ expect(fs.existsSync(blobPath)).toBe(false);
64
+ });
65
+ it('wipes cache when the next-version file is missing (first-run-after-upgrade safety)', async () => {
66
+ fs.writeFileSync(cliVersionFile, '1.1.40');
67
+ // nextVersionFile intentionally not written
68
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
69
+ expect(result).toBe(false);
70
+ expect(fs.existsSync(blobPath)).toBe(false);
71
+ });
72
+ it('wipes on workspace corruption (turbopack cache present but app/ missing)', async () => {
73
+ fs.writeFileSync(cliVersionFile, '1.1.42');
74
+ fs.writeFileSync(nextVersionFile, '^16.2.4');
75
+ fs.removeSync(path.join(workspace, 'app'));
76
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
77
+ expect(result).toBe(false);
78
+ expect(fs.existsSync(blobPath)).toBe(false);
79
+ });
80
+ it('removes a stale per-run lockfile when otherwise preserving cache', async () => {
81
+ fs.writeFileSync(cliVersionFile, '1.1.42');
82
+ fs.writeFileSync(nextVersionFile, '^16.2.4');
83
+ const runLock = path.join(nextCache, 'dev', 'lock');
84
+ fs.writeFileSync(runLock, '12345');
85
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
86
+ expect(result).toBe(true);
87
+ expect(fs.existsSync(runLock)).toBe(false);
88
+ });
89
+ it('handles missing CLI version file (first-ever run) — wipes (no Next.js version known either)', async () => {
90
+ // No version files at all — first run.
91
+ const result = await validateAndCleanCache({ nextCacheDir: nextCache, cliVersionFile, nextVersionFile, workspaceDir: workspace, currentNextVersion: '^16.2.4', verbose: false });
92
+ // Behavior on first run: no version files → wipe (the existing cache must
93
+ // be from before we tracked versions; safest to drop it).
94
+ expect(result).toBe(false);
95
+ expect(fs.existsSync(blobPath)).toBe(false);
96
+ });
97
+ it('preserves cache when CLI marker is missing but Next.js marker matches (asymmetric markers)', async () => {
98
+ // User manually deleted .jamdesk-cli-version, OR a partial write left
99
+ // only the next-version marker. The cachedNextVersion === currentNextVersion
100
+ // path should still preserve cache (no wipe needed; CLI marker isn't
101
+ // load-bearing for cache-format compatibility).
102
+ fs.writeFileSync(nextVersionFile, '^16.2.4');
103
+ // cliVersionFile intentionally not written
104
+ const result = await validateAndCleanCache({
105
+ nextCacheDir: nextCache,
106
+ cliVersionFile,
107
+ nextVersionFile,
108
+ workspaceDir: workspace,
109
+ currentNextVersion: '^16.2.4',
110
+ verbose: false,
111
+ });
112
+ expect(result).toBe(true);
113
+ expect(fs.existsSync(blobPath)).toBe(true);
114
+ });
115
+ });
116
+ //# sourceMappingURL=dev-validate-cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-validate-cache.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/dev-validate-cache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dev-workspace-symlinks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-workspace-symlinks.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/dev-workspace-symlinks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,105 @@
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
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'docs.json'))).toBe(true);
53
+ });
54
+ it('skips entries whose names are in the skip set', async () => {
55
+ await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es', 'fr']));
56
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
57
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'development', 'foo.mdx'))).toBe(true);
58
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'docs.json'))).toBe(true);
59
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'images', 'logo.png'))).toBe(true);
60
+ // Skipped:
61
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
62
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'fr'))).toBe(false);
63
+ });
64
+ it('rebuilds the workspace links from scratch on subsequent calls', async () => {
65
+ // First call: no skip
66
+ await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set());
67
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(true);
68
+ // Second call: skip es
69
+ await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es']));
70
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
71
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'fr'))).toBe(true);
72
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
73
+ });
74
+ it('handles a pre-existing single symlink at workspaceProjectDir (legacy layout)', async () => {
75
+ // Pre-create the legacy single-symlink layout
76
+ fs.mkdirpSync(path.dirname(workspaceProjectDir));
77
+ fs.symlinkSync(projectDir, workspaceProjectDir, 'junction');
78
+ await prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set(['es', 'fr']));
79
+ const lstat = fs.lstatSync(workspaceProjectDir);
80
+ expect(lstat.isDirectory()).toBe(true);
81
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'ai', 'intro.mdx'))).toBe(true);
82
+ expect(fs.existsSync(path.join(workspaceProjectDir, 'es'))).toBe(false);
83
+ });
84
+ it('surfaces friendly error (not raw stack trace) when fs.rm throws ENOTEMPTY', async () => {
85
+ // Regression: before safeRemoveCache, fs.remove raised an unfriendly stack
86
+ // trace when Turbopack still held open files. Now safeRemoveCache detects
87
+ // the race and calls process.exit(1) with a human-readable message.
88
+ const rmSpy = vi.spyOn(fs, 'rm');
89
+ const exitSpy = vi.spyOn(process, 'exit').mockImplementation(((code) => {
90
+ throw new Error(`process.exit:${code}`);
91
+ }));
92
+ const errorSpy = vi.spyOn(output, 'error').mockImplementation(() => undefined);
93
+ const enotempty = Object.assign(new Error('ENOTEMPTY'), { code: 'ENOTEMPTY' });
94
+ // fs.rm will be called by safeRemoveCache; make it fail with ENOTEMPTY
95
+ // even after the internal maxRetries — simulate persistent race condition.
96
+ rmSpy.mockRejectedValue(enotempty);
97
+ await expect(prepareProjectWorkspaceLinks(projectDir, workspaceProjectDir, new Set())).rejects.toThrow('process.exit:1');
98
+ const msg = errorSpy.mock.calls[0]?.[0] ?? '';
99
+ expect(msg).toContain('Another `jamdesk dev` instance');
100
+ expect(msg).toContain('pkill -f');
101
+ expect(exitSpy).toHaveBeenCalledWith(1);
102
+ vi.restoreAllMocks();
103
+ });
104
+ });
105
+ //# sourceMappingURL=dev-workspace-symlinks.test.js.map
@@ -0,0 +1 @@
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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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=language-filter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-filter.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/language-filter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,95 @@
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 } 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
+ //# sourceMappingURL=language-filter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"language-filter.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/language-filter.test.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,10 @@ const SYNCED_FILES = [
30
30
  // This file MUST be identical - it controls which languages are highlighted
31
31
  // and which are passed through to special components like Mermaid
32
32
  ['build-service/lib/shiki-config.ts', 'cli/vendored/lib/shiki-config.ts'],
33
+ ['build-service/lib/cache-tags.ts', 'cli/vendored/lib/cache-tags.ts'],
34
+ ['build-service/lib/revalidation-helpers.ts', 'cli/vendored/lib/revalidation-helpers.ts'],
35
+ ['build-service/lib/revalidation-trigger.ts', 'cli/vendored/lib/revalidation-trigger.ts'],
36
+ ['build-service/lib/r2-manifest.ts', 'cli/vendored/lib/r2-manifest.ts'],
33
37
  ];
34
38
  describe('CLI vendored files sync', () => {
35
39
  it.each(SYNCED_FILES)('should have matching content: %s', async (buildServicePath, vendoredPath) => {
@@ -1 +1 @@
1
- {"version":3,"file":"vendored-sync.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/vendored-sync.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vendored-sync.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/vendored-sync.test.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,19 @@ export interface DevOptions {
26
26
  verbose: boolean;
27
27
  webpack?: boolean;
28
28
  clean?: boolean;
29
+ lang?: string;
30
+ allLangs?: boolean;
29
31
  }
32
+ /**
33
+ * Returns the resolved (installed) Next.js version from
34
+ * `<depsDir>/node_modules/next/package.json`. Falls back to the
35
+ * declared pin in REQUIRED_DEPS when the file doesn't exist (first run
36
+ * before deps install, though ensureDependencies runs earlier so this
37
+ * path is defensive).
38
+ *
39
+ * Exported for unit testing.
40
+ */
41
+ export declare function getInstalledNextVersion(depsDir: string): Promise<string>;
30
42
  /**
31
43
  * Sync vendored files to workspace, only copying files that changed.
32
44
  * This preserves file timestamps for unchanged files, allowing Turbopack
@@ -38,11 +50,44 @@ export declare function syncVendoredFiles(srcDir: string, destDir: string, filte
38
50
  removed: number;
39
51
  }>;
40
52
  /**
41
- * Validates the .next cache and cleans it if corrupted or stale.
42
- * Returns true if cache is valid and usable, false if it was cleared.
53
+ * Validates the .next cache and clears it when reuse would be unsafe.
54
+ * Returns true if cache is valid and reusable, false if it was cleared.
55
+ *
56
+ * Wipe conditions:
57
+ * - Next.js version changed (Turbopack persistent-cache encoding may
58
+ * have changed; safest to start fresh)
59
+ * - Workspace corruption (turbopack cache exists but app/ does not —
60
+ * happens when a previous dev session was killed mid-prep)
61
+ * - First-ever run with a pre-existing cache but no version files
62
+ * (cache predates version tracking; can't verify compatibility)
63
+ *
64
+ * Preserve conditions (everything else, in particular):
65
+ * - CLI version changed but Next.js version is the same. Turbopack's
66
+ * content-addressed cache invalidates per-file when syncVendoredFiles
67
+ * copies changed vendored files; we don't need a wipe.
68
+ *
69
+ * Exported for unit testing.
70
+ */
71
+ export interface ValidateCacheOptions {
72
+ nextCacheDir: string;
73
+ cliVersionFile: string;
74
+ nextVersionFile: string;
75
+ workspaceDir: string;
76
+ currentNextVersion: string;
77
+ verbose: boolean;
78
+ }
79
+ export declare function validateAndCleanCache(opts: ValidateCacheOptions): Promise<boolean>;
80
+ /**
81
+ * Prepares <workspace>/projects/<name>/ as a directory with per-entry symlinks
82
+ * to the real <projectDir>/. Skip set entries are omitted — used to keep
83
+ * non-active-language directories out of Turbopack's filesystem scan, which
84
+ * scales linearly with file count. See docs/plans/2026-04-29-jamdesk-dev-compile-perf.md.
85
+ *
86
+ * Always rebuilds from scratch (cheap; only symlinks). Handles legacy
87
+ * single-symlink layout (CLI ≤1.1.42) by replacing it with the new layout.
43
88
  *
44
89
  * Exported for unit testing.
45
90
  */
46
- export declare function validateAndCleanCache(nextCacheDir: string, cliVersionFile: string, workspaceDir: string, verbose: boolean): Promise<boolean>;
91
+ export declare function prepareProjectWorkspaceLinks(projectDir: string, workspaceProjectDir: string, skip: Set<string>): Promise<void>;
47
92
  export declare function dev(options: DevOptions): Promise<void>;
48
93
  //# sourceMappingURL=dev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyBH;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAO/E;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAiJD;;;;GAIG;AAGH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,GACxC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAuE/D;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,OAAO,CAAC,CA6BlB;AAED,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAqtB5D"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0BH;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAO/E;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqCD;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO9E;AA+GD;;;;GAIG;AAGH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,GACxC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAuE/D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,OAAO,CAAC,CA4DlB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,MAAM,EAClB,mBAAmB,EAAE,MAAM,EAC3B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAChB,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,wBAAsB,GAAG,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAsvB5D"}