@worldware/msg-cli 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.
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const core_1 = require("@oclif/core");
13
+ const child_process_1 = require("child_process");
14
+ const fs_1 = require("fs");
15
+ const readline_1 = require("readline");
16
+ const path_1 = require("path");
17
+ const init_helpers_js_1 = require("../lib/init-helpers.js");
18
+ /**
19
+ * Scaffolds a msg project: directories, package.json entries, and dependencies.
20
+ */
21
+ class Init extends core_1.Command {
22
+ run() {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ var _a, _b, _c;
25
+ const { flags } = yield this.parse(Init);
26
+ const cwd = process.cwd();
27
+ const pkgPath = (0, init_helpers_js_1.findPackageJsonPath)(cwd);
28
+ if (!pkgPath) {
29
+ this.error("package.json not found. Run this command from the project root.", {
30
+ exit: 1,
31
+ });
32
+ }
33
+ let i18nDir = (_a = flags.i18nDir) !== null && _a !== void 0 ? _a : init_helpers_js_1.DEFAULT_I18N_DIR;
34
+ let l10nDir = (_b = flags.l10nDir) !== null && _b !== void 0 ? _b : init_helpers_js_1.DEFAULT_L10N_DIR;
35
+ if (flags.interactive) {
36
+ const rl = (0, readline_1.createInterface)({ input: process.stdin, output: process.stdout });
37
+ const ask = (question, defaultVal) => new Promise((resolve) => {
38
+ rl.question(`${question} [${defaultVal}]: `, (answer) => {
39
+ resolve(((answer === null || answer === void 0 ? void 0 : answer.trim()) || defaultVal).trim() || defaultVal);
40
+ });
41
+ });
42
+ if (!flags.i18nDir) {
43
+ i18nDir = yield ask("i18n directory path (relative to project root):", init_helpers_js_1.DEFAULT_I18N_DIR);
44
+ }
45
+ if (!flags.l10nDir) {
46
+ l10nDir = yield ask("l10n directory path (relative to project root):", init_helpers_js_1.DEFAULT_L10N_DIR);
47
+ }
48
+ rl.close();
49
+ }
50
+ const rootDir = (0, path_1.join)(cwd);
51
+ const validation = (0, init_helpers_js_1.validatePaths)(rootDir, i18nDir, l10nDir);
52
+ if (!validation.valid) {
53
+ this.error((_c = validation.error) !== null && _c !== void 0 ? _c : "Invalid paths", { exit: 1 });
54
+ }
55
+ let pkg;
56
+ try {
57
+ pkg = (0, init_helpers_js_1.readPackageJson)(pkgPath);
58
+ }
59
+ catch (err) {
60
+ const msg = err instanceof Error ? err.message : "Invalid or unreadable package.json";
61
+ this.error(msg, { exit: 1 });
62
+ }
63
+ const alreadyInit = (0, init_helpers_js_1.isAlreadyInitialized)(pkg, rootDir, i18nDir, l10nDir);
64
+ if (alreadyInit && !flags.force) {
65
+ this.warn("Project appears already initialized for msg. Use -f or --force to re-run.");
66
+ return;
67
+ }
68
+ this.log("Creating i18n and l10n directories...");
69
+ try {
70
+ (0, init_helpers_js_1.ensureDirectoriesWithGitkeep)(rootDir, i18nDir, l10nDir, flags.force);
71
+ }
72
+ catch (err) {
73
+ const msg = err instanceof Error ? err.message : "Failed to create directories";
74
+ this.error(msg, { exit: 1 });
75
+ }
76
+ this.log("Updating package.json...");
77
+ pkg = (0, init_helpers_js_1.addDirectoriesToPackageJson)(pkg, i18nDir, l10nDir);
78
+ pkg = (0, init_helpers_js_1.addImportAliasesToPackageJson)(pkg, i18nDir, l10nDir);
79
+ pkg = (0, init_helpers_js_1.addScriptsToPackageJson)(pkg);
80
+ try {
81
+ (0, init_helpers_js_1.writePackageJson)(pkgPath, pkg);
82
+ }
83
+ catch (err) {
84
+ const msg = err instanceof Error ? err.message : "Failed to write package.json";
85
+ this.error(msg, { exit: 1 });
86
+ }
87
+ const tsconfigPath = (0, path_1.join)(rootDir, "tsconfig.json");
88
+ if ((0, fs_1.existsSync)(tsconfigPath)) {
89
+ this.log("Updating tsconfig.json for path aliases...");
90
+ try {
91
+ (0, init_helpers_js_1.addTsconfigPaths)(tsconfigPath, i18nDir, l10nDir);
92
+ }
93
+ catch (err) {
94
+ this.warn(err instanceof Error ? err.message : "Could not update tsconfig.json; you may add paths manually.");
95
+ }
96
+ }
97
+ this.log("Installing @worldware/msg...");
98
+ const installResult = (0, child_process_1.spawnSync)("npm", ["install", "@worldware/msg@latest", "--save"], { cwd: rootDir, shell: true, stdio: "inherit" });
99
+ if (installResult.status !== 0) {
100
+ this.error("Failed to install @worldware/msg. Check your network and npm registry.", {
101
+ exit: 1,
102
+ });
103
+ }
104
+ this.log("Init complete.");
105
+ });
106
+ }
107
+ }
108
+ Init.description = "Scaffold a msg project (i18n/l10n directories, package.json, and dependencies)";
109
+ Init.examples = [
110
+ "<%= config.bin %> <%= command.id %>",
111
+ "<%= config.bin %> <%= command.id %> --i18nDir lib/i18n --l10nDir data/l10n",
112
+ "<%= config.bin %> <%= command.id %> -f",
113
+ ];
114
+ Init.flags = {
115
+ help: core_1.Flags.help({ char: "h" }),
116
+ interactive: core_1.Flags.boolean({
117
+ char: "i",
118
+ description: "Prompt for i18n and l10n directory paths",
119
+ }),
120
+ force: core_1.Flags.boolean({
121
+ char: "f",
122
+ description: "Force clean install; overwrite existing msg setup",
123
+ }),
124
+ i18nDir: core_1.Flags.string({
125
+ description: "Relative path for the i18n directory",
126
+ }),
127
+ l10nDir: core_1.Flags.string({
128
+ description: "Relative path for the l10n directory",
129
+ }),
130
+ };
131
+ exports.default = Init;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.calculateRelativePath = calculateRelativePath;
46
+ exports.importMsgProjectFile = importMsgProjectFile;
47
+ exports.writeMsgProjectFile = writeMsgProjectFile;
48
+ exports.loadPackageJsonForCreateProject = loadPackageJsonForCreateProject;
49
+ const fs_1 = require("fs");
50
+ const path_1 = require("path");
51
+ const url_1 = require("url");
52
+ const init_helpers_js_1 = require("./init-helpers.js");
53
+ /**
54
+ * Calculates the relative path from the i18n projects directory to the l10n translations directory.
55
+ * @param projectsDir - Absolute path to i18n/projects (e.g. root/i18n/projects)
56
+ * @param translationsDir - Absolute path to l10n/translations (e.g. root/l10n/translations)
57
+ * @returns Relative path string from projects to translations, suitable for import()
58
+ */
59
+ function calculateRelativePath(projectsDir, translationsDir) {
60
+ const rel = (0, path_1.relative)(projectsDir, translationsDir);
61
+ return rel.startsWith(".") ? rel : `./${rel}`;
62
+ }
63
+ /**
64
+ * Imports an existing MsgProject module from the projects directory.
65
+ * @param projectsDir - Absolute path to i18n/projects
66
+ * @param projectName - Name of the project (file name without extension)
67
+ * @returns The default export (MsgProject instance or data) or undefined if not found
68
+ */
69
+ function importMsgProjectFile(projectsDir, projectName) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ var _a;
72
+ const basePath = (0, path_1.join)(projectsDir, projectName);
73
+ const exts = [".ts", ".js"];
74
+ for (const ext of exts) {
75
+ const p = `${basePath}${ext}`;
76
+ if ((0, fs_1.existsSync)(p)) {
77
+ try {
78
+ const url = (0, url_1.pathToFileURL)(p).href;
79
+ const mod = yield Promise.resolve(`${url}`).then(s => __importStar(require(s)));
80
+ return ((_a = mod === null || mod === void 0 ? void 0 : mod.default) !== null && _a !== void 0 ? _a : mod);
81
+ }
82
+ catch (_b) {
83
+ return undefined;
84
+ }
85
+ }
86
+ }
87
+ return undefined;
88
+ });
89
+ }
90
+ /**
91
+ * Writes an MsgProject file to the projects directory.
92
+ * @param filePath - Absolute path of the file to write (including extension)
93
+ * @param content - Full file content string
94
+ */
95
+ function writeMsgProjectFile(filePath, content) {
96
+ const dir = (0, path_1.dirname)(filePath);
97
+ if (!(0, fs_1.existsSync)(dir)) {
98
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
99
+ }
100
+ (0, fs_1.writeFileSync)(filePath, content, "utf-8");
101
+ }
102
+ /**
103
+ * Loads package.json from the given directory (walking up to find it).
104
+ * Requires directories.i18n and directories.l10n.
105
+ * @param cwd - Directory to start from (e.g. process.cwd())
106
+ * @returns Parsed package.json with directories.i18n and directories.l10n
107
+ * @throws Error if package.json not found or missing directories.i18n / directories.l10n
108
+ */
109
+ function loadPackageJsonForCreateProject(cwd) {
110
+ const ctx = (0, init_helpers_js_1.loadPackageJsonForMsg)(cwd, { requireL10n: true });
111
+ return ctx.pkg;
112
+ }
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.readPackageJsonForCreateResource = readPackageJsonForCreateResource;
46
+ exports.importMsgProjectForResource = importMsgProjectForResource;
47
+ exports.generateMsgResourceContent = generateMsgResourceContent;
48
+ exports.writeMsgResourceFile = writeMsgResourceFile;
49
+ const fs_1 = require("fs");
50
+ const path_1 = require("path");
51
+ const url_1 = require("url");
52
+ const init_helpers_js_1 = require("./init-helpers.js");
53
+ /**
54
+ * Reads package.json and returns i18n directory and module type info.
55
+ * @param cwd - Directory to start from (e.g. process.cwd())
56
+ * @returns Object with i18nDir, isEsm, useTypeScript
57
+ * @throws Error if package.json not found or missing directories.i18n
58
+ */
59
+ function readPackageJsonForCreateResource(cwd) {
60
+ const ctx = (0, init_helpers_js_1.loadPackageJsonForMsg)(cwd);
61
+ return {
62
+ i18nDir: ctx.i18nDir,
63
+ isEsm: ctx.isEsm,
64
+ useTypeScript: ctx.useTypeScript,
65
+ };
66
+ }
67
+ /**
68
+ * Derives dir (ltr/rtl) from a locale string based on language subtag.
69
+ * @param sourceLocale - Full locale (e.g. "en", "ar-SA", "he-IL")
70
+ * @returns "rtl" for ar/he, "ltr" otherwise
71
+ */
72
+ function dirFromSourceLocale(sourceLocale) {
73
+ var _a, _b;
74
+ const lang = (_b = (_a = sourceLocale.split("-")[0]) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : "";
75
+ return lang === "ar" || lang === "he" ? "rtl" : "ltr";
76
+ }
77
+ /**
78
+ * Imports the MsgProject module and returns sourceLocale and dir (ltr/rtl).
79
+ * @param projectsDir - Absolute path to i18n/projects
80
+ * @param projectName - Name of the project file (without extension)
81
+ * @returns Object with sourceLocale and dir, or undefined if project not found
82
+ */
83
+ function importMsgProjectForResource(projectsDir, projectName) {
84
+ return __awaiter(this, void 0, void 0, function* () {
85
+ var _a, _b;
86
+ const basePath = (0, path_1.join)(projectsDir, projectName);
87
+ const exts = [".ts", ".js"];
88
+ for (const ext of exts) {
89
+ const p = `${basePath}${ext}`;
90
+ if ((0, fs_1.existsSync)(p)) {
91
+ try {
92
+ const url = (0, url_1.pathToFileURL)(p).href;
93
+ const mod = yield Promise.resolve(`${url}`).then(s => __importStar(require(s)));
94
+ const data = ((_a = mod === null || mod === void 0 ? void 0 : mod.default) !== null && _a !== void 0 ? _a : mod);
95
+ const sourceLocale = (_b = data === null || data === void 0 ? void 0 : data.locales) === null || _b === void 0 ? void 0 : _b.sourceLocale;
96
+ if (!sourceLocale || typeof sourceLocale !== "string") {
97
+ return undefined;
98
+ }
99
+ return {
100
+ sourceLocale,
101
+ dir: dirFromSourceLocale(sourceLocale),
102
+ };
103
+ }
104
+ catch (_c) {
105
+ return undefined;
106
+ }
107
+ }
108
+ }
109
+ return undefined;
110
+ });
111
+ }
112
+ /**
113
+ * Generates the MsgResource file content as a string.
114
+ * @param params - Title, projectName, sourceLocale, dir, and isEsm
115
+ * @returns The generated file content
116
+ */
117
+ function generateMsgResourceContent(params) {
118
+ const { title, projectName, sourceLocale, dir, isEsm } = params;
119
+ const projectImport = `../projects/${projectName}`;
120
+ const messagesBlock = ` messages: [
121
+ {
122
+ key: 'example.message',
123
+ value: 'Example message.',
124
+ notes: [
125
+ { type: 'description', content: 'This is an example message. You can delete it.' }
126
+ ]
127
+ }
128
+ ]`;
129
+ const titleStr = `'${title.replace(/'/g, "\\'")}'`;
130
+ const langStr = `'${sourceLocale.replace(/'/g, "\\'")}'`;
131
+ const dirStr = `'${dir}'`;
132
+ if (isEsm) {
133
+ return `import { MsgResource } from '@worldware/msg';
134
+ import project from '${projectImport}';
135
+
136
+ export default MsgResource.create({
137
+ title: ${titleStr},
138
+ attributes: {
139
+ lang: ${langStr},
140
+ dir: ${dirStr}
141
+ },
142
+ notes: [
143
+ { type: 'DESCRIPTION', content: 'This is a generated file. Replace this description with your own.' }
144
+ ],
145
+ ${messagesBlock}
146
+ }, project);
147
+ `;
148
+ }
149
+ return `const { MsgResource } = require('@worldware/msg');
150
+ const project = require('${projectImport}');
151
+
152
+ module.exports = MsgResource.create({
153
+ title: ${titleStr},
154
+ attributes: {
155
+ lang: ${langStr},
156
+ dir: ${dirStr}
157
+ },
158
+ notes: [
159
+ { type: 'DESCRIPTION', content: 'This is a generated file. Replace this description with your own.' }
160
+ ],
161
+ ${messagesBlock}
162
+ }, project);
163
+ `;
164
+ }
165
+ /**
166
+ * Writes the MsgResource content to file.
167
+ * @param filePath - Absolute path of the file to write (including extension)
168
+ * @param content - Full file content string
169
+ */
170
+ function writeMsgResourceFile(filePath, content) {
171
+ const dir = (0, path_1.dirname)(filePath);
172
+ if (!(0, fs_1.existsSync)(dir)) {
173
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
174
+ }
175
+ (0, fs_1.writeFileSync)(filePath, content, "utf-8");
176
+ }
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_L10N_DIR = exports.DEFAULT_I18N_DIR = void 0;
4
+ exports.findPackageJsonPath = findPackageJsonPath;
5
+ exports.readPackageJson = readPackageJson;
6
+ exports.loadPackageJsonForMsg = loadPackageJsonForMsg;
7
+ exports.writePackageJson = writePackageJson;
8
+ exports.validatePaths = validatePaths;
9
+ exports.ensureDirectoriesWithGitkeep = ensureDirectoriesWithGitkeep;
10
+ exports.isAlreadyInitialized = isAlreadyInitialized;
11
+ exports.addDirectoriesToPackageJson = addDirectoriesToPackageJson;
12
+ exports.addImportAliasesToPackageJson = addImportAliasesToPackageJson;
13
+ exports.addScriptsToPackageJson = addScriptsToPackageJson;
14
+ exports.addTsconfigPaths = addTsconfigPaths;
15
+ const fs_1 = require("fs");
16
+ const path_1 = require("path");
17
+ /** Default relative path for the i18n directory. */
18
+ exports.DEFAULT_I18N_DIR = "src/i18n";
19
+ /** Default relative path for the l10n directory. */
20
+ exports.DEFAULT_L10N_DIR = "res/l10n";
21
+ /**
22
+ * Finds package.json by walking up from the given directory.
23
+ * @param cwd - Directory to start from (e.g. process.cwd())
24
+ * @returns Absolute path to package.json, or null if not found
25
+ */
26
+ function findPackageJsonPath(cwd) {
27
+ let dir = cwd;
28
+ for (;;) {
29
+ const p = (0, path_1.join)(dir, "package.json");
30
+ if ((0, fs_1.existsSync)(p))
31
+ return p;
32
+ const parent = (0, path_1.dirname)(dir);
33
+ if (parent === dir)
34
+ return null;
35
+ dir = parent;
36
+ }
37
+ }
38
+ /**
39
+ * Reads and parses package.json.
40
+ * @param pkgPath - Absolute path to package.json
41
+ * @returns Parsed package.json object
42
+ * @throws Error if file is unreadable or invalid JSON
43
+ */
44
+ function readPackageJson(pkgPath) {
45
+ const raw = (0, fs_1.readFileSync)(pkgPath, "utf-8");
46
+ try {
47
+ const parsed = JSON.parse(raw);
48
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
49
+ throw new Error("package.json must be a JSON object");
50
+ }
51
+ return parsed;
52
+ }
53
+ catch (err) {
54
+ if (err instanceof SyntaxError) {
55
+ throw new Error("Invalid package.json: " + err.message);
56
+ }
57
+ throw err;
58
+ }
59
+ }
60
+ /**
61
+ * Loads package.json for msg commands (create project, create resource, etc.).
62
+ * Finds package.json from cwd, validates directories, and derives module info.
63
+ * @param cwd - Directory to start from (e.g. process.cwd())
64
+ * @param options - requireL10n: if true, directories.l10n must exist (default: false)
65
+ * @returns Package context with pkgPath, rootDir, pkg, i18nDir, l10nDir?, isEsm, useTypeScript
66
+ * @throws Error if package.json not found or required directories missing
67
+ */
68
+ function loadPackageJsonForMsg(cwd, options) {
69
+ const pkgPath = findPackageJsonPath(cwd);
70
+ if (!pkgPath) {
71
+ throw new Error("package.json not found. Run this command from the project root.");
72
+ }
73
+ let pkg;
74
+ try {
75
+ pkg = readPackageJson(pkgPath);
76
+ }
77
+ catch (err) {
78
+ const msg = err instanceof Error ? err.message : "package.json could not be parsed.";
79
+ throw new Error(msg);
80
+ }
81
+ const dirs = pkg.directories;
82
+ if (!dirs || typeof dirs !== "object" || !dirs.i18n) {
83
+ throw new Error("package.json must contain directories.i18n. Run 'msg init' first.");
84
+ }
85
+ if ((options === null || options === void 0 ? void 0 : options.requireL10n) && !dirs.l10n) {
86
+ throw new Error("package.json must contain directories.i18n and directories.l10n. Run 'msg init' first.");
87
+ }
88
+ const rootDir = (0, path_1.dirname)(pkgPath);
89
+ const useTypeScript = (0, fs_1.existsSync)((0, path_1.join)(rootDir, "tsconfig.json"));
90
+ const isEsm = pkg.type === "module";
91
+ return {
92
+ pkgPath,
93
+ rootDir,
94
+ pkg,
95
+ i18nDir: dirs.i18n,
96
+ l10nDir: dirs.l10n,
97
+ isEsm,
98
+ useTypeScript,
99
+ };
100
+ }
101
+ /**
102
+ * Writes package.json to disk with trailing newline.
103
+ * @param pkgPath - Absolute path to package.json
104
+ * @param pkg - Object to serialize
105
+ */
106
+ function writePackageJson(pkgPath, pkg) {
107
+ const json = JSON.stringify(pkg, null, 2) + "\n";
108
+ (0, fs_1.writeFileSync)(pkgPath, json, "utf-8");
109
+ }
110
+ /**
111
+ * Validates i18n and l10n paths (relative, non-empty, no absolute segments).
112
+ * @param rootDir - Project root (absolute)
113
+ * @param i18nDir - Relative i18n path
114
+ * @param l10nDir - Relative l10n path
115
+ * @returns Object with valid: boolean and optional error message
116
+ */
117
+ function validatePaths(rootDir, i18nDir, l10nDir) {
118
+ const trimmedI18n = i18nDir.trim();
119
+ const trimmedL10n = l10nDir.trim();
120
+ if (!trimmedI18n)
121
+ return { valid: false, error: "i18n directory path cannot be empty" };
122
+ if (!trimmedL10n)
123
+ return { valid: false, error: "l10n directory path cannot be empty" };
124
+ if (trimmedI18n.startsWith("/") || /^[A-Za-z]:/.test(trimmedI18n)) {
125
+ return { valid: false, error: "i18n path must be relative" };
126
+ }
127
+ if (trimmedL10n.startsWith("/") || /^[A-Za-z]:/.test(trimmedL10n)) {
128
+ return { valid: false, error: "l10n path must be relative" };
129
+ }
130
+ return { valid: true };
131
+ }
132
+ const GITKEEP = ".gitkeep";
133
+ /**
134
+ * Ensures i18n and l10n directory trees exist and adds .gitkeep to leaf dirs.
135
+ * Leaf dirs: i18n/projects, i18n/resources, l10n/translations, l10n/xliff.
136
+ * @param rootDir - Project root (absolute)
137
+ * @param i18nDir - Relative i18n path
138
+ * @param l10nDir - Relative l10n path
139
+ * @param force - If true, overwrite/recreate even when dirs exist or are non-empty
140
+ */
141
+ function ensureDirectoriesWithGitkeep(rootDir, i18nDir, l10nDir, force) {
142
+ const leaves = [
143
+ (0, path_1.join)(rootDir, i18nDir, "projects"),
144
+ (0, path_1.join)(rootDir, i18nDir, "resources"),
145
+ (0, path_1.join)(rootDir, l10nDir, "translations"),
146
+ (0, path_1.join)(rootDir, l10nDir, "xliff"),
147
+ ];
148
+ for (const leaf of leaves) {
149
+ const parent = (0, path_1.dirname)(leaf);
150
+ if (!(0, fs_1.existsSync)(parent)) {
151
+ (0, fs_1.mkdirSync)(parent, { recursive: true });
152
+ }
153
+ if (!(0, fs_1.existsSync)(leaf)) {
154
+ (0, fs_1.mkdirSync)(leaf, { recursive: true });
155
+ }
156
+ const gitkeepPath = (0, path_1.join)(leaf, GITKEEP);
157
+ if (force || !(0, fs_1.existsSync)(gitkeepPath)) {
158
+ (0, fs_1.writeFileSync)(gitkeepPath, "", "utf-8");
159
+ }
160
+ }
161
+ }
162
+ /**
163
+ * Returns true if package.json already has msg directories and they exist on disk.
164
+ */
165
+ function isAlreadyInitialized(pkg, rootDir, i18nDir, l10nDir) {
166
+ const dirs = pkg.directories;
167
+ if (!dirs || typeof dirs !== "object")
168
+ return false;
169
+ if (dirs.i18n !== i18nDir || dirs.l10n !== l10nDir)
170
+ return false;
171
+ const i18nFull = (0, path_1.join)(rootDir, i18nDir);
172
+ const l10nFull = (0, path_1.join)(rootDir, l10nDir);
173
+ return (0, fs_1.existsSync)(i18nFull) && (0, fs_1.existsSync)(l10nFull);
174
+ }
175
+ /**
176
+ * Adds or overwrites directories.i18n, directories.l10n, directories.root in package.json.
177
+ */
178
+ function addDirectoriesToPackageJson(pkg, i18nDir, l10nDir) {
179
+ const directories = Object.assign(Object.assign({}, pkg.directories), { i18n: i18nDir, l10n: l10nDir, root: "." });
180
+ return Object.assign(Object.assign({}, pkg), { directories });
181
+ }
182
+ /**
183
+ * Adds import aliases #i18n/*, #l10n/*, #root/* to package.json imports.
184
+ */
185
+ function addImportAliasesToPackageJson(pkg, i18nDir, l10nDir) {
186
+ const imports = Object.assign(Object.assign({}, pkg.imports), { "#i18n/*": `${i18nDir}/*`, "#l10n/*": `${l10nDir}/*`, "#root/*": "./*" });
187
+ return Object.assign(Object.assign({}, pkg), { imports });
188
+ }
189
+ /**
190
+ * Adds i18n-export and l10n-import scripts to package.json.
191
+ */
192
+ function addScriptsToPackageJson(pkg) {
193
+ const scripts = Object.assign(Object.assign({}, pkg.scripts), { "i18n-export": "msg export:resources", "l10n-import": "msg import:translations" });
194
+ return Object.assign(Object.assign({}, pkg), { scripts });
195
+ }
196
+ /**
197
+ * Reads tsconfig.json, adds baseUrl and paths for #i18n/*, #l10n/*, #root/*, and writes it back.
198
+ * Merges into existing compilerOptions.paths and compilerOptions.baseUrl if present.
199
+ * @param tsconfigPath - Absolute path to tsconfig.json
200
+ * @param i18nDir - Relative i18n path
201
+ * @param l10nDir - Relative l10n path
202
+ */
203
+ function addTsconfigPaths(tsconfigPath, i18nDir, l10nDir) {
204
+ var _a;
205
+ const raw = (0, fs_1.readFileSync)(tsconfigPath, "utf-8");
206
+ let config;
207
+ try {
208
+ config = JSON.parse(raw);
209
+ }
210
+ catch (_b) {
211
+ throw new Error("Invalid tsconfig.json");
212
+ }
213
+ if (!config.compilerOptions)
214
+ config.compilerOptions = {};
215
+ const co = config.compilerOptions;
216
+ co.baseUrl = (_a = co.baseUrl) !== null && _a !== void 0 ? _a : ".";
217
+ co.paths = Object.assign(Object.assign({}, co.paths), { "#i18n/*": [`${i18nDir}/*`], "#l10n/*": [`${l10nDir}/*`], "#root/*": ["./*"] });
218
+ (0, fs_1.writeFileSync)(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
219
+ }