diff --git a/README.md b/README.md index 839cdce..329592e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,16 @@ or bunx @bertdeblock/generate-template-registry@latest ``` +## Options + +### `--path` + +Generate a template registry at a custom path + +```shell +npx @bertdeblock/generate-template-registry@latest --path="app/glint/template-registry.ts" +``` + ## Caveats - If your app or addon has components, helpers or modifiers with the same name, duplicate template registry entries will be generated, which will need to be fixed manually diff --git a/bin/generate-template-registry.js b/bin/generate-template-registry.js index 370528c..2415cd8 100755 --- a/bin/generate-template-registry.js +++ b/bin/generate-template-registry.js @@ -1,6 +1,6 @@ #!/usr/bin/env node // eslint-disable-next-line n/no-missing-import -import { generateTemplateRegistry } from "../dist/generate-template-registry.js"; +import { cli } from "../dist/cli.js"; -generateTemplateRegistry(); +cli(); diff --git a/package.json b/package.json index f378021..fc0662d 100644 --- a/package.json +++ b/package.json @@ -32,13 +32,15 @@ "change-case": "^5.4.3", "execa": "^8.0.1", "fast-glob": "^3.3.2", - "fs-extra": "^11.2.0" + "fs-extra": "^11.2.0", + "yargs": "^17.7.2" }, "devDependencies": { "@release-it-plugins/lerna-changelog": "^6.1.0", "@types/fs-extra": "^11.0.4", "@types/node": "^20.11.20", "@types/recursive-readdir": "^2.2.4", + "@types/yargs": "^17.0.32", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", "@vitest/coverage-v8": "^1.2.2", @@ -50,6 +52,7 @@ "release-it": "^17.0.3", "type-fest": "^4.10.3", "typescript": "^5.3.3", + "uuid": "^9.0.1", "vitest": "^1.2.2" }, "packageManager": "pnpm@8.6.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5358710..766b72a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: fs-extra: specifier: ^11.2.0 version: 11.2.0 + yargs: + specifier: ^17.7.2 + version: 17.7.2 devDependencies: '@release-it-plugins/lerna-changelog': @@ -34,6 +37,9 @@ devDependencies: '@types/recursive-readdir': specifier: ^2.2.4 version: 2.2.4 + '@types/yargs': + specifier: ^17.0.32 + version: 17.0.32 '@typescript-eslint/eslint-plugin': specifier: ^7.1.0 version: 7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3) @@ -67,6 +73,9 @@ devDependencies: typescript: specifier: ^5.3.3 version: 5.3.3 + uuid: + specifier: ^9.0.1 + version: 9.0.1 vitest: specifier: ^1.2.2 version: 1.3.1(@types/node@20.11.20) @@ -838,6 +847,16 @@ packages: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: true + /@types/yargs-parser@21.0.3: + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + dev: true + + /@types/yargs@17.0.32: + resolution: {integrity: sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==} + dependencies: + '@types/yargs-parser': 21.0.3 + dev: true + /@typescript-eslint/eslint-plugin@7.1.0(@typescript-eslint/parser@7.1.0)(eslint@8.57.0)(typescript@5.3.3): resolution: {integrity: sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1113,7 +1132,6 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} @@ -1132,7 +1150,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -1517,7 +1534,6 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} @@ -1535,7 +1551,6 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -1543,7 +1558,6 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /concat-map@0.0.1: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} @@ -1782,7 +1796,6 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -1947,7 +1960,6 @@ packages: /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} - dev: true /escape-goat@4.0.0: resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} @@ -2302,7 +2314,6 @@ packages: /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - dev: true /get-east-asian-width@1.2.0: resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} @@ -2814,7 +2825,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} @@ -4266,7 +4276,6 @@ packages: /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - dev: true /resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -4611,7 +4620,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -4667,7 +4675,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} @@ -5003,6 +5010,11 @@ packages: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} dev: true + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: true + /uvu@0.5.6: resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} engines: {node: '>=8'} @@ -5226,7 +5238,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -5258,7 +5269,6 @@ packages: /y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - dev: true /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -5272,7 +5282,6 @@ packages: /yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - dev: true /yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} @@ -5298,7 +5307,6 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} diff --git a/src/cli.ts b/src/cli.ts new file mode 100644 index 0000000..0cf4a1f --- /dev/null +++ b/src/cli.ts @@ -0,0 +1,17 @@ +import { cwd } from "node:process"; +import { hideBin } from "yargs/helpers"; +import yargs from "yargs/yargs"; +import { generateTemplateRegistry } from "./generate-template-registry.js"; + +export async function cli() { + const options = await yargs(hideBin(process.argv)) + .options({ + path: { + describe: "Generate a template registry at a custom path", + type: "string", + }, + }) + .strict().argv; + + await generateTemplateRegistry(cwd(), { path: options.path }); +} diff --git a/src/generate-template-registry.ts b/src/generate-template-registry.ts index fb17a15..af31c86 100644 --- a/src/generate-template-registry.ts +++ b/src/generate-template-registry.ts @@ -1,11 +1,10 @@ import chalk from "chalk"; import { pascalCase } from "change-case"; import { execa } from "execa"; -import { readJson } from "fs-extra/esm"; +import { ensureDir, readJson } from "fs-extra/esm"; import { writeFile } from "node:fs/promises"; import { EOL } from "node:os"; -import { join } from "node:path"; -import { cwd as processCwd } from "node:process"; +import { isAbsolute, join, parse } from "node:path"; import { getEntries, isAddon, @@ -22,7 +21,10 @@ import { const TAB = " "; -export async function generateTemplateRegistry(cwd = processCwd()) { +export async function generateTemplateRegistry( + cwd: string, + options: { path?: string } = {}, +) { let packageJson: EmberPackageJson; try { @@ -95,12 +97,18 @@ export async function generateTemplateRegistry(cwd = processCwd()) { templateRegistryEntriesContent.push(entriesContent); } - const templateRegistryPath = join(cwd, entriesDir, "template-registry.ts"); + const templateRegistryPath = options.path + ? isAbsolute(options.path) + ? options.path + : join(cwd, options.path) + : join(cwd, entriesDir, "template-registry.ts"); + const templateRegistryContent = `${templateRegistryImportsContent.join(EOL)} export default interface ${pascalCase(packageJson.name)}Registry { ${templateRegistryEntriesContent.join(EOL)}} `; + await ensureDir(parse(templateRegistryPath).dir); await writeFile(templateRegistryPath, templateRegistryContent); try { diff --git a/test/__snapshots__/generate-template-registry.test.ts.snap b/test/__snapshots__/generate-template-registry.test.ts.snap index 892004e..ace2f95 100644 --- a/test/__snapshots__/generate-template-registry.test.ts.snap +++ b/test/__snapshots__/generate-template-registry.test.ts.snap @@ -1,5 +1,38 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`generates a template registry at a custom path 1`] = ` +"// components +import type Bar from "app/components/bar"; +import type FooBar from "app/components/foo-bar"; +import type Foo from "app/components/foo"; +import type FooBar2 from "app/components/foo/bar"; + +// helpers +import type Foo2 from "app/helpers/foo"; + +// modifiers +import type Foo3 from "app/modifiers/foo"; + +export default interface AppRegistry { + // components + bar: typeof Bar; + Bar: typeof Bar; + "foo-bar": typeof FooBar; + FooBar: typeof FooBar; + foo: typeof Foo; + Foo: typeof Foo; + "foo/bar": typeof FooBar2; + "Foo::Bar": typeof FooBar2; + + // helpers + foo: typeof Foo2; + + // modifiers + foo: typeof Foo3; +} +" +`; + exports[`generates a template registry for a v1 addon 1`] = ` "// components import type Bar from "v1-addon/components/bar"; diff --git a/test/generate-template-registry.test.ts b/test/generate-template-registry.test.ts index 0b11725..c63e254 100644 --- a/test/generate-template-registry.test.ts +++ b/test/generate-template-registry.test.ts @@ -2,11 +2,17 @@ import fsExtra from "fs-extra"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; import recursiveCopy from "recursive-copy"; -import { it } from "vitest"; +import { v4 as uuidv4 } from "uuid"; +import { afterEach, it } from "vitest"; import { generateTemplateRegistry } from "../src/generate-template-registry.ts"; +let cwd: string; + +afterEach(() => fsExtra.remove(cwd)); + it("generates a template registry for an app", async (ctx) => { - const cwd = await copyBlueprint("app"); + cwd = await copyBlueprint("app"); + await generateTemplateRegistry(cwd); const templateRegistryContent = await readFile( @@ -18,7 +24,8 @@ it("generates a template registry for an app", async (ctx) => { }); it("generates a template registry for a v1 addon", async (ctx) => { - const cwd = await copyBlueprint("v1-addon"); + cwd = await copyBlueprint("v1-addon"); + await generateTemplateRegistry(cwd); const templateRegistryContent = await readFile( @@ -30,7 +37,8 @@ it("generates a template registry for a v1 addon", async (ctx) => { }); it("generates a template registry for a v2 addon", async (ctx) => { - const cwd = await copyBlueprint("v2-addon"); + cwd = await copyBlueprint("v2-addon"); + await generateTemplateRegistry(cwd); const templateRegistryContent = await readFile( @@ -41,10 +49,24 @@ it("generates a template registry for a v2 addon", async (ctx) => { ctx.expect(templateRegistryContent).toMatchSnapshot(); }); -async function copyBlueprint(name) { - const cwd = join("test/output", name); +it("generates a template registry at a custom path", async (ctx) => { + cwd = await copyBlueprint("app"); + + await generateTemplateRegistry(cwd, { + path: "app/glint/template-registry.ts", + }); + + const templateRegistryContent = await readFile( + join(cwd, "app/glint/template-registry.ts"), + "utf-8", + ); + + ctx.expect(templateRegistryContent).toMatchSnapshot(); +}); + +async function copyBlueprint(name: "app" | "v1-addon" | "v2-addon") { + const cwd = join("test/output", uuidv4()); - await fsExtra.remove(cwd); await recursiveCopy(join("test", "blueprints", name), cwd); return cwd;