@x18n/core 0.0.13 → 0.0.14
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/index.d.mts +115 -9
- package/dist/index.mjs +180 -51
- package/package.json +1 -1
- package/schema/config.json +32 -10
- package/schema/plugin.json +32 -10
package/dist/index.d.mts
CHANGED
|
@@ -434,10 +434,14 @@ interface LocaleFinder {
|
|
|
434
434
|
readonly id: string;
|
|
435
435
|
readonly override?: boolean | undefined;
|
|
436
436
|
readonly find: (ctx: FinderContext) => Promise<readonly ResourceCandidate[]>;
|
|
437
|
-
/**
|
|
437
|
+
/**
|
|
438
|
+
* Where a new resource for (locale, namespace) should go. Used by edits that create files.
|
|
439
|
+
* `model` is the existing resource the new one would otherwise mirror: the key's source file when it has one.
|
|
440
|
+
*/
|
|
438
441
|
readonly resolveNew?: ((target: {
|
|
439
442
|
readonly locale: LocaleCode;
|
|
440
443
|
readonly namespace: string | undefined;
|
|
444
|
+
readonly model: Resource | undefined;
|
|
441
445
|
}, ctx: FinderContext) => string | undefined) | undefined;
|
|
442
446
|
}
|
|
443
447
|
interface FormatContext {
|
|
@@ -459,6 +463,28 @@ type MessageOp = {
|
|
|
459
463
|
readonly type: "delete";
|
|
460
464
|
readonly path: readonly string[];
|
|
461
465
|
};
|
|
466
|
+
/** A built-in format known by its extensions only: the module behind `load` is imported when a file of that format is read. */
|
|
467
|
+
interface FormatEntry {
|
|
468
|
+
/** File extensions without the dot. */
|
|
469
|
+
readonly extensions: readonly string[];
|
|
470
|
+
/** The module's default export creates a plugin that holds the format. */
|
|
471
|
+
readonly load: PluginLoader;
|
|
472
|
+
}
|
|
473
|
+
/** A format entry under its id, as plugins list it. */
|
|
474
|
+
interface LazyFormat extends FormatEntry {
|
|
475
|
+
readonly id: string;
|
|
476
|
+
}
|
|
477
|
+
/** A built-in scanner known by its extensions only: the module behind `load` is imported when a file it scans is first scanned. */
|
|
478
|
+
interface ScannerEntry {
|
|
479
|
+
/** File extensions without dot. `*` matches every file. */
|
|
480
|
+
readonly extensions: readonly string[];
|
|
481
|
+
/** The module's default export creates a plugin that holds the scanner. */
|
|
482
|
+
readonly load: PluginLoader;
|
|
483
|
+
}
|
|
484
|
+
/** A scanner entry under its id, as plugins list it. */
|
|
485
|
+
interface LazyScanner extends ScannerEntry {
|
|
486
|
+
readonly id: string;
|
|
487
|
+
}
|
|
462
488
|
interface Format {
|
|
463
489
|
readonly id: string;
|
|
464
490
|
/** File extensions without the dot. */
|
|
@@ -496,6 +522,8 @@ interface Scope {
|
|
|
496
522
|
readonly range: TextRange;
|
|
497
523
|
readonly namespace?: string | undefined;
|
|
498
524
|
readonly keyPrefix?: string | undefined;
|
|
525
|
+
/** The key prefix applies only to keys it defines; others resolve without it, as page messages that extend global ones. */
|
|
526
|
+
readonly optionalPrefix?: boolean | undefined;
|
|
499
527
|
}
|
|
500
528
|
interface HardString {
|
|
501
529
|
readonly text: string;
|
|
@@ -703,6 +731,10 @@ interface Plugin {
|
|
|
703
731
|
readonly manifests?: readonly ManifestReader[] | undefined;
|
|
704
732
|
readonly localeFinders?: readonly LocaleFinder[] | undefined;
|
|
705
733
|
readonly formats?: readonly Format[] | undefined;
|
|
734
|
+
/** Formats that load their module when a file of that format is first read. */
|
|
735
|
+
readonly lazyFormats?: readonly LazyFormat[] | undefined;
|
|
736
|
+
/** Scanners that load their module when a file they scan is first scanned. */
|
|
737
|
+
readonly lazyScanners?: readonly LazyScanner[] | undefined;
|
|
706
738
|
readonly scanners?: readonly Scanner[] | undefined;
|
|
707
739
|
readonly extractors?: readonly Extractor[] | undefined;
|
|
708
740
|
readonly checks?: readonly Check[] | undefined;
|
|
@@ -727,6 +759,32 @@ interface Preset {
|
|
|
727
759
|
}
|
|
728
760
|
/** A preset factory as referenced by name from YAML and JSON configs. */
|
|
729
761
|
type PresetFactory = (options?: JsonObject) => Preset;
|
|
762
|
+
/** Loads a plugin on demand, usually `() => import('./plugin')`: the module's default export creates it. */
|
|
763
|
+
type PluginLoader = () => Promise<{
|
|
764
|
+
readonly default: () => Plugin;
|
|
765
|
+
}>;
|
|
766
|
+
/** Loads a reporter on demand, usually `() => import('./reporter')`: the module's default export is the reporter. */
|
|
767
|
+
type ReporterLoader = () => Promise<{
|
|
768
|
+
readonly default: Reporter;
|
|
769
|
+
}>;
|
|
770
|
+
/** Loads a preset on demand, usually `() => import('./preset')`: the module's default export is the factory. */
|
|
771
|
+
type PresetLoader = () => Promise<{
|
|
772
|
+
readonly default: PresetFactory;
|
|
773
|
+
}>;
|
|
774
|
+
/** When a built-in preset applies. Cheap, so it stays loaded while the preset module does not. */
|
|
775
|
+
interface PresetDetection {
|
|
776
|
+
/** Without `detect`, the preset applies only when listed in config. */
|
|
777
|
+
readonly detect?: ((ctx: DetectContext) => boolean | Promise<boolean>) | undefined;
|
|
778
|
+
/** When true and detected, presets that come after it are not applied. */
|
|
779
|
+
readonly exclusive?: boolean | undefined;
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* A built-in preset split in two: the detection is always loaded; the module behind `load`
|
|
783
|
+
* is imported only when it passes or the preset is listed in config.
|
|
784
|
+
*/
|
|
785
|
+
interface PresetEntry extends PresetDetection {
|
|
786
|
+
readonly load: PresetLoader;
|
|
787
|
+
}
|
|
730
788
|
//#endregion
|
|
731
789
|
//#region src/types/config.d.ts
|
|
732
790
|
/** Where a value comes from: an argument, or a property of an object argument. */
|
|
@@ -742,8 +800,8 @@ interface CallSpec {
|
|
|
742
800
|
readonly kind?: "call" | "selector" | "accessor" | undefined;
|
|
743
801
|
/** Which argument holds the key. Default 0. */
|
|
744
802
|
readonly key?: number | ArgSource | undefined;
|
|
745
|
-
/** Where the namespace comes from (call option, or factory argument when `factory` is true)
|
|
746
|
-
readonly namespace?: ArgSource | undefined;
|
|
803
|
+
/** Where the namespace comes from (call option, or factory argument when `factory` is true), or a fixed namespace such as `'shared'` for a hook that wraps `useTranslation('shared')`. */
|
|
804
|
+
readonly namespace?: ArgSource | string | undefined;
|
|
747
805
|
/** Where a key prefix comes from (`keyPrefix` option, or next-intl `useTranslations('a.b')`). */
|
|
748
806
|
readonly keyPrefix?: ArgSource | undefined;
|
|
749
807
|
/** The callee is a factory that returns the translate function, for example `useI18n()` returning `{ t }`. */
|
|
@@ -835,6 +893,12 @@ interface NamespaceOptions {
|
|
|
835
893
|
readonly separator?: string | undefined;
|
|
836
894
|
/** Namespace of keys written without one. */
|
|
837
895
|
readonly default?: string | undefined;
|
|
896
|
+
/** Namespaces searched in order when a key written without a namespace is not in its own (i18next `fallbackNS`). */
|
|
897
|
+
readonly fallback?: readonly string[] | undefined;
|
|
898
|
+
/** Namespace of keys written without one, per glob of source files, before `default`: `{ 'packages/admin/**': 'admin' }`. */
|
|
899
|
+
readonly byFile?: {
|
|
900
|
+
readonly [glob: string]: string;
|
|
901
|
+
} | undefined;
|
|
838
902
|
/** How `{namespaces}` folders name a namespace: `dot` makes `pages/home` into `pages.home`, `slash` keeps `pages/home`, `camel` makes `todos-page` into `todosPage`. Default `dot`. */
|
|
839
903
|
readonly fromPath?: "dot" | "slash" | "camel" | undefined;
|
|
840
904
|
}
|
|
@@ -926,6 +990,10 @@ interface ResolvedConfig {
|
|
|
926
990
|
readonly namespace: {
|
|
927
991
|
readonly separator: string;
|
|
928
992
|
readonly default: string | undefined;
|
|
993
|
+
readonly fallback: readonly string[];
|
|
994
|
+
readonly byFile: {
|
|
995
|
+
readonly [glob: string]: string;
|
|
996
|
+
};
|
|
929
997
|
readonly fromPath: NamespaceFromPath;
|
|
930
998
|
};
|
|
931
999
|
readonly usage: {
|
|
@@ -984,6 +1052,34 @@ export declare function keyMatcher(globs: readonly string[]): (key: KeyString) =
|
|
|
984
1052
|
/** Matcher over posix file paths. */
|
|
985
1053
|
export declare function pathMatcher(globs: readonly string[]): (path: string) => boolean;
|
|
986
1054
|
//#endregion
|
|
1055
|
+
//#region src/library.d.ts
|
|
1056
|
+
interface LibraryOptions {
|
|
1057
|
+
/** The plugin name, prefixed by the caller: `x18n:vue-i18n`. */
|
|
1058
|
+
readonly name: string;
|
|
1059
|
+
readonly calls?: readonly CallSpec[];
|
|
1060
|
+
/** i18n-ally style `usageMatchRegex` patterns, for languages without an AST scanner. */
|
|
1061
|
+
readonly regex?: readonly RegexScannerSpec[];
|
|
1062
|
+
/** The library's placeholder syntaxes. */
|
|
1063
|
+
readonly placeholders?: readonly PlaceholderSyntax[];
|
|
1064
|
+
readonly derivedKeys?: readonly string[];
|
|
1065
|
+
readonly namespace?: {
|
|
1066
|
+
readonly separator?: string;
|
|
1067
|
+
readonly default?: string;
|
|
1068
|
+
readonly fromPath?: NamespaceFromPath;
|
|
1069
|
+
};
|
|
1070
|
+
/** Default resource patterns (i18n-ally `preferredLocalePaths` + `pathMatcher`). */
|
|
1071
|
+
readonly resources?: readonly ResourceSpec[];
|
|
1072
|
+
readonly keyStyle?: "nested" | "flat";
|
|
1073
|
+
readonly templates?: readonly ExtractTemplate[];
|
|
1074
|
+
readonly finders?: readonly LocaleFinder[];
|
|
1075
|
+
readonly normalizeKey?: Plugin["normalizeKey"];
|
|
1076
|
+
readonly transformResource?: ResourceTransform;
|
|
1077
|
+
readonly extra?: (draft: ConfigDraft, ctx: ConfigContext) => void | Promise<void>;
|
|
1078
|
+
readonly translatePrompt?: Plugin["translatePrompt"];
|
|
1079
|
+
}
|
|
1080
|
+
/** One plugin holding a library's call specs, regexes, namespace rules, defaults and extraction templates. */
|
|
1081
|
+
export declare function createLibraryPlugin(options: LibraryOptions): Plugin;
|
|
1082
|
+
//#endregion
|
|
987
1083
|
//#region src/locale.d.ts
|
|
988
1084
|
/** Lenient display parse of a locale code (`zh_CN`, `pt-BR`, `sr-Latn-RS`). Never changes the code. */
|
|
989
1085
|
export declare function parseLocaleInfo(code: LocaleCode): LocaleInfo;
|
|
@@ -1036,9 +1132,11 @@ interface CapabilityConflict {
|
|
|
1036
1132
|
/** The capabilities of the final plugin list, indexed for the project engine. */
|
|
1037
1133
|
interface PluginRegistry {
|
|
1038
1134
|
readonly plugins: readonly Plugin[];
|
|
1039
|
-
|
|
1135
|
+
/** Every format, loaded or not; use `formatFor` and `loadFormatFor` to run one. */
|
|
1136
|
+
readonly formats: readonly (Format | LazyFormat)[];
|
|
1040
1137
|
readonly finders: readonly LocaleFinder[];
|
|
1041
|
-
|
|
1138
|
+
/** Every scanner, loaded or not. */
|
|
1139
|
+
readonly scanners: readonly (Scanner | LazyScanner)[];
|
|
1042
1140
|
readonly checks: ReadonlyMap<string, Check>;
|
|
1043
1141
|
readonly reporters: ReadonlyMap<string, Reporter>;
|
|
1044
1142
|
readonly placeholders: readonly PlaceholderSyntax[];
|
|
@@ -1046,10 +1144,18 @@ interface PluginRegistry {
|
|
|
1046
1144
|
readonly extractors: readonly Extractor[];
|
|
1047
1145
|
readonly histories: readonly HistoryProvider[];
|
|
1048
1146
|
readonly conflicts: readonly CapabilityConflict[];
|
|
1049
|
-
/**
|
|
1147
|
+
/** The loaded format for a file path: config `formats` first, then the first format that claims the extension. */
|
|
1050
1148
|
readonly formatFor: (path: string, hint?: string) => Format | undefined;
|
|
1149
|
+
/** The id of the format `formatFor` would pick, loaded or not. */
|
|
1150
|
+
readonly formatIdFor: (path: string, hint?: string) => string | undefined;
|
|
1151
|
+
/** The loaded format with this id. */
|
|
1051
1152
|
readonly formatById: (id: string) => Format | undefined;
|
|
1052
|
-
|
|
1153
|
+
/** Loads the format of a file path, once, and returns it. */
|
|
1154
|
+
readonly loadFormatFor: (path: string, hint?: string) => Promise<Format | undefined>;
|
|
1155
|
+
/** The scanners that handle a file path, loaded or not. */
|
|
1156
|
+
readonly scannersFor: (path: string) => readonly (Scanner | LazyScanner)[];
|
|
1157
|
+
/** Loads the scanners of a file path, once each, and returns them. */
|
|
1158
|
+
readonly loadScannersFor: (path: string) => Promise<readonly Scanner[]>;
|
|
1053
1159
|
}
|
|
1054
1160
|
export declare function extensionOf(path: string): string;
|
|
1055
1161
|
export declare function createRegistry(input: readonly Plugin[], config: Pick<ResolvedConfig, "formats">): PluginRegistry;
|
|
@@ -1116,7 +1222,7 @@ export declare function renderPrompt(data: PromptData): string;
|
|
|
1116
1222
|
interface Builtins {
|
|
1117
1223
|
readonly plugins: readonly Plugin[];
|
|
1118
1224
|
readonly presets: {
|
|
1119
|
-
readonly [name: string]:
|
|
1225
|
+
readonly [name: string]: PresetEntry;
|
|
1120
1226
|
};
|
|
1121
1227
|
}
|
|
1122
1228
|
interface ResolveConfigOptions {
|
|
@@ -1473,4 +1579,4 @@ export declare function rangeContains(range: TextRange, offset: number): boolean
|
|
|
1473
1579
|
/** Fast non-cryptographic string hash (cyrb53), as hex. */
|
|
1474
1580
|
export declare function hashText(text: string): string;
|
|
1475
1581
|
//#endregion
|
|
1476
|
-
export type { Analysis, Answers, ApplyResult, ArgSource, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeyContext, KeyId, KeyInfo, KeyQuery, KeyString, KeyTarget, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetExplanation, PresetFactory, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptItem, PromptOptions, RegexScannerSpec, ReportInput, ReportSummary, Reporter, ReporterContext, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ScanContext, ScanResult, Scanner, SchemaIssue, Scope, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, TreeLeaf, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
|
|
1582
|
+
export type { Analysis, Answers, ApplyResult, ArgSource, Builtins, CacheStore, CallSpec, CapabilityConflict, Check, CheckContext, CheckIds, CheckLevel, CheckSetting, ConfigContext, ConfigDraft, ConfigOrigin, CreateProjectOptions, Decision, DecisionOption, DetectContext, Diagnostic, DiagnosticInput, Disposable, EditOp, Envelope, ErrorCode, ErrorInfo, ExtractItem, ExtractOptions, ExtractTemplate, Extractor, FileChange, FileEvent, FileScan, FinderContext, Fix, Format, FormatContext, FormatEntry, GlobOptions, GlossaryEntry, HardString, HistoryConfigs, HistoryContext, HistoryOptions, HistoryProvider, HistorySettings, Host, JsonObject, JsonPrimitive, JsonValue, KeyContext, KeyId, KeyInfo, KeyQuery, KeyString, KeyTarget, LazyFormat, LazyScanner, LibraryOptions, LineIndex, LinePosition, LocaleCode, LocaleCoverage, LocaleFinder, LocaleInfo, Location, Logger, ManifestReader, MemoryHost, Message, MessageChange, MessageHistory, MessageLeaf, MessageOp, MessageTree, NamespaceFromPath, NamespaceOptions, NodeRange, OverlayHost, ParseResult, Placeholder, PlaceholderComparer, PlaceholderProblem, PlaceholderSyntax, Plan, Plugin, PluginConfigs, PluginFile, PluginLoader, PluginOption, PluginRegistry, PluginSpecifier, PluralForm, Preset, PresetDetection, PresetEntry, PresetExplanation, PresetFactory, PresetLoader, PresetOption, Project, ProjectChange, ProjectExplanation, PromptContext, PromptData, PromptDraft, PromptItem, PromptOptions, RegexScannerSpec, ReportInput, ReportSummary, Reporter, ReporterContext, ReporterLoader, ResolveConfigOptions, ResolvedConfig, Resource, ResourceBlock, ResourceCandidate, ResourceId, ResourceNode, ResourceSpec, ResourceTransform, ResourceTransformContext, ScanContext, ScanResult, Scanner, ScannerEntry, SchemaIssue, Scope, SerializableConfig, Setup, Severity, SourceFile, TextEdit, TextRange, TreeLeaf, Usage, UsageCandidate, UsageContext, UsageKind, UsageMatch, UsageOptions, UsageQuery, UserConfig, WireDiagnostic, WireRange, WriteOptions, X18nError };
|
package/dist/index.mjs
CHANGED
|
@@ -138,7 +138,9 @@ function defineConfig(config) {
|
|
|
138
138
|
}
|
|
139
139
|
//#endregion
|
|
140
140
|
//#region src/errors.ts
|
|
141
|
-
|
|
141
|
+
function at(p) {
|
|
142
|
+
return p.line === void 0 ? p.path : `${p.path}:${p.line}`;
|
|
143
|
+
}
|
|
142
144
|
/**
|
|
143
145
|
* Every coded error. Groups: 0xxx config, 1xxx plugin, 2xxx discovery and parse,
|
|
144
146
|
* 3xxx scanning, 4xxx edits, 5xxx reviews. `fix` stays static so the docs can show it.
|
|
@@ -331,6 +333,40 @@ function pathMatcher(globs) {
|
|
|
331
333
|
return (path) => regexes.some((re) => re.test(path));
|
|
332
334
|
}
|
|
333
335
|
//#endregion
|
|
336
|
+
//#region src/library.ts
|
|
337
|
+
/** One plugin holding a library's call specs, regexes, namespace rules, defaults and extraction templates. */
|
|
338
|
+
function createLibraryPlugin(options) {
|
|
339
|
+
return {
|
|
340
|
+
name: options.name,
|
|
341
|
+
async config(draft, ctx) {
|
|
342
|
+
if (options.namespace) draft.namespace = {
|
|
343
|
+
...draft.namespace,
|
|
344
|
+
separator: options.namespace.separator ?? draft.namespace.separator,
|
|
345
|
+
default: draft.namespace.default ?? options.namespace.default,
|
|
346
|
+
fromPath: options.namespace.fromPath ?? draft.namespace.fromPath
|
|
347
|
+
};
|
|
348
|
+
if (options.resources) draft.resources = [...draft.resources, ...options.resources.filter((spec) => !draft.resources.some((r) => r.pattern === spec.pattern))];
|
|
349
|
+
if (options.keyStyle && draft.keyStyle === "auto" && !ctx.user.keyStyle) draft.keyStyle = options.keyStyle;
|
|
350
|
+
draft.usage = {
|
|
351
|
+
...draft.usage,
|
|
352
|
+
calls: [...draft.usage.calls, ...options.calls ?? []],
|
|
353
|
+
regex: [...draft.usage.regex, ...options.regex ?? []],
|
|
354
|
+
derivedKeys: [...draft.usage.derivedKeys, ...options.derivedKeys ?? []]
|
|
355
|
+
};
|
|
356
|
+
await options.extra?.(draft, ctx);
|
|
357
|
+
},
|
|
358
|
+
placeholders: options.placeholders,
|
|
359
|
+
localeFinders: options.finders,
|
|
360
|
+
normalizeKey: options.normalizeKey,
|
|
361
|
+
transformResource: options.transformResource,
|
|
362
|
+
translatePrompt: options.translatePrompt,
|
|
363
|
+
extractors: options.templates ? [{
|
|
364
|
+
id: options.name,
|
|
365
|
+
templates: options.templates
|
|
366
|
+
}] : void 0
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
//#endregion
|
|
334
370
|
//#region src/locale.ts
|
|
335
371
|
const defaultRegions = {
|
|
336
372
|
ar: "SA",
|
|
@@ -585,8 +621,8 @@ function uniqueById(items, kind) {
|
|
|
585
621
|
}
|
|
586
622
|
function createRegistry(input, config) {
|
|
587
623
|
const plugins = sortByEnforce(input);
|
|
588
|
-
const formats = plugins.flatMap((plugin) => plugin.formats ?? []);
|
|
589
|
-
const scanners = plugins.flatMap((plugin) => plugin.scanners ?? []);
|
|
624
|
+
const formats = plugins.flatMap((plugin) => [...plugin.formats ?? [], ...plugin.lazyFormats ?? []]);
|
|
625
|
+
const scanners = plugins.flatMap((plugin) => [...plugin.scanners ?? [], ...plugin.lazyScanners ?? []]);
|
|
590
626
|
const finders = [...uniqueById(plugins.flatMap((plugin) => plugin.localeFinders ?? []), "Locale finder").values()];
|
|
591
627
|
const checks = uniqueById(plugins.flatMap((plugin) => plugin.checks ?? []), "Check");
|
|
592
628
|
const reporters = uniqueById(plugins.flatMap((plugin) => plugin.reporters ?? []), "Reporter");
|
|
@@ -602,8 +638,58 @@ function createRegistry(input, config) {
|
|
|
602
638
|
});
|
|
603
639
|
else byExtension.set(extension, format);
|
|
604
640
|
}
|
|
605
|
-
const
|
|
641
|
+
const isLazy = (format) => "load" in format;
|
|
642
|
+
const loaded = new Map(formats.filter((format) => !isLazy(format)).map((format) => [format.id, format]));
|
|
643
|
+
const loading = /* @__PURE__ */ new Map();
|
|
644
|
+
const formatById = (id) => loaded.get(id);
|
|
645
|
+
const formatIdFor = (path, hint) => {
|
|
646
|
+
if (hint) return hint;
|
|
647
|
+
const extension = extensionOf(path);
|
|
648
|
+
return config.formats[extension] ?? byExtension.get(extension)?.id;
|
|
649
|
+
};
|
|
650
|
+
const loadFormat = (id) => {
|
|
651
|
+
const ready = loaded.get(id);
|
|
652
|
+
if (ready) return Promise.resolve(ready);
|
|
653
|
+
const lazy = formats.find((format) => format.id === id);
|
|
654
|
+
if (!lazy || !isLazy(lazy)) return Promise.resolve(void 0);
|
|
655
|
+
let pending = loading.get(id);
|
|
656
|
+
if (!pending) {
|
|
657
|
+
pending = lazy.load().then((mod) => {
|
|
658
|
+
const format = mod.default().formats?.find((item) => item.id === id && !isLazy(item));
|
|
659
|
+
if (format) loaded.set(id, format);
|
|
660
|
+
return format;
|
|
661
|
+
});
|
|
662
|
+
loading.set(id, pending);
|
|
663
|
+
}
|
|
664
|
+
return pending;
|
|
665
|
+
};
|
|
606
666
|
const scannerCache = /* @__PURE__ */ new Map();
|
|
667
|
+
const loadedScanners = new Map(scanners.filter((scanner) => !("load" in scanner)).map((scanner) => [scanner.id, scanner]));
|
|
668
|
+
const loadingScanners = /* @__PURE__ */ new Map();
|
|
669
|
+
const loadScanner = (entry) => {
|
|
670
|
+
if (!("load" in entry)) return Promise.resolve(entry);
|
|
671
|
+
const ready = loadedScanners.get(entry.id);
|
|
672
|
+
if (ready) return Promise.resolve(ready);
|
|
673
|
+
let pending = loadingScanners.get(entry.id);
|
|
674
|
+
if (!pending) {
|
|
675
|
+
pending = entry.load().then((mod) => {
|
|
676
|
+
const scanner = mod.default().scanners?.find((item) => item.id === entry.id);
|
|
677
|
+
if (scanner) loadedScanners.set(entry.id, scanner);
|
|
678
|
+
return scanner;
|
|
679
|
+
});
|
|
680
|
+
loadingScanners.set(entry.id, pending);
|
|
681
|
+
}
|
|
682
|
+
return pending;
|
|
683
|
+
};
|
|
684
|
+
const scannersFor = (path) => {
|
|
685
|
+
const extension = extensionOf(path);
|
|
686
|
+
let list = scannerCache.get(extension);
|
|
687
|
+
if (!list) {
|
|
688
|
+
list = extension ? scanners.filter((scanner) => scanner.extensions.includes(extension) || scanner.extensions.includes("*")) : [];
|
|
689
|
+
scannerCache.set(extension, list);
|
|
690
|
+
}
|
|
691
|
+
return list;
|
|
692
|
+
};
|
|
607
693
|
return {
|
|
608
694
|
plugins,
|
|
609
695
|
formats,
|
|
@@ -617,20 +703,18 @@ function createRegistry(input, config) {
|
|
|
617
703
|
histories: plugins.flatMap((plugin) => plugin.histories ?? []),
|
|
618
704
|
conflicts,
|
|
619
705
|
formatById,
|
|
706
|
+
formatIdFor,
|
|
620
707
|
formatFor(path, hint) {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const mapped = config.formats[extension];
|
|
624
|
-
return mapped ? formatById(mapped) : byExtension.get(extension);
|
|
708
|
+
const id = formatIdFor(path, hint);
|
|
709
|
+
return id === void 0 ? void 0 : formatById(id);
|
|
625
710
|
},
|
|
626
|
-
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
return list;
|
|
711
|
+
async loadFormatFor(path, hint) {
|
|
712
|
+
const id = formatIdFor(path, hint);
|
|
713
|
+
return id === void 0 ? void 0 : loadFormat(id);
|
|
714
|
+
},
|
|
715
|
+
scannersFor,
|
|
716
|
+
async loadScannersFor(path) {
|
|
717
|
+
return (await Promise.all(scannersFor(path).map(loadScanner))).filter((scanner) => scanner !== void 0);
|
|
634
718
|
}
|
|
635
719
|
};
|
|
636
720
|
}
|
|
@@ -665,7 +749,7 @@ const callSpec = v.strictObject({
|
|
|
665
749
|
"accessor"
|
|
666
750
|
])),
|
|
667
751
|
key: v.optional(v.union([v.number(), argSource])),
|
|
668
|
-
namespace: v.optional(argSource),
|
|
752
|
+
namespace: v.optional(v.union([argSource, v.string()])),
|
|
669
753
|
keyPrefix: v.optional(argSource),
|
|
670
754
|
factory: v.optional(v.boolean()),
|
|
671
755
|
returns: v.optional(strings),
|
|
@@ -724,6 +808,8 @@ const checkSetting = v.union([level, v.tuple([level, jsonObject])]);
|
|
|
724
808
|
const namespaceOptions = v.strictObject({
|
|
725
809
|
separator: v.optional(v.string()),
|
|
726
810
|
default: v.optional(v.string()),
|
|
811
|
+
fallback: v.optional(strings),
|
|
812
|
+
byFile: v.optional(v.record(v.string(), v.string())),
|
|
727
813
|
fromPath: v.optional(v.picklist([
|
|
728
814
|
"dot",
|
|
729
815
|
"slash",
|
|
@@ -949,8 +1035,12 @@ function innermost(scopes, range) {
|
|
|
949
1035
|
return best;
|
|
950
1036
|
}
|
|
951
1037
|
function createUsageResolver(config, namespaced, normalizers) {
|
|
952
|
-
const separator = config.namespace
|
|
953
|
-
|
|
1038
|
+
const { separator, fallback } = config.namespace;
|
|
1039
|
+
const byFile = Object.entries(config.namespace.byFile).map(([glob, namespace]) => ({
|
|
1040
|
+
matches: pathMatcher([glob]),
|
|
1041
|
+
namespace
|
|
1042
|
+
}));
|
|
1043
|
+
return { resolve(candidate, scopes, path, scanner, has) {
|
|
954
1044
|
const scope = innermost(scopes, candidate.range);
|
|
955
1045
|
const ctx = { path };
|
|
956
1046
|
let text = candidate.raw;
|
|
@@ -961,8 +1051,8 @@ function createUsageResolver(config, namespaced, normalizers) {
|
|
|
961
1051
|
break;
|
|
962
1052
|
}
|
|
963
1053
|
}
|
|
964
|
-
const scopeNamespace = candidate.namespace ?? scope?.namespace;
|
|
965
|
-
|
|
1054
|
+
const scopeNamespace = candidate.namespace ?? scope?.namespace ?? byFile.find((rule) => rule.matches(path))?.namespace;
|
|
1055
|
+
let scopePrefix = candidate.keyPrefix ?? scope?.keyPrefix;
|
|
966
1056
|
let namespace;
|
|
967
1057
|
let explicit = false;
|
|
968
1058
|
if (namespaced) {
|
|
@@ -973,10 +1063,17 @@ function createUsageResolver(config, namespaced, normalizers) {
|
|
|
973
1063
|
text = text.slice(index + separator.length);
|
|
974
1064
|
} else namespace = scopeNamespace ?? config.namespace.default;
|
|
975
1065
|
}
|
|
976
|
-
if (scopePrefix && !explicit)
|
|
1066
|
+
if (scopePrefix && !explicit) {
|
|
1067
|
+
const prefixed = text ? `${scopePrefix}.${text}` : scopePrefix;
|
|
1068
|
+
const lookup = candidate.dynamic === "prefix" ? prefixed.replace(/\.$/, "") : prefixed;
|
|
1069
|
+
if (candidate.keyPrefix || !scope?.optionalPrefix || has(joinKey(namespace, lookup.split(".")))) text = prefixed;
|
|
1070
|
+
else scopePrefix = void 0;
|
|
1071
|
+
}
|
|
1072
|
+
const keyPath = text === "" ? [] : text.split(".");
|
|
977
1073
|
let key;
|
|
978
1074
|
if (candidate.dynamic === "unknown") key = void 0;
|
|
979
|
-
else key = joinKey(namespace,
|
|
1075
|
+
else key = joinKey(namespace, keyPath);
|
|
1076
|
+
if (key !== void 0 && namespaced && !explicit && candidate.dynamic === false && !has(key)) key = fallback.map((ns) => joinKey(ns, keyPath)).find(has) ?? key;
|
|
980
1077
|
return {
|
|
981
1078
|
key,
|
|
982
1079
|
raw: candidate.raw,
|
|
@@ -1345,7 +1442,8 @@ function mirrorPath(path, from, to) {
|
|
|
1345
1442
|
function importEdit(text, lines) {
|
|
1346
1443
|
const missing = lines.filter((line) => !text.includes(line.trim()));
|
|
1347
1444
|
if (missing.length === 0) return void 0;
|
|
1348
|
-
const
|
|
1445
|
+
const open = /^\s*---\r?\n/.exec(text);
|
|
1446
|
+
const script = (open && /^---\s*$/m.test(text.slice(open[0].length)) ? open : void 0) ?? /<script\b[^>]*>\r?\n?/.exec(text);
|
|
1349
1447
|
let at = 0;
|
|
1350
1448
|
if (script) at = script.index + script[0].length;
|
|
1351
1449
|
else {
|
|
@@ -1439,7 +1537,9 @@ var PlanBuilder = class {
|
|
|
1439
1537
|
const sourceState = source && this.stateById.get(source.resource);
|
|
1440
1538
|
const namespace = namespaced ? this.keyId(key).namespace ?? this.config.namespace.default : sourceState?.resource.namespace;
|
|
1441
1539
|
const exists = (path) => resources.some((r) => r.resource.path === path);
|
|
1442
|
-
const
|
|
1540
|
+
const sibling = (r) => r.resource.locale === locale && r.resource.namespace !== void 0 && namespace !== void 0;
|
|
1541
|
+
const model = (sourceState && fits(sourceState) ? sourceState : void 0) ?? resources.find((s) => s.resource.locale === sourceLocale && fits(s)) ?? resources.find(fits) ?? resources.find(sibling);
|
|
1542
|
+
const resolved = this.options.resolveNew(locale, namespace, model?.resource);
|
|
1443
1543
|
if (resolved && !exists(resolved)) {
|
|
1444
1544
|
const format = this.options.registry.formatFor(resolved);
|
|
1445
1545
|
if (format?.serialize) {
|
|
@@ -1455,8 +1555,6 @@ var PlanBuilder = class {
|
|
|
1455
1555
|
return target;
|
|
1456
1556
|
}
|
|
1457
1557
|
}
|
|
1458
|
-
const sibling = (r) => r.resource.locale === locale && r.resource.namespace !== void 0 && namespace !== void 0;
|
|
1459
|
-
const model = (sourceState && fits(sourceState) ? sourceState : void 0) ?? resources.find((s) => s.resource.locale === sourceLocale && fits(s)) ?? resources.find(fits) ?? resources.find(sibling);
|
|
1460
1558
|
const format = model && this.options.registry.formatById(model.resource.format);
|
|
1461
1559
|
let path = model && mirrorPath(model.resource.path, model.resource.locale, locale);
|
|
1462
1560
|
if (model && path && namespace !== void 0 && model.resource.namespace !== void 0 && model.resource.namespace !== namespace) path = mirrorPath(path, model.resource.namespace, namespace);
|
|
@@ -2265,6 +2363,22 @@ function createProject(options) {
|
|
|
2265
2363
|
if (state.error) return void 0;
|
|
2266
2364
|
return new Map([...walkLeaves(state.tree)].map((leaf) => [keyFor(state.resource, leaf.path), leaf.value]));
|
|
2267
2365
|
}
|
|
2366
|
+
/** A path for a new resource, from the locale finders. */
|
|
2367
|
+
function newResourcePath(locale, namespace, model) {
|
|
2368
|
+
const ctx = {
|
|
2369
|
+
host,
|
|
2370
|
+
config,
|
|
2371
|
+
extensions: [...new Set(registry.formats.flatMap((format) => format.extensions))]
|
|
2372
|
+
};
|
|
2373
|
+
for (const finder of registry.finders) {
|
|
2374
|
+
const path = finder.resolveNew?.({
|
|
2375
|
+
locale,
|
|
2376
|
+
namespace,
|
|
2377
|
+
model
|
|
2378
|
+
}, ctx);
|
|
2379
|
+
if (path) return path;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2268
2382
|
async function loadResources(candidates, reread) {
|
|
2269
2383
|
const next = /* @__PURE__ */ new Map();
|
|
2270
2384
|
const paths = [...new Set(candidates.map((c) => c.path))];
|
|
@@ -2276,6 +2390,7 @@ function createProject(options) {
|
|
|
2276
2390
|
}
|
|
2277
2391
|
}));
|
|
2278
2392
|
namespaced = config.namespace.separator !== "." && candidates.some((c) => c.namespace !== void 0);
|
|
2393
|
+
await Promise.all(candidates.map((candidate) => registry.loadFormatFor(candidate.path, candidate.format)));
|
|
2279
2394
|
for (const candidate of candidates) {
|
|
2280
2395
|
const text = fileTexts.get(candidate.path);
|
|
2281
2396
|
if (text === void 0) continue;
|
|
@@ -2296,6 +2411,11 @@ function createProject(options) {
|
|
|
2296
2411
|
resolver = createUsageResolver(config, namespaced, normalizers);
|
|
2297
2412
|
resolved = /* @__PURE__ */ new WeakMap();
|
|
2298
2413
|
resourceIndex = void 0;
|
|
2414
|
+
const namespaces = /* @__PURE__ */ new Set([void 0, ...[...resourceStates.values()].map((state) => state.resource.namespace)]);
|
|
2415
|
+
await Promise.all([...namespaces].map((namespace) => {
|
|
2416
|
+
const path = newResourcePath(sourceLocale, namespace, void 0);
|
|
2417
|
+
return path ? registry.loadFormatFor(path) : void 0;
|
|
2418
|
+
}));
|
|
2299
2419
|
}
|
|
2300
2420
|
async function scanOne(file) {
|
|
2301
2421
|
const usages = [];
|
|
@@ -2303,7 +2423,7 @@ function createProject(options) {
|
|
|
2303
2423
|
const keySlots = [];
|
|
2304
2424
|
const hardStrings = [];
|
|
2305
2425
|
const calls = config.usage.calls.filter((spec) => ![spec.callee].flat().some((name) => config.usage.ignoreCallees.includes(name)));
|
|
2306
|
-
for (const scanner of registry.
|
|
2426
|
+
for (const scanner of await registry.loadScannersFor(file.path)) {
|
|
2307
2427
|
const result = await scanner.scan(file, {
|
|
2308
2428
|
config,
|
|
2309
2429
|
calls
|
|
@@ -2470,12 +2590,13 @@ function createProject(options) {
|
|
|
2470
2590
|
};
|
|
2471
2591
|
}
|
|
2472
2592
|
function buildIndex() {
|
|
2473
|
-
resourceIndex ??= buildResourceIndex();
|
|
2593
|
+
const { messages, parents, derived } = resourceIndex ??= buildResourceIndex();
|
|
2594
|
+
const has = (key) => messages.has(key) || parents.has(key) || derived.has(key);
|
|
2474
2595
|
const usages = [];
|
|
2475
2596
|
for (const [path, state] of [...files].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) {
|
|
2476
2597
|
let list = resolved.get(state);
|
|
2477
2598
|
if (!list) {
|
|
2478
|
-
list = state.result.usages.map((candidate) => resolver.resolve(candidate, state.result.scopes ?? [], path, "scanner"));
|
|
2599
|
+
list = state.result.usages.map((candidate) => resolver.resolve(candidate, state.result.scopes ?? [], path, "scanner", has));
|
|
2479
2600
|
resolved.set(state, list);
|
|
2480
2601
|
}
|
|
2481
2602
|
usages.push(...list);
|
|
@@ -2708,8 +2829,9 @@ function createProject(options) {
|
|
|
2708
2829
|
const before = idx();
|
|
2709
2830
|
const paths = new Set(events.map((event) => event.path));
|
|
2710
2831
|
const resourcePaths = new Set([...resourceStates.values()].map((state) => state.resource.path));
|
|
2711
|
-
const maybeResource = (path) => !resourcePaths.has(path) && registry.
|
|
2712
|
-
const
|
|
2832
|
+
const maybeResource = (path) => !resourcePaths.has(path) && registry.formatIdFor(path) !== void 0 && !usageEligible(path);
|
|
2833
|
+
const blockPaths = new Set([...resourceStates.values()].filter((state) => state.resource.block).map((state) => state.resource.path));
|
|
2834
|
+
const structural = events.some((event) => event.type !== "change" || maybeResource(event.path) || blockPaths.has(event.path));
|
|
2713
2835
|
if (structural || [...paths].some((path) => resourcePaths.has(path))) await loadResources(structural ? await discover() : [...resourceStates.values()].map((state) => ({
|
|
2714
2836
|
...state.resource,
|
|
2715
2837
|
format: state.resource.format === "unknown" ? void 0 : state.resource.format
|
|
@@ -2750,20 +2872,7 @@ function createProject(options) {
|
|
|
2750
2872
|
text: (path) => fileTexts.get(path) ?? files.get(path)?.text,
|
|
2751
2873
|
basesOf,
|
|
2752
2874
|
toStored: (target, path) => storedPathOf(target)(path),
|
|
2753
|
-
resolveNew
|
|
2754
|
-
const ctx = {
|
|
2755
|
-
host,
|
|
2756
|
-
config,
|
|
2757
|
-
extensions: [...new Set(registry.formats.flatMap((format) => format.extensions))]
|
|
2758
|
-
};
|
|
2759
|
-
for (const finder of registry.finders) {
|
|
2760
|
-
const path = finder.resolveNew?.({
|
|
2761
|
-
locale,
|
|
2762
|
-
namespace
|
|
2763
|
-
}, ctx);
|
|
2764
|
-
if (path) return path;
|
|
2765
|
-
}
|
|
2766
|
-
},
|
|
2875
|
+
resolveNew: newResourcePath,
|
|
2767
2876
|
update
|
|
2768
2877
|
});
|
|
2769
2878
|
const ready = (async () => {
|
|
@@ -2894,6 +3003,8 @@ function defaultDraft() {
|
|
|
2894
3003
|
namespace: {
|
|
2895
3004
|
separator: ".",
|
|
2896
3005
|
default: void 0,
|
|
3006
|
+
fallback: [],
|
|
3007
|
+
byFile: {},
|
|
2897
3008
|
fromPath: "dot"
|
|
2898
3009
|
},
|
|
2899
3010
|
usage: {
|
|
@@ -2977,6 +3088,11 @@ function applyLayer(draft, layer, origin, origins) {
|
|
|
2977
3088
|
draft.namespace = {
|
|
2978
3089
|
separator: layer.namespace.separator ?? draft.namespace.separator,
|
|
2979
3090
|
default: layer.namespace.default ?? draft.namespace.default,
|
|
3091
|
+
fallback: layer.namespace.fallback ?? draft.namespace.fallback,
|
|
3092
|
+
byFile: {
|
|
3093
|
+
...draft.namespace.byFile,
|
|
3094
|
+
...layer.namespace.byFile
|
|
3095
|
+
},
|
|
2980
3096
|
fromPath: layer.namespace.fromPath ?? draft.namespace.fromPath
|
|
2981
3097
|
};
|
|
2982
3098
|
mark("namespace");
|
|
@@ -3081,8 +3197,8 @@ function createLoader(options) {
|
|
|
3081
3197
|
if (option === false || option === void 0) return void 0;
|
|
3082
3198
|
if (typeof option === "object" && !Array.isArray(option)) return option;
|
|
3083
3199
|
const [specifier, pluginOptions] = typeof option === "string" ? [option, void 0] : [option[0], option[1]];
|
|
3084
|
-
const
|
|
3085
|
-
if (
|
|
3200
|
+
const entry = options.builtins.presets[specifier];
|
|
3201
|
+
if (entry) return (await entry.load()).default(pluginOptions);
|
|
3086
3202
|
if (!options.loadPlugin) throw errors.X18N1003({ preset: specifier });
|
|
3087
3203
|
return options.loadPlugin(specifier, pluginOptions);
|
|
3088
3204
|
};
|
|
@@ -3118,7 +3234,17 @@ async function expandPresets(presets, userPlugins, listed, detectCtx) {
|
|
|
3118
3234
|
name: preset.name,
|
|
3119
3235
|
reason: "after-exclusive"
|
|
3120
3236
|
});
|
|
3121
|
-
else
|
|
3237
|
+
else if ("entry" in preset) {
|
|
3238
|
+
const applies = listed || (preset.entry.detect ? await preset.entry.detect(detectCtx) : false);
|
|
3239
|
+
explanations.push({
|
|
3240
|
+
name: preset.name,
|
|
3241
|
+
reason: applies ? listed ? "listed" : "detected" : "not-detected"
|
|
3242
|
+
});
|
|
3243
|
+
if (!applies) continue;
|
|
3244
|
+
const loaded = (await preset.entry.load()).default();
|
|
3245
|
+
for (const child of loaded.plugins) await flatten(child, false);
|
|
3246
|
+
stopped = preset.entry.exclusive === true || loaded.exclusive === true;
|
|
3247
|
+
} else stopped = await flatten(preset, listed);
|
|
3122
3248
|
for (const plugin of userPlugins) await flatten(plugin, true);
|
|
3123
3249
|
return {
|
|
3124
3250
|
plugins,
|
|
@@ -3141,7 +3267,10 @@ async function resolveConfig(options) {
|
|
|
3141
3267
|
const userPlugins = await loadAll(load, [...options.parent?.plugins ?? [], ...user.plugins ?? []]);
|
|
3142
3268
|
const presetOption = user.presets ?? options.parent?.presets ?? "auto";
|
|
3143
3269
|
const auto = presetOption === "auto";
|
|
3144
|
-
const presets = auto ? Object.
|
|
3270
|
+
const presets = auto ? Object.entries(builtins.presets).map(([name, entry]) => ({
|
|
3271
|
+
name,
|
|
3272
|
+
entry
|
|
3273
|
+
})) : (await loadAll(load, presetOption)).map((item) => isPreset(item) ? item : {
|
|
3145
3274
|
name: item.name,
|
|
3146
3275
|
plugins: [item]
|
|
3147
3276
|
});
|
|
@@ -3182,4 +3311,4 @@ async function resolveConfig(options) {
|
|
|
3182
3311
|
};
|
|
3183
3312
|
}
|
|
3184
3313
|
//#endregion
|
|
3185
|
-
export { ConfigSchema, PluginFileSchema, applyMessageOps, applyTextEdits, buildTree, contractVersion, createLineIndex, createMemoryHost, createOverlayHost, createPlaceholderComparer, createProject, createRegistry, defineConfig, definePlugin, definePreset, diffText, envelope, errors, extensionOf, globToRegExp, hashText, isJsonObject, isKeyUnder, isMessageLeaf, isMessageTree, isPreset, isX18nError, joinKey, keyMatcher, localeSpellingKey, parseLocaleInfo, parseMessageBatch, pathId, pathMatcher, placeholderProblems, rangeContains, renderPrompt, replaceKeyPrefix, resolveConfig, sortTree, splitKey, toErrorInfo, toWireDiagnostic, toWireRange, validate, walkLeaves };
|
|
3314
|
+
export { ConfigSchema, PluginFileSchema, applyMessageOps, applyTextEdits, buildTree, contractVersion, createLibraryPlugin, createLineIndex, createMemoryHost, createOverlayHost, createPlaceholderComparer, createProject, createRegistry, defineConfig, definePlugin, definePreset, diffText, envelope, errors, extensionOf, globToRegExp, hashText, isJsonObject, isKeyUnder, isMessageLeaf, isMessageTree, isPreset, isX18nError, joinKey, keyMatcher, localeSpellingKey, parseLocaleInfo, parseMessageBatch, pathId, pathMatcher, placeholderProblems, rangeContains, renderPrompt, replaceKeyPrefix, resolveConfig, sortTree, splitKey, toErrorInfo, toWireDiagnostic, toWireRange, validate, walkLeaves };
|
package/package.json
CHANGED
package/schema/config.json
CHANGED
|
@@ -145,6 +145,21 @@
|
|
|
145
145
|
"default": {
|
|
146
146
|
"type": "string"
|
|
147
147
|
},
|
|
148
|
+
"fallback": {
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": {
|
|
151
|
+
"type": "string"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"byFile": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"propertyNames": {
|
|
157
|
+
"type": "string"
|
|
158
|
+
},
|
|
159
|
+
"additionalProperties": {
|
|
160
|
+
"type": "string"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
148
163
|
"fromPath": {
|
|
149
164
|
"enum": [
|
|
150
165
|
"dot",
|
|
@@ -221,19 +236,26 @@
|
|
|
221
236
|
]
|
|
222
237
|
},
|
|
223
238
|
"namespace": {
|
|
224
|
-
"
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
"
|
|
239
|
+
"anyOf": [
|
|
240
|
+
{
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {
|
|
243
|
+
"arg": {
|
|
244
|
+
"type": "number"
|
|
245
|
+
},
|
|
246
|
+
"property": {
|
|
247
|
+
"type": "string"
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
"required": [
|
|
251
|
+
"arg"
|
|
252
|
+
],
|
|
253
|
+
"additionalProperties": false
|
|
228
254
|
},
|
|
229
|
-
|
|
255
|
+
{
|
|
230
256
|
"type": "string"
|
|
231
257
|
}
|
|
232
|
-
|
|
233
|
-
"required": [
|
|
234
|
-
"arg"
|
|
235
|
-
],
|
|
236
|
-
"additionalProperties": false
|
|
258
|
+
]
|
|
237
259
|
},
|
|
238
260
|
"keyPrefix": {
|
|
239
261
|
"type": "object",
|
package/schema/plugin.json
CHANGED
|
@@ -90,6 +90,21 @@
|
|
|
90
90
|
"default": {
|
|
91
91
|
"type": "string"
|
|
92
92
|
},
|
|
93
|
+
"fallback": {
|
|
94
|
+
"type": "array",
|
|
95
|
+
"items": {
|
|
96
|
+
"type": "string"
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"byFile": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"propertyNames": {
|
|
102
|
+
"type": "string"
|
|
103
|
+
},
|
|
104
|
+
"additionalProperties": {
|
|
105
|
+
"type": "string"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
93
108
|
"fromPath": {
|
|
94
109
|
"enum": [
|
|
95
110
|
"dot",
|
|
@@ -166,19 +181,26 @@
|
|
|
166
181
|
]
|
|
167
182
|
},
|
|
168
183
|
"namespace": {
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
"
|
|
184
|
+
"anyOf": [
|
|
185
|
+
{
|
|
186
|
+
"type": "object",
|
|
187
|
+
"properties": {
|
|
188
|
+
"arg": {
|
|
189
|
+
"type": "number"
|
|
190
|
+
},
|
|
191
|
+
"property": {
|
|
192
|
+
"type": "string"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"required": [
|
|
196
|
+
"arg"
|
|
197
|
+
],
|
|
198
|
+
"additionalProperties": false
|
|
173
199
|
},
|
|
174
|
-
|
|
200
|
+
{
|
|
175
201
|
"type": "string"
|
|
176
202
|
}
|
|
177
|
-
|
|
178
|
-
"required": [
|
|
179
|
-
"arg"
|
|
180
|
-
],
|
|
181
|
-
"additionalProperties": false
|
|
203
|
+
]
|
|
182
204
|
},
|
|
183
205
|
"keyPrefix": {
|
|
184
206
|
"type": "object",
|