jamdesk 1.1.132 → 1.1.134
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.
- package/dist/__tests__/unit/vendored-sync.test.js +77 -25
- package/dist/__tests__/unit/vendored-sync.test.js.map +1 -1
- package/package.json +1 -1
- package/vendored/components/mdx/MDXComponents.tsx +3 -0
- package/vendored/components/mdx/Widget.tsx +148 -0
- package/vendored/lib/docs-isr.ts +28 -4
- package/vendored/workspace-package-lock.json +34 -34
- package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts +0 -2
- package/dist/__tests__/unit/dev-workspace-symlinks.test.d.ts.map +0 -1
- package/dist/__tests__/unit/dev-workspace-symlinks.test.js +0 -112
- package/dist/__tests__/unit/dev-workspace-symlinks.test.js.map +0 -1
- package/dist/__tests__/unit/language-filter.test.d.ts +0 -2
- package/dist/__tests__/unit/language-filter.test.d.ts.map +0 -1
- package/dist/__tests__/unit/language-filter.test.js +0 -166
- package/dist/__tests__/unit/language-filter.test.js.map +0 -1
- package/dist/lib/language-filter.d.ts +0 -31
- package/dist/lib/language-filter.d.ts.map +0 -1
- package/dist/lib/language-filter.js +0 -14
- package/dist/lib/language-filter.js.map +0 -1
|
@@ -16,39 +16,26 @@ import path from 'path';
|
|
|
16
16
|
import { fileURLToPath } from 'url';
|
|
17
17
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* NON-lib vendored files that must stay byte-identical between build-service and
|
|
20
|
+
* cli/vendored. Everything under `cli/vendored/lib/` is covered comprehensively by
|
|
21
|
+
* the "vendored/lib comprehensive sync" suite below, so do NOT list lib/ files
|
|
22
|
+
* here — add them to that suite's exception set only if vendor.js transforms them.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
24
|
+
* Add a non-lib file here when it's vendored and must match its source. Only list
|
|
25
|
+
* files that should be IDENTICAL: some vendored files (e.g. the stripped-down
|
|
26
|
+
* MDXComponents.tsx, the PlaygroundModal-stripped OpenApiEndpoint.tsx) are
|
|
27
|
+
* intentionally transformed by vendor.js and must NOT be listed.
|
|
25
28
|
*
|
|
26
29
|
* Format: [build-service path, cli/vendored path]
|
|
27
30
|
*/
|
|
28
31
|
const SYNCED_FILES = [
|
|
29
|
-
// Shiki configuration (affects syntax highlighting and language detection)
|
|
30
|
-
// This file MUST be identical - it controls which languages are highlighted
|
|
31
|
-
// and which are passed through to special components like Mermaid
|
|
32
|
-
['build-service/lib/shiki-config.ts', 'cli/vendored/lib/shiki-config.ts'],
|
|
33
|
-
['build-service/lib/shiki-client.ts', 'cli/vendored/lib/shiki-client.ts'],
|
|
34
|
-
['build-service/lib/cache-tags.ts', 'cli/vendored/lib/cache-tags.ts'],
|
|
35
|
-
['build-service/lib/revalidation-helpers.ts', 'cli/vendored/lib/revalidation-helpers.ts'],
|
|
36
|
-
['build-service/lib/revalidation-trigger.ts', 'cli/vendored/lib/revalidation-trigger.ts'],
|
|
37
|
-
['build-service/lib/fs-readfile.ts', 'cli/vendored/lib/fs-readfile.ts'],
|
|
38
|
-
['build-service/lib/r2-manifest.ts', 'cli/vendored/lib/r2-manifest.ts'],
|
|
39
|
-
['build-service/lib/rehype-unwrap-nested-anchors.ts', 'cli/vendored/lib/rehype-unwrap-nested-anchors.ts'],
|
|
40
|
-
// Mintlify migration detection — error-parser reads MIGRATION_DOCS_URL from
|
|
41
|
-
// validate-config, so the two files must move together. Drift here means
|
|
42
|
-
// CLI surfaces would miss the migrate hint while production catches it.
|
|
43
|
-
['build-service/lib/validate-config.ts', 'cli/vendored/lib/validate-config.ts'],
|
|
44
|
-
['build-service/lib/build/error-parser.ts', 'cli/vendored/lib/build/error-parser.ts'],
|
|
45
32
|
// Theme rendering — `themes/index.ts` lowercases in `getTheme()`, `Header.tsx`
|
|
46
|
-
// reads `themeConfig.name` (canonical case), `layout-helpers.tsx`
|
|
47
|
-
//
|
|
48
|
-
// theme
|
|
33
|
+
// reads `themeConfig.name` (canonical case), and `lib/layout-helpers.tsx`
|
|
34
|
+
// (covered by the comprehensive lib suite) lowercases `data-theme` so
|
|
35
|
+
// `body[data-theme="nebula"]` selectors match for any-case theme strings — all
|
|
36
|
+
// three must move together with the canonical.
|
|
49
37
|
['build-service/themes/index.ts', 'cli/vendored/themes/index.ts'],
|
|
50
38
|
['build-service/components/navigation/Header.tsx', 'cli/vendored/components/navigation/Header.tsx'],
|
|
51
|
-
['build-service/lib/layout-helpers.tsx', 'cli/vendored/lib/layout-helpers.tsx'],
|
|
52
39
|
];
|
|
53
40
|
describe('CLI vendored files sync', () => {
|
|
54
41
|
it.each(SYNCED_FILES)('should have matching content: %s', async (buildServicePath, vendoredPath) => {
|
|
@@ -80,6 +67,71 @@ describe('CLI vendored files sync', () => {
|
|
|
80
67
|
}
|
|
81
68
|
});
|
|
82
69
|
});
|
|
70
|
+
/**
|
|
71
|
+
* `cli/vendored/lib/` is a near-verbatim byte-copy of `build-service/lib/`
|
|
72
|
+
* produced by scripts/vendor.js. A hand-maintained allowlist (the old approach)
|
|
73
|
+
* silently MISSED any file it didn't enumerate — e.g. `isr-build-executor.ts`
|
|
74
|
+
* could drift between source and vendored with zero CI failure, shipping a stale
|
|
75
|
+
* copy to `jamdesk dev`. This suite inverts the guarantee: it walks EVERY vendored
|
|
76
|
+
* lib file and requires a byte-match, so new vendored files are covered
|
|
77
|
+
* automatically and the only sanctioned way to differ is to appear on the small,
|
|
78
|
+
* explicit exception set below.
|
|
79
|
+
*
|
|
80
|
+
* EXCEPTIONS = the files vendor.js intentionally rewrites instead of byte-copying.
|
|
81
|
+
* An empirical `cmp` of the whole tree must yield EXACTLY these (169 files / 2
|
|
82
|
+
* exceptions at time of writing). The stale-exception guard below fails if one of
|
|
83
|
+
* these stops differing; the count guard fails if the walk resolves nothing.
|
|
84
|
+
*/
|
|
85
|
+
const VENDORED_LIB_INTENTIONAL_DIFFERENCES = new Set([
|
|
86
|
+
'r2-content.ts', // vendor.js writes an AWS-SDK-free stub (the real one needs the S3 client)
|
|
87
|
+
'openapi/index.ts', // vendor.js strips the playground-codegen re-export (codegen isn't vendored)
|
|
88
|
+
]);
|
|
89
|
+
function walkTsFiles(dir, base = dir) {
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
92
|
+
const full = path.join(dir, entry.name);
|
|
93
|
+
if (entry.isDirectory())
|
|
94
|
+
out.push(...walkTsFiles(full, base));
|
|
95
|
+
else if (/\.tsx?$/.test(entry.name))
|
|
96
|
+
out.push(path.relative(base, full).replace(/\\/g, '/'));
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
const BUILDER_ROOT = path.join(__dirname, '../../../..');
|
|
101
|
+
const VENDORED_LIB_FILES = walkTsFiles(path.join(BUILDER_ROOT, 'cli/vendored/lib')).filter((rel) => !VENDORED_LIB_INTENTIONAL_DIFFERENCES.has(rel));
|
|
102
|
+
describe('CLI vendored/lib comprehensive sync (no silent drift)', () => {
|
|
103
|
+
it('discovers the full vendored lib tree (guards against an empty/0-file walk)', () => {
|
|
104
|
+
// ~169 files at time of writing; >100 proves the walk resolved the real tree
|
|
105
|
+
// and the it.each below is not silently testing nothing (e.g. wrong root path).
|
|
106
|
+
expect(VENDORED_LIB_FILES.length).toBeGreaterThan(100);
|
|
107
|
+
});
|
|
108
|
+
it.each(VENDORED_LIB_FILES)('cli/vendored/lib/%s is byte-identical to its build-service/lib source', (rel) => {
|
|
109
|
+
const vendored = path.join(BUILDER_ROOT, 'cli/vendored/lib', rel);
|
|
110
|
+
const source = path.join(BUILDER_ROOT, 'build-service/lib', rel);
|
|
111
|
+
if (!fs.existsSync(source)) {
|
|
112
|
+
throw new Error(`cli/vendored/lib/${rel} has no build-service/lib counterpart.\n` +
|
|
113
|
+
`If vendor.js synthesizes it, add '${rel}' to VENDORED_LIB_INTENTIONAL_DIFFERENCES.`);
|
|
114
|
+
}
|
|
115
|
+
const vendoredContent = fs.readFileSync(vendored, 'utf-8');
|
|
116
|
+
const sourceContent = fs.readFileSync(source, 'utf-8');
|
|
117
|
+
if (vendoredContent !== sourceContent) {
|
|
118
|
+
throw new Error(`Vendored drift: cli/vendored/lib/${rel} differs from build-service/lib/${rel}.\n\n` +
|
|
119
|
+
`Run "npm run vendor" to resync.\n` +
|
|
120
|
+
`If vendor.js intentionally transforms this file, add '${rel}' to ` +
|
|
121
|
+
`VENDORED_LIB_INTENTIONAL_DIFFERENCES (and document the transform).`);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Keep the exception list honest: if a listed file stops differing (the
|
|
125
|
+
// transform was removed), it must be dropped from the set so real drift on that
|
|
126
|
+
// file is caught again.
|
|
127
|
+
it.each([...VENDORED_LIB_INTENTIONAL_DIFFERENCES])('intentional-difference %s exists in both trees and actually differs', (rel) => {
|
|
128
|
+
const vendored = path.join(BUILDER_ROOT, 'cli/vendored/lib', rel);
|
|
129
|
+
const source = path.join(BUILDER_ROOT, 'build-service/lib', rel);
|
|
130
|
+
expect(fs.existsSync(vendored), `${rel} is an intentional-difference but missing from vendored`).toBe(true);
|
|
131
|
+
expect(fs.existsSync(source), `${rel} is an intentional-difference but missing from build-service`).toBe(true);
|
|
132
|
+
expect(fs.readFileSync(vendored, 'utf-8') === fs.readFileSync(source, 'utf-8'), `${rel} is byte-identical — remove it from VENDORED_LIB_INTENTIONAL_DIFFERENCES`).toBe(false);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
83
135
|
describe('CLI package.json files entries', () => {
|
|
84
136
|
it('should have valid paths for all vendored/public entries', async () => {
|
|
85
137
|
const cliRoot = path.join(__dirname, '../../..');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vendored-sync.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/vendored-sync.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D
|
|
1
|
+
{"version":3,"file":"vendored-sync.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/vendored-sync.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.134",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -42,6 +42,7 @@ import { Columns } from './Columns';
|
|
|
42
42
|
import { View, ViewProvider, ViewSelector, ViewWrapper } from './View';
|
|
43
43
|
import { YouTube } from './YouTube';
|
|
44
44
|
import { Video } from './Video';
|
|
45
|
+
import { Widget } from './Widget';
|
|
45
46
|
import { Visibility } from './Visibility';
|
|
46
47
|
import { MdxErrorBlock } from './MdxErrorBlock';
|
|
47
48
|
|
|
@@ -248,6 +249,8 @@ export const MDXComponents = {
|
|
|
248
249
|
YouTube,
|
|
249
250
|
// Video player for local video files
|
|
250
251
|
Video,
|
|
252
|
+
// Live "What's New" widget demo (renders a trigger that opens the hosted modal)
|
|
253
|
+
Widget,
|
|
251
254
|
// Audience-conditional content. Filtered at MDX-compile time by
|
|
252
255
|
// lib/remark-visibility.ts for the HTML render path and by
|
|
253
256
|
// lib/visibility-filter.ts for text surfaces (.md export, llms-full.txt,
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useId, type ReactElement } from 'react';
|
|
4
|
+
import { useProjectSlug } from '../../lib/project-slug-context';
|
|
5
|
+
import { useLinkPrefix } from '../../lib/link-prefix-context';
|
|
6
|
+
|
|
7
|
+
export interface WidgetProps {
|
|
8
|
+
/** Docs path the modal opens. Default "/changelog". */
|
|
9
|
+
page?: string;
|
|
10
|
+
/** Force the modal color scheme. */
|
|
11
|
+
theme?: 'auto' | 'light' | 'dark';
|
|
12
|
+
/** Trigger button text (also the modal aria-label). Default "What's new". */
|
|
13
|
+
label?: string;
|
|
14
|
+
/** Bind to your own element (CSS selector) instead of rendering a button. */
|
|
15
|
+
trigger?: string;
|
|
16
|
+
/** Modal width / height / corner radius (any CSS length). */
|
|
17
|
+
width?: string;
|
|
18
|
+
height?: string;
|
|
19
|
+
radius?: string;
|
|
20
|
+
/** Set false to hide the unread dot. */
|
|
21
|
+
unread?: boolean;
|
|
22
|
+
/** Unread dot color (hex or CSS color name). */
|
|
23
|
+
unreadColor?: string;
|
|
24
|
+
/** Override the localStorage "seen" key (rarely needed). */
|
|
25
|
+
project?: string;
|
|
26
|
+
/** Extra class names for the rendered button. */
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// `page` flows into a cross-origin iframe `src` (<base><page>?embed=1). In a
|
|
31
|
+
// PUBLIC component an attacker-authored page value must not be coercible into an
|
|
32
|
+
// absolute/protocol-relative URL that frames another origin. Accept only a clean
|
|
33
|
+
// same-site relative path; anything else falls back to the safe default.
|
|
34
|
+
// Trim first so an authoring typo (a trailing space → "/changelog ?embed=1",
|
|
35
|
+
// a malformed URL that silently 404s the modal) can't slip through, and a
|
|
36
|
+
// leading-space path still resolves instead of falling back.
|
|
37
|
+
// Exported for direct unit testing.
|
|
38
|
+
export function safePage(page: string): string {
|
|
39
|
+
const trimmed = page.trim();
|
|
40
|
+
if (!trimmed.startsWith('/') || trimmed.startsWith('//') || trimmed.includes('://') || trimmed.includes('\\')) {
|
|
41
|
+
return '/changelog';
|
|
42
|
+
}
|
|
43
|
+
return trimmed;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function Widget({
|
|
47
|
+
page = '/changelog',
|
|
48
|
+
theme,
|
|
49
|
+
label = "What's new",
|
|
50
|
+
trigger,
|
|
51
|
+
width,
|
|
52
|
+
height,
|
|
53
|
+
radius,
|
|
54
|
+
unread,
|
|
55
|
+
unreadColor,
|
|
56
|
+
project,
|
|
57
|
+
className,
|
|
58
|
+
}: WidgetProps): ReactElement | null {
|
|
59
|
+
const slug = useProjectSlug();
|
|
60
|
+
const prefix = useLinkPrefix(); // '/docs' when hostAtDocs, else ''
|
|
61
|
+
|
|
62
|
+
// useId() returns ":r0:"-style ids — invalid in a CSS selector and in
|
|
63
|
+
// querySelector('#...'), which is exactly what the widget runs on
|
|
64
|
+
// data-trigger. Strip the colons.
|
|
65
|
+
const buttonId = `jd-w-${useId().replace(/:/g, '')}`;
|
|
66
|
+
|
|
67
|
+
// Canonical ISR origin that renders ?embed=1. Custom domains can't, so always
|
|
68
|
+
// target <slug>.jamdesk.app (+ /docs when hostAtDocs) — never window.origin,
|
|
69
|
+
// and NO caller override: a `base` prop would let MDX frame an arbitrary
|
|
70
|
+
// origin in the chromeless overlay (clickjacking). Empty slug → no-op.
|
|
71
|
+
const resolvedBase = slug ? `https://${slug}.jamdesk.app${prefix}` : '';
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!resolvedBase) {
|
|
75
|
+
// No project slug in this context, so there's no canonical
|
|
76
|
+
// <slug>.jamdesk.app origin to bind the trigger to (see resolvedBase
|
|
77
|
+
// above). That's the normal state in local `jamdesk dev`, which runs a
|
|
78
|
+
// single project with no slug — the deployed multi-tenant build gets the
|
|
79
|
+
// slug from middleware's x-project-slug header. Without this line the
|
|
80
|
+
// button looks broken: no script, no click listener, and nothing in the
|
|
81
|
+
// console. Surface a one-line hint instead. Never fires in production,
|
|
82
|
+
// where the slug is always set.
|
|
83
|
+
console.info(
|
|
84
|
+
"[Jamdesk Widget] Live demo is inert here — the trigger binds to " +
|
|
85
|
+
"https://<slug>.jamdesk.app and only runs on the deployed docs site, " +
|
|
86
|
+
"not in local `jamdesk dev`.",
|
|
87
|
+
);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const triggerSel = trigger ?? `#${buttonId}`;
|
|
91
|
+
|
|
92
|
+
// Idempotent guard. React Strict Mode double-invokes effects (mount →
|
|
93
|
+
// cleanup → mount) in dev, and soft-nav can re-render — never stack two
|
|
94
|
+
// managed scripts for the same trigger (two scripts → two click listeners →
|
|
95
|
+
// the modal opens twice). Key on the trigger selector.
|
|
96
|
+
const sel = `script[data-jd-widget-managed][data-widget-key="${triggerSel.replace(/"/g, '\\"')}"]`;
|
|
97
|
+
if (document.querySelector(sel)) return;
|
|
98
|
+
|
|
99
|
+
const s = document.createElement('script');
|
|
100
|
+
// resolvedBase is always https://<slug>.jamdesk.app[/docs] — well-formed, no
|
|
101
|
+
// try/catch needed. widget.js lives at the subdomain ROOT, never under /docs.
|
|
102
|
+
s.src = `${new URL(resolvedBase).origin}/_jd/widget.js`;
|
|
103
|
+
s.async = true;
|
|
104
|
+
s.dataset.base = resolvedBase;
|
|
105
|
+
s.dataset.page = safePage(page); // reject absolute/protocol-relative paths
|
|
106
|
+
s.dataset.trigger = triggerSel;
|
|
107
|
+
if (theme) s.dataset.theme = theme;
|
|
108
|
+
if (width) s.dataset.width = width;
|
|
109
|
+
if (height) s.dataset.height = height;
|
|
110
|
+
if (radius) s.dataset.radius = radius;
|
|
111
|
+
if (unread === false) s.dataset.unread = 'off';
|
|
112
|
+
if (unreadColor) s.dataset.unreadColor = unreadColor;
|
|
113
|
+
if (project) s.dataset.project = project;
|
|
114
|
+
s.setAttribute('data-jd-widget-managed', '');
|
|
115
|
+
s.setAttribute('data-widget-key', triggerSel); // idempotency key (above)
|
|
116
|
+
document.body.appendChild(s);
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
// Soft-nav cleanup: remove the script. The widget's click listener lives
|
|
120
|
+
// on the button (unmounted with this component) — we always bind a
|
|
121
|
+
// trigger, so no floating launcher is left orphaned. (If the modal is open
|
|
122
|
+
// at unmount the widget's own ESC/close path still tears the dialog down.)
|
|
123
|
+
s.remove();
|
|
124
|
+
};
|
|
125
|
+
}, [
|
|
126
|
+
resolvedBase, page, trigger, theme, width, height, radius,
|
|
127
|
+
unread, unreadColor, project, buttonId,
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
if (trigger) return null;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<button
|
|
134
|
+
type="button"
|
|
135
|
+
id={buttonId}
|
|
136
|
+
className={
|
|
137
|
+
'jd-widget-trigger my-4 inline-flex cursor-pointer items-center gap-2 rounded-lg ' +
|
|
138
|
+
'border border-gray-200 bg-white px-4 py-2 text-sm font-medium ' +
|
|
139
|
+
'text-gray-900 shadow-sm transition hover:shadow-md ' +
|
|
140
|
+
'dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100' +
|
|
141
|
+
(className ? ` ${className}` : '')
|
|
142
|
+
}
|
|
143
|
+
>
|
|
144
|
+
<span aria-hidden="true">✦</span>
|
|
145
|
+
{label}
|
|
146
|
+
</button>
|
|
147
|
+
);
|
|
148
|
+
}
|
package/vendored/lib/docs-isr.ts
CHANGED
|
@@ -22,6 +22,13 @@ import {
|
|
|
22
22
|
} from './r2-content';
|
|
23
23
|
import { isIsrMode } from './page-isr-helpers';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Thrown by getDocsConfig when a project is absent in R2 and has no dev-fallback.
|
|
27
|
+
* Shared so projectExists can distinguish "genuinely missing" from a transient
|
|
28
|
+
* R2 error without coupling to a duplicated literal.
|
|
29
|
+
*/
|
|
30
|
+
const PROJECT_NOT_FOUND_PREFIX = 'Project not found in R2';
|
|
31
|
+
|
|
25
32
|
/**
|
|
26
33
|
* Ensure ISR mode is enabled, throw otherwise.
|
|
27
34
|
*/
|
|
@@ -99,7 +106,7 @@ async function getDocsConfigUncached(projectSlug: string): Promise<DocsConfig> {
|
|
|
99
106
|
// fall through to throw below
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
|
-
throw new Error(
|
|
109
|
+
throw new Error(`${PROJECT_NOT_FOUND_PREFIX}: ${projectSlug}`);
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
/**
|
|
@@ -192,9 +199,26 @@ export async function getSnippet(
|
|
|
192
199
|
*/
|
|
193
200
|
export async function projectExists(projectSlug: string): Promise<boolean> {
|
|
194
201
|
requireIsrMode();
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
// Route through getDocsConfig (the React cache()-wrapped reader) instead of a
|
|
203
|
+
// raw fetchDocsConfig so this existence check dedupes with the config read the
|
|
204
|
+
// page render performs in the same request — render-doc-page.tsx calls this
|
|
205
|
+
// immediately before loader.getConfig() in BOTH the metadata and render passes,
|
|
206
|
+
// and the raw call was paying its own R2 round-trip each time.
|
|
207
|
+
//
|
|
208
|
+
// getDocsConfig folds in the same dev-fallback this used to do explicitly and
|
|
209
|
+
// throws PROJECT_NOT_FOUND_PREFIX only when the project is absent in R2 *and*
|
|
210
|
+
// dev-fallback. Map that one case back to the original devFallbackDir check
|
|
211
|
+
// (preserving the dir-exists-but-unreadable-docs.json dev edge) and re-throw
|
|
212
|
+
// genuine (transient) R2 errors, so true/false/throw outcomes are unchanged.
|
|
213
|
+
try {
|
|
214
|
+
await getDocsConfig(projectSlug);
|
|
215
|
+
return true;
|
|
216
|
+
} catch (err) {
|
|
217
|
+
if (err instanceof Error && err.message.startsWith(PROJECT_NOT_FOUND_PREFIX)) {
|
|
218
|
+
return devFallbackDir(projectSlug) !== null;
|
|
219
|
+
}
|
|
220
|
+
throw err;
|
|
221
|
+
}
|
|
198
222
|
}
|
|
199
223
|
|
|
200
224
|
/**
|
|
@@ -184,9 +184,9 @@
|
|
|
184
184
|
"license": "Apache-2.0"
|
|
185
185
|
},
|
|
186
186
|
"node_modules/@emnapi/runtime": {
|
|
187
|
-
"version": "1.
|
|
188
|
-
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.
|
|
189
|
-
"integrity": "sha512-
|
|
187
|
+
"version": "1.11.0",
|
|
188
|
+
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz",
|
|
189
|
+
"integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==",
|
|
190
190
|
"license": "MIT",
|
|
191
191
|
"optional": true,
|
|
192
192
|
"dependencies": {
|
|
@@ -1580,15 +1580,15 @@
|
|
|
1580
1580
|
}
|
|
1581
1581
|
},
|
|
1582
1582
|
"node_modules/@tailwindcss/typography": {
|
|
1583
|
-
"version": "0.5.
|
|
1584
|
-
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.
|
|
1585
|
-
"integrity": "sha512-
|
|
1583
|
+
"version": "0.5.20",
|
|
1584
|
+
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz",
|
|
1585
|
+
"integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==",
|
|
1586
1586
|
"license": "MIT",
|
|
1587
1587
|
"dependencies": {
|
|
1588
1588
|
"postcss-selector-parser": "6.0.10"
|
|
1589
1589
|
},
|
|
1590
1590
|
"peerDependencies": {
|
|
1591
|
-
"tailwindcss": ">=3.0.0 ||
|
|
1591
|
+
"tailwindcss": ">=3.0.0 || >=4.0.0 || insiders"
|
|
1592
1592
|
}
|
|
1593
1593
|
},
|
|
1594
1594
|
"node_modules/@terrastruct/d2": {
|
|
@@ -1911,9 +1911,9 @@
|
|
|
1911
1911
|
}
|
|
1912
1912
|
},
|
|
1913
1913
|
"node_modules/@types/mdx": {
|
|
1914
|
-
"version": "2.0.
|
|
1915
|
-
"resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.
|
|
1916
|
-
"integrity": "sha512
|
|
1914
|
+
"version": "2.0.14",
|
|
1915
|
+
"resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.14.tgz",
|
|
1916
|
+
"integrity": "sha512-T48PeuJtvLosNTPVhfnIp3i/n3a4g4Bad7YCq5k64D4u7NwDrAotikQ+5+sjtUvBmxCMlbo3dVL+C2dP0rWHzg==",
|
|
1917
1917
|
"license": "MIT"
|
|
1918
1918
|
},
|
|
1919
1919
|
"node_modules/@types/ms": {
|
|
@@ -1932,18 +1932,18 @@
|
|
|
1932
1932
|
}
|
|
1933
1933
|
},
|
|
1934
1934
|
"node_modules/@types/node": {
|
|
1935
|
-
"version": "25.9.
|
|
1936
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.
|
|
1937
|
-
"integrity": "sha512-
|
|
1935
|
+
"version": "25.9.2",
|
|
1936
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
|
|
1937
|
+
"integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
|
|
1938
1938
|
"license": "MIT",
|
|
1939
1939
|
"dependencies": {
|
|
1940
1940
|
"undici-types": ">=7.24.0 <7.24.7"
|
|
1941
1941
|
}
|
|
1942
1942
|
},
|
|
1943
1943
|
"node_modules/@types/react": {
|
|
1944
|
-
"version": "19.2.
|
|
1945
|
-
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.
|
|
1946
|
-
"integrity": "sha512-
|
|
1944
|
+
"version": "19.2.17",
|
|
1945
|
+
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
|
|
1946
|
+
"integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
|
|
1947
1947
|
"license": "MIT",
|
|
1948
1948
|
"dependencies": {
|
|
1949
1949
|
"csstype": "^3.2.2"
|
|
@@ -2145,9 +2145,9 @@
|
|
|
2145
2145
|
}
|
|
2146
2146
|
},
|
|
2147
2147
|
"node_modules/baseline-browser-mapping": {
|
|
2148
|
-
"version": "2.10.
|
|
2149
|
-
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.
|
|
2150
|
-
"integrity": "sha512-
|
|
2148
|
+
"version": "2.10.34",
|
|
2149
|
+
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
|
|
2150
|
+
"integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
|
|
2151
2151
|
"license": "Apache-2.0",
|
|
2152
2152
|
"bin": {
|
|
2153
2153
|
"baseline-browser-mapping": "dist/cli.cjs"
|
|
@@ -2208,9 +2208,9 @@
|
|
|
2208
2208
|
"license": "MIT"
|
|
2209
2209
|
},
|
|
2210
2210
|
"node_modules/caniuse-lite": {
|
|
2211
|
-
"version": "1.0.
|
|
2212
|
-
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.
|
|
2213
|
-
"integrity": "sha512-
|
|
2211
|
+
"version": "1.0.30001797",
|
|
2212
|
+
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz",
|
|
2213
|
+
"integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==",
|
|
2214
2214
|
"funding": [
|
|
2215
2215
|
{
|
|
2216
2216
|
"type": "opencollective",
|
|
@@ -2939,15 +2939,15 @@
|
|
|
2939
2939
|
"license": "MIT"
|
|
2940
2940
|
},
|
|
2941
2941
|
"node_modules/electron-to-chromium": {
|
|
2942
|
-
"version": "1.5.
|
|
2943
|
-
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.
|
|
2944
|
-
"integrity": "sha512-
|
|
2942
|
+
"version": "1.5.368",
|
|
2943
|
+
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.368.tgz",
|
|
2944
|
+
"integrity": "sha512-7RckJJK4uESJF9PxvfMWd3TGqIiieUTG4HxnKaKuIpGbcr+r2ZEB3g2gAhCP3Fqm42vJSzLfgab9eva/C4/XVw==",
|
|
2945
2945
|
"license": "ISC"
|
|
2946
2946
|
},
|
|
2947
2947
|
"node_modules/enhanced-resolve": {
|
|
2948
|
-
"version": "5.
|
|
2949
|
-
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.
|
|
2950
|
-
"integrity": "sha512-
|
|
2948
|
+
"version": "5.23.0",
|
|
2949
|
+
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz",
|
|
2950
|
+
"integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==",
|
|
2951
2951
|
"license": "MIT",
|
|
2952
2952
|
"dependencies": {
|
|
2953
2953
|
"graceful-fs": "^4.2.4",
|
|
@@ -3238,9 +3238,9 @@
|
|
|
3238
3238
|
}
|
|
3239
3239
|
},
|
|
3240
3240
|
"node_modules/fuse.js": {
|
|
3241
|
-
"version": "7.4.
|
|
3242
|
-
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.4.
|
|
3243
|
-
"integrity": "sha512-
|
|
3241
|
+
"version": "7.4.2",
|
|
3242
|
+
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.4.2.tgz",
|
|
3243
|
+
"integrity": "sha512-LVbzjD4WA6UP5B1UnP8wuaXJiLnqMdM/E4fiJXTJ5haJ5b/MBNsK29h2fm6swEoQaVQjvYFWKLE2RanyZIoRVQ==",
|
|
3244
3244
|
"license": "Apache-2.0",
|
|
3245
3245
|
"engines": {
|
|
3246
3246
|
"node": ">=10"
|
|
@@ -6079,9 +6079,9 @@
|
|
|
6079
6079
|
}
|
|
6080
6080
|
},
|
|
6081
6081
|
"node_modules/semver": {
|
|
6082
|
-
"version": "7.8.
|
|
6083
|
-
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.
|
|
6084
|
-
"integrity": "sha512-
|
|
6082
|
+
"version": "7.8.3",
|
|
6083
|
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.3.tgz",
|
|
6084
|
+
"integrity": "sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==",
|
|
6085
6085
|
"license": "ISC",
|
|
6086
6086
|
"optional": true,
|
|
6087
6087
|
"bin": {
|
|
@@ -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 +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"}
|