@x18n/node 0.0.0 → 0.0.2
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/package.json +3 -3
- package/dist/index.d.mts +0 -84
- package/dist/index.mjs +0 -714
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@x18n/node",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.2",
|
|
5
5
|
"description": "Node.js host for x18n: file system access, config and plugin loading",
|
|
6
6
|
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"node": ">=22"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@x18n/builtins": "0.0.
|
|
29
|
-
"@x18n/core": "0.0.
|
|
28
|
+
"@x18n/builtins": "0.0.2",
|
|
29
|
+
"@x18n/core": "0.0.2",
|
|
30
30
|
"chokidar": "^5.0.0",
|
|
31
31
|
"ignore": "^7.0.12",
|
|
32
32
|
"jiti": "^2.6.1",
|
package/dist/index.d.mts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { Builtins, Host, JsonObject, Logger, Plugin, Preset, Project, UserConfig } from "@x18n/core";
|
|
2
|
-
//#region src/config.d.ts
|
|
3
|
-
export declare const configFileNames: readonly string[];
|
|
4
|
-
/** The config file in `dir`, if any. */
|
|
5
|
-
export declare function findConfigFile(dir: string): string | undefined;
|
|
6
|
-
/** The nearest config file at or above `cwd`, stopping at the git root. */
|
|
7
|
-
export declare function findConfigUpward(cwd: string): string | undefined;
|
|
8
|
-
/** Imports a JS or TS module (config, plugin, executed resource). */
|
|
9
|
-
export declare function importModule(file: string): Promise<unknown>;
|
|
10
|
-
export declare function loadConfigFile(file: string): Promise<UserConfig>;
|
|
11
|
-
/** Loads a YAML plugin file (`*.x18n-plugin.yml`). */
|
|
12
|
-
export declare function loadYamlPlugin(file: string): Promise<Plugin | Preset>;
|
|
13
|
-
/** Resolves a plugin specifier relative to the config directory: a YAML file, a module path, or a package name. */
|
|
14
|
-
export declare function createPluginLoader(dir: string): (specifier: string, options: JsonObject | undefined) => Promise<Plugin | Preset>;
|
|
15
|
-
//#endregion
|
|
16
|
-
//#region src/host.d.ts
|
|
17
|
-
interface NodeHostOptions {
|
|
18
|
-
/** Directory of the persistent cache, or false to disable it. Default `node_modules/.cache/x18n` under the root. */
|
|
19
|
-
readonly cacheDir?: string | false | undefined;
|
|
20
|
-
readonly logger?: Logger | undefined;
|
|
21
|
-
}
|
|
22
|
-
interface NodeHost extends Host {
|
|
23
|
-
/** Writes the persistent cache to disk. */
|
|
24
|
-
readonly flush: () => Promise<void>;
|
|
25
|
-
}
|
|
26
|
-
export declare const stderrLogger: Logger;
|
|
27
|
-
/** The directory holding `.git`, at or above `start`. */
|
|
28
|
-
export declare function findGitRoot(start: string): string | undefined;
|
|
29
|
-
export declare function createNodeHost(root: string, options?: NodeHostOptions): NodeHost;
|
|
30
|
-
//#endregion
|
|
31
|
-
//#region src/load.d.ts
|
|
32
|
-
interface LoadOptions {
|
|
33
|
-
/** Where to look for a config file (upward). Default: the process cwd. */
|
|
34
|
-
readonly cwd: string;
|
|
35
|
-
/** Explicit config file. */
|
|
36
|
-
readonly configFile?: string | undefined;
|
|
37
|
-
/** Look for a config in parent directories too. Default true. */
|
|
38
|
-
readonly upward?: boolean | undefined;
|
|
39
|
-
/** Only this project of a monorepo. */
|
|
40
|
-
readonly project?: string | undefined;
|
|
41
|
-
/** Disable the persistent cache. */
|
|
42
|
-
readonly cache?: boolean | undefined;
|
|
43
|
-
readonly builtins?: Builtins | undefined;
|
|
44
|
-
readonly logger?: NodeHost["logger"];
|
|
45
|
-
/** Wraps each project's host, for example with an overlay of unsaved editor buffers. */
|
|
46
|
-
readonly wrapHost?: ((host: NodeHost) => Host) | undefined;
|
|
47
|
-
}
|
|
48
|
-
interface LoadedProject {
|
|
49
|
-
readonly name: string;
|
|
50
|
-
/** Absolute project root. */
|
|
51
|
-
readonly root: string;
|
|
52
|
-
readonly configFile: string | undefined;
|
|
53
|
-
readonly host: NodeHost;
|
|
54
|
-
readonly project: Project;
|
|
55
|
-
}
|
|
56
|
-
interface Workspace {
|
|
57
|
-
/** Absolute root of the workspace (the directory of the root config, or the cwd). */
|
|
58
|
-
readonly root: string;
|
|
59
|
-
readonly configFile: string | undefined;
|
|
60
|
-
readonly projects: readonly LoadedProject[];
|
|
61
|
-
}
|
|
62
|
-
/** Finds the config, resolves every project and waits until each is indexed. */
|
|
63
|
-
export declare function loadWorkspace(options: LoadOptions): Promise<Workspace>;
|
|
64
|
-
//#endregion
|
|
65
|
-
//#region src/migrate.d.ts
|
|
66
|
-
interface MigrationFile {
|
|
67
|
-
/** Path relative to the project root. */
|
|
68
|
-
readonly path: string;
|
|
69
|
-
readonly content: string;
|
|
70
|
-
}
|
|
71
|
-
interface MigrationResult {
|
|
72
|
-
readonly files: readonly MigrationFile[];
|
|
73
|
-
/** What was dropped or needs attention, for the report. */
|
|
74
|
-
readonly notes: readonly string[];
|
|
75
|
-
/** i18n-ally settings that can be removed after the migration. */
|
|
76
|
-
readonly removable: readonly string[];
|
|
77
|
-
}
|
|
78
|
-
interface MigrateOptions {
|
|
79
|
-
readonly format?: "ts" | "yml" | "json" | undefined;
|
|
80
|
-
}
|
|
81
|
-
/** Converts i18n-ally settings, custom framework and reviews into x18n files. Never deletes i18n-ally files. */
|
|
82
|
-
export declare function migrateI18nAlly(root: string, options?: MigrateOptions): Promise<MigrationResult>;
|
|
83
|
-
//#endregion
|
|
84
|
-
export type { LoadOptions, LoadedProject, MigrateOptions, MigrationFile, MigrationResult, NodeHost, NodeHostOptions, Workspace };
|
package/dist/index.mjs
DELETED
|
@@ -1,714 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { compileYamlPlugin } from "@x18n/builtins/yaml-plugin";
|
|
7
|
-
import { PluginFileSchema, X18nError, createProject, hashText, isX18nError, resolveConfig, validate } from "@x18n/core";
|
|
8
|
-
import { createJiti } from "jiti";
|
|
9
|
-
import { parse, stringify } from "yaml";
|
|
10
|
-
import process from "node:process";
|
|
11
|
-
import { watch } from "chokidar";
|
|
12
|
-
import ignore from "ignore";
|
|
13
|
-
import { glob } from "tinyglobby";
|
|
14
|
-
import { createBuiltins } from "@x18n/builtins";
|
|
15
|
-
import { applyEdits, modify, parse as parse$1 } from "jsonc-parser";
|
|
16
|
-
//#region src/config.ts
|
|
17
|
-
const configFileNames = [
|
|
18
|
-
"x18n.config.ts",
|
|
19
|
-
"x18n.config.mts",
|
|
20
|
-
"x18n.config.js",
|
|
21
|
-
"x18n.config.mjs",
|
|
22
|
-
"x18n.config.yml",
|
|
23
|
-
"x18n.config.yaml",
|
|
24
|
-
"x18n.config.json"
|
|
25
|
-
];
|
|
26
|
-
/** The config file in `dir`, if any. */
|
|
27
|
-
function findConfigFile(dir) {
|
|
28
|
-
for (const name of configFileNames) {
|
|
29
|
-
const file = join(dir, name);
|
|
30
|
-
if (existsSync(file)) return file;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/** The nearest config file at or above `cwd`, stopping at the git root. */
|
|
34
|
-
function findConfigUpward(cwd) {
|
|
35
|
-
let dir = resolve(cwd);
|
|
36
|
-
while (true) {
|
|
37
|
-
const found = findConfigFile(dir);
|
|
38
|
-
if (found) return found;
|
|
39
|
-
const parent = dirname(dir);
|
|
40
|
-
if (parent === dir || existsSync(join(dir, ".git"))) return void 0;
|
|
41
|
-
dir = parent;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const jiti = createJiti(import.meta.url, {
|
|
45
|
-
moduleCache: false,
|
|
46
|
-
interopDefault: true
|
|
47
|
-
});
|
|
48
|
-
/** Imports a JS or TS module (config, plugin, executed resource). */
|
|
49
|
-
async function importModule(file) {
|
|
50
|
-
return jiti.import(pathToFileURL(file).href, { default: true });
|
|
51
|
-
}
|
|
52
|
-
function isObject(value) {
|
|
53
|
-
return typeof value === "object" && value !== null;
|
|
54
|
-
}
|
|
55
|
-
async function loadConfigFile(file) {
|
|
56
|
-
try {
|
|
57
|
-
if (/\.ya?ml$/.test(file)) return parse(await readFile(file, "utf8")) ?? {};
|
|
58
|
-
if (file.endsWith(".json")) return JSON.parse(await readFile(file, "utf8"));
|
|
59
|
-
const loaded = await importModule(file);
|
|
60
|
-
const config = typeof loaded === "function" ? await loaded() : loaded;
|
|
61
|
-
if (!isObject(config)) throw new X18nError("X18N0002", `${file} does not export a config object`);
|
|
62
|
-
return config;
|
|
63
|
-
} catch (error) {
|
|
64
|
-
if (isX18nError(error)) throw error;
|
|
65
|
-
throw new X18nError("X18N0002", `${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/** Loads a YAML plugin file (`*.x18n-plugin.yml`). */
|
|
69
|
-
async function loadYamlPlugin(file) {
|
|
70
|
-
const data = parse(await readFile(file, "utf8"));
|
|
71
|
-
const result = validate(PluginFileSchema, data);
|
|
72
|
-
if (!result.success) throw new X18nError("X18N1004", `${file}: ${result.issues.map((i) => `${i.path || "(root)"}: ${i.message}`).join("; ")}`);
|
|
73
|
-
return compileYamlPlugin(result.output);
|
|
74
|
-
}
|
|
75
|
-
function isPluginLike(value) {
|
|
76
|
-
return isObject(value) && "name" in value && typeof value.name === "string";
|
|
77
|
-
}
|
|
78
|
-
/** Resolves a plugin specifier relative to the config directory: a YAML file, a module path, or a package name. */
|
|
79
|
-
function createPluginLoader(dir) {
|
|
80
|
-
return async (specifier, options) => {
|
|
81
|
-
try {
|
|
82
|
-
const file = specifier.startsWith(".") || isAbsolute(specifier) ? resolve(dir, specifier) : createRequire(join(dir, "package.json")).resolve(specifier);
|
|
83
|
-
if (/\.ya?ml$/.test(file)) return await loadYamlPlugin(file);
|
|
84
|
-
const loaded = await importModule(file);
|
|
85
|
-
const plugin = typeof loaded === "function" ? loaded(options) : loaded;
|
|
86
|
-
if (!isPluginLike(plugin)) throw new Error("the module does not export a plugin or a plugin factory");
|
|
87
|
-
return plugin;
|
|
88
|
-
} catch (error) {
|
|
89
|
-
if (isX18nError(error)) throw error;
|
|
90
|
-
throw new X18nError("X18N1001", `${specifier}: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
/** Paths from `root` up to the git root, relative to `root` (`..`, `../..`), whose manifests count for detection. */
|
|
95
|
-
function manifestRootsOf(root, gitRoot) {
|
|
96
|
-
const out = [];
|
|
97
|
-
if (!gitRoot) return out;
|
|
98
|
-
let dir = resolve(root);
|
|
99
|
-
while (dir !== gitRoot && dir.startsWith(gitRoot)) {
|
|
100
|
-
dir = dirname(dir);
|
|
101
|
-
out.push(relative(root, dir).replace(/\\/g, "/"));
|
|
102
|
-
}
|
|
103
|
-
return out;
|
|
104
|
-
}
|
|
105
|
-
//#endregion
|
|
106
|
-
//#region src/host.ts
|
|
107
|
-
const toPosix = (path) => sep === "\\" ? path.replace(/\\/g, "/") : path;
|
|
108
|
-
function isNotFound(error) {
|
|
109
|
-
return error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "EISDIR");
|
|
110
|
-
}
|
|
111
|
-
const stderrLogger = {
|
|
112
|
-
info: (message) => process.stderr.write(`${message}\n`),
|
|
113
|
-
warn: (message) => process.stderr.write(`warning: ${message}\n`),
|
|
114
|
-
error: (message) => process.stderr.write(`error: ${message}\n`)
|
|
115
|
-
};
|
|
116
|
-
/** The directory holding `.git`, at or above `start`. */
|
|
117
|
-
function findGitRoot(start) {
|
|
118
|
-
let dir = resolve(start);
|
|
119
|
-
while (true) {
|
|
120
|
-
if (existsSync(join(dir, ".git"))) return dir;
|
|
121
|
-
const parent = dirname(dir);
|
|
122
|
-
if (parent === dir) return void 0;
|
|
123
|
-
dir = parent;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/** Every `.gitignore` that applies under `root`: the ones above it up to the git root, and nested ones. */
|
|
127
|
-
async function loadGitignore(root) {
|
|
128
|
-
const rules = [];
|
|
129
|
-
const gitRoot = findGitRoot(root) ?? root;
|
|
130
|
-
const add = async (dir) => {
|
|
131
|
-
const text = await readFile(join(dir, ".gitignore"), "utf8").catch(() => void 0);
|
|
132
|
-
if (text !== void 0) rules.push({
|
|
133
|
-
base: dir,
|
|
134
|
-
ig: ignore().add(text)
|
|
135
|
-
});
|
|
136
|
-
};
|
|
137
|
-
let dir = root;
|
|
138
|
-
const chain = [];
|
|
139
|
-
while (dir.startsWith(gitRoot)) {
|
|
140
|
-
chain.push(dir);
|
|
141
|
-
if (dir === gitRoot) break;
|
|
142
|
-
dir = dirname(dir);
|
|
143
|
-
}
|
|
144
|
-
for (const item of chain.reverse()) await add(item);
|
|
145
|
-
const nested = await glob(["**/.gitignore"], {
|
|
146
|
-
cwd: root,
|
|
147
|
-
ignore: ["**/node_modules/**", ".gitignore"],
|
|
148
|
-
dot: true
|
|
149
|
-
});
|
|
150
|
-
for (const file of nested) await add(join(root, dirname(file)));
|
|
151
|
-
return (path) => {
|
|
152
|
-
const absolute = join(root, path);
|
|
153
|
-
return rules.some(({ base, ig }) => {
|
|
154
|
-
const rel = toPosix(relative(base, absolute));
|
|
155
|
-
return rel !== "" && !rel.startsWith("..") && ig.ignores(rel);
|
|
156
|
-
});
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
/** A persistent cache in one JSON file. Only entries used in this session are written back, so it stays bounded. */
|
|
160
|
-
function createFileCache(dir) {
|
|
161
|
-
const file = join(dir, "cache.json");
|
|
162
|
-
let entries;
|
|
163
|
-
const used = /* @__PURE__ */ new Map();
|
|
164
|
-
let dirty = false;
|
|
165
|
-
const load = () => {
|
|
166
|
-
entries ??= readFile(file, "utf8").then((text) => {
|
|
167
|
-
const data = JSON.parse(text);
|
|
168
|
-
return new Map(typeof data === "object" && data !== null ? Object.entries(data).filter((e) => typeof e[1] === "string") : []);
|
|
169
|
-
}).catch(() => /* @__PURE__ */ new Map());
|
|
170
|
-
return entries;
|
|
171
|
-
};
|
|
172
|
-
return {
|
|
173
|
-
async get(key) {
|
|
174
|
-
const value = (await load()).get(key);
|
|
175
|
-
if (value !== void 0) used.set(key, value);
|
|
176
|
-
return value;
|
|
177
|
-
},
|
|
178
|
-
async set(key, value) {
|
|
179
|
-
used.set(key, value);
|
|
180
|
-
dirty = true;
|
|
181
|
-
},
|
|
182
|
-
async flush() {
|
|
183
|
-
if (!dirty) return;
|
|
184
|
-
await mkdir(dir, { recursive: true });
|
|
185
|
-
await writeFile(file, JSON.stringify(Object.fromEntries(used)));
|
|
186
|
-
dirty = false;
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
function createNodeHost(root, options = {}) {
|
|
191
|
-
const absoluteRoot = resolve(root);
|
|
192
|
-
let gitignore;
|
|
193
|
-
const cacheDir = options.cacheDir === false ? void 0 : options.cacheDir ?? join(absoluteRoot, "node_modules/.cache/x18n");
|
|
194
|
-
const cache = cacheDir ? createFileCache(join(cacheDir, hashText(absoluteRoot))) : void 0;
|
|
195
|
-
const full = (path) => resolve(absoluteRoot, path);
|
|
196
|
-
return {
|
|
197
|
-
root: absoluteRoot,
|
|
198
|
-
logger: options.logger,
|
|
199
|
-
cache,
|
|
200
|
-
async readFile(path) {
|
|
201
|
-
try {
|
|
202
|
-
return await readFile(full(path), "utf8");
|
|
203
|
-
} catch (error) {
|
|
204
|
-
if (isNotFound(error)) return void 0;
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
async writeFile(path, content) {
|
|
209
|
-
const file = full(path);
|
|
210
|
-
await mkdir(dirname(file), { recursive: true });
|
|
211
|
-
await writeFile(file, content, "utf8");
|
|
212
|
-
},
|
|
213
|
-
async deleteFile(path) {
|
|
214
|
-
await rm(full(path), { force: true });
|
|
215
|
-
},
|
|
216
|
-
async glob(patterns, globOptions = {}) {
|
|
217
|
-
const paths = (await glob([...patterns], {
|
|
218
|
-
cwd: absoluteRoot,
|
|
219
|
-
ignore: [...globOptions.ignore ?? []],
|
|
220
|
-
dot: false,
|
|
221
|
-
followSymbolicLinks: false
|
|
222
|
-
})).map(toPosix);
|
|
223
|
-
if (!globOptions.gitignore) return paths.sort();
|
|
224
|
-
gitignore ??= loadGitignore(absoluteRoot);
|
|
225
|
-
const ignored = await gitignore;
|
|
226
|
-
return paths.filter((path) => !ignored(path)).sort();
|
|
227
|
-
},
|
|
228
|
-
watch(_patterns, onChange) {
|
|
229
|
-
let pending = [];
|
|
230
|
-
let timer;
|
|
231
|
-
const push = (type) => (file) => {
|
|
232
|
-
const path = toPosix(relative(absoluteRoot, file));
|
|
233
|
-
if (path.startsWith("..") || path.includes("node_modules/") || path.startsWith(".git/")) return;
|
|
234
|
-
pending.push({
|
|
235
|
-
type,
|
|
236
|
-
path
|
|
237
|
-
});
|
|
238
|
-
clearTimeout(timer);
|
|
239
|
-
timer = setTimeout(() => {
|
|
240
|
-
const events = pending;
|
|
241
|
-
pending = [];
|
|
242
|
-
onChange(events);
|
|
243
|
-
}, 50);
|
|
244
|
-
};
|
|
245
|
-
const watcher = watch(absoluteRoot, {
|
|
246
|
-
ignoreInitial: true,
|
|
247
|
-
ignored: (path) => /[/\\](?:node_modules|\.git)(?:[/\\]|$)/.test(path)
|
|
248
|
-
});
|
|
249
|
-
watcher.on("add", push("create")).on("change", push("change")).on("unlink", push("delete"));
|
|
250
|
-
return { dispose: () => {
|
|
251
|
-
clearTimeout(timer);
|
|
252
|
-
watcher.close();
|
|
253
|
-
} };
|
|
254
|
-
},
|
|
255
|
-
flush: async () => cache?.flush()
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
//#endregion
|
|
259
|
-
//#region src/load.ts
|
|
260
|
-
async function projectSpecs(rootConfigFile, cwd) {
|
|
261
|
-
const configDir = rootConfigFile ? dirname(rootConfigFile) : resolve(cwd);
|
|
262
|
-
const rootConfig = rootConfigFile ? await loadConfigFile(rootConfigFile) : {};
|
|
263
|
-
const root = resolve(configDir, rootConfig.root ?? ".");
|
|
264
|
-
if (!rootConfig.projects?.length) return {
|
|
265
|
-
root,
|
|
266
|
-
specs: [{
|
|
267
|
-
root,
|
|
268
|
-
config: rootConfig,
|
|
269
|
-
parent: void 0,
|
|
270
|
-
configFile: rootConfigFile
|
|
271
|
-
}]
|
|
272
|
-
};
|
|
273
|
-
const { projects, ...parent } = rootConfig;
|
|
274
|
-
const dirs = await glob([...projects], {
|
|
275
|
-
cwd: root,
|
|
276
|
-
onlyDirectories: true,
|
|
277
|
-
ignore: ["**/node_modules/**"]
|
|
278
|
-
});
|
|
279
|
-
const specs = [];
|
|
280
|
-
for (const dir of dirs.sort()) {
|
|
281
|
-
const projectRoot = resolve(root, dir);
|
|
282
|
-
const file = findConfigFile(projectRoot);
|
|
283
|
-
const config = file ? await loadConfigFile(file) : {};
|
|
284
|
-
specs.push({
|
|
285
|
-
root: resolve(projectRoot, config.root ?? "."),
|
|
286
|
-
config,
|
|
287
|
-
parent,
|
|
288
|
-
configFile: file
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
return {
|
|
292
|
-
root,
|
|
293
|
-
specs
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
/** Finds the config, resolves every project and waits until each is indexed. */
|
|
297
|
-
async function loadWorkspace(options) {
|
|
298
|
-
const configFile = options.configFile ? resolve(options.cwd, options.configFile) : options.upward === false ? findConfigFile(resolve(options.cwd)) : findConfigUpward(options.cwd);
|
|
299
|
-
const { root, specs } = await projectSpecs(configFile, options.cwd);
|
|
300
|
-
const builtins = options.builtins ?? createBuiltins();
|
|
301
|
-
const gitRoot = findGitRoot(root);
|
|
302
|
-
const loaded = [];
|
|
303
|
-
for (const spec of specs) {
|
|
304
|
-
const name = spec.config.name ?? (spec.root === root ? relative(dirname(root), root) : relative(root, spec.root)).replace(/\\/g, "/");
|
|
305
|
-
if (options.project !== void 0 && options.project !== name) continue;
|
|
306
|
-
const host = createNodeHost(spec.root, {
|
|
307
|
-
cacheDir: options.cache === false ? false : void 0,
|
|
308
|
-
logger: options.logger
|
|
309
|
-
});
|
|
310
|
-
const projectHost = options.wrapHost?.(host) ?? host;
|
|
311
|
-
const setup = await resolveConfig({
|
|
312
|
-
host: projectHost,
|
|
313
|
-
config: spec.config,
|
|
314
|
-
parent: spec.parent,
|
|
315
|
-
builtins,
|
|
316
|
-
name,
|
|
317
|
-
runtime: "node",
|
|
318
|
-
loadPlugin: createPluginLoader(spec.configFile ? dirname(spec.configFile) : spec.root),
|
|
319
|
-
manifestRoots: manifestRootsOf(spec.root, gitRoot)
|
|
320
|
-
});
|
|
321
|
-
const project = createProject({
|
|
322
|
-
host: projectHost,
|
|
323
|
-
setup
|
|
324
|
-
});
|
|
325
|
-
await project.ready;
|
|
326
|
-
await host.flush();
|
|
327
|
-
loaded.push({
|
|
328
|
-
name,
|
|
329
|
-
root: spec.root,
|
|
330
|
-
configFile: spec.configFile,
|
|
331
|
-
host,
|
|
332
|
-
project
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
if (options.project !== void 0 && loaded.length === 0) throw new X18nError("X18N0003", options.project);
|
|
336
|
-
return {
|
|
337
|
-
root,
|
|
338
|
-
configFile,
|
|
339
|
-
projects: loaded
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
//#endregion
|
|
343
|
-
//#region src/migrate.ts
|
|
344
|
-
const frameworkPresets = {
|
|
345
|
-
"vue": "vue-i18n",
|
|
346
|
-
"vue-sfc": "vue-i18n",
|
|
347
|
-
"vuex": "vue-i18n",
|
|
348
|
-
"nuxt": "nuxt-i18n",
|
|
349
|
-
"react": "react-intl",
|
|
350
|
-
"react-i18next": "react-i18next",
|
|
351
|
-
"next-i18next": "react-i18next",
|
|
352
|
-
"i18next": "i18next",
|
|
353
|
-
"i18next-shopify": "i18next-shopify",
|
|
354
|
-
"next-intl": "next-intl",
|
|
355
|
-
"next-international": "next-international",
|
|
356
|
-
"next-translate": "next-translate",
|
|
357
|
-
"svelte": "svelte-i18n",
|
|
358
|
-
"vscode": "vscode-extension",
|
|
359
|
-
"ngx-translate": "ngx-translate",
|
|
360
|
-
"transloco": "transloco",
|
|
361
|
-
"flutter": "flutter",
|
|
362
|
-
"ember": "ember-intl",
|
|
363
|
-
"i18n-tag": "i18n-tag",
|
|
364
|
-
"fluent-vue": "fluent-vue",
|
|
365
|
-
"fluent-vue-sfc": "fluent-vue",
|
|
366
|
-
"php-joomla": "php-joomla",
|
|
367
|
-
"php-gettext": "php-gettext",
|
|
368
|
-
"laravel": "laravel",
|
|
369
|
-
"chrome-ext": "chrome-extension",
|
|
370
|
-
"ruby-rails": "ruby-rails",
|
|
371
|
-
"polyglot": "polyglot",
|
|
372
|
-
"globalize": "globalize",
|
|
373
|
-
"ui5": "ui5",
|
|
374
|
-
"lingui": "lingui",
|
|
375
|
-
"jekyll": "jekyll",
|
|
376
|
-
"i18n-js": "i18n-js"
|
|
377
|
-
};
|
|
378
|
-
const languageExtensions = {
|
|
379
|
-
javascript: [
|
|
380
|
-
"js",
|
|
381
|
-
"mjs",
|
|
382
|
-
"cjs"
|
|
383
|
-
],
|
|
384
|
-
typescript: [
|
|
385
|
-
"ts",
|
|
386
|
-
"mts",
|
|
387
|
-
"cts"
|
|
388
|
-
],
|
|
389
|
-
javascriptreact: ["jsx"],
|
|
390
|
-
typescriptreact: ["tsx"],
|
|
391
|
-
vue: ["vue"],
|
|
392
|
-
html: ["html"],
|
|
393
|
-
svelte: ["svelte"],
|
|
394
|
-
php: ["php"],
|
|
395
|
-
blade: ["blade.php"],
|
|
396
|
-
ruby: ["rb"],
|
|
397
|
-
erb: ["erb"],
|
|
398
|
-
dart: ["dart"],
|
|
399
|
-
json: ["json"],
|
|
400
|
-
yaml: ["yml", "yaml"],
|
|
401
|
-
handlebars: ["hbs"]
|
|
402
|
-
};
|
|
403
|
-
const keygenStyles = {
|
|
404
|
-
"camelCase": "camel",
|
|
405
|
-
"snake_case": "snake",
|
|
406
|
-
"kebab-case": "kebab"
|
|
407
|
-
};
|
|
408
|
-
const editorSettings = {
|
|
409
|
-
annotations: "x18n.annotations",
|
|
410
|
-
annotationInPlace: "x18n.inPlace",
|
|
411
|
-
annotationMaxLength: "x18n.annotationMaxLength",
|
|
412
|
-
displayLanguage: "x18n.displayLocale"
|
|
413
|
-
};
|
|
414
|
-
const handledByDefaults = [
|
|
415
|
-
"readonly",
|
|
416
|
-
"encoding",
|
|
417
|
-
"fullReloadOnChanged",
|
|
418
|
-
"includeSubfolders",
|
|
419
|
-
"ignoreFiles",
|
|
420
|
-
"editor.preferEditor",
|
|
421
|
-
"showFlags",
|
|
422
|
-
"annotationDelimiter",
|
|
423
|
-
"languageTagSystem",
|
|
424
|
-
"targetPickingStrategy",
|
|
425
|
-
"extract.targetPickingStrategy"
|
|
426
|
-
];
|
|
427
|
-
async function readText(path) {
|
|
428
|
-
return readFile(path, "utf8").catch(() => void 0);
|
|
429
|
-
}
|
|
430
|
-
function isRecord(value) {
|
|
431
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
432
|
-
}
|
|
433
|
-
const asStrings = (value) => (Array.isArray(value) ? value : value === void 0 ? [] : [value]).filter((v) => typeof v === "string");
|
|
434
|
-
/** `i18n-ally.*` and legacy `vue-i18n-ally.*` keys from `.vscode/settings.json` and `*.code-workspace` files. */
|
|
435
|
-
async function readSettings(root) {
|
|
436
|
-
const settings = /* @__PURE__ */ new Map();
|
|
437
|
-
const keys = [];
|
|
438
|
-
const sources = [];
|
|
439
|
-
const vscode = await readText(join(root, ".vscode/settings.json"));
|
|
440
|
-
if (vscode !== void 0) sources.push(parse$1(vscode));
|
|
441
|
-
for (const file of await readdir(root).catch(() => [])) if (file.endsWith(".code-workspace")) {
|
|
442
|
-
const data = parse$1(await readText(join(root, file)) ?? "{}");
|
|
443
|
-
if (isRecord(data)) sources.push(data.settings);
|
|
444
|
-
}
|
|
445
|
-
for (const source of sources) {
|
|
446
|
-
if (!isRecord(source)) continue;
|
|
447
|
-
for (const [key, value] of Object.entries(source)) {
|
|
448
|
-
const match = /^(?:vue-)?i18n-ally\.(.+)$/.exec(key);
|
|
449
|
-
if (match && !settings.has(match[1])) {
|
|
450
|
-
settings.set(match[1], value);
|
|
451
|
-
keys.push(key);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
settings,
|
|
457
|
-
keys
|
|
458
|
-
};
|
|
459
|
-
}
|
|
460
|
-
/** `localesPaths` + `pathMatcher` + `dirStructure` + `namespace` as resource patterns with the same tokens. */
|
|
461
|
-
function resourcesOf(settings, executed) {
|
|
462
|
-
const paths = asStrings(settings.get("localesPaths")).map((path) => path.replace(/^\.\//, "").replace(/\/$/, ""));
|
|
463
|
-
const matcher = settings.get("pathMatcher");
|
|
464
|
-
const structure = settings.get("dirStructure");
|
|
465
|
-
const namespace = settings.get("namespace") === true;
|
|
466
|
-
return paths.flatMap((dir) => {
|
|
467
|
-
if (typeof matcher === "string") return [`${dir}/${matcher}`];
|
|
468
|
-
if (structure === "file") return [`${dir}/{locale}.{ext}`];
|
|
469
|
-
if (structure === "dir") return [namespace ? `${dir}/{locale}/{namespaces}.{ext}` : `${dir}/{locale}/**/*.{ext}`];
|
|
470
|
-
return [`${dir}/{locale}.{ext}`, namespace ? `${dir}/{locale}/{namespaces}.{ext}` : `${dir}/{locale}/**/*.{ext}`];
|
|
471
|
-
}).map((pattern) => executed && /\.(?:[jt]s|\{ext\})$/.test(pattern) ? {
|
|
472
|
-
pattern,
|
|
473
|
-
execute: true
|
|
474
|
-
} : { pattern });
|
|
475
|
-
}
|
|
476
|
-
function templatesOf(value) {
|
|
477
|
-
return (Array.isArray(value) ? value : []).flatMap((entry) => {
|
|
478
|
-
if (typeof entry === "string") return [{ template: entry.replace(/\$1/g, "{key}") }];
|
|
479
|
-
if (!isRecord(entry)) return [];
|
|
480
|
-
const files = asStrings(entry.include);
|
|
481
|
-
return asStrings(entry.templates ?? entry.template).map((template) => ({
|
|
482
|
-
template: template.replace(/\$1/g, "{key}"),
|
|
483
|
-
...files.length ? { files } : {}
|
|
484
|
-
}));
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
function flagOf(country) {
|
|
488
|
-
return String.fromCodePoint(...[...country.toUpperCase()].map((c) => 127462 + c.charCodeAt(0) - 65));
|
|
489
|
-
}
|
|
490
|
-
/** `.vscode/i18n-ally-custom-framework.yml` into YAML plugin parts. */
|
|
491
|
-
function customFramework(data, parts) {
|
|
492
|
-
if (!isRecord(data)) return;
|
|
493
|
-
const extensions = asStrings(data.languageIds).flatMap((id) => languageExtensions[id] ?? [id]);
|
|
494
|
-
const patterns = asStrings(data.usageMatchRegex);
|
|
495
|
-
if (patterns.length) parts.regex.push({
|
|
496
|
-
extensions: extensions.length ? extensions : [
|
|
497
|
-
"js",
|
|
498
|
-
"jsx",
|
|
499
|
-
"ts",
|
|
500
|
-
"tsx",
|
|
501
|
-
"vue"
|
|
502
|
-
],
|
|
503
|
-
patterns
|
|
504
|
-
});
|
|
505
|
-
const scope = asStrings(data.scopeRangeRegex)[0];
|
|
506
|
-
if (scope) parts.scopes = scope;
|
|
507
|
-
parts.templates.push(...templatesOf(data.refactorTemplates));
|
|
508
|
-
if (data.monopoly === true) parts.exclusive = true;
|
|
509
|
-
}
|
|
510
|
-
function configFile(config, format) {
|
|
511
|
-
if (format === "yml") return {
|
|
512
|
-
path: "x18n.config.yml",
|
|
513
|
-
content: `# yaml-language-server: $schema=https://x18n.antfu.dev/schema/config.json\n${stringify(config)}`
|
|
514
|
-
};
|
|
515
|
-
if (format === "json") return {
|
|
516
|
-
path: "x18n.config.json",
|
|
517
|
-
content: `${JSON.stringify({
|
|
518
|
-
$schema: "https://x18n.antfu.dev/schema/config.json",
|
|
519
|
-
...config
|
|
520
|
-
}, null, 2)}\n`
|
|
521
|
-
};
|
|
522
|
-
return {
|
|
523
|
-
path: "x18n.config.ts",
|
|
524
|
-
content: `import { defineConfig } from 'x18n'\n\n// Migrated from i18n-ally by \`x18n migrate i18n-ally\`.\nexport default defineConfig(${JSON.stringify(config, null, 2).replace(/"([a-z_$][\w$]*)":/gi, "$1:").replace(/'/g, "\\'").replace(/"/g, "'")})\n`
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
function readerOf(settings) {
|
|
528
|
-
const get = (key) => settings.get(key);
|
|
529
|
-
return {
|
|
530
|
-
get,
|
|
531
|
-
str: (key) => {
|
|
532
|
-
const value = get(key);
|
|
533
|
-
return typeof value === "string" ? value : void 0;
|
|
534
|
-
},
|
|
535
|
-
num: (key) => {
|
|
536
|
-
const value = get(key);
|
|
537
|
-
return typeof value === "number" ? value : void 0;
|
|
538
|
-
},
|
|
539
|
-
list: (key) => asStrings(get(key)),
|
|
540
|
-
pick: (key, values) => values.find((value) => value === get(key))
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
function formatsOf(r) {
|
|
544
|
-
const formats = {};
|
|
545
|
-
for (const parser of r.list("enabledParsers")) formats[parser === "yaml" ? "yml" : parser] = parser;
|
|
546
|
-
const extend = r.get("parsers.extendFileExtensions");
|
|
547
|
-
if (isRecord(extend)) {
|
|
548
|
-
for (const [ext, parser] of Object.entries(extend)) if (typeof parser === "string") formats[ext.replace(/^\./, "")] = parser;
|
|
549
|
-
}
|
|
550
|
-
return formats;
|
|
551
|
-
}
|
|
552
|
-
function extractOf(r) {
|
|
553
|
-
const keygen = r.pick("extract.keygenStrategy", [
|
|
554
|
-
"slug",
|
|
555
|
-
"random",
|
|
556
|
-
"empty",
|
|
557
|
-
"source"
|
|
558
|
-
]);
|
|
559
|
-
const keyCase = keygenStyles[r.str("extract.keygenStyle") ?? ""];
|
|
560
|
-
const byFiles = r.get("extract.ignoredByFiles");
|
|
561
|
-
return {
|
|
562
|
-
...keygen ? { keygen } : {},
|
|
563
|
-
...keyCase ? { keyCase } : {},
|
|
564
|
-
...r.str("extract.keyPrefix") ? { keyPrefix: r.str("extract.keyPrefix") } : {},
|
|
565
|
-
...r.num("extract.keyMaxLength") !== void 0 ? { keyMaxLength: r.num("extract.keyMaxLength") } : {},
|
|
566
|
-
...r.list("extract.ignored").length ? { ignore: r.list("extract.ignored") } : {},
|
|
567
|
-
...isRecord(byFiles) ? { ignoreByFile: Object.fromEntries(Object.entries(byFiles).map(([glob, list]) => [glob, asStrings(list)])) } : {},
|
|
568
|
-
...r.get("extract.autoDetect") === true ? { autoDetect: true } : {}
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
function writeOf(r) {
|
|
572
|
-
const sort = r.get("sortKeys") === true ? r.get("sortCompare") === "locale" ? "locale" : "binary" : void 0;
|
|
573
|
-
const indent = r.get("tabStyle") === "tab" ? "tab" : r.num("indent");
|
|
574
|
-
const keepFilled = r.get("keepFulfilled") === true;
|
|
575
|
-
if (!sort && indent === void 0 && !keepFilled) return void 0;
|
|
576
|
-
return {
|
|
577
|
-
...sort ? { sort } : {},
|
|
578
|
-
...indent !== void 0 ? { indent } : {},
|
|
579
|
-
...keepFilled ? { keepFilled } : {}
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
function presetsOf(r, notes) {
|
|
583
|
-
return r.list("enabledFrameworks").flatMap((id) => {
|
|
584
|
-
if (id === "general" || id === "custom") return [];
|
|
585
|
-
const preset = frameworkPresets[id];
|
|
586
|
-
if (!preset) notes.push(`Framework "${id}" has no built-in preset yet; it was left out.`);
|
|
587
|
-
return preset ? [preset] : [];
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
function notesOf(settings, notes) {
|
|
591
|
-
if ([...settings.keys()].some((key) => key.startsWith("translate."))) notes.push("`translate.*` settings were dropped: x18n writes translate prompts instead (`x18n prompt translate --to <locale>`).");
|
|
592
|
-
if ([...settings.keys()].some((key) => key.startsWith("review."))) notes.push("Review settings move to `review.*` once reviews ship (1.x); they were not migrated yet.");
|
|
593
|
-
for (const key of handledByDefaults) if (settings.has(key)) notes.push(`\`i18n-ally.${key}\` is handled by defaults or dropped.`);
|
|
594
|
-
}
|
|
595
|
-
function mapConfig(settings, parts, notes) {
|
|
596
|
-
const r = readerOf(settings);
|
|
597
|
-
const presets = presetsOf(r, notes);
|
|
598
|
-
const formats = formatsOf(r);
|
|
599
|
-
const executed = r.get("parsers.typescript.compilerOptions") !== void 0 || r.get("parsers.typescript.tsNodePath") !== void 0;
|
|
600
|
-
if (executed) notes.push("JS/TS locale files were executed by i18n-ally; their patterns get `execute: true` and become read-only.");
|
|
601
|
-
const keyStyle = r.get("disablePathParsing") === true ? "flat" : r.pick("keystyle", ["flat", "nested"]);
|
|
602
|
-
const usage = {
|
|
603
|
-
...r.list("keysInUse").length ? { keysInUse: r.list("keysInUse") } : {},
|
|
604
|
-
...r.list("usage.derivedKeyRules").length ? { derivedKeys: r.list("usage.derivedKeyRules") } : {},
|
|
605
|
-
...r.list("usage.scanningIgnore").length ? { exclude: r.list("usage.scanningIgnore") } : {}
|
|
606
|
-
};
|
|
607
|
-
const extract = extractOf(r);
|
|
608
|
-
const write = writeOf(r);
|
|
609
|
-
parts.templates.push(...templatesOf(r.get("refactor.templates")));
|
|
610
|
-
const regexPatterns = [...r.list("regex.usageMatch"), ...r.list("regex.usageMatchAppend")];
|
|
611
|
-
if (regexPatterns.length) parts.regex.push({
|
|
612
|
-
extensions: [
|
|
613
|
-
"js",
|
|
614
|
-
"jsx",
|
|
615
|
-
"ts",
|
|
616
|
-
"tsx",
|
|
617
|
-
"vue",
|
|
618
|
-
"html",
|
|
619
|
-
"svelte"
|
|
620
|
-
],
|
|
621
|
-
patterns: regexPatterns
|
|
622
|
-
});
|
|
623
|
-
const countries = r.get("localeCountryMap");
|
|
624
|
-
if (isRecord(countries)) {
|
|
625
|
-
for (const [locale, country] of Object.entries(countries)) if (typeof country === "string" && /^[a-z]{2}$/i.test(country)) parts.localeInfo[locale] = { flag: flagOf(country) };
|
|
626
|
-
}
|
|
627
|
-
notesOf(settings, notes);
|
|
628
|
-
const resources = resourcesOf(settings, executed);
|
|
629
|
-
return {
|
|
630
|
-
...r.str("sourceLanguage") ? { sourceLocale: r.str("sourceLanguage") } : {},
|
|
631
|
-
...r.list("ignoredLocales").length ? { ignoreLocales: r.list("ignoredLocales") } : {},
|
|
632
|
-
...presets.length || parts.exclusive ? { presets: parts.exclusive ? [] : presets } : {},
|
|
633
|
-
...resources.length ? { resources } : {},
|
|
634
|
-
...Object.keys(formats).length ? { formats } : {},
|
|
635
|
-
...keyStyle ? { keyStyle } : {},
|
|
636
|
-
...r.str("defaultNamespace") ? { namespace: { default: r.str("defaultNamespace") } } : {},
|
|
637
|
-
...Object.keys(usage).length ? { usage } : {},
|
|
638
|
-
...write ? { write } : {},
|
|
639
|
-
...Object.keys(extract).length ? { extract } : {}
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
/** Converts i18n-ally settings, custom framework and reviews into x18n files. Never deletes i18n-ally files. */
|
|
643
|
-
async function migrateI18nAlly(root, options = {}) {
|
|
644
|
-
const { settings, keys } = await readSettings(root);
|
|
645
|
-
const customText = await readText(join(root, ".vscode/i18n-ally-custom-framework.yml"));
|
|
646
|
-
const reviews = await readText(join(root, ".vscode/i18n-ally-reviews.yml"));
|
|
647
|
-
const notes = [];
|
|
648
|
-
if (keys.length === 0 && customText === void 0) return {
|
|
649
|
-
files: [],
|
|
650
|
-
notes: ["No i18n-ally settings found."],
|
|
651
|
-
removable: []
|
|
652
|
-
};
|
|
653
|
-
const parts = {
|
|
654
|
-
regex: [],
|
|
655
|
-
scopes: void 0,
|
|
656
|
-
templates: [],
|
|
657
|
-
localeInfo: {},
|
|
658
|
-
exclusive: false
|
|
659
|
-
};
|
|
660
|
-
if (customText !== void 0) customFramework(parse(customText), parts);
|
|
661
|
-
const config = mapConfig(settings, parts, notes);
|
|
662
|
-
const files = [];
|
|
663
|
-
const plugin = {
|
|
664
|
-
$schema: "../node_modules/@x18n/core/schema/plugin.json",
|
|
665
|
-
name: "custom",
|
|
666
|
-
...parts.regex.length ? { scanners: { regex: parts.regex } } : {},
|
|
667
|
-
...parts.scopes ? { scopes: { regex: parts.scopes } } : {},
|
|
668
|
-
...parts.templates.length ? { extract: { templates: parts.templates } } : {},
|
|
669
|
-
...Object.keys(parts.localeInfo).length ? { localeInfo: parts.localeInfo } : {}
|
|
670
|
-
};
|
|
671
|
-
const needsPlugin = parts.regex.length > 0 || parts.scopes !== void 0 || parts.templates.length > 0 || Object.keys(parts.localeInfo).length > 0;
|
|
672
|
-
const finalConfig = needsPlugin ? {
|
|
673
|
-
...config,
|
|
674
|
-
plugins: ["./x18n/custom.x18n-plugin.yml"]
|
|
675
|
-
} : config;
|
|
676
|
-
files.push(configFile(finalConfig, options.format ?? "ts"));
|
|
677
|
-
if (needsPlugin) {
|
|
678
|
-
const { $schema, ...rest } = plugin;
|
|
679
|
-
files.push({
|
|
680
|
-
path: "x18n/custom.x18n-plugin.yml",
|
|
681
|
-
content: `# yaml-language-server: $schema=${$schema}\n${stringify(rest)}`
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
let vscodeText = await readText(join(root, ".vscode/settings.json"));
|
|
685
|
-
if (vscodeText !== void 0) {
|
|
686
|
-
let changed = false;
|
|
687
|
-
for (const [from, to] of Object.entries(editorSettings)) if (settings.has(from)) {
|
|
688
|
-
vscodeText = applyEdits(vscodeText, modify(vscodeText, [to], settings.get(from), { formattingOptions: {
|
|
689
|
-
insertSpaces: true,
|
|
690
|
-
tabSize: 2
|
|
691
|
-
} }));
|
|
692
|
-
changed = true;
|
|
693
|
-
}
|
|
694
|
-
if (changed) files.push({
|
|
695
|
-
path: ".vscode/settings.json",
|
|
696
|
-
content: vscodeText
|
|
697
|
-
});
|
|
698
|
-
}
|
|
699
|
-
if (reviews !== void 0 && !existsSync(join(root, ".x18n/reviews.yml"))) {
|
|
700
|
-
files.push({
|
|
701
|
-
path: ".x18n/reviews.yml",
|
|
702
|
-
content: reviews
|
|
703
|
-
});
|
|
704
|
-
notes.push("Reviews were copied to `.x18n/reviews.yml`; x18n reads them once reviews ship (1.x).");
|
|
705
|
-
}
|
|
706
|
-
if (parts.exclusive) notes.push("The custom framework was exclusive (`monopoly`), so built-in presets are turned off (`presets: []`).");
|
|
707
|
-
return {
|
|
708
|
-
files,
|
|
709
|
-
notes,
|
|
710
|
-
removable: [...keys, ...customText !== void 0 ? [".vscode/i18n-ally-custom-framework.yml"] : []]
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
//#endregion
|
|
714
|
-
export { configFileNames, createNodeHost, createPluginLoader, findConfigFile, findConfigUpward, findGitRoot, importModule, loadConfigFile, loadWorkspace, loadYamlPlugin, migrateI18nAlly, stderrLogger };
|