diff --git a/src/loader.ts b/src/loader.ts index f89b130..5203dbf 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -7,9 +7,10 @@ * file that was distributed with this source code. */ -import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' +import string from '@poppinss/utils/string' import { join, isAbsolute } from 'node:path' +import { readFileSync, readdirSync } from 'node:fs' import type { LoaderContract, LoaderTemplate } from './types.js' /** @@ -46,6 +47,34 @@ export class Loader implements LoaderContract { } } + /** + * Returns a list of components for a given disk + */ + #getDiskComponents(diskName: string): { componentName: string; tagName: string }[] { + const diskBasePath = this.#mountedDirs.get(diskName)! + const files = readdirSync(join(diskBasePath, 'components'), { + recursive: true, + encoding: 'utf8', + }).filter((file) => file.endsWith('.edge')) + + return files.map((file) => { + const fileName = file.replace(/\.edge$/, '') + const componentPath = `components/${fileName}` + const tagName = fileName + .split('/') + .filter((segment, index) => { + return index === 0 || segment !== 'index' + }) + .map((segment) => string.camelCase(segment)) + .join('.') + + return { + componentName: diskName !== 'default' ? `${diskName}::${componentPath}` : componentPath, + tagName: diskName !== 'default' ? `${diskName}.${tagName}` : tagName, + } + }) + } + /** * Extracts the disk name and the template name from the template * path expression. @@ -104,7 +133,7 @@ export class Loader implements LoaderContract { * // output * * { - * 'form.label': { template: '/users/virk/code/app/form/label' } + * 'form.label': { template: 'Template contents' } * } * ``` */ @@ -260,4 +289,23 @@ export class Loader implements LoaderContract { remove(templatePath: string) { this.#preRegistered.delete(templatePath) } + + /** + * Returns a list of components from all the disks. We assume + * the components are stored within the components directory. + * + * Also, we treat all in-memory templates as components. + * + * The return path is same the path you will pass to the `@component` + * tag. + */ + listComponents(): { diskName: string; components: string[] }[] { + const diskNames = [...this.#mountedDirs.keys()] + return diskNames.map((diskName) => { + return { + diskName, + components: this.#getDiskComponents(diskName), + } + }) + } } diff --git a/tests/loader.spec.ts b/tests/loader.spec.ts index 2092e22..796fafb 100644 --- a/tests/loader.spec.ts +++ b/tests/loader.spec.ts @@ -148,4 +148,117 @@ test.group('Loader', () => { assert.throws(fn, 'Cannot override previously registered "my-view" template') }) + + test('get components for the default disk', async ({ assert, fs }) => { + await fs.create('components/foo.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/foo', + tagName: 'foo', + }, + ], + }, + ]) + }) + + test('get components from nested directories', async ({ assert, fs }) => { + await fs.create('components/modal/root.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/modal/root', + tagName: 'modal.root', + }, + ], + }, + ]) + }) + + test('get components from nested directories from a named disk', async ({ assert, fs }) => { + await fs.create('components/modal/root.edge', 'Hello world') + + const loader = new Loader() + loader.mount('uikit', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'uikit', + components: [ + { + componentName: 'uikit::components/modal/root', + tagName: 'uikit.modal.root', + }, + ], + }, + ]) + }) + + test('rename nested components saved in index.edge file', async ({ assert, fs }) => { + await fs.create('components/modal/index.edge', 'Hello world') + await fs.create('components/index.edge', 'Hello world') + + const loader = new Loader() + loader.mount('uikit', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'uikit', + components: [ + { + componentName: 'uikit::components/index', + tagName: 'uikit.index', + }, + { + componentName: 'uikit::components/modal/index', + tagName: 'uikit.modal', + }, + ], + }, + ]) + }) + + test('rename nested components saved in index.edge file from default disk', async ({ + assert, + fs, + }) => { + await fs.create('components/modal/index.edge', 'Hello world') + await fs.create('components/index.edge', 'Hello world') + + const loader = new Loader() + loader.mount('default', fs.basePath) + + const componentsList = loader.listComponents() + assert.deepEqual(componentsList, [ + { + diskName: 'default', + components: [ + { + componentName: 'components/index', + tagName: 'index', + }, + { + componentName: 'components/modal/index', + tagName: 'modal', + }, + ], + }, + ]) + }) })