docs-i18n 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{src/admin/ui → admin/app}/components/JobDialog.tsx +21 -2
- package/{src/admin/ui → admin/app}/components/JobPanel.tsx +1 -1
- package/{src/admin/ui → admin/app}/components/Preview.tsx +2 -5
- package/{src/admin/ui → admin/app}/lib/api.ts +18 -39
- package/admin/app/routeTree.gen.ts +68 -0
- package/admin/app/router.tsx +23 -0
- package/admin/app/routes/__root.tsx +55 -0
- package/admin/app/routes/index.tsx +416 -0
- package/{src/admin/ui → admin/app}/styles.css +36 -3
- package/admin/package.json +27 -0
- package/admin/server/functions/jobs.ts +53 -0
- package/admin/server/functions/misc.ts +84 -0
- package/{src/admin/server/routes → admin/server/functions}/models.ts +16 -29
- package/admin/server/functions/status.ts +61 -0
- package/admin/server/index.ts +35 -0
- package/admin/server/init.ts +46 -0
- package/{src/admin → admin}/server/services/job-manager.ts +39 -10
- package/{src/admin → admin}/server/services/status.ts +6 -6
- package/admin/tsconfig.json +19 -0
- package/{src/admin → admin}/vite.config.ts +8 -2
- package/dist/{assemble-7H4QCW35.js → assemble-CP2BRYQJ.js} +6 -4
- package/dist/{chunk-A3YQNPKZ.js → chunk-CLYUAWZE.js} +1 -1
- package/dist/{chunk-YN4VJHCQ.js → chunk-JHBSHTXC.js} +1 -1
- package/dist/chunk-L64GJ4OB.js +32 -0
- package/dist/{chunk-SKKZIV3L.js → chunk-PNKVD2UK.js} +1 -29
- package/dist/{chunk-XEOYZUHS.js → chunk-QKIR7RKQ.js} +4 -31
- package/dist/chunk-TRURQFP4.js +31 -0
- package/dist/cli.js +108 -23
- package/dist/index.d.ts +41 -1
- package/dist/index.js +92 -3
- package/dist/{rescan-O5D3CYC2.js → rescan-HXMWFAOC.js} +5 -3
- package/dist/{status-F4MYIAAY.js → status-AGZDXOTZ.js} +4 -2
- package/dist/{translate-ZIVKNAC4.js → translate-A5X6MX4Y.js} +14 -7
- package/dist/upload-XL6KG6S2.js +132 -0
- package/package.json +17 -15
- package/template/app/components/BlogArticle.tsx +159 -0
- package/template/app/components/BlogList.tsx +88 -0
- package/template/app/components/Breadcrumbs.tsx +81 -0
- package/template/app/components/Card.tsx +31 -0
- package/template/app/components/Doc.tsx +191 -0
- package/template/app/components/DocBreadcrumb.tsx +60 -0
- package/template/app/components/DocContainer.tsx +13 -0
- package/template/app/components/DocTitle.tsx +11 -0
- package/template/app/components/DocsLayout.tsx +715 -0
- package/template/app/components/Dropdown.tsx +116 -0
- package/template/app/components/FallbackBanner.tsx +36 -0
- package/template/app/components/Footer.tsx +29 -0
- package/template/app/components/FrameworkSelect.tsx +150 -0
- package/template/app/components/LibraryCard.tsx +178 -0
- package/template/app/components/LocaleSwitcher.tsx +43 -0
- package/template/app/components/Navbar.tsx +430 -0
- package/template/app/components/PostNotFound.tsx +20 -0
- package/template/app/components/SearchButton.tsx +32 -0
- package/template/app/components/Select.tsx +103 -0
- package/template/app/components/Spinner.tsx +18 -0
- package/template/app/components/ThemeProvider.tsx +141 -0
- package/template/app/components/ThemeToggle.tsx +31 -0
- package/template/app/components/Toc.tsx +86 -0
- package/template/app/components/VersionSelect.tsx +118 -0
- package/template/app/components/icons/BSkyIcon.tsx +27 -0
- package/template/app/components/icons/BaseballCapIcon.tsx +25 -0
- package/template/app/components/icons/BrandXIcon.tsx +28 -0
- package/template/app/components/icons/CheckCircleIcon.tsx +28 -0
- package/template/app/components/icons/CogsIcon.tsx +25 -0
- package/template/app/components/icons/DiscordIcon.tsx +24 -0
- package/template/app/components/icons/GithubIcon.tsx +24 -0
- package/template/app/components/icons/GoogleIcon.tsx +24 -0
- package/template/app/components/icons/InstagramIcon.tsx +24 -0
- package/template/app/components/icons/NpmIcon.tsx +26 -0
- package/template/app/components/icons/YinYangIcon.tsx +26 -0
- package/template/app/components/icons/YouTubeIcon.tsx +24 -0
- package/template/app/components/markdown/CodeBlock.tsx +254 -0
- package/template/app/components/markdown/FileTabs.tsx +58 -0
- package/template/app/components/markdown/FrameworkContent.tsx +76 -0
- package/template/app/components/markdown/Markdown.tsx +216 -0
- package/template/app/components/markdown/MarkdownContent.tsx +89 -0
- package/template/app/components/markdown/MarkdownFrameworkHandler.tsx +66 -0
- package/template/app/components/markdown/MarkdownHeadingContext.tsx +35 -0
- package/template/app/components/markdown/MarkdownLink.tsx +46 -0
- package/template/app/components/markdown/MarkdownTabsHandler.tsx +109 -0
- package/template/app/components/markdown/PackageManagerTabs.tsx +95 -0
- package/template/app/components/markdown/Tabs.tsx +139 -0
- package/template/app/components/markdown/index.ts +15 -0
- package/template/app/components/ui/Button.tsx +141 -0
- package/template/app/components/ui/InlineCode.tsx +16 -0
- package/template/app/components/ui/MarkdownImg.tsx +21 -0
- package/template/app/config/frameworks.ts +93 -0
- package/template/app/contexts/SearchContext.tsx +36 -0
- package/template/app/db/index.ts +17 -0
- package/template/app/db/schema.ts +74 -0
- package/template/app/hooks/useClickOutside.ts +106 -0
- package/template/app/routeTree.gen.ts +584 -0
- package/template/app/router.tsx +29 -0
- package/template/app/routes/$lang.$project.$version.docs.$.tsx +128 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.$framework.$.tsx +106 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.$framework.index.tsx +27 -0
- package/template/app/routes/$lang.$project.$version.docs.framework.index.tsx +44 -0
- package/template/app/routes/$lang.$project.$version.docs.index.tsx +27 -0
- package/template/app/routes/$lang.$project.$version.docs.tsx +70 -0
- package/template/app/routes/$lang.$project.$version.tsx +69 -0
- package/template/app/routes/$lang.$project.docs.$.tsx +104 -0
- package/template/app/routes/$lang.$project.docs.index.tsx +20 -0
- package/template/app/routes/$lang.$project.docs.tsx +79 -0
- package/template/app/routes/$lang.$project.tsx +89 -0
- package/template/app/routes/$lang.blog.$.tsx +82 -0
- package/template/app/routes/$lang.blog.index.tsx +56 -0
- package/template/app/routes/$lang.blog.tsx +26 -0
- package/template/app/routes/$lang.docs.$.tsx +100 -0
- package/template/app/routes/$lang.docs.framework.$framework.$.tsx +104 -0
- package/template/app/routes/$lang.docs.framework.$framework.index.tsx +32 -0
- package/template/app/routes/$lang.docs.framework.index.tsx +47 -0
- package/template/app/routes/$lang.docs.index.tsx +20 -0
- package/template/app/routes/$lang.docs.tsx +90 -0
- package/template/app/routes/$lang.tsx +16 -0
- package/template/app/routes/__root.tsx +180 -0
- package/template/app/routes/index.tsx +89 -0
- package/template/app/site.config.ts +182 -0
- package/template/app/styles/app.css +1029 -0
- package/template/app/types/index.ts +77 -0
- package/template/app/utils/blog.server.ts +193 -0
- package/template/app/utils/blog.ts +42 -0
- package/template/app/utils/config.ts +120 -0
- package/template/app/utils/content-loader.ts +400 -0
- package/template/app/utils/dates.ts +29 -0
- package/template/app/utils/docs.server.ts +150 -0
- package/template/app/utils/markdown/filterFrameworkContent.ts +233 -0
- package/template/app/utils/markdown/index.ts +2 -0
- package/template/app/utils/markdown/installCommand.ts +143 -0
- package/template/app/utils/markdown/plugins/collectHeadings.ts +104 -0
- package/template/app/utils/markdown/plugins/extractCodeMeta.ts +57 -0
- package/template/app/utils/markdown/plugins/helpers.ts +33 -0
- package/template/app/utils/markdown/plugins/index.ts +8 -0
- package/template/app/utils/markdown/plugins/parseCommentComponents.ts +103 -0
- package/template/app/utils/markdown/plugins/transformCommentComponents.ts +23 -0
- package/template/app/utils/markdown/plugins/transformFrameworkComponent.ts +217 -0
- package/template/app/utils/markdown/plugins/transformTabsComponent.ts +359 -0
- package/template/app/utils/markdown/processor.ts +75 -0
- package/template/app/utils/site-config.tsx +11 -0
- package/template/app/utils/upload.ts +232 -0
- package/template/app/utils/useLocalStorage.ts +65 -0
- package/template/app/utils/utils.ts +23 -0
- package/template/package.json +54 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/fonts/Inter-latin-ext.woff2 +0 -0
- package/template/public/fonts/Inter-latin.woff2 +0 -0
- package/template/public/images/frameworks/angular-logo.svg +1 -0
- package/template/public/images/frameworks/js-logo.svg +1 -0
- package/template/public/images/frameworks/lit-logo.svg +1 -0
- package/template/public/images/frameworks/preact-logo.svg +6 -0
- package/template/public/images/frameworks/qwik-logo.svg +1 -0
- package/template/public/images/frameworks/react-logo.svg +1 -0
- package/template/public/images/frameworks/solid-logo.svg +1 -0
- package/template/public/images/frameworks/svelte-logo.svg +1 -0
- package/template/public/images/frameworks/vue-logo.svg +4 -0
- package/template/tsconfig.json +24 -0
- package/template/vite.config.ts +43 -0
- package/template/wrangler.jsonc +16 -0
- package/README.md +0 -161
- package/dist/server-73AVSOL5.js +0 -598
- package/src/admin/index.html +0 -13
- package/src/admin/server/index.ts +0 -138
- package/src/admin/server/routes/jobs.ts +0 -113
- package/src/admin/server/routes/status.ts +0 -57
- package/src/admin/ui/App.tsx +0 -332
- package/src/admin/ui/main.tsx +0 -19
- /package/{src/admin/ui → admin/app}/components/FileList.tsx +0 -0
- /package/{src/admin/ui → admin/app}/components/LangGrid.tsx +0 -0
- /package/{src/admin/ui → admin/app}/components/ProgressBar.tsx +0 -0
- /package/{src/admin/ui → admin/app}/lib/flags.ts +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createServerFn } from '@tanstack/react-start';
|
|
2
|
+
import {
|
|
3
|
+
getCache,
|
|
4
|
+
getFileBlocks,
|
|
5
|
+
getFileCoverage,
|
|
6
|
+
getLangs,
|
|
7
|
+
getOverview,
|
|
8
|
+
getVersions,
|
|
9
|
+
rescan,
|
|
10
|
+
} from '../services/status';
|
|
11
|
+
import { ensureInit } from '../init';
|
|
12
|
+
|
|
13
|
+
export const fetchStatus = createServerFn({ method: 'GET' }).handler(async () => {
|
|
14
|
+
await ensureInit();
|
|
15
|
+
return {
|
|
16
|
+
versions: getVersions(),
|
|
17
|
+
langs: ['en', ...getLangs()],
|
|
18
|
+
data: getOverview(),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export const fetchFileCoverage = createServerFn({ method: 'GET' })
|
|
23
|
+
.inputValidator((d: { version: string; lang: string }) => d)
|
|
24
|
+
.handler(async ({ data }) => {
|
|
25
|
+
await ensureInit();
|
|
26
|
+
const { version, lang } = data;
|
|
27
|
+
if (lang === 'en') {
|
|
28
|
+
const anyLang = getLangs()[0];
|
|
29
|
+
const coverage = getFileCoverage(version, anyLang);
|
|
30
|
+
return coverage.map((f) => ({ file: f.file, total: f.total, translated: f.total }));
|
|
31
|
+
}
|
|
32
|
+
return getFileCoverage(version, lang);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const fetchFileBlocks = createServerFn({ method: 'GET' })
|
|
36
|
+
.inputValidator((d: { version: string; lang: string; path: string }) => d)
|
|
37
|
+
.handler(async ({ data }) => {
|
|
38
|
+
await ensureInit();
|
|
39
|
+
const { version, lang, path } = data;
|
|
40
|
+
const blocks = getFileBlocks(version, lang, path);
|
|
41
|
+
if (!blocks) throw new Error('File not found');
|
|
42
|
+
return { file: path, lang, version, blocks };
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const deleteCacheEntry = createServerFn({ method: 'POST' })
|
|
46
|
+
.inputValidator((d: { version: string; lang: string; key: string }) => d)
|
|
47
|
+
.handler(async ({ data }) => {
|
|
48
|
+
await ensureInit();
|
|
49
|
+
const { lang, key } = data;
|
|
50
|
+
const cache = getCache();
|
|
51
|
+
cache.db.prepare('DELETE FROM translations WHERE lang = ? AND key = ?').run(lang, key);
|
|
52
|
+
return { deleted: key, lang };
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const rescanVersion = createServerFn({ method: 'POST' })
|
|
56
|
+
.inputValidator((d: { version: string }) => d)
|
|
57
|
+
.handler(async ({ data }) => {
|
|
58
|
+
await ensureInit();
|
|
59
|
+
const count = rescan(data.version);
|
|
60
|
+
return { version: data.version, files: count };
|
|
61
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { resolve, dirname } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { writeFileSync } from 'node:fs';
|
|
4
|
+
import { spawn } from 'node:child_process';
|
|
5
|
+
import { tmpdir } from 'node:os';
|
|
6
|
+
import type { DocsI18nConfig } from 'docs-i18n';
|
|
7
|
+
import { initAdmin } from './init';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Start the admin dashboard dev server.
|
|
11
|
+
* Called by the CLI `docs-i18n admin` command.
|
|
12
|
+
*/
|
|
13
|
+
export async function startAdmin(config: DocsI18nConfig, port: number) {
|
|
14
|
+
initAdmin(config);
|
|
15
|
+
|
|
16
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
17
|
+
// Admin package root is the directory containing this server/ folder
|
|
18
|
+
const adminRoot = resolve(dirname(thisFile), '..');
|
|
19
|
+
|
|
20
|
+
// Write project root so the admin server functions can find config + content.
|
|
21
|
+
const tmpFile = resolve(tmpdir(), 'docs-i18n-project-root');
|
|
22
|
+
writeFileSync(tmpFile, process.cwd());
|
|
23
|
+
|
|
24
|
+
const child = spawn('npx', ['vite', '--port', String(port)], {
|
|
25
|
+
cwd: adminRoot,
|
|
26
|
+
stdio: 'inherit',
|
|
27
|
+
env: { ...process.env, DOCS_I18N_PROJECT_ROOT: process.cwd() },
|
|
28
|
+
shell: true,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
32
|
+
|
|
33
|
+
// Keep the process alive until the child exits.
|
|
34
|
+
await new Promise<void>(() => {});
|
|
35
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import type { DocsI18nConfig } from 'docs-i18n';
|
|
5
|
+
import { initStatus } from './services/status';
|
|
6
|
+
import { setConfig } from './services/job-manager';
|
|
7
|
+
|
|
8
|
+
let _initialized = false;
|
|
9
|
+
let _config: DocsI18nConfig | undefined;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Initialize admin server services with the project config.
|
|
13
|
+
*/
|
|
14
|
+
export function initAdmin(config: DocsI18nConfig) {
|
|
15
|
+
_config = config;
|
|
16
|
+
initStatus(config);
|
|
17
|
+
setConfig(config);
|
|
18
|
+
_initialized = true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getConfig(): DocsI18nConfig | undefined {
|
|
22
|
+
return _config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getProjectRoot(): string {
|
|
26
|
+
if (process.env.DOCS_I18N_PROJECT_ROOT) return process.env.DOCS_I18N_PROJECT_ROOT;
|
|
27
|
+
// Read from temp file written by CLI at startup
|
|
28
|
+
try {
|
|
29
|
+
const tmpDir = process.env.TMPDIR || process.env.TEMP || '/tmp';
|
|
30
|
+
return readFileSync(resolve(tmpDir, 'docs-i18n-project-root'), 'utf-8').trim();
|
|
31
|
+
} catch {}
|
|
32
|
+
return process.cwd();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Ensure admin is initialized. Lazy-loads config on first call.
|
|
37
|
+
*/
|
|
38
|
+
export async function ensureInit() {
|
|
39
|
+
if (_initialized) return;
|
|
40
|
+
if (typeof window !== 'undefined') return;
|
|
41
|
+
const projectRoot = getProjectRoot();
|
|
42
|
+
const configPath = `${projectRoot}/docs-i18n.config.ts`;
|
|
43
|
+
const mod = await import(/* @vite-ignore */ configPath);
|
|
44
|
+
const config: DocsI18nConfig = mod.default ?? mod;
|
|
45
|
+
initAdmin(config);
|
|
46
|
+
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { type ChildProcess, spawn } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
import { resolve } from 'node:path';
|
|
3
|
-
import type { DocsI18nConfig } from '
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import type { DocsI18nConfig } from 'docs-i18n';
|
|
5
|
+
|
|
6
|
+
function getProjectRoot(): string {
|
|
7
|
+
if (process.env.DOCS_I18N_PROJECT_ROOT) return process.env.DOCS_I18N_PROJECT_ROOT;
|
|
8
|
+
try {
|
|
9
|
+
const tmpDir = process.env.TMPDIR || process.env.TEMP || '/tmp';
|
|
10
|
+
return readFileSync(resolve(tmpDir, 'docs-i18n-project-root'), 'utf-8').trim();
|
|
11
|
+
} catch {}
|
|
12
|
+
return process.cwd();
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
let _config: DocsI18nConfig | undefined;
|
|
10
16
|
export function setConfig(config: DocsI18nConfig) { _config = config; }
|
|
@@ -70,15 +76,25 @@ export class JobManager {
|
|
|
70
76
|
};
|
|
71
77
|
this.jobs.set(id, job);
|
|
72
78
|
|
|
79
|
+
// The version may be a compound key "project/version" from the admin UI.
|
|
80
|
+
// Split it into separate --project and --version args for the CLI.
|
|
81
|
+
let projectArg = opts.project ?? '';
|
|
82
|
+
let versionArg = opts.version;
|
|
83
|
+
if (!projectArg && opts.version.includes('/')) {
|
|
84
|
+
const parts = opts.version.split('/');
|
|
85
|
+
projectArg = parts.slice(0, -1).join('/');
|
|
86
|
+
versionArg = parts[parts.length - 1];
|
|
87
|
+
}
|
|
88
|
+
|
|
73
89
|
const args = [
|
|
74
90
|
'translate',
|
|
75
91
|
'--lang', opts.lang,
|
|
76
|
-
'--version',
|
|
92
|
+
'--version', versionArg,
|
|
77
93
|
'--max', String(opts.max ?? 999),
|
|
78
94
|
'--concurrency', String(opts.concurrency ?? 3),
|
|
79
95
|
];
|
|
80
96
|
|
|
81
|
-
if (
|
|
97
|
+
if (projectArg) args.push('--project', projectArg);
|
|
82
98
|
if (opts.model) args.push('--model', opts.model);
|
|
83
99
|
if (opts.files?.length) args.push('--files', opts.files.join(','));
|
|
84
100
|
|
|
@@ -86,10 +102,23 @@ export class JobManager {
|
|
|
86
102
|
const apiKey = _config?.llm?.apiKey || process.env.OPENROUTER_API_KEY || process.env.OPENAI_API_KEY || '';
|
|
87
103
|
if (apiKey) args.push('--api-key', apiKey);
|
|
88
104
|
|
|
89
|
-
const
|
|
90
|
-
|
|
105
|
+
const projectRoot = getProjectRoot();
|
|
106
|
+
|
|
107
|
+
// Load .env from project root so child process has access to env vars
|
|
108
|
+
const childEnv: Record<string, string> = { ...process.env, NO_TTY: '1', FORCE_COLOR: '0' };
|
|
109
|
+
try {
|
|
110
|
+
const envContent = readFileSync(resolve(projectRoot, '.env'), 'utf-8');
|
|
111
|
+
for (const line of envContent.split('\n')) {
|
|
112
|
+
const match = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*)\s*$/);
|
|
113
|
+
if (match && !childEnv[match[1]]) childEnv[match[1]] = match[2];
|
|
114
|
+
}
|
|
115
|
+
} catch {}
|
|
116
|
+
|
|
117
|
+
const proc = spawn('npx', ['docs-i18n', ...args], {
|
|
118
|
+
cwd: projectRoot,
|
|
91
119
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
92
|
-
env:
|
|
120
|
+
env: childEnv,
|
|
121
|
+
shell: true,
|
|
93
122
|
});
|
|
94
123
|
this.processes.set(id, proc);
|
|
95
124
|
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
|
-
import type { DocsI18nConfig } from '
|
|
4
|
-
import { flattenSources } from '
|
|
5
|
-
import { TranslationCache } from '../../../core/cache';
|
|
6
|
-
import { parseMdx } from '../../../core/parser';
|
|
3
|
+
import type { DocsI18nConfig } from 'docs-i18n';
|
|
4
|
+
import { flattenSources, TranslationCache, parseMdx } from 'docs-i18n';
|
|
7
5
|
|
|
8
6
|
let _config: DocsI18nConfig;
|
|
9
7
|
let _cache: TranslationCache | null = null;
|
|
8
|
+
let _projectRoot: string = process.cwd();
|
|
10
9
|
|
|
11
10
|
export function initStatus(config: DocsI18nConfig) {
|
|
12
11
|
_config = config;
|
|
13
12
|
_cache = null; // reset
|
|
13
|
+
_projectRoot = process.env.DOCS_I18N_PROJECT_ROOT || process.cwd();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export function getVersions(): string[] {
|
|
@@ -23,14 +23,14 @@ export function getLangs(): string[] {
|
|
|
23
23
|
|
|
24
24
|
export function getCache(): TranslationCache {
|
|
25
25
|
if (!_cache) {
|
|
26
|
-
_cache = new TranslationCache(resolve(_config.cacheDir ?? '.cache'));
|
|
26
|
+
_cache = new TranslationCache(resolve(_projectRoot, _config.cacheDir ?? '.cache'));
|
|
27
27
|
}
|
|
28
28
|
return _cache;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function getSourcePath(versionKey: string): string | null {
|
|
32
32
|
const source = flattenSources(_config).find((s) => s.versionKey === versionKey);
|
|
33
|
-
return source ? resolve(source.sourcePath) : null;
|
|
33
|
+
return source ? resolve(_projectRoot, source.sourcePath) : null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function walkFiles(dir: string): string[] {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"jsx": "react-jsx",
|
|
13
|
+
"paths": {
|
|
14
|
+
"docs-i18n": ["../core/src/index.ts"]
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"include": ["app", "server"],
|
|
18
|
+
"exclude": ["node_modules"]
|
|
19
|
+
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
// @ts-nocheck — Vite config, not checked by project tsc
|
|
2
|
-
import react from '@vitejs/plugin-react';
|
|
3
2
|
import { defineConfig } from 'vite';
|
|
3
|
+
import { tanstackStart } from '@tanstack/react-start/plugin/vite';
|
|
4
|
+
import react from '@vitejs/plugin-react';
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
|
-
plugins: [
|
|
7
|
+
plugins: [
|
|
8
|
+
tanstackStart({
|
|
9
|
+
srcDirectory: 'app',
|
|
10
|
+
}),
|
|
11
|
+
react(),
|
|
12
|
+
],
|
|
7
13
|
});
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
assemble
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-CLYUAWZE.js";
|
|
4
|
+
import "./chunk-JHBSHTXC.js";
|
|
5
5
|
import {
|
|
6
6
|
TranslationCache
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-QKIR7RKQ.js";
|
|
8
8
|
import {
|
|
9
9
|
flattenSources
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TRURQFP4.js";
|
|
11
|
+
import "./chunk-L64GJ4OB.js";
|
|
12
|
+
import "./chunk-PNKVD2UK.js";
|
|
11
13
|
|
|
12
14
|
// src/commands/assemble.ts
|
|
13
15
|
import {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/core/sqlite.ts
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
var isBun = typeof globalThis.Bun !== "undefined";
|
|
4
|
+
function openDatabase(filepath) {
|
|
5
|
+
if (isBun) {
|
|
6
|
+
const req2 = createRequire(import.meta.url);
|
|
7
|
+
const mod = req2("bun:sqlite");
|
|
8
|
+
const Database2 = mod.Database ?? mod.default ?? mod;
|
|
9
|
+
const raw = new Database2(filepath);
|
|
10
|
+
return {
|
|
11
|
+
prepare: (sql) => {
|
|
12
|
+
const stmt = raw.query(sql);
|
|
13
|
+
return {
|
|
14
|
+
run: (...params) => stmt.run(...params),
|
|
15
|
+
get: (...params) => stmt.get(...params),
|
|
16
|
+
all: (...params) => stmt.all(...params)
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
exec: (sql) => raw.run(sql),
|
|
20
|
+
pragma: (p) => raw.run(`PRAGMA ${p}`),
|
|
21
|
+
transaction: (fn) => raw.transaction(fn),
|
|
22
|
+
close: () => raw.close()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const req = createRequire(import.meta.url);
|
|
26
|
+
const Database = req("better-sqlite3");
|
|
27
|
+
return new Database(filepath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
openDatabase
|
|
32
|
+
};
|
|
@@ -19,36 +19,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
};
|
|
20
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
21
|
|
|
22
|
-
// src/config.ts
|
|
23
|
-
async function loadConfig(path) {
|
|
24
|
-
const configPath = path ?? "docs-i18n.config.ts";
|
|
25
|
-
try {
|
|
26
|
-
const mod = await import(
|
|
27
|
-
/* @vite-ignore */
|
|
28
|
-
`${process.cwd()}/${configPath}`
|
|
29
|
-
);
|
|
30
|
-
return mod.default ?? mod;
|
|
31
|
-
} catch {
|
|
32
|
-
throw new Error(`Cannot load config from ${configPath}. Create a docs-i18n.config.ts file.`);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function flattenSources(config) {
|
|
36
|
-
const projects = Object.entries(config.projects);
|
|
37
|
-
const singleProject = projects.length === 1;
|
|
38
|
-
return projects.flatMap(
|
|
39
|
-
([projectId, project]) => Object.entries(project.sources).map(([version, sourcePath]) => ({
|
|
40
|
-
project: projectId,
|
|
41
|
-
version,
|
|
42
|
-
sourcePath,
|
|
43
|
-
versionKey: singleProject ? version : `${projectId}/${version}`
|
|
44
|
-
}))
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
22
|
export {
|
|
49
23
|
__esm,
|
|
50
24
|
__export,
|
|
51
|
-
__toCommonJS
|
|
52
|
-
loadConfig,
|
|
53
|
-
flattenSources
|
|
25
|
+
__toCommonJS
|
|
54
26
|
};
|
|
@@ -1,37 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openDatabase
|
|
3
|
+
} from "./chunk-L64GJ4OB.js";
|
|
4
|
+
|
|
1
5
|
// src/core/cache.ts
|
|
2
6
|
import fs from "fs";
|
|
3
7
|
import path from "path";
|
|
4
|
-
|
|
5
|
-
// src/core/sqlite.ts
|
|
6
|
-
import { createRequire } from "module";
|
|
7
|
-
var isBun = typeof globalThis.Bun !== "undefined";
|
|
8
|
-
function openDatabase(filepath) {
|
|
9
|
-
if (isBun) {
|
|
10
|
-
const req2 = createRequire(import.meta.url);
|
|
11
|
-
const mod = req2("bun:sqlite");
|
|
12
|
-
const Database2 = mod.Database ?? mod.default ?? mod;
|
|
13
|
-
const raw = new Database2(filepath);
|
|
14
|
-
return {
|
|
15
|
-
prepare: (sql) => {
|
|
16
|
-
const stmt = raw.query(sql);
|
|
17
|
-
return {
|
|
18
|
-
run: (...params) => stmt.run(...params),
|
|
19
|
-
get: (...params) => stmt.get(...params),
|
|
20
|
-
all: (...params) => stmt.all(...params)
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
exec: (sql) => raw.run(sql),
|
|
24
|
-
pragma: (p) => raw.run(`PRAGMA ${p}`),
|
|
25
|
-
transaction: (fn) => raw.transaction(fn),
|
|
26
|
-
close: () => raw.close()
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const req = createRequire(import.meta.url);
|
|
30
|
-
const Database = req("better-sqlite3");
|
|
31
|
-
return new Database(filepath);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/core/cache.ts
|
|
35
8
|
var TranslationCache = class {
|
|
36
9
|
db;
|
|
37
10
|
cacheDir;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
async function loadConfig(path) {
|
|
3
|
+
const configPath = path ?? "docs-i18n.config.ts";
|
|
4
|
+
const fullPath = configPath.startsWith("/") ? configPath : `${process.cwd()}/${configPath}`;
|
|
5
|
+
try {
|
|
6
|
+
const mod = await import(
|
|
7
|
+
/* @vite-ignore */
|
|
8
|
+
fullPath
|
|
9
|
+
);
|
|
10
|
+
return mod.default ?? mod;
|
|
11
|
+
} catch {
|
|
12
|
+
throw new Error(`Cannot load config from ${fullPath}. Create a docs-i18n.config.ts file.`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function flattenSources(config) {
|
|
16
|
+
const projects = Object.entries(config.projects);
|
|
17
|
+
const singleProject = projects.length === 1;
|
|
18
|
+
return projects.flatMap(
|
|
19
|
+
([projectId, project]) => Object.entries(project.sources).map(([version, sourcePath]) => ({
|
|
20
|
+
project: projectId,
|
|
21
|
+
version,
|
|
22
|
+
sourcePath,
|
|
23
|
+
versionKey: singleProject ? version : `${projectId}/${version}`
|
|
24
|
+
}))
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {
|
|
29
|
+
loadConfig,
|
|
30
|
+
flattenSources
|
|
31
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
loadConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TRURQFP4.js";
|
|
5
|
+
import "./chunk-PNKVD2UK.js";
|
|
5
6
|
|
|
6
7
|
// src/cli.ts
|
|
7
|
-
import { existsSync, readFileSync } from "fs";
|
|
8
|
-
import { resolve } from "path";
|
|
9
8
|
import { createRequire } from "module";
|
|
10
|
-
var envPath = resolve(process.cwd(), ".env");
|
|
11
|
-
if (existsSync(envPath)) {
|
|
12
|
-
for (const line of readFileSync(envPath, "utf-8").split("\n")) {
|
|
13
|
-
const trimmed = line.trim();
|
|
14
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
15
|
-
const eq = trimmed.indexOf("=");
|
|
16
|
-
if (eq < 0) continue;
|
|
17
|
-
const key = trimmed.slice(0, eq).trim();
|
|
18
|
-
let val = trimmed.slice(eq + 1).trim();
|
|
19
|
-
if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'"))
|
|
20
|
-
val = val.slice(1, -1);
|
|
21
|
-
if (!process.env[key]) process.env[key] = val;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
9
|
var args = process.argv.slice(2);
|
|
25
10
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
26
11
|
const require2 = createRequire(import.meta.url);
|
|
@@ -36,6 +21,92 @@ function getOpt(name, def = "") {
|
|
|
36
21
|
function hasFlag(name) {
|
|
37
22
|
return args.includes(`--${name}`);
|
|
38
23
|
}
|
|
24
|
+
async function handleSiteCommand() {
|
|
25
|
+
const validSub = /* @__PURE__ */ new Set(["dev", "build", "upload", "deploy"]);
|
|
26
|
+
const subCommand = args.slice(1).find((a) => validSub.has(a));
|
|
27
|
+
const port = Number(getOpt("port", "3000"));
|
|
28
|
+
const { resolve, dirname } = await import("path");
|
|
29
|
+
const { fileURLToPath } = await import("url");
|
|
30
|
+
const { writeFileSync } = await import("fs");
|
|
31
|
+
const { spawn } = await import("child_process");
|
|
32
|
+
const os = await import("os");
|
|
33
|
+
const thisFile = fileURLToPath(import.meta.url);
|
|
34
|
+
const pkgRoot = resolve(dirname(thisFile), "..");
|
|
35
|
+
const { existsSync: exists } = await import("fs");
|
|
36
|
+
let templateRoot;
|
|
37
|
+
const nmPath = resolve(pkgRoot, "node_modules", "@docs-i18n", "template");
|
|
38
|
+
if (exists(nmPath)) {
|
|
39
|
+
const { realpathSync } = await import("fs");
|
|
40
|
+
templateRoot = realpathSync(nmPath);
|
|
41
|
+
} else {
|
|
42
|
+
templateRoot = resolve(pkgRoot, "template");
|
|
43
|
+
}
|
|
44
|
+
const tmpFile = resolve(os.tmpdir(), "docs-i18n-project-root");
|
|
45
|
+
writeFileSync(tmpFile, process.cwd());
|
|
46
|
+
const { existsSync } = await import("fs");
|
|
47
|
+
const { execSync } = await import("child_process");
|
|
48
|
+
if (!existsSync(resolve(templateRoot, "node_modules"))) {
|
|
49
|
+
console.log("Installing template dependencies...");
|
|
50
|
+
execSync("npm install --no-audit --no-fund", {
|
|
51
|
+
cwd: templateRoot,
|
|
52
|
+
stdio: "inherit"
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (!subCommand || subCommand === "dev") {
|
|
56
|
+
const child = spawn("npx", ["vite", "--port", String(port)], {
|
|
57
|
+
cwd: templateRoot,
|
|
58
|
+
stdio: "inherit",
|
|
59
|
+
env: { ...process.env, DOCS_I18N_PROJECT_ROOT: process.cwd() },
|
|
60
|
+
shell: true
|
|
61
|
+
});
|
|
62
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
63
|
+
} else if (subCommand === "build") {
|
|
64
|
+
const child = spawn("npx", ["vite", "build"], {
|
|
65
|
+
cwd: templateRoot,
|
|
66
|
+
stdio: "inherit",
|
|
67
|
+
env: { ...process.env, DOCS_I18N_PROJECT_ROOT: process.cwd() },
|
|
68
|
+
shell: true
|
|
69
|
+
});
|
|
70
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
71
|
+
} else if (subCommand === "upload") {
|
|
72
|
+
console.log("Uploading content to D1...");
|
|
73
|
+
const { collectContentFiles, generateContentSql, collectTranslations, generateTranslationSql } = await import("./upload-XL6KG6S2.js");
|
|
74
|
+
const projectRoot = process.cwd();
|
|
75
|
+
const contentRows = collectContentFiles(projectRoot);
|
|
76
|
+
const contentSql = generateContentSql(contentRows);
|
|
77
|
+
console.log(`Collected ${contentRows.length} content files.`);
|
|
78
|
+
const translationData = collectTranslations(projectRoot);
|
|
79
|
+
const translationSql = generateTranslationSql(translationData);
|
|
80
|
+
console.log(`Collected ${translationData.sources.length} sources, ${translationData.translations.length} translations.`);
|
|
81
|
+
const allSql = [...contentSql, ...translationSql];
|
|
82
|
+
const sqlFile = resolve(os.tmpdir(), "docs-i18n-upload.sql");
|
|
83
|
+
writeFileSync(sqlFile, allSql.join("\n"));
|
|
84
|
+
const dbName = getOpt("db", "docs-i18n-db");
|
|
85
|
+
const child = spawn("npx", ["wrangler", "d1", "execute", dbName, "--file", sqlFile], {
|
|
86
|
+
cwd: templateRoot,
|
|
87
|
+
stdio: "inherit",
|
|
88
|
+
shell: true
|
|
89
|
+
});
|
|
90
|
+
child.on("exit", (code) => {
|
|
91
|
+
if (code === 0) {
|
|
92
|
+
console.log("Upload complete.");
|
|
93
|
+
}
|
|
94
|
+
process.exit(code ?? 0);
|
|
95
|
+
});
|
|
96
|
+
} else if (subCommand === "deploy") {
|
|
97
|
+
const child = spawn("npx", ["wrangler", "deploy"], {
|
|
98
|
+
cwd: templateRoot,
|
|
99
|
+
stdio: "inherit",
|
|
100
|
+
env: { ...process.env },
|
|
101
|
+
shell: true
|
|
102
|
+
});
|
|
103
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
104
|
+
} else {
|
|
105
|
+
console.error(`Unknown site subcommand: ${subCommand}`);
|
|
106
|
+
console.log("Usage: docs-i18n site [dev|build|upload|deploy]");
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
39
110
|
async function main() {
|
|
40
111
|
if (!command || command === "--help" || command === "-h") {
|
|
41
112
|
console.log(`
|
|
@@ -47,6 +118,7 @@ Commands:
|
|
|
47
118
|
rescan Rescan source files and clean orphans
|
|
48
119
|
status Show translation coverage
|
|
49
120
|
admin Start admin dashboard
|
|
121
|
+
site Manage documentation site (dev, build, upload, deploy)
|
|
50
122
|
|
|
51
123
|
Options:
|
|
52
124
|
--lang <code> Target language (e.g., zh-hans)
|
|
@@ -59,10 +131,14 @@ Options:
|
|
|
59
131
|
--max-tokens <n> Max output tokens (default: 16384)
|
|
60
132
|
--context-length <n> Model context window (default: 32768)
|
|
61
133
|
--dry-run Preview without making changes
|
|
62
|
-
--port <n> Admin dashboard port (default: 3456)
|
|
134
|
+
--port <n> Admin dashboard port (default: 3456) / Site dev port (default: 3000)
|
|
63
135
|
`);
|
|
64
136
|
return;
|
|
65
137
|
}
|
|
138
|
+
if (command === "site") {
|
|
139
|
+
await handleSiteCommand();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
66
142
|
const configPath = getOpt("config") || void 0;
|
|
67
143
|
const config = await loadConfig(configPath);
|
|
68
144
|
switch (command) {
|
|
@@ -72,7 +148,7 @@ Options:
|
|
|
72
148
|
console.error("Error: --lang is required");
|
|
73
149
|
process.exit(1);
|
|
74
150
|
}
|
|
75
|
-
const { translate } = await import("./translate-
|
|
151
|
+
const { translate } = await import("./translate-A5X6MX4Y.js");
|
|
76
152
|
await translate(config, {
|
|
77
153
|
lang,
|
|
78
154
|
project: getOpt("project") || void 0,
|
|
@@ -89,7 +165,7 @@ Options:
|
|
|
89
165
|
break;
|
|
90
166
|
}
|
|
91
167
|
case "assemble": {
|
|
92
|
-
const { assembleAll } = await import("./assemble-
|
|
168
|
+
const { assembleAll } = await import("./assemble-CP2BRYQJ.js");
|
|
93
169
|
await assembleAll(config, {
|
|
94
170
|
project: getOpt("project") || void 0,
|
|
95
171
|
version: getOpt("version") || void 0,
|
|
@@ -98,7 +174,7 @@ Options:
|
|
|
98
174
|
break;
|
|
99
175
|
}
|
|
100
176
|
case "rescan": {
|
|
101
|
-
const { rescan } = await import("./rescan-
|
|
177
|
+
const { rescan } = await import("./rescan-HXMWFAOC.js");
|
|
102
178
|
await rescan(config, {
|
|
103
179
|
project: getOpt("project") || void 0,
|
|
104
180
|
version: getOpt("version") || void 0
|
|
@@ -106,13 +182,22 @@ Options:
|
|
|
106
182
|
break;
|
|
107
183
|
}
|
|
108
184
|
case "status": {
|
|
109
|
-
const { status } = await import("./status-
|
|
185
|
+
const { status } = await import("./status-AGZDXOTZ.js");
|
|
110
186
|
await status(config, { lang: getOpt("lang") || void 0 });
|
|
111
187
|
break;
|
|
112
188
|
}
|
|
113
189
|
case "admin": {
|
|
114
190
|
const port = Number(getOpt("port", "3456"));
|
|
115
|
-
const {
|
|
191
|
+
const { resolve: res, dirname: dn } = await import("path");
|
|
192
|
+
const { fileURLToPath: toPath } = await import("url");
|
|
193
|
+
const { existsSync: ex, realpathSync: rp } = await import("fs");
|
|
194
|
+
const coreRoot = res(dn(toPath(import.meta.url)), "..");
|
|
195
|
+
const adminNm = res(coreRoot, "node_modules", "@docs-i18n", "admin");
|
|
196
|
+
const adminRoot = ex(adminNm) ? rp(adminNm) : res(coreRoot, "admin");
|
|
197
|
+
const { startAdmin } = await import(
|
|
198
|
+
/* @vite-ignore */
|
|
199
|
+
res(adminRoot, "server", "index.ts")
|
|
200
|
+
);
|
|
116
201
|
await startAdmin(config, port);
|
|
117
202
|
break;
|
|
118
203
|
}
|