c59097b0ac
- i18n loader at apps/readest.koplugin/i18n.lua: isolated callable,
falls back to KOReader's gettext when a string is untranslated
- Translation catalog at locales/<i18next-code>/translation.po for 31
languages, mirroring apps/readest-app/public/locales/
- scripts/extract-i18n.js (scan _("...") and _([[...]]); preserve
existing, drop obsolete, add new) and scripts/apply-translations.js
(bulk import from /tmp/koplugin-translations/<lang>.json)
- Mirror apps/readest-app SyncInfoDialog: rename showMetaHashInfo to
showSyncInfo, dialog title "Sync Info", new Last Synced row computed
as max(last_synced_at_config, last_synced_at_notes) from doc_settings
- syncconfig.lua / syncannotations.lua mark per-book sync timestamps
on push/pull success
- Rename "Meta Hash" -> "Book Fingerprint" in koplugin and
apps/readest-app SyncInfoDialog.tsx; translations propagated to
all readest-app locales
- "book config" -> "reading progress" wording across user-facing
strings (matches QiuYukang fork terminology)
- Replace "Log out as " / "Login failed: " concat prefixes with
T(_("...%1..."), arg) placeholder pattern (RTL / verb-final friendly)
- pnpm lint:lua: luajit -b syntax check across koplugin .lua files;
soft-skips when luajit is missing locally; CI installs luajit and
runs the check unconditionally
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
55 lines
1.9 KiB
JavaScript
55 lines
1.9 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Syntax-check every Lua file in apps/readest.koplugin against LuaJIT (the
|
|
* runtime KOReader uses). `luajit -b <src> /dev/null` parses the source and
|
|
* emits bytecode to /dev/null, returning non-zero on any syntax error without
|
|
* executing the file. We use LuaJIT specifically — not stock Lua — because
|
|
* the koplugin relies on LuaJIT-only features (`ffi/*`) and stock luac
|
|
* silently accepts a few constructs LuaJIT rejects (e.g. `<const>`).
|
|
*
|
|
* Invoked from `pnpm lint:lua`. Soft-skips with a notice when luajit is not
|
|
* installed; CI installs luajit and runs unconditionally.
|
|
*/
|
|
|
|
import { spawnSync } from 'node:child_process';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
const KOPLUGIN_DIR = path.resolve(__dirname, '..', '..', 'readest.koplugin');
|
|
|
|
if (!fs.existsSync(KOPLUGIN_DIR)) {
|
|
console.error(`koplugin directory not found at ${KOPLUGIN_DIR}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const luaFiles = fs
|
|
.readdirSync(KOPLUGIN_DIR)
|
|
.filter((f) => f.endsWith('.lua'))
|
|
.map((f) => path.join(KOPLUGIN_DIR, f))
|
|
.sort();
|
|
|
|
if (luaFiles.length === 0) {
|
|
console.log('No .lua files to check.');
|
|
process.exit(0);
|
|
}
|
|
|
|
const which = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['luajit']);
|
|
if (which.status !== 0) {
|
|
console.warn(
|
|
'lint:lua: luajit not found, skipping koplugin syntax check. ' +
|
|
'Install LuaJIT (e.g. `brew install luajit` on macOS, `apt-get install luajit` on Linux) ' +
|
|
'to enable this check locally. CI runs it unconditionally.',
|
|
);
|
|
process.exit(0);
|
|
}
|
|
|
|
const devNull = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
let failed = 0;
|
|
for (const file of luaFiles) {
|
|
const r = spawnSync('luajit', ['-b', file, devNull], { stdio: 'inherit' });
|
|
if (r.status !== 0) failed++;
|
|
}
|
|
process.exit(failed === 0 ? 0 : 1);
|