From a7f185206d25f1f36dcb00d1c41c2aea7d1c50b3 Mon Sep 17 00:00:00 2001 From: Rl0007 <12agrawalrahul@gmail.com> Date: Mon, 5 Oct 2026 11:47:40 +0530 Subject: [PATCH 1/2] feat(plugins): give a page a detail view with [...id].vue --- packages/plugin-kit/README.md | 15 +++ packages/plugin-kit/test/kit.test.js | 136 ++++++++++++++++++++++++++- packages/plugin-kit/vite.js | 75 ++++++++++++++- 3 files changed, 222 insertions(+), 4 deletions(-) diff --git a/packages/plugin-kit/README.md b/packages/plugin-kit/README.md index 3cc559e0..ab5b029f 100644 --- a/packages/plugin-kit/README.md +++ b/packages/plugin-kit/README.md @@ -93,6 +93,21 @@ export const plugin = { Every other folder and file under `commera/` is yours: components, composables, sub-pages. Only an `index.vue` at a placement path is built as a plugin. +### Detail pages + +Put a `[...id].vue` next to a page's `index.vue` to give it a detail view: + +``` +pages/orders/index.vue /commera/plugins//orders +pages/orders/[...id].vue /commera/plugins//orders/SAL-ORD-0001 +``` + +Everything after the page name reaches the detail as one prop named after the file, slashes included, so +`orders/INV/2026/001` gives `id = 'INV/2026/001'`. Declare it with `defineProps({ id: String })`. The detail +has no `plugin` block: it shares the page's sidebar row, `requires` and `condition`. Open it with +`navigate(\`orders/${name}\`)` and set its header with `usePage()`. A page holds one `[...name].vue`; +`[id].vue` (one URL segment) is reserved and is not built. + ## App logo Put an optional `commera/icon.svg` next to `pages/`. Set it once: it is the icon of everything your app diff --git a/packages/plugin-kit/test/kit.test.js b/packages/plugin-kit/test/kit.test.js index 5dd6f6c6..f766d280 100644 --- a/packages/plugin-kit/test/kit.test.js +++ b/packages/plugin-kit/test/kit.test.js @@ -12,7 +12,7 @@ import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { after, describe, test } from 'node:test'; import { fileURLToPath, pathToFileURL } from 'node:url'; -import commera from '../vite.js'; +import commera, { discoverPlugins } from '../vite.js'; // The kit installs nothing of its own; the dashboard's node_modules carries the vite and vue it builds with. const here = dirname(fileURLToPath(import.meta.url)); @@ -463,6 +463,140 @@ describe('guards', () => { }); }); +describe('detail pages', () => { + const detail = `\n\n`; + + // The built page runs on the real vue; only the host's usePlugin() is stood in for, so `path` can be set. + async function renderAt(outDir, entry, paths) { + writeFileSync(join(outDir, 'package.json'), '{"type":"module"}'); + const admin = join(outDir, '../../node_modules/@commera/admin'); + mkdirSync(admin, { recursive: true }); + writeFileSync( + join(admin, 'package.json'), + '{"name":"@commera/admin","type":"module","exports":"./index.js"}', + ); + writeFileSync( + join(admin, 'index.js'), + "import { ref } from 'vue'\nexport const path = ref('')\nexport const usePlugin = () => ({ path })\n", + ); + const { path } = await import(pathToFileURL(join(admin, 'index.js')).href); + const { createSSRApp } = await import( + pathToFileURL(join(dashboardModules, 'vue/index.mjs')).href + ); + const { renderToString } = await import( + pathToFileURL(join(dashboardModules, 'vue/server-renderer/index.mjs')) + .href + ); + const { default: Page } = await import( + pathToFileURL(join(outDir, entry)).href + ); + const html = []; + for (const value of paths) { + path.value = value; + html.push(await renderToString(createSSRApp(Page))); + } + return html; + } + + test('[...id].vue shows for any path under the page and gets all of it as its prop', async () => { + const { manifest, outDir } = await buildApp({ + 'pages/jobs/index.vue': vue( + `{ label: 'Print jobs', icon: 'printer' }`, + '', + ), + 'pages/jobs/[...id].vue': detail, + }); + assert.deepEqual( + manifest.entries.map((entry) => [entry.name, entry.module]), + [['jobs', 'pages/jobs.js']], + ); + assert.deepEqual( + await renderAt(outDir, 'pages/jobs.js', ['', 'JOB-1', 'INV/2026/001']), + [ + '
list
', + '
JOB-1
', + '
INV/2026/001
', + ], + ); + }); + + test('the prop is named after the file', async () => { + const { outDir } = await buildApp({ + 'pages/jobs/index.vue': page, + 'pages/jobs/[...jobName].vue': `\n\n`, + }); + assert.deepEqual(await renderAt(outDir, 'pages/jobs.js', ['JOB-7']), [ + '
JOB-7
', + ]); + }); + + test('[id].vue warns and is not built', async () => { + const files = { + 'pages/jobs/index.vue': page, + 'pages/jobs/[id].vue': + '', + }; + const compiler = await import( + pathToFileURL(join(dashboardModules, 'vue/compiler-sfc/index.mjs')).href + ); + const warnings = []; + const { entries } = discoverPlugins(join(makeApp(files), 'commera'), { + app: APP, + compiler, + warn: (message) => warnings.push(message), + }); + assert.equal(entries[0].detail, undefined); + assert.match( + warnings[0], + /pages\/jobs\/\[id\]\.vue is reserved[\s\S]*\[\.\.\.id\]\.vue/, + ); + const { read } = await buildApp(files); + assert.doesNotMatch(read('pages/jobs.js'), /single/); + }); + + test('outside a page folder fails', async () => { + await buildFails( + { + 'pages/jobs/index.vue': page, + 'order/cards/status/[...id].vue': detail, + }, + /order\/cards\/status\/\[\.\.\.id\]\.vue: only a page folder/, + ); + }); + + test('without the page index.vue fails', async () => { + await buildFails( + { 'pages/jobs/[...id].vue': detail }, + /pages\/jobs\/\[\.\.\.id\]\.vue: add pages\/jobs\/index\.vue/, + ); + }); + + test('two in one page fails', async () => { + await buildFails( + { + 'pages/jobs/index.vue': page, + 'pages/jobs/[...id].vue': detail, + 'pages/jobs/[...name].vue': detail, + }, + /pages\/jobs\/\[\.\.\.name\]\.vue: pages\/jobs already has pages\/jobs\/\[\.\.\.id\]\.vue/, + ); + }); + + test('a prop name that is not camelCase fails', async () => { + await buildFails( + { 'pages/jobs/index.vue': page, 'pages/jobs/[...job-id].vue': detail }, + /'job-id' must be a camelCase prop name/, + ); + }); + + test('a plugin block in it fails', async () => { + await buildFails( + { 'pages/jobs/index.vue': page, 'pages/jobs/[...id].vue': page }, + /pages\/jobs\/\[\.\.\.id\]\.vue declares a plugin block, but only a placement's index\.vue may/, + ); + }); +}); + describe('a clean build', () => { test('the manifest covers every placement and the shipped JS keeps no dotted path', async () => { const card = (label) => diff --git a/packages/plugin-kit/vite.js b/packages/plugin-kit/vite.js index a0eddeed..623d36aa 100644 --- a/packages/plugin-kit/vite.js +++ b/packages/plugin-kit/vite.js @@ -25,6 +25,10 @@ const NAME = /^[a-z0-9][a-z0-9-]{0,39}$/; const DOTTED_PATH = /^[A-Za-z_]\w*(\.[A-Za-z_]\w*)+$/; const EMPTY_ENTRY = '\0commera-empty-entry'; const EMPTY_ENTRY_PATH = '__commera_empty_entry__'; +const PAGE_ENTRY = '\0commera-page:'; +const PAGE_ENTRY_PATH = '__commera_page__/'; +const DYNAMIC_FILE = /^\[(\.\.\.)?([^\]]*)\]\.vue$/; +const PARAM = /^[a-z][A-Za-z0-9]{0,39}$/; const SKIPPED_DIRS = new Set(['node_modules', 'dist']); const ICON_FILE = 'icon.svg'; const ICON_LIMIT = 20 * 1024; @@ -343,11 +347,25 @@ export function discoverPlugins( const errors = []; const entries = []; const seen = new Map(); + const details = new Map(); for (const file of walk(sourceDir).sort()) { const display = toPosix(relative(sourceDir, file)); const isIndex = basename(file) === 'index.vue'; const folder = toPosix(dirname(display)); const placement = isIndex ? matchPlacement(folder) : null; + const dynamic = basename(file).match(DYNAMIC_FILE); + if (dynamic) { + const [, rest, param] = dynamic; + const problem = getDetailProblem(folder, rest, param, details); + if (problem === 'reserved') { + warn( + `${display} is reserved for a later Commera; it was not built. Name it [...${param}].vue to get the rest of the URL`, + ); + continue; + } + if (problem) errors.push(`${display}: ${problem}`); + else details.set(folder, { file, param, display }); + } const block = readPluginBlock(compiler, file, display); if (!placement) { @@ -406,9 +424,46 @@ export function discoverPlugins( plugin, }); } + for (const [folder, detail] of details) { + const entry = entries.find( + (entry) => entry.place === 'pages' && `pages/${entry.name}` === folder, + ); + if (entry) entry.detail = detail; + else if (!existsSync(join(sourceDir, folder, 'index.vue'))) + errors.push( + `${detail.display}: add ${folder}/index.vue; a detail page needs its list page`, + ); + } return { entries, errors }; } +function getDetailProblem(folder, rest, param, details) { + if (matchPlacement(folder)?.place !== 'pages') + return 'only a page folder (pages//) may hold a [...param].vue'; + if (!rest) return 'reserved'; + if (!PARAM.test(param)) + return `'${param}' must be a camelCase prop name, such as [...id].vue`; + if (details.has(folder)) + return `${folder} already has ${details.get(folder).display}`; +} + +// The page stays mounted while its URL changes, so the detail is keyed by its id to load each record fresh. +function pageEntryCode(entry) { + const { file, param } = entry.detail; + return [ + "import { defineComponent, h } from 'vue';", + "import { usePlugin } from '@commera/admin';", + `import Page from ${JSON.stringify(entry.file)};`, + `import Detail from ${JSON.stringify(file)};`, + 'export default defineComponent({', + '\tsetup() {', + '\t\tconst { path } = usePlugin();', + `\t\treturn () => (path.value ? h(Detail, { key: path.value, ${param}: path.value }) : h(Page));`, + '\t},', + '});', + ].join('\n'); +} + function readHostFile(hostDir, name) { const path = join(hostDir, name); return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : null; @@ -431,7 +486,7 @@ function stripPlainScript(compiler, code, id) { return code.slice(0, start) + code.slice(end); } -function guard({ hostDir, compiler, pluginFiles }) { +function guard({ hostDir, compiler, pluginFiles, pageEntries }) { const knownClasses = readHostFile(hostDir, 'classes.json'); const sharedExports = readHostFile(hostDir, 'shared-exports.json'); const knownClassSet = knownClasses ? new Set(knownClasses) : null; @@ -447,6 +502,8 @@ function guard({ hostDir, compiler, pluginFiles }) { }, resolveId(source) { if (source.endsWith(EMPTY_ENTRY_PATH)) return EMPTY_ENTRY; + const pageEntry = source.split(PAGE_ENTRY_PATH)[1]; + if (pageEntries.has(pageEntry)) return `${PAGE_ENTRY}${pageEntry}`; const sharesRoot = SHARED_ROOTS.some((name) => source.startsWith(`${name}/`), ); @@ -460,6 +517,8 @@ function guard({ hostDir, compiler, pluginFiles }) { }, load(id) { if (id === EMPTY_ENTRY) return 'export {};'; + if (id.startsWith(PAGE_ENTRY)) + return pageEntries.get(id.slice(PAGE_ENTRY.length)); }, transform(code, id) { if (id.includes('/node_modules/')) return; @@ -662,14 +721,24 @@ export default async function commeraPlugin({ .join('\n')}\n`, ); } + const pageEntries = new Map( + entries + .filter((entry) => entry.detail) + .map((entry) => [entry.entryName, pageEntryCode(entry)]), + ); const moduleEntries = Object.fromEntries( entries .filter((entry) => entry.entryName) - .map((entry) => [entry.entryName, entry.file]), + .map((entry) => [ + entry.entryName, + pageEntries.has(entry.entryName) + ? `${PAGE_ENTRY_PATH}${entry.entryName}` + : entry.file, + ]), ); const pluginFiles = new Set(entries.map((entry) => entry.file)); return [ - guard({ hostDir: resolvedHostDir, compiler, pluginFiles }), + guard({ hostDir: resolvedHostDir, compiler, pluginFiles, pageEntries }), vue(), finish({ app, entries, icon: appIcon.icon }), { From 86a5f858ce7f34868f19c91b94c0402c55b62bbf Mon Sep 17 00:00:00 2001 From: Rl0007 <12agrawalrahul@gmail.com> Date: Mon, 5 Oct 2026 11:48:12 +0530 Subject: [PATCH 2/2] docs(plugins): add a screenshot of a plugin detail page --- .../implementation/media/plugin-detail-page.png | Bin 0 -> 31417 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/implementation/media/plugin-detail-page.png diff --git a/docs/implementation/media/plugin-detail-page.png b/docs/implementation/media/plugin-detail-page.png new file mode 100644 index 0000000000000000000000000000000000000000..5ebc9a290f7563f0080b45ece70a5f47972b98bf GIT binary patch literal 31417 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 Yb(-9(rmOnjcv*?vB&UgoWnZuV2Lm$7!vFvP literal 0 HcmV?d00001