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/` alias (added by `msg init`).
+- Imports the project via the `#i18n/projects/.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/` alias from `msg init`.
+ * Project import uses the `#i18n/projects/.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/` 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/.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/';
+import project from '#i18n/projects/.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/`.
+- It `must` import the project via `#i18n/projects/.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");
});