@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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Joel Sahleen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # msg-cli
2
+
3
+ ## Overview
4
+
5
+ msg-cli is a command-line tool for the [msg](https://github.com/worldware-studios/msg) library. It helps you scaffold internationalization (i18n) and localization (l10n) layout and wire up your project for use with msg.
6
+
7
+ **Current status:** Implemented commands: `init` (scaffold i18n/l10n and config), `create project` (template a new MsgProject file in i18n/projects), and `create resource` (template a new MsgResource file in i18n/resources).
8
+
9
+ ## Installation
10
+
11
+ Install globally:
12
+
13
+ ```bash
14
+ npm install -g @worldware/msg-cli
15
+ ```
16
+
17
+ Or use via `npx`:
18
+
19
+ ```bash
20
+ npx @worldware/msg-cli <command>
21
+ ```
22
+
23
+ For project-local setup, run `msg init` in your project root to add msg and msg-cli as dependencies and scaffold directories and config.
24
+
25
+ ## Core Concepts
26
+
27
+ - **i18n (internationalization):** Source message projects and resources. MsgProject and MsgResource files live under the i18n directory (default `src/i18n`).
28
+ - **l10n (localization):** Exported XLIFF files and imported translation JSON. Exports and translation data live under the l10n directory (default `res/l10n`).
29
+ - **Import aliases:** The init command adds `#i18n/*` and `#l10n/*` (and `#root/*`) to `package.json` so you can import with short paths like `import project from '#i18n/projects/main'`.
30
+
31
+ ## Usage
32
+
33
+ **Commands:** `init`, `create project`, `create resource`.
34
+
35
+ ### init
36
+
37
+ Scaffold a msg project: create i18n and l10n directories, update `package.json` (directories, import aliases, scripts), optionally update `tsconfig.json` for path aliases, and install `@worldware/msg`.
38
+
39
+ ```bash
40
+ msg init
41
+ ```
42
+
43
+ | Flag | Description |
44
+ |------|-------------|
45
+ | `-h`, `--help` | Show help for the init command. |
46
+ | `-i` | Interactive: prompt for i18n and l10n directory paths. |
47
+ | `-f`, `--force` | Force a clean run; overwrite or re-apply existing msg setup. |
48
+ | `--i18nDir <path>` | Relative path for the i18n directory (default: `src/i18n`). |
49
+ | `--l10nDir <path>` | Relative path for the l10n directory (default: `res/l10n`). |
50
+
51
+ **Examples:**
52
+
53
+ ```bash
54
+ # Default layout (src/i18n, res/l10n)
55
+ msg init
56
+
57
+ # Custom paths
58
+ msg init --i18nDir lib/i18n --l10nDir data/l10n
59
+
60
+ # Re-run and overwrite existing setup
61
+ msg init -f
62
+
63
+ # Prompt for paths
64
+ msg init -i
65
+ ```
66
+
67
+ **What init does:**
68
+
69
+ 1. Creates `i18n` with subdirectories `projects` and `resources`, and `l10n` with `translations` and `xliff` (or your custom paths).
70
+ 2. Adds `.gitkeep` in each leaf directory.
71
+ 3. Adds `directories.i18n`, `directories.l10n`, and `directories.root` to `package.json`.
72
+ 4. Adds import aliases `#i18n/*`, `#l10n/*`, and `#root/*` to `package.json`.
73
+ 5. Adds scripts `i18n-export` and `l10n-import` (pointing to `msg export:resources` and `msg import:translations`) for when those commands are available.
74
+ 6. If `tsconfig.json` exists, adds `compilerOptions.baseUrl` and `compilerOptions.paths` for the aliases.
75
+ 7. Installs the latest `@worldware/msg` as a dependency.
76
+
77
+ ### create project
78
+
79
+ Create a new MsgProject file in the i18n projects directory. Requires `package.json` with `directories.i18n` and `directories.l10n` (run `msg init` first).
80
+
81
+ ```bash
82
+ msg create project <projectName> <source> <targets...> [--extend <name>]
83
+ ```
84
+
85
+ | Argument | Required | Description |
86
+ |-------------|----------|------------------------------------------|
87
+ | `projectName` | Yes | Name of the project (used as file name). |
88
+ | `source` | Yes | Source locale (e.g. `en`). |
89
+ | `targets` | Yes (≥1) | Target locale(s), e.g. `fr`, `de`, `es`. |
90
+
91
+ | Flag | Short | Description |
92
+ |------------|-------|--------------------------------|
93
+ | `--extend` | `-e` | Extend an existing project. |
94
+ | `--help` | `-h` | Show help for create project. |
95
+
96
+ **Examples:**
97
+
98
+ ```bash
99
+ # Create project myApp with source en and targets fr, de
100
+ msg create project myApp en fr de
101
+
102
+ # Extend an existing project
103
+ msg create project extendedApp en de --extend base
104
+
105
+ # Help
106
+ msg create project -h
107
+ ```
108
+
109
+ **Behavior:**
110
+
111
+ - Writes the file to `i18n/projects/<projectName>.js` or `.ts` (TypeScript if `tsconfig.json` exists).
112
+ - Uses ES module or CommonJS export based on `package.json` `"type"`.
113
+ - Generates a translation loader that imports from `l10n/translations` using the relative path from `i18n/projects` (from `directories` in package.json).
114
+ - With `--extend <name>`, merges target locales and pseudoLocale from the existing project.
115
+ - Errors if the project name already exists, package.json is missing or invalid, or required directories are not configured.
116
+
117
+ ### create resource
118
+
119
+ Create a new MsgResource file in the i18n resources directory. Requires `msg init` and a project file in `i18n/projects` (run `msg create project` first).
120
+
121
+ ```bash
122
+ msg create resource <projectName> <title> [-f] [-e]
123
+ ```
124
+
125
+ | Argument | Required | Description |
126
+ |-------------|----------|----------------------------------------------------|
127
+ | `projectName` | Yes | Name of the project to import in the MsgResource. |
128
+ | `title` | Yes | Title of the resource and file name (e.g. `messages` → `messages.msg.js`). |
129
+
130
+ | Flag | Short | Description |
131
+ |------------|-------|------------------------------------------|
132
+ | `--force` | `-f` | Overwrite an existing resource file. |
133
+ | `--edit` | `-e` | Open the file for editing after creation.|
134
+ | `--help` | `-h` | Show help for create resource. |
135
+
136
+ **Examples:**
137
+
138
+ ```bash
139
+ # Create resource messages for project myProject
140
+ msg create resource myProject messages
141
+
142
+ # Overwrite existing resource
143
+ msg create resource myProject messages --force
144
+
145
+ # Create and open in editor
146
+ msg create resource myProject messages --edit
147
+ ```
148
+
149
+ **Behavior:**
150
+
151
+ - Writes the file to `i18n/resources/<title>.msg.js` or `.msg.ts` (TypeScript if `tsconfig.json` exists).
152
+ - Uses ES module or CommonJS export based on `package.json` `"type"`.
153
+ - Sets `lang` from the project's `sourceLocale` and `dir` to `rtl` for Arabic/Hebrew, `ltr` otherwise.
154
+ - Includes a minimal example message. Validates that the generated file is importable.
155
+ - Errors if i18n/projects or i18n/resources does not exist, the project is not found, or the resource file already exists (unless `--force`).
156
+
157
+ ## API Reference
158
+
159
+ The CLI does not expose a programmatic API. For library usage, see [@worldware/msg](https://github.com/worldware-studios/msg).
160
+
161
+ ## Development
162
+
163
+ - **Setup:** `npm install`
164
+ - **Build:** `npm run build`
165
+ - **Tests:** `npm run test`
166
+ - **Coverage:** `npm run coverage`
167
+
168
+ Source layout:
169
+
170
+ - `src/commands/` — CLI commands (init, create/project, create/resource).
171
+ - `src/lib/` — Shared utilities, init helpers, create-project helpers, and create-resource helpers.
172
+ - `src/specs/` — Feature and command specs.
173
+ - `src/tests/` — Vitest tests and fixtures.
174
+
175
+ ## License
176
+
177
+ MIT. See [LICENSE](LICENSE).
178
+
179
+ ## Keywords
180
+
181
+ i18n, l10n, internationalization, localization, xliff, msg, cli
package/bin/dev.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\dev" %*
package/bin/dev.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node_modules/.bin/ts-node
2
+
3
+ // eslint-disable-next-line unicorn/prefer-top-level-await
4
+ ;(async () => {
5
+ const oclif = await import('@oclif/core')
6
+ await oclif.execute({development: true, dir: __dirname})
7
+ })()
package/bin/run.cmd ADDED
@@ -0,0 +1,3 @@
1
+ @echo off
2
+
3
+ node "%~dp0\run" %*
package/bin/run.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+
3
+ // eslint-disable-next-line unicorn/prefer-top-level-await
4
+ (async () => {
5
+ const oclif = await import('@oclif/core')
6
+ await oclif.execute({dir: __dirname})
7
+ })()
@@ -0,0 +1,160 @@
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 fs_1 = require("fs");
14
+ const path_1 = require("path");
15
+ const create_project_helpers_js_1 = require("../../lib/create-project-helpers.js");
16
+ const init_helpers_js_1 = require("../../lib/init-helpers.js");
17
+ /**
18
+ * Creates a new MsgProject file in the i18n projects directory.
19
+ */
20
+ class CreateProject extends core_1.Command {
21
+ run() {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ var _a, _b;
24
+ const { argv, flags } = yield this.parse(CreateProject);
25
+ const [projectName, source, ...targets] = argv;
26
+ if (!(projectName === null || projectName === void 0 ? void 0 : projectName.trim())) {
27
+ this.error("projectName is required.", { exit: 1 });
28
+ }
29
+ if (!(source === null || source === void 0 ? void 0 : source.trim())) {
30
+ this.error("source locale is required.", { exit: 1 });
31
+ }
32
+ if (!(targets === null || targets === void 0 ? void 0 : targets.length) || targets.every((t) => !(t === null || t === void 0 ? void 0 : t.trim()))) {
33
+ this.error("At least one target locale is required.", { exit: 1 });
34
+ }
35
+ const cwd = process.cwd();
36
+ const pkgPath = (0, init_helpers_js_1.findPackageJsonPath)(cwd);
37
+ if (!pkgPath) {
38
+ this.error("package.json not found. Run this command from the project root.", { exit: 1 });
39
+ }
40
+ let pkg;
41
+ try {
42
+ pkg = (0, create_project_helpers_js_1.loadPackageJsonForCreateProject)(cwd);
43
+ }
44
+ catch (err) {
45
+ const msg = err instanceof Error ? err.message : String(err);
46
+ this.error(msg, { exit: 1 });
47
+ }
48
+ const rootDir = (0, path_1.join)(pkgPath, "..");
49
+ const i18nDir = pkg.directories.i18n;
50
+ const l10nDir = pkg.directories.l10n;
51
+ const projectsDir = (0, path_1.join)(rootDir, i18nDir, "projects");
52
+ const translationsDir = (0, path_1.join)(rootDir, l10nDir, "translations");
53
+ const relPath = (0, create_project_helpers_js_1.calculateRelativePath)(projectsDir, translationsDir);
54
+ const useTypeScript = (0, fs_1.existsSync)((0, path_1.join)(rootDir, "tsconfig.json"));
55
+ const isEsm = pkg.type === "module";
56
+ const ext = useTypeScript ? ".ts" : ".js";
57
+ const outPath = (0, path_1.join)(projectsDir, `${projectName}${ext}`);
58
+ if ((0, fs_1.existsSync)(outPath)) {
59
+ this.error(`A project with the name '${projectName}' already exists.`, { exit: 1 });
60
+ }
61
+ let targetLocales = {};
62
+ let pseudoLocale = "en-XA";
63
+ if (flags.extend) {
64
+ const base = yield (0, create_project_helpers_js_1.importMsgProjectFile)(projectsDir, flags.extend);
65
+ if (!base) {
66
+ this.error(`Project '${flags.extend}' could not be found to extend.`, { exit: 1 });
67
+ }
68
+ if (((_a = base.locales) === null || _a === void 0 ? void 0 : _a.targetLocales) && typeof base.locales.targetLocales === "object") {
69
+ targetLocales = Object.assign({}, base.locales.targetLocales);
70
+ }
71
+ if ((_b = base.locales) === null || _b === void 0 ? void 0 : _b.pseudoLocale) {
72
+ pseudoLocale = base.locales.pseudoLocale;
73
+ }
74
+ }
75
+ targetLocales[source] = [source];
76
+ for (const t of targets) {
77
+ if (t === null || t === void 0 ? void 0 : t.trim())
78
+ targetLocales[t.trim()] = [t.trim()];
79
+ }
80
+ const loaderPathLine = "const path = `${TRANSLATION_IMPORT_PATH}/${project}/${language}/${title}.json`;";
81
+ const loaderWarnLine = "console.warn(`Translations for locale ${language} could not be loaded.`, error);";
82
+ const loaderBody = `${loaderPathLine}
83
+ try {
84
+ const module = await import(path, { with: { type: 'json' } });
85
+ return module.default;
86
+ } catch (error) {
87
+ ${loaderWarnLine}
88
+ return {
89
+ title,
90
+ attributes: { lang: language, dir: '' },
91
+ notes: [],
92
+ messages: []
93
+ };
94
+ }`;
95
+ const importPath = relPath.replace(/\\/g, "/");
96
+ const content = isEsm
97
+ ? `import { MsgProject } from '@worldware/msg';
98
+
99
+ const TRANSLATION_IMPORT_PATH = ${JSON.stringify(importPath)};
100
+ const loader = async (project, title, language) => {
101
+ ${loaderBody}
102
+ };
103
+
104
+ export default MsgProject.create({
105
+ project: { name: ${JSON.stringify(projectName)}, version: 1 },
106
+ locales: {
107
+ sourceLocale: ${JSON.stringify(source)},
108
+ pseudoLocale: ${JSON.stringify(pseudoLocale)},
109
+ targetLocales: ${JSON.stringify(targetLocales)}
110
+ },
111
+ loader
112
+ });
113
+ `
114
+ : `const { MsgProject } = require('@worldware/msg');
115
+
116
+ const TRANSLATION_IMPORT_PATH = ${JSON.stringify(importPath)};
117
+ const loader = async (project, title, language) => {
118
+ ${loaderBody}
119
+ };
120
+
121
+ module.exports = MsgProject.create({
122
+ project: { name: ${JSON.stringify(projectName)}, version: 1 },
123
+ locales: {
124
+ sourceLocale: ${JSON.stringify(source)},
125
+ pseudoLocale: ${JSON.stringify(pseudoLocale)},
126
+ targetLocales: ${JSON.stringify(targetLocales)}
127
+ },
128
+ loader
129
+ });
130
+ `;
131
+ this.log("Creating MsgProject file...");
132
+ (0, create_project_helpers_js_1.writeMsgProjectFile)(outPath, content);
133
+ this.log(`Created ${outPath}`);
134
+ });
135
+ }
136
+ }
137
+ CreateProject.description = "Create a new MsgProject file in the projects directory (i18n/projects)";
138
+ CreateProject.strict = false;
139
+ CreateProject.args = {
140
+ projectName: core_1.Args.string({
141
+ required: false,
142
+ description: "Name of the project (used as file name)",
143
+ }),
144
+ source: core_1.Args.string({
145
+ required: false,
146
+ description: "Source locale for the project",
147
+ }),
148
+ targets: core_1.Args.string({
149
+ required: false,
150
+ description: "Target locale(s) (variadic)",
151
+ }),
152
+ };
153
+ CreateProject.flags = {
154
+ help: core_1.Flags.help({ char: "h" }),
155
+ extend: core_1.Flags.string({
156
+ char: "e",
157
+ description: "Extend an existing project",
158
+ }),
159
+ };
160
+ exports.default = CreateProject;
@@ -0,0 +1,169 @@
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
+ const core_1 = require("@oclif/core");
46
+ const fs_1 = require("fs");
47
+ const path_1 = require("path");
48
+ const url_1 = require("url");
49
+ const create_resource_helpers_js_1 = require("../../lib/create-resource-helpers.js");
50
+ const init_helpers_js_1 = require("../../lib/init-helpers.js");
51
+ /**
52
+ * Creates a new MsgResource file in the i18n resources directory.
53
+ */
54
+ class CreateResource extends core_1.Command {
55
+ run() {
56
+ return __awaiter(this, void 0, void 0, function* () {
57
+ const { argv, flags } = yield this.parse(CreateResource);
58
+ const [projectName, title] = argv;
59
+ if (!(projectName === null || projectName === void 0 ? void 0 : projectName.trim())) {
60
+ this.error("projectName is required. Run 'msg init' first if you have not.", {
61
+ exit: 1,
62
+ });
63
+ }
64
+ if (!(title === null || title === void 0 ? void 0 : title.trim())) {
65
+ this.error("title is required.", { exit: 1 });
66
+ }
67
+ const cwd = process.cwd();
68
+ const pkgPath = (0, init_helpers_js_1.findPackageJsonPath)(cwd);
69
+ if (!pkgPath) {
70
+ this.error("package.json not found. Run this command from the project root.", {
71
+ exit: 1,
72
+ });
73
+ }
74
+ let pkgInfo;
75
+ try {
76
+ pkgInfo = (0, create_resource_helpers_js_1.readPackageJsonForCreateResource)(cwd);
77
+ }
78
+ catch (err) {
79
+ const msg = err instanceof Error ? err.message : String(err);
80
+ this.error(msg, { exit: 1 });
81
+ }
82
+ const rootDir = (0, path_1.join)(pkgPath, "..");
83
+ const i18nDir = (0, path_1.join)(rootDir, pkgInfo.i18nDir);
84
+ const projectsDir = (0, path_1.join)(i18nDir, "projects");
85
+ const resourcesDir = (0, path_1.join)(i18nDir, "resources");
86
+ if (!(0, fs_1.existsSync)(i18nDir)) {
87
+ this.error(`i18n directory '${pkgInfo.i18nDir}' does not exist. Run 'msg init' first.`, { exit: 1 });
88
+ }
89
+ if (!(0, fs_1.existsSync)(projectsDir)) {
90
+ this.error(`i18n/projects directory does not exist. Run 'msg init' first.`, { exit: 1 });
91
+ }
92
+ if (!(0, fs_1.existsSync)(resourcesDir)) {
93
+ this.error(`i18n/resources directory does not exist. Run 'msg init' first.`, { exit: 1 });
94
+ }
95
+ const projectData = yield (0, create_resource_helpers_js_1.importMsgProjectForResource)(projectsDir, projectName.trim());
96
+ if (!projectData) {
97
+ this.error(`Project '${projectName}' not found or could not be loaded. Check i18n/projects for a matching file.`, { exit: 1 });
98
+ }
99
+ const ext = pkgInfo.useTypeScript ? ".ts" : ".js";
100
+ const outPath = (0, path_1.join)(resourcesDir, `${title.trim()}.msg${ext}`);
101
+ if ((0, fs_1.existsSync)(outPath) && !flags.force) {
102
+ this.error(`Resource file '${title.trim()}.msg${ext}' already exists. Use -f or --force to overwrite.`, { exit: 1 });
103
+ }
104
+ const content = (0, create_resource_helpers_js_1.generateMsgResourceContent)({
105
+ title: title.trim(),
106
+ projectName: projectName.trim(),
107
+ sourceLocale: projectData.sourceLocale,
108
+ dir: projectData.dir,
109
+ isEsm: pkgInfo.isEsm,
110
+ });
111
+ try {
112
+ (0, create_resource_helpers_js_1.writeMsgResourceFile)(outPath, content);
113
+ }
114
+ catch (err) {
115
+ const msg = err instanceof Error ? err.message : String(err);
116
+ this.error(`Could not generate resource file: ${msg}`, { exit: 1 });
117
+ }
118
+ try {
119
+ const url = (0, url_1.pathToFileURL)(outPath).href;
120
+ yield Promise.resolve(`${url}`).then(s => __importStar(require(s)));
121
+ }
122
+ catch (err) {
123
+ try {
124
+ (0, fs_1.unlinkSync)(outPath);
125
+ }
126
+ catch (_a) {
127
+ // best-effort cleanup
128
+ }
129
+ const msg = err instanceof Error ? err.message : String(err);
130
+ this.error(`Generated file is invalid or not importable: ${msg}`, { exit: 1 });
131
+ }
132
+ this.log(`Created ${outPath}`);
133
+ if (flags.edit) {
134
+ const editor = process.env.VISUAL || process.env.EDITOR;
135
+ if (editor) {
136
+ const { spawn } = yield Promise.resolve().then(() => __importStar(require("child_process")));
137
+ spawn(editor, [outPath], { stdio: "inherit", detached: true });
138
+ }
139
+ else {
140
+ this.warn("EDITOR or VISUAL not set. Open the file manually.");
141
+ }
142
+ }
143
+ });
144
+ }
145
+ }
146
+ CreateResource.description = "Create a new MsgResource file in the resources directory (i18n/resources)";
147
+ CreateResource.strict = false;
148
+ CreateResource.args = {
149
+ projectName: core_1.Args.string({
150
+ required: false,
151
+ description: "Name of the project to import in the MsgResource file",
152
+ }),
153
+ title: core_1.Args.string({
154
+ required: false,
155
+ description: "Title of the resource and file name for the file",
156
+ }),
157
+ };
158
+ CreateResource.flags = {
159
+ help: core_1.Flags.help({ char: "h" }),
160
+ force: core_1.Flags.boolean({
161
+ char: "f",
162
+ description: "Overwrite an existing resource file",
163
+ }),
164
+ edit: core_1.Flags.boolean({
165
+ char: "e",
166
+ description: "Open the file for editing after creation",
167
+ }),
168
+ };
169
+ exports.default = CreateResource;