From c7ae9c98aad5ff084219d0613375d02f24e49aae Mon Sep 17 00:00:00 2001 From: Romain Hamel Date: Tue, 24 Jun 2025 18:54:39 +0200 Subject: [PATCH 1/2] feat(vue): detect vue entrypoint from vite index --- packages/vue/src/index.ts | 5 ++-- packages/vue/src/plugins/renderer.ts | 34 ++++++++++++++++----- packages/vue/test/renderer.spec.ts | 45 ++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index d71686e2..44f59847 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -10,10 +10,8 @@ import type { VitePlugin } from 'unplugin' export const compodium = /* #__PURE__ */ (opts?: Partial>) => { const options = defu(opts, { - rootDir: './', dir: './compodium', - includeLibraryCollections: true, - mainPath: 'src/main.ts' + includeLibraryCollections: true }) as PluginOptions options.componentDirs ??= [ @@ -31,6 +29,7 @@ export const compodium = /* #__PURE__ */ (opts?: Partial]*>/gi) || [] + for (const tag of scriptTags) { + // Extract src attribute + const srcMatch = tag.match(/src=["']([^"']+)["']/i) + if (srcMatch && srcMatch[1]) { + const path = srcMatch[1].replace(/^\//, './') + if (path.startsWith('http://') || path.startsWith('https://')) continue + return path + } + } +} + export function rendererPlugin(config: PluginConfig): VitePlugin { + const indexPath = resolve(config.rootDir, 'index.html') + const index = readFileSync(indexPath, 'utf-8') + + const inferredMainPath = inferMainPath(index) + const mainPath = resolve(config.rootDir, (config.mainPath ?? inferredMainPath) as string) + if (!config.mainPath && !inferredMainPath) { + throw new Error('[Compodium] Could not infer main script path. Use the mainPath option to specify the path to your Vue script file containing createApp().') + } + return { name: 'compodium:renderer', enforce: 'pre', configureServer(server) { server.middlewares.use('/__compodium__/renderer', async (_req, res) => { try { - // TODO: Should read the user's index.html and replace the body. - const indexPath = resolve(config.rootDir, 'index.html') - let index = readFileSync(indexPath, 'utf-8') - index = index.replace( + const rendererIndex = index.replace( /]*>[\s\S]*<\/body>/i, `
@@ -23,7 +43,7 @@ export function rendererPlugin(config: PluginConfig): VitePlugin { ` ) res.setHeader('Content-Type', 'text/html') - res.end(index) + res.end(rendererIndex) } catch { res.statusCode = 500 res.end('Internal Server Error') @@ -38,9 +58,9 @@ export function rendererPlugin(config: PluginConfig): VitePlugin { load(id) { if (id === '\0@compodium/renderer.ts') { // Read the user's main entrypoint file - const mainPath = resolve(config.rootDir, config.mainPath ?? 'src/main.ts') + if (!existsSync(mainPath)) { - throw new Error(`[Compodium] failed to resolve main file ${config.mainPath}.`) + throw new Error(`[Compodium] failed to resolve main file ${mainPath}. Use the mainPath option to specify the path to your Vue script file containing createApp().`) } const mainDir = dirname(mainPath) diff --git a/packages/vue/test/renderer.spec.ts b/packages/vue/test/renderer.spec.ts index a2982409..e34e644e 100644 --- a/packages/vue/test/renderer.spec.ts +++ b/packages/vue/test/renderer.spec.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from 'vitest' import { createViteServer } from './utils' +import { inferMainPath } from '../src/plugins/renderer' describe('renderer', async () => { const server = await createViteServer('./fixtures/basic') @@ -28,3 +29,47 @@ describe('renderer', async () => { }) }) }) + +describe('inferMainPath', async () => { + it('works', async () => { + expect(inferMainPath(` + + + + + + + Compodium Playground + + +
+ + + + `)).toBe('./src/main.ts') + }) + + it('ignores remote scripts', async () => { + expect(inferMainPath(` + + + + + + + `)).toBe(undefined) + }) + + it('returns first match', async () => { + expect(inferMainPath(` + + + + + + + + + `)).toBe('./src/main.ts') + }) +}) From 651cb988c1ddd427abd1855931933d89bc97f97f Mon Sep 17 00:00:00 2001 From: Romain Hamel Date: Wed, 25 Jun 2025 22:57:12 +0200 Subject: [PATCH 2/2] fix: config resolution inconsistencies --- packages/core/src/index.ts | 70 +++----------------- packages/core/src/plugins/collections.ts | 81 +++++++++++++++++++++--- packages/core/src/plugins/colors.ts | 7 +- packages/core/src/plugins/devtools.ts | 11 +++- packages/core/src/plugins/examples.ts | 25 ++++---- packages/core/src/plugins/iconify.ts | 6 +- packages/core/src/plugins/meta/index.ts | 37 ++++++----- packages/core/src/plugins/utils.ts | 5 +- packages/core/src/types.ts | 14 ++-- packages/nuxt/src/module.ts | 5 +- packages/nuxt/test/basic.nuxt.test.ts | 2 +- packages/vue/src/index.ts | 30 ++------- packages/vue/src/plugins/renderer.ts | 31 +++++---- packages/vue/src/plugins/vueDevtools.ts | 13 +++- 14 files changed, 178 insertions(+), 159 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 3f14aeab..97623298 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,75 +1,21 @@ -import { joinURL } from 'ufo' - -import { libraryCollections as libraryCollectionsConfig } from '@compodium/examples' import { collectionsPlugin } from './plugins/collections' import { extendMetaPlugin, metaPlugin } from './plugins/meta' import { examplePlugin } from './plugins/examples' import { devtoolsPlugin } from './plugins/devtools' import { colorsPlugin } from './plugins/colors' import { iconifyPlugin } from './plugins/iconify' - -import type { Collection, PluginConfig, PluginOptions } from './types' -import { resolve } from 'pathe' +import type { PluginOptions } from './types' export * from './types' -export function resolveConfig(options: PluginOptions): PluginConfig { - const exampleDir = { - path: joinURL(options.rootDir, options.dir, 'examples'), - pattern: '**/*.{vue,tsx}' - } - - const componentDirs = options?.componentDirs.map((dir) => { - const componentDir = typeof dir === 'string' ? { path: dir } : dir - return { - pattern: '**/*.{vue,tsx}', - ...componentDir, - path: resolve(options.rootDir, componentDir.path), - ignore: (componentDir.ignore ?? []).concat(options.ignore ?? []) - } - }).filter(collection => !collection.path?.includes('node_modules/')) - - const componentCollection: Collection = { - name: 'Components', - exampleDir, - dirs: componentDirs - } - - const libraryCollections = options.includeLibraryCollections - ? libraryCollectionsConfig.map(collection => ({ - ...collection, - exampleDir: { - path: collection.exampleDir, - pattern: '**/*.{vue,tsx}', - prefix: collection.prefix - }, - dirs: [{ - path: collection.path, - pattern: '**/*.{vue,tsx}', - ignore: collection.ignore, - prefix: collection.prefix - }] - })) - : [] - - return { - ...options, - libraryCollections, - componentCollection, - baseUrl: '/' - } -} - export const compodium = /* #__PURE__ */ (options: PluginOptions) => { - const config = resolveConfig(options) - return [ - collectionsPlugin(config), - metaPlugin(config), - extendMetaPlugin(config), - devtoolsPlugin(config), - examplePlugin(config), - iconifyPlugin(config), - colorsPlugin(config) + collectionsPlugin(options), + metaPlugin(options), + extendMetaPlugin(options), + devtoolsPlugin(options), + examplePlugin(options), + iconifyPlugin(options), + colorsPlugin(options) ] } diff --git a/packages/core/src/plugins/collections.ts b/packages/core/src/plugins/collections.ts index e852db01..c15eaf42 100644 --- a/packages/core/src/plugins/collections.ts +++ b/packages/core/src/plugins/collections.ts @@ -1,24 +1,83 @@ -import type { PluginConfig } from '../types' +import { libraryCollections as libraryCollectionsConfig } from '@compodium/examples' +import type { PluginOptions, Collection } from '../types' import { scanComponents } from './utils' import { watch } from 'chokidar' import type { VitePlugin } from 'unplugin' import { resolve } from 'pathe' +import { joinURL } from 'ufo' +import type { ResolvedConfig } from 'vite' + +export function resolveCollections(options: PluginOptions, viteConfig: ResolvedConfig): Collection[] { + const rootDir = options.rootDir ?? viteConfig.root + + const exampleDir = { + path: joinURL(rootDir, options.dir, 'examples'), + pattern: '**/*.{vue,tsx}' + } + + const componentDirs = options?.componentDirs.map((dir) => { + const componentDir = typeof dir === 'string' ? { path: dir } : dir + return { + pattern: '**/*.{vue,tsx}', + ...componentDir, + path: resolve(rootDir, componentDir.path), + ignore: (componentDir.ignore ?? []).concat(options.ignore ?? []) + } + }).filter(collection => !collection.path?.includes('node_modules/')) + + const componentCollection: Collection = { + name: 'Components', + exampleDir, + dirs: componentDirs + } + + const libraryCollections = options.includeLibraryCollections + ? libraryCollectionsConfig.map(collection => ({ + ...collection, + exampleDir: { + path: collection.exampleDir, + pattern: '**/*.{vue,tsx}', + prefix: collection.prefix + }, + dirs: [{ + path: collection.path, + pattern: '**/*.{vue,tsx}', + ignore: collection.ignore, + prefix: collection.prefix + }] + })) + : [] + + return [ + componentCollection, + ...libraryCollections + ] +} + +export function collectionsPlugin(options: PluginOptions): VitePlugin { + let collections: Collection[] -export function collectionsPlugin(config: PluginConfig): VitePlugin { return { name: 'compodium:collections', + apply: 'serve', + enforce: 'post', + + configResolved(viteConfig) { + collections = resolveCollections(options, viteConfig) + }, + configureServer(server) { server.middlewares.use('/__compodium__/api/collections', async (_, res) => { try { - const collections = await Promise.all([config.componentCollection, ...config.libraryCollections].map(async (col) => { - const components = await scanComponents(col.dirs, config.rootDir) - const examples = await scanComponents([col.exampleDir], config.rootDir) + const result = await Promise.all(collections.map(async (col) => { + const components = await scanComponents(col.dirs) + const examples = await scanComponents([col.exampleDir]) const collectionComponents = components.flatMap((c) => { const componentExamples = examples?.filter(e => e.pascalName.startsWith(`${c.pascalName}Example`)).map(e => ({ ...e, isExample: true, - componentPath: resolve(config.rootDir, c.filePath) + componentPath: c.filePath })) const mainExample = componentExamples.find(e => e.pascalName === `${c.pascalName}Example`) @@ -42,7 +101,7 @@ export function collectionsPlugin(config: PluginConfig): VitePlugin { })) res.setHeader('Content-Type', 'application/json') - res.write(JSON.stringify(collections)) + res.write(JSON.stringify(result)) res.end() } catch { res.statusCode = 500 @@ -50,10 +109,12 @@ export function collectionsPlugin(config: PluginConfig): VitePlugin { } }) + const componentCollection = collections.find(c => c.name === 'Components') as Collection + const watchedPaths = [ - ...config.componentCollection.dirs, - config.componentCollection.exampleDir - ].map(d => resolve(config.rootDir, d.path)) + ...componentCollection.dirs, + componentCollection.exampleDir + ].map(d => d.path) // Watch for changes in example directory const watcher = watch(watchedPaths, { diff --git a/packages/core/src/plugins/colors.ts b/packages/core/src/plugins/colors.ts index a17282d5..85e9888b 100644 --- a/packages/core/src/plugins/colors.ts +++ b/packages/core/src/plugins/colors.ts @@ -1,18 +1,19 @@ import type { VitePlugin } from 'unplugin' -import type { PluginConfig } from '../types' +import type { PluginOptions } from '../types' /** * This plugin is responsible for getting the generated virtual templates and * making them available to the Vue build. */ -export function colorsPlugin(config: PluginConfig): VitePlugin { +export function colorsPlugin(options: PluginOptions): VitePlugin { return { name: 'compodium:colors', + apply: 'serve', configureServer(server) { server.middlewares.use('/__compodium__/api/colors', async (req, res) => { try { res.setHeader('Content-Type', 'application/json') - res.end(JSON.stringify(config.extras?.colors)) + res.end(JSON.stringify(options.extras?.colors)) } catch (err) { res.statusCode = 500 res.end(JSON.stringify({ error: `Failed to fetch iconify API: ${err}` })) diff --git a/packages/core/src/plugins/devtools.ts b/packages/core/src/plugins/devtools.ts index 64e79c31..a600b486 100644 --- a/packages/core/src/plugins/devtools.ts +++ b/packages/core/src/plugins/devtools.ts @@ -1,17 +1,22 @@ import type { VitePlugin } from 'unplugin' import sirv from 'sirv' -import type { PluginConfig } from '../types' +import type { PluginOptions } from '../types' import { resolve, dirname } from 'pathe' import { fileURLToPath } from 'node:url' import { existsSync } from 'node:fs' import { joinURL } from 'ufo' import { resolvePathSync } from 'mlly' -export function devtoolsPlugin(config: PluginConfig): VitePlugin { - const userPreview = resolve(joinURL(config.rootDir, config.dir, 'preview.vue')) +export function devtoolsPlugin(options: PluginOptions): VitePlugin { + let userPreview: string return { name: 'compodium:devtools', + apply: 'serve', + + configResolved(viteConfig) { + userPreview = resolve(joinURL(options.rootDir ?? viteConfig.root, options.dir, 'preview.vue')) + }, config(config) { if (process.env.COMPODIUM_DEVTOOLS_URL) { config.server ||= {} diff --git a/packages/core/src/plugins/examples.ts b/packages/core/src/plugins/examples.ts index 96c1dd15..4eac1e1a 100644 --- a/packages/core/src/plugins/examples.ts +++ b/packages/core/src/plugins/examples.ts @@ -1,18 +1,21 @@ import fs from 'node:fs/promises' import type { VitePlugin } from 'unplugin' -import type { PluginConfig } from '../types' -import { resolve } from 'node:path' +import type { Collection, PluginOptions } from '../types' +import { resolveCollections } from './collections' + +export function examplePlugin(options: PluginOptions): VitePlugin { + let collections: Collection[] -export function examplePlugin(config: PluginConfig): VitePlugin { return { name: 'compodium:examples', + apply: 'serve', - configureServer(server) { - const paths = [ - config.componentCollection.exampleDir, - ...config.libraryCollections.map(c => c.exampleDir) - ].map(d => resolve(config.rootDir, d.path)) + configResolved(viteConfig) { + collections = resolveCollections(options, viteConfig) + }, + configureServer(server) { + const allowedPaths = collections.map(c => c.exampleDir.path) server.middlewares.use('/__compodium__/api/example', async (req, res) => { try { const url = new URL(req.url!, `http://${req.headers.host}`) @@ -24,9 +27,9 @@ export function examplePlugin(config: PluginConfig): VitePlugin { return } - if (!paths.find(p => path.startsWith(p))) { + if (!allowedPaths.find(p => path.startsWith(p))) { res.statusCode = 403 - res.end(JSON.stringify({ error: 'Forbidden' })) + res.end(JSON.stringify({ error: 'Forbidden', message: `${allowedPaths}\n ${path}` })) return } @@ -35,7 +38,7 @@ export function examplePlugin(config: PluginConfig): VitePlugin { let result = exampleCode.toString() .replace(/extendCompodiumMeta\s*\([\s\S]*?\)\s*;?/g, '') - if (config._nuxt) { + if (options._nuxt) { result = result .replace(/import .* from 'vue'/, '') } diff --git a/packages/core/src/plugins/iconify.ts b/packages/core/src/plugins/iconify.ts index 497b00cf..add34f71 100644 --- a/packages/core/src/plugins/iconify.ts +++ b/packages/core/src/plugins/iconify.ts @@ -1,11 +1,11 @@ import type { VitePlugin } from 'unplugin' -import type { PluginConfig } from '../types' +import type { PluginOptions } from '../types' import { joinURL } from 'ufo' -export function iconifyPlugin(_config: PluginConfig): VitePlugin { +export function iconifyPlugin(_options: PluginOptions): VitePlugin { return { name: 'compodium:iconify', - + apply: 'serve', configureServer(server) { server.middlewares.use('/__compodium__/api/iconify', async (req, res) => { try { diff --git a/packages/core/src/plugins/meta/index.ts b/packages/core/src/plugins/meta/index.ts index b04eb4d2..37737e59 100644 --- a/packages/core/src/plugins/meta/index.ts +++ b/packages/core/src/plugins/meta/index.ts @@ -1,13 +1,13 @@ import { readFile } from 'node:fs/promises' -import type { PluginConfig } from '../../types' +import type { Collection, PluginOptions } from '../../types' import { createChecker } from './checker' import { watch } from 'chokidar' import type { VitePlugin } from 'unplugin' -import { resolve } from 'pathe' import AST from 'unplugin-ast/vite' +import { resolveCollections } from '../collections' -export function extendMetaPlugin(_config: PluginConfig): VitePlugin { +export function extendMetaPlugin(_options: PluginOptions): VitePlugin { return AST({ include: [/\.[jt]sx?$/, /\.vue$/], enforce: 'post', @@ -24,20 +24,25 @@ export function extendMetaPlugin(_config: PluginConfig): VitePlugin { }) } -export function metaPlugin(config: PluginConfig): VitePlugin { - const checkerDirs = [ - ...config.componentCollection.dirs, - config.componentCollection.exampleDir, - ...config.libraryCollections.flatMap(c => c.dirs), - ...config.libraryCollections.map(c => c.exampleDir) - ] - - const checker = createChecker(checkerDirs) +export function metaPlugin(options: PluginOptions): VitePlugin { + let collections: Collection[] return { name: 'compodium:meta', + enforce: 'pre', + apply: 'serve', + + configResolved(viteConfig) { + collections = resolveCollections(options, viteConfig) + }, configureServer(server) { + const checkerDirs = collections.flatMap(c => [ + ...c.dirs, + c.exampleDir + ]) + const checker = createChecker(checkerDirs) + server.middlewares.use('/__compodium__/api/meta', async (req, res) => { try { const url = new URL(req.url!, `http://${req.headers.host}`) @@ -65,10 +70,12 @@ export function metaPlugin(config: PluginConfig): VitePlugin { } }) + const componentCollection = collections.find(c => c.name === 'Components') as Collection + const watchedPaths = [ - ...config.componentCollection.dirs, - config.componentCollection.exampleDir - ].map(d => resolve(config.rootDir, d.path)) + ...componentCollection.dirs, + componentCollection.exampleDir + ].map(d => d.path) // Watch for changes in example directory const watcher = watch(watchedPaths, { diff --git a/packages/core/src/plugins/utils.ts b/packages/core/src/plugins/utils.ts index 67da1c75..89552325 100644 --- a/packages/core/src/plugins/utils.ts +++ b/packages/core/src/plugins/utils.ts @@ -43,10 +43,9 @@ export function resolveComponentNameSegments(fileName: string, prefixParts: stri * Scan the components inside different components folders * and return a unique list of components * @param dirs all folders where components are defined - * @param srcDir src path of your app * @returns {Promise} Component found promise */ -export async function scanComponents(dirs: ComponentsDir[], srcDir: string): Promise { +export async function scanComponents(dirs: ComponentsDir[]): Promise { // All scanned components const components: Component[] = [] @@ -123,7 +122,7 @@ export async function scanComponents(dirs: ComponentsDir[], srcDir: string): Pro // Ignore files like `~/components/index.vue` which end up not having a name at all if (!pascalName) { - console.warn(`[Compodium] Component did not resolve to a file name in \`~/${relative(srcDir, filePath)}\`.`) + console.warn(`[Compodium] Component did not resolve to a file name in \`${filePath}\`.`) continue } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 5b703484..e8fa884b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -5,9 +5,13 @@ import type { InputSchema } from './plugins/meta/infer' export type PluginOptions = { /** * Project root directory. - * @defaultValue `process.cwd()` */ - rootDir: string + rootDir?: string + + /** + * Project base url. + */ + baseUrl?: string /** * Customize the directories where components are discovered @@ -53,12 +57,6 @@ export type PluginOptions = { _nuxt?: boolean } -export type PluginConfig = PluginOptions & { - libraryCollections: Collection[] - componentCollection: Collection - baseUrl: string -} - export type IconifyIcon = string & {} export type * from './plugins/meta/infer' diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 41104389..9d42986d 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -7,7 +7,7 @@ import { defu } from 'defu' import { compodium } from '@compodium/core' import type { PluginOptions } from '@compodium/core' -export type ModuleOptions = Omit +export type ModuleOptions = Omit export default defineNuxtModule({ meta: { @@ -20,8 +20,6 @@ export default defineNuxtModule({ }, async setup(options, nuxt) { - // addImports({ name: 'extendCompodiumMeta', from: '@compodium/core/runtime/composables/extendCompodiumMeta' }) - if (!nuxt.options.dev) return const { resolve } = createResolver(import.meta.url) @@ -49,6 +47,7 @@ export default defineNuxtModule({ addVitePlugin(compodium({ componentDirs: dirs, rootDir: nuxt.options.rootDir, + baseUrl: nuxt.options.app.baseURL, _nuxt: true, ...options })) diff --git a/packages/nuxt/test/basic.nuxt.test.ts b/packages/nuxt/test/basic.nuxt.test.ts index 76693b0a..9f4574a2 100644 --- a/packages/nuxt/test/basic.nuxt.test.ts +++ b/packages/nuxt/test/basic.nuxt.test.ts @@ -58,7 +58,7 @@ describe('basic', async () => { it('works', async () => { const example = await $fetch(`/__compodium__/api/example`, { query: { - path: joinURL(rootDir, '/compodium/examples/BasicComponentExample.vue') + path: joinURL(rootDir, './compodium/examples/BasicComponentExample.vue') } }) expect(example).toMatchInlineSnapshot(` diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 44f59847..8b744ad4 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,14 +1,12 @@ import type { CompodiumMeta, PluginOptions } from '@compodium/core' -import { compodium as core, resolveConfig } from '@compodium/core' +import { compodium as core } from '@compodium/core' + import { rendererPlugin } from './plugins/renderer' import { vueDevtoolsPlugin } from './plugins/vueDevtools' + import { defu } from 'defu' -import { libraryCollections } from '@compodium/examples' -import { existsSync } from 'node:fs' -import { resolve } from 'pathe' -import type { VitePlugin } from 'unplugin' -export const compodium = /* #__PURE__ */ (opts?: Partial>) => { +export const compodium = /* #__PURE__ */ (opts?: Partial>) => { const options = defu(opts, { dir: './compodium', includeLibraryCollections: true @@ -18,24 +16,10 @@ export const compodium = /* #__PURE__ */ (opts?: Partial existsSync(resolve(options.rootDir, `node_modules/${c.package}`))) - options.componentDirs = options.componentDirs.concat(collections) - } - - const config = resolveConfig(options) - return [ - { - configResolved(viteConfig) { - config.baseUrl = viteConfig.base - config.rootDir = viteConfig.root - } - } as VitePlugin, - - core(config), - rendererPlugin(config), - vueDevtoolsPlugin(config) + core(options), + rendererPlugin(options), + vueDevtoolsPlugin(options) ] } diff --git a/packages/vue/src/plugins/renderer.ts b/packages/vue/src/plugins/renderer.ts index 176baacd..64be675e 100644 --- a/packages/vue/src/plugins/renderer.ts +++ b/packages/vue/src/plugins/renderer.ts @@ -1,5 +1,5 @@ import type { VitePlugin } from 'unplugin' -import type { PluginConfig } from '@compodium/core' +import type { PluginOptions } from '@compodium/core' import { readFileSync, existsSync } from 'node:fs' import { dirname, resolve } from 'node:path' import { joinURL } from 'ufo' @@ -19,19 +19,25 @@ export function inferMainPath(indexContent: string) { } } -export function rendererPlugin(config: PluginConfig): VitePlugin { - const indexPath = resolve(config.rootDir, 'index.html') - const index = readFileSync(indexPath, 'utf-8') - - const inferredMainPath = inferMainPath(index) - const mainPath = resolve(config.rootDir, (config.mainPath ?? inferredMainPath) as string) - if (!config.mainPath && !inferredMainPath) { - throw new Error('[Compodium] Could not infer main script path. Use the mainPath option to specify the path to your Vue script file containing createApp().') - } +export function rendererPlugin(options: PluginOptions): VitePlugin { + let rootDir: string + let index: string + let mainPath: string + let baseUrl: string return { name: 'compodium:renderer', enforce: 'pre', + apply: 'serve', + configResolved(viteConfig) { + rootDir = options.rootDir ?? viteConfig.root + baseUrl = options.baseUrl ?? viteConfig.base + + index = readFileSync(resolve(rootDir, 'index.html'), 'utf-8') + const inferredMainPath = inferMainPath(index) + mainPath = resolve(rootDir, (options.mainPath ?? inferredMainPath) as string) + }, + configureServer(server) { server.middlewares.use('/__compodium__/renderer', async (_req, res) => { try { @@ -39,7 +45,7 @@ export function rendererPlugin(config: PluginConfig): VitePlugin { /]*>[\s\S]*<\/body>/i, `
- + ` ) res.setHeader('Content-Type', 'text/html') @@ -58,6 +64,9 @@ export function rendererPlugin(config: PluginConfig): VitePlugin { load(id) { if (id === '\0@compodium/renderer.ts') { // Read the user's main entrypoint file + if (!mainPath) { + throw new Error('[Compodium] Could not infer main script path. Use the mainPath option to specify the path to your Vue script file containing createApp().') + } if (!existsSync(mainPath)) { throw new Error(`[Compodium] failed to resolve main file ${mainPath}. Use the mainPath option to specify the path to your Vue script file containing createApp().`) diff --git a/packages/vue/src/plugins/vueDevtools.ts b/packages/vue/src/plugins/vueDevtools.ts index b9980652..f4949b2f 100644 --- a/packages/vue/src/plugins/vueDevtools.ts +++ b/packages/vue/src/plugins/vueDevtools.ts @@ -1,12 +1,19 @@ import type { VitePlugin } from 'unplugin' -import type { PluginConfig } from '@compodium/core' +import type { PluginOptions } from '@compodium/core' import { colors } from 'consola/utils' import { version } from '../../package.json' import { joinURL } from 'ufo' -export function vueDevtoolsPlugin(config: PluginConfig): VitePlugin { +export function vueDevtoolsPlugin(_config: PluginOptions): VitePlugin { + let baseUrl = '/' return { name: 'compodium:devtools:vue', + apply: 'serve', + + configResolved(viteConfig) { + baseUrl = viteConfig.base + }, + configureServer(server) { const _printUrls = server.printUrls @@ -59,7 +66,7 @@ export function vueDevtoolsPlugin(config: PluginConfig): VitePlugin { injectTo: 'head-prepend' as const, attrs: { type: 'module', - src: joinURL(config.baseUrl, `/@id/virtual:compodium:devtools`) + src: joinURL(baseUrl, `/@id/virtual:compodium:devtools`) } } ]