glotfile 0.6.2 → 0.6.4
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/server/cli.js
CHANGED
|
@@ -3225,8 +3225,28 @@ function computeUsedKeys(state, cache2) {
|
|
|
3225
3225
|
if (p.prefix) prefixes.push(p.prefix);
|
|
3226
3226
|
}
|
|
3227
3227
|
}
|
|
3228
|
+
const matchers = [];
|
|
3229
|
+
const seenLiterals = /* @__PURE__ */ new Set();
|
|
3230
|
+
for (const entry of Object.values(cache2.files)) {
|
|
3231
|
+
for (const l of entry.literals ?? []) {
|
|
3232
|
+
if (!l.literal || seenLiterals.has(l.literal)) continue;
|
|
3233
|
+
seenLiterals.add(l.literal);
|
|
3234
|
+
matchers.push(literalMatcher(l.literal));
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3228
3237
|
const keep = (state.config.scan?.keep ?? []).map(globToRegExp);
|
|
3229
|
-
return Object.keys(state.keys).filter((key) => exact.has(key) || prefixes.some((p) => key.startsWith(p)) || keep.some((re) => re.test(key))).sort();
|
|
3238
|
+
return Object.keys(state.keys).filter((key) => exact.has(key) || prefixes.some((p) => key.startsWith(p)) || matchers.some((matches) => matches(key)) || keep.some((re) => re.test(key))).sort();
|
|
3239
|
+
}
|
|
3240
|
+
function escapeRe(s) {
|
|
3241
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3242
|
+
}
|
|
3243
|
+
function literalMatcher(literal) {
|
|
3244
|
+
if (/%[sd]/.test(literal)) {
|
|
3245
|
+
const re = new RegExp(`^${literal.split(/%[sd]/).map(escapeRe).join("[^.]+")}$`);
|
|
3246
|
+
return (key) => re.test(key);
|
|
3247
|
+
}
|
|
3248
|
+
if (literal.endsWith(".")) return (key) => key.startsWith(literal);
|
|
3249
|
+
return (key) => key === literal || key.startsWith(literal + ".");
|
|
3230
3250
|
}
|
|
3231
3251
|
var init_scan = __esm({
|
|
3232
3252
|
"src/server/scan.ts"() {
|
|
@@ -3243,7 +3263,7 @@ import { join as join3, extname as extname2, relative } from "path";
|
|
|
3243
3263
|
function scannerForExt(ext) {
|
|
3244
3264
|
return EXT_SCANNER[ext] ?? null;
|
|
3245
3265
|
}
|
|
3246
|
-
function
|
|
3266
|
+
function escapeRe2(s) {
|
|
3247
3267
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3248
3268
|
}
|
|
3249
3269
|
function detectFlutterAccessors(content) {
|
|
@@ -3260,7 +3280,7 @@ function flutterPatterns(content, opts) {
|
|
|
3260
3280
|
...FLUTTER_ACCESSOR_DEFAULTS,
|
|
3261
3281
|
...detectFlutterAccessors(content),
|
|
3262
3282
|
...opts?.accessors ?? []
|
|
3263
|
-
])].map(
|
|
3283
|
+
])].map(escapeRe2);
|
|
3264
3284
|
return [
|
|
3265
3285
|
// AppLocalizations.of(context)!.key — tolerates the !/? null-assertion.
|
|
3266
3286
|
/AppLocalizations\.of\([^)]*\)[!?]?\.([a-zA-Z_][a-zA-Z0-9_]*)/g,
|
|
@@ -3345,6 +3365,32 @@ function extractPrefixes(content, scanner) {
|
|
|
3345
3365
|
result.sort((a, b) => a.line - b.line || a.col - b.col);
|
|
3346
3366
|
return result;
|
|
3347
3367
|
}
|
|
3368
|
+
function extractLiterals(content) {
|
|
3369
|
+
const starts = lineStartOffsets(content);
|
|
3370
|
+
const result = [];
|
|
3371
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3372
|
+
for (const pattern of STRING_LITERALS) {
|
|
3373
|
+
const re = new RegExp(pattern.source, "g");
|
|
3374
|
+
let m;
|
|
3375
|
+
while ((m = re.exec(content)) !== null) {
|
|
3376
|
+
let text = m[1];
|
|
3377
|
+
const marker = text.search(/\{\$|\$\{/);
|
|
3378
|
+
if (marker !== -1) {
|
|
3379
|
+
text = text.slice(0, marker);
|
|
3380
|
+
if (!text.endsWith(".")) continue;
|
|
3381
|
+
}
|
|
3382
|
+
if (!KEY_SHAPE.test(text)) continue;
|
|
3383
|
+
const { line, col } = offsetToLineCol(starts, m.index);
|
|
3384
|
+
const dedup = `${line}:${col}:${text}`;
|
|
3385
|
+
if (!seen.has(dedup)) {
|
|
3386
|
+
seen.add(dedup);
|
|
3387
|
+
result.push({ literal: text, line, col });
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
result.sort((a, b) => a.line - b.line || a.col - b.col);
|
|
3392
|
+
return result;
|
|
3393
|
+
}
|
|
3348
3394
|
function matchesGlob(relPath, glob) {
|
|
3349
3395
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(//g, ".*");
|
|
3350
3396
|
return new RegExp(`^${escaped}$`).test(relPath);
|
|
@@ -3419,13 +3465,14 @@ function runScan(projectRoot, opts, existing) {
|
|
|
3419
3465
|
mtime,
|
|
3420
3466
|
size,
|
|
3421
3467
|
refs: extractRefs(content, scanner, opts),
|
|
3422
|
-
prefixes: extractPrefixes(content, scanner)
|
|
3468
|
+
prefixes: extractPrefixes(content, scanner),
|
|
3469
|
+
literals: extractLiterals(content)
|
|
3423
3470
|
};
|
|
3424
3471
|
}
|
|
3425
3472
|
saveUsageCache(projectRoot, cache2);
|
|
3426
3473
|
return cache2;
|
|
3427
3474
|
}
|
|
3428
|
-
var PATTERNS, PREFIX_PATTERNS, CACHE_VERSION, EXT_SCANNER, ALWAYS_EXCLUDE, FLUTTER_ACCESSOR_DEFAULTS;
|
|
3475
|
+
var PATTERNS, PREFIX_PATTERNS, CACHE_VERSION, EXT_SCANNER, ALWAYS_EXCLUDE, FLUTTER_ACCESSOR_DEFAULTS, KEY_SHAPE, STRING_LITERALS;
|
|
3429
3476
|
var init_scanner = __esm({
|
|
3430
3477
|
"src/server/scanner.ts"() {
|
|
3431
3478
|
"use strict";
|
|
@@ -3482,7 +3529,7 @@ var init_scanner = __esm({
|
|
|
3482
3529
|
/(?<!\.)(?<![a-zA-Z0-9_$])\bt\s*\(\s*`([^`$]*)\$\{/g
|
|
3483
3530
|
]
|
|
3484
3531
|
};
|
|
3485
|
-
CACHE_VERSION =
|
|
3532
|
+
CACHE_VERSION = 6;
|
|
3486
3533
|
EXT_SCANNER = {
|
|
3487
3534
|
".php": "laravel",
|
|
3488
3535
|
".vue": "js-i18n",
|
|
@@ -3516,6 +3563,12 @@ var init_scanner = __esm({
|
|
|
3516
3563
|
"__pycache__"
|
|
3517
3564
|
]);
|
|
3518
3565
|
FLUTTER_ACCESSOR_DEFAULTS = ["l10n", "loc", "localizations", "translations"];
|
|
3566
|
+
KEY_SHAPE = /^[A-Za-z0-9_][A-Za-z0-9_/-]*(?:\.(?:[A-Za-z0-9_-]+|%[sd]))+\.?$/;
|
|
3567
|
+
STRING_LITERALS = [
|
|
3568
|
+
/'([^'\\\n]+)'/g,
|
|
3569
|
+
/"([^"\\\n]+)"/g,
|
|
3570
|
+
/`([^`\\\n]+)`/g
|
|
3571
|
+
];
|
|
3519
3572
|
}
|
|
3520
3573
|
});
|
|
3521
3574
|
|
|
@@ -6728,23 +6781,34 @@ function createApi(deps) {
|
|
|
6728
6781
|
app.get("/scan/usage", (c) => {
|
|
6729
6782
|
const key = c.req.query("key") ?? "";
|
|
6730
6783
|
const cache2 = loadUsageCache(projectRoot);
|
|
6731
|
-
if (!cache2) return c.json({ indexed: false, count: 0, refs: [], prefixCount: 0, prefixRefs: [] });
|
|
6784
|
+
if (!cache2) return c.json({ indexed: false, count: 0, refs: [], prefixCount: 0, prefixRefs: [], literalCount: 0, literalRefs: [] });
|
|
6732
6785
|
const refs = [];
|
|
6733
6786
|
const prefixRefs = [];
|
|
6787
|
+
const literalRefs = [];
|
|
6734
6788
|
for (const [file, entry] of Object.entries(cache2.files)) {
|
|
6735
6789
|
const abs = resolve9(projectRoot, file);
|
|
6790
|
+
const refLines = /* @__PURE__ */ new Set();
|
|
6736
6791
|
for (const r of entry.refs) {
|
|
6737
|
-
if (r.key === key)
|
|
6792
|
+
if (r.key === key) {
|
|
6793
|
+
refs.push({ file, abs, line: r.line, col: r.col, scanner: r.scanner });
|
|
6794
|
+
refLines.add(r.line);
|
|
6795
|
+
}
|
|
6738
6796
|
}
|
|
6739
6797
|
for (const p of entry.prefixes) {
|
|
6740
6798
|
if (key.startsWith(p.prefix)) {
|
|
6741
6799
|
prefixRefs.push({ file, abs, line: p.line, col: p.col, scanner: p.scanner, prefix: p.prefix });
|
|
6742
6800
|
}
|
|
6743
6801
|
}
|
|
6802
|
+
for (const l of entry.literals ?? []) {
|
|
6803
|
+
if (literalMatcher(l.literal)(key) && !refLines.has(l.line)) {
|
|
6804
|
+
literalRefs.push({ file, abs, line: l.line, col: l.col, literal: l.literal });
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6744
6807
|
}
|
|
6745
6808
|
const byFileLine = (a, b) => a.file.localeCompare(b.file) || a.line - b.line;
|
|
6746
6809
|
refs.sort(byFileLine);
|
|
6747
6810
|
prefixRefs.sort(byFileLine);
|
|
6811
|
+
literalRefs.sort(byFileLine);
|
|
6748
6812
|
return c.json({
|
|
6749
6813
|
indexed: true,
|
|
6750
6814
|
scannedAt: cache2.scannedAt,
|
|
@@ -6752,7 +6816,9 @@ function createApi(deps) {
|
|
|
6752
6816
|
count: refs.length,
|
|
6753
6817
|
refs,
|
|
6754
6818
|
prefixCount: prefixRefs.length,
|
|
6755
|
-
prefixRefs
|
|
6819
|
+
prefixRefs,
|
|
6820
|
+
literalCount: literalRefs.length,
|
|
6821
|
+
literalRefs
|
|
6756
6822
|
});
|
|
6757
6823
|
});
|
|
6758
6824
|
app.get("/scan/used", (c) => {
|
package/dist/server/server.js
CHANGED
|
@@ -860,8 +860,28 @@ function computeUsedKeys(state, cache2) {
|
|
|
860
860
|
if (p.prefix) prefixes.push(p.prefix);
|
|
861
861
|
}
|
|
862
862
|
}
|
|
863
|
+
const matchers = [];
|
|
864
|
+
const seenLiterals = /* @__PURE__ */ new Set();
|
|
865
|
+
for (const entry of Object.values(cache2.files)) {
|
|
866
|
+
for (const l of entry.literals ?? []) {
|
|
867
|
+
if (!l.literal || seenLiterals.has(l.literal)) continue;
|
|
868
|
+
seenLiterals.add(l.literal);
|
|
869
|
+
matchers.push(literalMatcher(l.literal));
|
|
870
|
+
}
|
|
871
|
+
}
|
|
863
872
|
const keep = (state.config.scan?.keep ?? []).map(globToRegExp);
|
|
864
|
-
return Object.keys(state.keys).filter((key) => exact.has(key) || prefixes.some((p) => key.startsWith(p)) || keep.some((re) => re.test(key))).sort();
|
|
873
|
+
return Object.keys(state.keys).filter((key) => exact.has(key) || prefixes.some((p) => key.startsWith(p)) || matchers.some((matches) => matches(key)) || keep.some((re) => re.test(key))).sort();
|
|
874
|
+
}
|
|
875
|
+
function escapeRe(s) {
|
|
876
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
877
|
+
}
|
|
878
|
+
function literalMatcher(literal) {
|
|
879
|
+
if (/%[sd]/.test(literal)) {
|
|
880
|
+
const re = new RegExp(`^${literal.split(/%[sd]/).map(escapeRe).join("[^.]+")}$`);
|
|
881
|
+
return (key) => re.test(key);
|
|
882
|
+
}
|
|
883
|
+
if (literal.endsWith(".")) return (key) => key.startsWith(literal);
|
|
884
|
+
return (key) => key === literal || key.startsWith(literal + ".");
|
|
865
885
|
}
|
|
866
886
|
|
|
867
887
|
// src/server/scanner.ts
|
|
@@ -919,7 +939,7 @@ var PREFIX_PATTERNS = {
|
|
|
919
939
|
/(?<!\.)(?<![a-zA-Z0-9_$])\bt\s*\(\s*`([^`$]*)\$\{/g
|
|
920
940
|
]
|
|
921
941
|
};
|
|
922
|
-
var CACHE_VERSION =
|
|
942
|
+
var CACHE_VERSION = 6;
|
|
923
943
|
var EXT_SCANNER = {
|
|
924
944
|
".php": "laravel",
|
|
925
945
|
".vue": "js-i18n",
|
|
@@ -956,7 +976,7 @@ function scannerForExt(ext) {
|
|
|
956
976
|
return EXT_SCANNER[ext] ?? null;
|
|
957
977
|
}
|
|
958
978
|
var FLUTTER_ACCESSOR_DEFAULTS = ["l10n", "loc", "localizations", "translations"];
|
|
959
|
-
function
|
|
979
|
+
function escapeRe2(s) {
|
|
960
980
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
961
981
|
}
|
|
962
982
|
function detectFlutterAccessors(content) {
|
|
@@ -973,7 +993,7 @@ function flutterPatterns(content, opts) {
|
|
|
973
993
|
...FLUTTER_ACCESSOR_DEFAULTS,
|
|
974
994
|
...detectFlutterAccessors(content),
|
|
975
995
|
...opts?.accessors ?? []
|
|
976
|
-
])].map(
|
|
996
|
+
])].map(escapeRe2);
|
|
977
997
|
return [
|
|
978
998
|
// AppLocalizations.of(context)!.key — tolerates the !/? null-assertion.
|
|
979
999
|
/AppLocalizations\.of\([^)]*\)[!?]?\.([a-zA-Z_][a-zA-Z0-9_]*)/g,
|
|
@@ -1058,6 +1078,38 @@ function extractPrefixes(content, scanner) {
|
|
|
1058
1078
|
result.sort((a, b) => a.line - b.line || a.col - b.col);
|
|
1059
1079
|
return result;
|
|
1060
1080
|
}
|
|
1081
|
+
var KEY_SHAPE = /^[A-Za-z0-9_][A-Za-z0-9_/-]*(?:\.(?:[A-Za-z0-9_-]+|%[sd]))+\.?$/;
|
|
1082
|
+
var STRING_LITERALS = [
|
|
1083
|
+
/'([^'\\\n]+)'/g,
|
|
1084
|
+
/"([^"\\\n]+)"/g,
|
|
1085
|
+
/`([^`\\\n]+)`/g
|
|
1086
|
+
];
|
|
1087
|
+
function extractLiterals(content) {
|
|
1088
|
+
const starts = lineStartOffsets(content);
|
|
1089
|
+
const result = [];
|
|
1090
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1091
|
+
for (const pattern of STRING_LITERALS) {
|
|
1092
|
+
const re = new RegExp(pattern.source, "g");
|
|
1093
|
+
let m;
|
|
1094
|
+
while ((m = re.exec(content)) !== null) {
|
|
1095
|
+
let text = m[1];
|
|
1096
|
+
const marker = text.search(/\{\$|\$\{/);
|
|
1097
|
+
if (marker !== -1) {
|
|
1098
|
+
text = text.slice(0, marker);
|
|
1099
|
+
if (!text.endsWith(".")) continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (!KEY_SHAPE.test(text)) continue;
|
|
1102
|
+
const { line, col } = offsetToLineCol(starts, m.index);
|
|
1103
|
+
const dedup = `${line}:${col}:${text}`;
|
|
1104
|
+
if (!seen.has(dedup)) {
|
|
1105
|
+
seen.add(dedup);
|
|
1106
|
+
result.push({ literal: text, line, col });
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
result.sort((a, b) => a.line - b.line || a.col - b.col);
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
1061
1113
|
function matchesGlob(relPath, glob) {
|
|
1062
1114
|
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(//g, ".*");
|
|
1063
1115
|
return new RegExp(`^${escaped}$`).test(relPath);
|
|
@@ -1132,7 +1184,8 @@ function runScan(projectRoot, opts, existing) {
|
|
|
1132
1184
|
mtime,
|
|
1133
1185
|
size,
|
|
1134
1186
|
refs: extractRefs(content, scanner, opts),
|
|
1135
|
-
prefixes: extractPrefixes(content, scanner)
|
|
1187
|
+
prefixes: extractPrefixes(content, scanner),
|
|
1188
|
+
literals: extractLiterals(content)
|
|
1136
1189
|
};
|
|
1137
1190
|
}
|
|
1138
1191
|
saveUsageCache(projectRoot, cache2);
|
|
@@ -6199,23 +6252,34 @@ function createApi(deps) {
|
|
|
6199
6252
|
app.get("/scan/usage", (c) => {
|
|
6200
6253
|
const key = c.req.query("key") ?? "";
|
|
6201
6254
|
const cache2 = loadUsageCache(projectRoot);
|
|
6202
|
-
if (!cache2) return c.json({ indexed: false, count: 0, refs: [], prefixCount: 0, prefixRefs: [] });
|
|
6255
|
+
if (!cache2) return c.json({ indexed: false, count: 0, refs: [], prefixCount: 0, prefixRefs: [], literalCount: 0, literalRefs: [] });
|
|
6203
6256
|
const refs = [];
|
|
6204
6257
|
const prefixRefs = [];
|
|
6258
|
+
const literalRefs = [];
|
|
6205
6259
|
for (const [file, entry] of Object.entries(cache2.files)) {
|
|
6206
6260
|
const abs = resolve9(projectRoot, file);
|
|
6261
|
+
const refLines = /* @__PURE__ */ new Set();
|
|
6207
6262
|
for (const r of entry.refs) {
|
|
6208
|
-
if (r.key === key)
|
|
6263
|
+
if (r.key === key) {
|
|
6264
|
+
refs.push({ file, abs, line: r.line, col: r.col, scanner: r.scanner });
|
|
6265
|
+
refLines.add(r.line);
|
|
6266
|
+
}
|
|
6209
6267
|
}
|
|
6210
6268
|
for (const p of entry.prefixes) {
|
|
6211
6269
|
if (key.startsWith(p.prefix)) {
|
|
6212
6270
|
prefixRefs.push({ file, abs, line: p.line, col: p.col, scanner: p.scanner, prefix: p.prefix });
|
|
6213
6271
|
}
|
|
6214
6272
|
}
|
|
6273
|
+
for (const l of entry.literals ?? []) {
|
|
6274
|
+
if (literalMatcher(l.literal)(key) && !refLines.has(l.line)) {
|
|
6275
|
+
literalRefs.push({ file, abs, line: l.line, col: l.col, literal: l.literal });
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6215
6278
|
}
|
|
6216
6279
|
const byFileLine = (a, b) => a.file.localeCompare(b.file) || a.line - b.line;
|
|
6217
6280
|
refs.sort(byFileLine);
|
|
6218
6281
|
prefixRefs.sort(byFileLine);
|
|
6282
|
+
literalRefs.sort(byFileLine);
|
|
6219
6283
|
return c.json({
|
|
6220
6284
|
indexed: true,
|
|
6221
6285
|
scannedAt: cache2.scannedAt,
|
|
@@ -6223,7 +6287,9 @@ function createApi(deps) {
|
|
|
6223
6287
|
count: refs.length,
|
|
6224
6288
|
refs,
|
|
6225
6289
|
prefixCount: prefixRefs.length,
|
|
6226
|
-
prefixRefs
|
|
6290
|
+
prefixRefs,
|
|
6291
|
+
literalCount: literalRefs.length,
|
|
6292
|
+
literalRefs
|
|
6227
6293
|
});
|
|
6228
6294
|
});
|
|
6229
6295
|
app.get("/scan/used", (c) => {
|