diff --git a/README.md b/README.md index 743d9de..1b44bcb 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ msg create resource myProject messages --edit - Writes the file to `i18n/resources/.msg.js` (always `.js`, including TypeScript projects). - Uses ES module or CommonJS syntax based on `package.json` `"type"` or presence of `tsconfig.json`. -- Imports the project via the `#i18n/projects/<projectName>` alias (added by `msg init`). +- Imports the project via the `#i18n/projects/<projectName>.js` alias (added by `msg init`). - Sets `lang` from the project's `sourceLocale` and `dir` to `rtl` for Arabic/Hebrew, `ltr` otherwise. - Exports a named `resource` plus an async `getMessages()` helper that returns `resource.getTranslation(getLang())`, so the resource can be pre-translated for the runtime locale. - Includes sample messages added via chainable `.add()`. Validates that the generated file is importable. @@ -166,7 +166,7 @@ msg create resource myProject messages --edit ```javascript import { MsgResource, getLang } from '@worldware/msg'; -import project from '#i18n/projects/myProject'; +import project from '#i18n/projects/myProject.js'; export const resource = MsgResource.create({ /* title, attributes, notes */ }, project); diff --git a/src/lib/create-resource-helpers.ts b/src/lib/create-resource-helpers.ts index 6fb985a..f3fdef3 100644 --- a/src/lib/create-resource-helpers.ts +++ b/src/lib/create-resource-helpers.ts @@ -126,7 +126,7 @@ function escapeSingleQuoted(value: string): string { * Emits ESM or CJS boilerplate that creates a MsgResource, adds sample * messages via chainable `.add()`, and exports `resource` plus an async * `getMessages` loader that calls `resource.getTranslation(getLang())`. - * Project import uses the `#i18n/projects/<name>` alias from `msg init`. + * Project import uses the `#i18n/projects/<name>.js` alias from `msg init`. * @param params - Title, projectName, sourceLocale, dir, and isEsm * @returns The generated file content */ @@ -142,7 +142,7 @@ export function generateMsgResourceContent(params: { const langStr = `'${escapeSingleQuoted(sourceLocale)}'`; const dirStr = `'${dir}'`; const resourceNote = `This is the ${escapeSingleQuoted(title)} resource.`; - const projectImport = `#i18n/projects/${escapeSingleQuoted(projectName)}`; + const projectImport = `#i18n/projects/${escapeSingleQuoted(projectName)}.js`; const createAndAdd = `MsgResource.create({ title: ${titleStr}, diff --git a/src/specs/create-resource-command.spec.md b/src/specs/create-resource-command.spec.md index fe55bfa..ed54ea3 100644 --- a/src/specs/create-resource-command.spec.md +++ b/src/specs/create-resource-command.spec.md @@ -2,13 +2,13 @@ The `create resource` command creates a `MsgResource` file in the `resources` subdirectory of the `i18n` created by the `init` command. Running the `init` command is a prerequisite to running the `create resource` command. An `MsgResource` file is a JavaScript file that exports a `MsgResource` instance (and a `getMessages` loader). These files have `.msg.` right before the `.js` extension, and are named after the resource `title`. For example, `messages.msg.js`. The command always writes a `.js` file, even when the surrounding project uses TypeScript. -The generated file uses the `projectName` and `title` arguments, imports the project via the `#i18n/projects/<projectName>` alias (configured by `msg init`), and scaffolds sample messages plus an async `getMessages()` helper that calls `resource.getTranslation(getLang())` so callers can load a pre-translated resource for the runtime locale. An ESM example: +The generated file uses the `projectName` and `title` arguments, imports the project via the `#i18n/projects/<projectName>.js` alias (configured by `msg init`), and scaffolds sample messages plus an async `getMessages()` helper that calls `resource.getTranslation(getLang())` so callers can load a pre-translated resource for the runtime locale. An ESM example: ```javascript /** ESM module **/ import { MsgResource, getLang } from '@worldware/msg'; -import project from '#i18n/projects/<projectName>'; +import project from '#i18n/projects/<projectName>.js'; /** Create a MsgResource object */ @@ -102,7 +102,7 @@ The general order of operations for the command happy path should be as follows: - It `must` be able to work on different platforms. - It `must` name the generated file using the `title` argument with a `.msg.js` suffix. - It `must` always write a `.js` file (not `.ts`), even when `tsconfig.json` is present. -- It `must` import the project via `#i18n/projects/<projectName>`. +- It `must` import the project via `#i18n/projects/<projectName>.js`. - It `must` export a named `resource` and an async `getMessages()` loader. - It `should` error if the `i18n/projects` or `i18n/resources` directories do not exist and prompt to run the `init` command. - It `should` error if the `projectName` or `title` arguments are not provided. @@ -239,7 +239,7 @@ The general order of operations for the command happy path should be as follows: - _[Create resource in ES module project]_ - Given: A project with `init` run, `package.json` with `"type": "module"`, and a project file `i18n/projects/myProject.js` - When: User runs `create resource myProject messages` - - Then: A file `i18n/resources/messages.msg.js` is created with valid MsgResource content, correct `title`, import from `#i18n/projects/myProject`, named `resource` export, `getMessages()`, and default `dir: 'ltr'` for non-RTL sourceLocale. + - Then: A file `i18n/resources/messages.msg.js` is created with valid MsgResource content, correct `title`, import from `#i18n/projects/myProject.js`, named `resource` export, `getMessages()`, and default `dir: 'ltr'` for non-RTL sourceLocale. - _[Create resource in CommonJS project]_ - Given: A project with `init` run, `package.json` without `"type": "module"` (or `"type": "commonjs"`), and a project file in `i18n/projects` @@ -290,7 +290,7 @@ The general order of operations for the command happy path should be as follows: - _[Project name matches exactly one project file]_ - Given: `i18n/projects/app.js` and `i18n/projects/other.js` exist - When: User runs `create resource app dashboard` - - Then: The generated file imports from `#i18n/projects/app` and no ambiguity error occurs. + - Then: The generated file imports from `#i18n/projects/app.js` and no ambiguity error occurs. - _[Source locale with compound tag still drives RTL]_ - Given: A project file with `locales.sourceLocale` set to `ar-SA` or `he-IL` @@ -305,7 +305,7 @@ The general order of operations for the command happy path should be as follows: - _[Short and minimal projectName and title]_ - Given: A project with `init` run - When: User runs `create resource p t` - - Then: A file `i18n/resources/t.msg.js` is created with `title: 't'` and project import from `#i18n/projects/p`, and the file is valid. + - Then: A file `i18n/resources/t.msg.js` is created with `title: 't'` and project import from `#i18n/projects/p.js`, and the file is valid. #### Errors diff --git a/src/tests/create-resource-helpers.test.ts b/src/tests/create-resource-helpers.test.ts index ffc2402..90d6ff5 100644 --- a/src/tests/create-resource-helpers.test.ts +++ b/src/tests/create-resource-helpers.test.ts @@ -190,7 +190,7 @@ describe("create-resource-helpers", () => { isEsm: true, }); expect(content).toContain("import { MsgResource, getLang } from '@worldware/msg'"); - expect(content).toContain("import project from '#i18n/projects/Main'"); + expect(content).toContain("import project from '#i18n/projects/Main.js'"); expect(content).toContain("title: 'Messages'"); expect(content).toContain("lang: 'en'"); expect(content).toContain("dir: 'ltr'"); @@ -213,7 +213,7 @@ describe("create-resource-helpers", () => { isEsm: false, }); expect(content).toContain("const { MsgResource, getLang } = require('@worldware/msg')"); - expect(content).toContain("require('#i18n/projects/Main')"); + expect(content).toContain("require('#i18n/projects/Main.js')"); expect(content).toContain("const resource = MsgResource.create"); expect(content).toContain("async function getMessages()"); expect(content).toContain("module.exports = {\n resource,\n getMessages\n}"); @@ -254,7 +254,7 @@ describe("create-resource-helpers", () => { isEsm: true, }); expect(content).toContain("title: 't'"); - expect(content).toContain("import project from '#i18n/projects/p'"); + expect(content).toContain("import project from '#i18n/projects/p.js'"); expect(content).toContain("export async function getMessages()"); }); @@ -278,7 +278,7 @@ describe("create-resource-helpers", () => { dir: "ltr", isEsm: true, }); - expect(content).toContain("import project from '#i18n/projects/O\\'Brien'"); + expect(content).toContain("import project from '#i18n/projects/O\\'Brien.js'"); }); test("escapes single quotes in sourceLocale", () => { diff --git a/src/tests/create-resource.test.ts b/src/tests/create-resource.test.ts index ddef2df..9a612e0 100644 --- a/src/tests/create-resource.test.ts +++ b/src/tests/create-resource.test.ts @@ -44,7 +44,6 @@ function setupValidProject( "#root/*": "./*", }, ...pkgOverrides, - directories, }; writeFileSync(join(tmp, "package.json"), JSON.stringify(pkg, null, 2)); const i18nDir = join(tmp, directories.i18n); @@ -103,7 +102,7 @@ describe("CreateResource command", () => { expect(existsSync(outPath)).toBe(true); const content = readFileSync(outPath, "utf-8"); expect(content).toContain("import { MsgResource, getLang } from '@worldware/msg'"); - expect(content).toContain("import project from '#i18n/projects/myProject'"); + expect(content).toContain("import project from '#i18n/projects/myProject.js'"); expect(content).toContain("title: 'messages'"); expect(content).toContain("dir: 'ltr'"); expect(content).toContain("export const resource = MsgResource.create"); @@ -117,7 +116,7 @@ describe("CreateResource command", () => { const content = readFileSync(join(tmp, "i18n", "resources", "messages.msg.js"), "utf-8"); expect(content).toContain("const { MsgResource, getLang } = require('@worldware/msg')"); - expect(content).toContain("require('#i18n/projects/myProject')"); + expect(content).toContain("require('#i18n/projects/myProject.js')"); expect(content).toContain("async function getMessages()"); expect(content).toContain("module.exports = {\n resource,\n getMessages\n}"); expect(content).toContain("dir: 'ltr'"); @@ -208,7 +207,7 @@ describe("CreateResource command", () => { const outPath = join(tmp, "i18n", "resources", "messages.msg.js"); expect(existsSync(outPath)).toBe(true); const content = readFileSync(outPath, "utf-8"); - expect(content).toContain("import project from '#i18n/projects/myApp'"); + expect(content).toContain("import project from '#i18n/projects/myApp.js'"); expect(content).toMatch(/lang:\s*['\"]en['\"]/); expect(content).toMatch(/dir:\s*['\"]ltr['\"]/); }); @@ -238,7 +237,7 @@ describe("CreateResource command", () => { expect(existsSync(join(tmp, "i18n", "resources", "t.msg.js"))).toBe(true); const content = readFileSync(join(tmp, "i18n", "resources", "t.msg.js"), "utf-8"); expect(content).toContain("title: 't'"); - expect(content).toContain("#i18n/projects/p"); + expect(content).toContain("#i18n/projects/p.js"); expect(content).toContain("getMessages"); });