@jasonshimmy/vite-plugin-cer-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/copilot-instructions.md +130 -0
- package/.github/workflows/publish.yml +206 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +10 -0
- package/IMPLEMENTATION_PLAN.md +391 -0
- package/README.md +231 -0
- package/VITE_PLUGIN_FRAMEWORK_PLAN.md +594 -0
- package/commits.txt +3 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts +2 -0
- package/dist/__tests__/plugin/path-utils.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/path-utils.test.js +305 -0
- package/dist/__tests__/plugin/path-utils.test.js.map +1 -0
- package/dist/__tests__/plugin/scanner.test.d.ts +2 -0
- package/dist/__tests__/plugin/scanner.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/scanner.test.js +143 -0
- package/dist/__tests__/plugin/scanner.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js +151 -0
- package/dist/__tests__/plugin/transforms/auto-import.test.js.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts +2 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js +151 -0
- package/dist/__tests__/plugin/transforms/head-inject.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/components.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/components.test.js +47 -0
- package/dist/__tests__/plugin/virtual/components.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/composables.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/composables.test.js +48 -0
- package/dist/__tests__/plugin/virtual/composables.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/layouts.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js +59 -0
- package/dist/__tests__/plugin/virtual/layouts.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js +58 -0
- package/dist/__tests__/plugin/virtual/middleware.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/plugins.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js +73 -0
- package/dist/__tests__/plugin/virtual/plugins.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/routes.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/routes.test.js +167 -0
- package/dist/__tests__/plugin/virtual/routes.test.js.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts +2 -0
- package/dist/__tests__/plugin/virtual/server-api.test.d.ts.map +1 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js +72 -0
- package/dist/__tests__/plugin/virtual/server-api.test.js.map +1 -0
- package/dist/__tests__/runtime/use-head.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-head.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-head.test.js +202 -0
- package/dist/__tests__/runtime/use-head.test.js.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts +2 -0
- package/dist/__tests__/runtime/use-page-data.test.d.ts.map +1 -0
- package/dist/__tests__/runtime/use-page-data.test.js +41 -0
- package/dist/__tests__/runtime/use-page-data.test.js.map +1 -0
- package/dist/cli/commands/build.d.ts +3 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +103 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +3 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +92 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +7 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +72 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/preview.d.ts +3 -0
- package/dist/cli/commands/preview.d.ts.map +1 -0
- package/dist/cli/commands/preview.js +191 -0
- package/dist/cli/commands/preview.js.map +1 -0
- package/dist/cli/create/index.d.ts +3 -0
- package/dist/cli/create/index.d.ts.map +1 -0
- package/dist/cli/create/index.js +184 -0
- package/dist/cli/create/index.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin/build-ssg.d.ts +12 -0
- package/dist/plugin/build-ssg.d.ts.map +1 -0
- package/dist/plugin/build-ssg.js +212 -0
- package/dist/plugin/build-ssg.js.map +1 -0
- package/dist/plugin/build-ssr.d.ts +10 -0
- package/dist/plugin/build-ssr.d.ts.map +1 -0
- package/dist/plugin/build-ssr.js +139 -0
- package/dist/plugin/build-ssr.js.map +1 -0
- package/dist/plugin/dev-server.d.ts +46 -0
- package/dist/plugin/dev-server.d.ts.map +1 -0
- package/dist/plugin/dev-server.js +194 -0
- package/dist/plugin/dev-server.js.map +1 -0
- package/dist/plugin/dts-generator.d.ts +27 -0
- package/dist/plugin/dts-generator.d.ts.map +1 -0
- package/dist/plugin/dts-generator.js +180 -0
- package/dist/plugin/dts-generator.js.map +1 -0
- package/dist/plugin/index.d.ts +13 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/index.js +298 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/path-utils.d.ts +57 -0
- package/dist/plugin/path-utils.d.ts.map +1 -0
- package/dist/plugin/path-utils.js +160 -0
- package/dist/plugin/path-utils.js.map +1 -0
- package/dist/plugin/scanner.d.ts +17 -0
- package/dist/plugin/scanner.d.ts.map +1 -0
- package/dist/plugin/scanner.js +54 -0
- package/dist/plugin/scanner.js.map +1 -0
- package/dist/plugin/transforms/auto-import.d.ts +14 -0
- package/dist/plugin/transforms/auto-import.d.ts.map +1 -0
- package/dist/plugin/transforms/auto-import.js +154 -0
- package/dist/plugin/transforms/auto-import.js.map +1 -0
- package/dist/plugin/transforms/head-inject.d.ts +29 -0
- package/dist/plugin/transforms/head-inject.d.ts.map +1 -0
- package/dist/plugin/transforms/head-inject.js +127 -0
- package/dist/plugin/transforms/head-inject.js.map +1 -0
- package/dist/plugin/virtual/components.d.ts +6 -0
- package/dist/plugin/virtual/components.d.ts.map +1 -0
- package/dist/plugin/virtual/components.js +22 -0
- package/dist/plugin/virtual/components.js.map +1 -0
- package/dist/plugin/virtual/composables.d.ts +6 -0
- package/dist/plugin/virtual/composables.d.ts.map +1 -0
- package/dist/plugin/virtual/composables.js +22 -0
- package/dist/plugin/virtual/composables.js.map +1 -0
- package/dist/plugin/virtual/error.d.ts +13 -0
- package/dist/plugin/virtual/error.d.ts.map +1 -0
- package/dist/plugin/virtual/error.js +32 -0
- package/dist/plugin/virtual/error.js.map +1 -0
- package/dist/plugin/virtual/layouts.d.ts +6 -0
- package/dist/plugin/virtual/layouts.d.ts.map +1 -0
- package/dist/plugin/virtual/layouts.js +33 -0
- package/dist/plugin/virtual/layouts.js.map +1 -0
- package/dist/plugin/virtual/loading.d.ts +11 -0
- package/dist/plugin/virtual/loading.d.ts.map +1 -0
- package/dist/plugin/virtual/loading.js +30 -0
- package/dist/plugin/virtual/loading.js.map +1 -0
- package/dist/plugin/virtual/middleware.d.ts +6 -0
- package/dist/plugin/virtual/middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/middleware.js +36 -0
- package/dist/plugin/virtual/middleware.js.map +1 -0
- package/dist/plugin/virtual/plugins.d.ts +6 -0
- package/dist/plugin/virtual/plugins.d.ts.map +1 -0
- package/dist/plugin/virtual/plugins.js +30 -0
- package/dist/plugin/virtual/plugins.js.map +1 -0
- package/dist/plugin/virtual/routes.d.ts +16 -0
- package/dist/plugin/virtual/routes.d.ts.map +1 -0
- package/dist/plugin/virtual/routes.js +131 -0
- package/dist/plugin/virtual/routes.js.map +1 -0
- package/dist/plugin/virtual/server-api.d.ts +6 -0
- package/dist/plugin/virtual/server-api.d.ts.map +1 -0
- package/dist/plugin/virtual/server-api.js +41 -0
- package/dist/plugin/virtual/server-api.js.map +1 -0
- package/dist/plugin/virtual/server-middleware.d.ts +6 -0
- package/dist/plugin/virtual/server-middleware.d.ts.map +1 -0
- package/dist/plugin/virtual/server-middleware.js +36 -0
- package/dist/plugin/virtual/server-middleware.js.map +1 -0
- package/dist/runtime/app-template.d.ts +10 -0
- package/dist/runtime/app-template.d.ts.map +1 -0
- package/dist/runtime/app-template.js +143 -0
- package/dist/runtime/app-template.js.map +1 -0
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.d.ts.map +1 -0
- package/dist/runtime/composables/index.js +3 -0
- package/dist/runtime/composables/index.js.map +1 -0
- package/dist/runtime/composables/use-head.d.ts +30 -0
- package/dist/runtime/composables/use-head.d.ts.map +1 -0
- package/dist/runtime/composables/use-head.js +182 -0
- package/dist/runtime/composables/use-head.js.map +1 -0
- package/dist/runtime/composables/use-page-data.d.ts +32 -0
- package/dist/runtime/composables/use-page-data.d.ts.map +1 -0
- package/dist/runtime/composables/use-page-data.js +41 -0
- package/dist/runtime/composables/use-page-data.js.map +1 -0
- package/dist/runtime/entry-client-template.d.ts +8 -0
- package/dist/runtime/entry-client-template.d.ts.map +1 -0
- package/dist/runtime/entry-client-template.js +18 -0
- package/dist/runtime/entry-client-template.js.map +1 -0
- package/dist/runtime/entry-server-template.d.ts +9 -0
- package/dist/runtime/entry-server-template.d.ts.map +1 -0
- package/dist/runtime/entry-server-template.js +72 -0
- package/dist/runtime/entry-server-template.js.map +1 -0
- package/dist/types/api.d.ts +16 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/config.d.ts +32 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +4 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/middleware.d.ts +6 -0
- package/dist/types/middleware.d.ts.map +1 -0
- package/dist/types/middleware.js +2 -0
- package/dist/types/middleware.js.map +1 -0
- package/dist/types/page.d.ts +21 -0
- package/dist/types/page.d.ts.map +1 -0
- package/dist/types/page.js +2 -0
- package/dist/types/page.js.map +1 -0
- package/dist/types/plugin.d.ts +12 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +2 -0
- package/dist/types/plugin.js.map +1 -0
- package/docs/cli.md +233 -0
- package/docs/components.md +114 -0
- package/docs/composables.md +99 -0
- package/docs/configuration.md +270 -0
- package/docs/data-loading.md +165 -0
- package/docs/getting-started.md +235 -0
- package/docs/head-management.md +206 -0
- package/docs/layouts.md +112 -0
- package/docs/middleware.md +140 -0
- package/docs/plugins.md +138 -0
- package/docs/rendering-modes.md +251 -0
- package/docs/routing.md +180 -0
- package/docs/server-api.md +172 -0
- package/docs/testing.md +462 -0
- package/package.json +75 -0
- package/src/__tests__/plugin/path-utils.test.ts +399 -0
- package/src/__tests__/plugin/scanner.test.ts +172 -0
- package/src/__tests__/plugin/transforms/auto-import.test.ts +229 -0
- package/src/__tests__/plugin/transforms/head-inject.test.ts +178 -0
- package/src/__tests__/plugin/virtual/components.test.ts +56 -0
- package/src/__tests__/plugin/virtual/composables.test.ts +57 -0
- package/src/__tests__/plugin/virtual/layouts.test.ts +70 -0
- package/src/__tests__/plugin/virtual/middleware.test.ts +68 -0
- package/src/__tests__/plugin/virtual/plugins.test.ts +84 -0
- package/src/__tests__/plugin/virtual/routes.test.ts +202 -0
- package/src/__tests__/plugin/virtual/server-api.test.ts +85 -0
- package/src/__tests__/runtime/use-head.test.ts +243 -0
- package/src/__tests__/runtime/use-page-data.test.ts +45 -0
- package/src/cli/commands/build.ts +114 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +81 -0
- package/src/cli/commands/preview.ts +218 -0
- package/src/cli/create/index.ts +250 -0
- package/src/cli/create/templates/spa/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/spa/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/spa/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/spa/cer.config.ts.tpl +6 -0
- package/src/cli/create/templates/spa/index.html.tpl +12 -0
- package/src/cli/create/templates/spa/package.json.tpl +18 -0
- package/src/cli/create/templates/ssg/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssg/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssg/app/pages/index.ts.tpl +17 -0
- package/src/cli/create/templates/ssg/cer.config.ts.tpl +13 -0
- package/src/cli/create/templates/ssg/index.html.tpl +12 -0
- package/src/cli/create/templates/ssg/package.json.tpl +19 -0
- package/src/cli/create/templates/ssr/app/app.ts.tpl +74 -0
- package/src/cli/create/templates/ssr/app/layouts/default.ts.tpl +15 -0
- package/src/cli/create/templates/ssr/app/pages/index.ts.tpl +8 -0
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +10 -0
- package/src/cli/create/templates/ssr/index.html.tpl +12 -0
- package/src/cli/create/templates/ssr/package.json.tpl +18 -0
- package/src/cli/index.ts +20 -0
- package/src/index.ts +13 -0
- package/src/plugin/build-ssg.ts +259 -0
- package/src/plugin/build-ssr.ts +147 -0
- package/src/plugin/dev-server.ts +266 -0
- package/src/plugin/dts-generator.ts +214 -0
- package/src/plugin/index.ts +330 -0
- package/src/plugin/path-utils.ts +186 -0
- package/src/plugin/scanner.ts +65 -0
- package/src/plugin/transforms/auto-import.ts +190 -0
- package/src/plugin/transforms/head-inject.ts +161 -0
- package/src/plugin/virtual/components.ts +28 -0
- package/src/plugin/virtual/composables.ts +28 -0
- package/src/plugin/virtual/error.ts +34 -0
- package/src/plugin/virtual/layouts.ts +41 -0
- package/src/plugin/virtual/loading.ts +33 -0
- package/src/plugin/virtual/middleware.ts +45 -0
- package/src/plugin/virtual/plugins.ts +36 -0
- package/src/plugin/virtual/routes.ts +147 -0
- package/src/plugin/virtual/server-api.ts +52 -0
- package/src/plugin/virtual/server-middleware.ts +44 -0
- package/src/runtime/app-template.ts +142 -0
- package/src/runtime/composables/index.ts +3 -0
- package/src/runtime/composables/use-head.ts +204 -0
- package/src/runtime/composables/use-page-data.ts +39 -0
- package/src/runtime/entry-client-template.ts +17 -0
- package/src/runtime/entry-server-template.ts +71 -0
- package/src/types/api.ts +19 -0
- package/src/types/config.ts +39 -0
- package/src/types/index.ts +6 -0
- package/src/types/middleware.ts +16 -0
- package/src/types/page.ts +29 -0
- package/src/types/plugin.ts +13 -0
- package/tsconfig.build.json +10 -0
- package/tsconfig.json +19 -0
- package/vitest.config.ts +29 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { resolve, join } from 'pathe'
|
|
2
|
+
import type { Plugin, ViteDevServer, ModuleNode } from 'vite'
|
|
3
|
+
import type { CerAppConfig } from '../types/config.js'
|
|
4
|
+
import type { ResolvedCerConfig } from './dev-server.js'
|
|
5
|
+
import { cerPlugin } from '@jasonshimmy/custom-elements-runtime/vite-plugin'
|
|
6
|
+
import { autoImportTransform } from './transforms/auto-import.js'
|
|
7
|
+
import { scanComposableExports, writeAutoImportDts, writeTsconfigPaths } from './dts-generator.js'
|
|
8
|
+
import { configureCerDevServer } from './dev-server.js'
|
|
9
|
+
import { generateRoutesCode } from './virtual/routes.js'
|
|
10
|
+
import { generateLayoutsCode } from './virtual/layouts.js'
|
|
11
|
+
import { generateComponentsCode } from './virtual/components.js'
|
|
12
|
+
import { generateComposablesCode } from './virtual/composables.js'
|
|
13
|
+
import { generatePluginsCode } from './virtual/plugins.js'
|
|
14
|
+
import { generateMiddlewareCode } from './virtual/middleware.js'
|
|
15
|
+
import { generateServerApiCode } from './virtual/server-api.js'
|
|
16
|
+
import { generateServerMiddlewareCode } from './virtual/server-middleware.js'
|
|
17
|
+
import { generateLoadingCode } from './virtual/loading.js'
|
|
18
|
+
import { generateErrorCode } from './virtual/error.js'
|
|
19
|
+
import { createWatcher } from './scanner.js'
|
|
20
|
+
|
|
21
|
+
// Virtual module IDs (raw)
|
|
22
|
+
const VIRTUAL_IDS = {
|
|
23
|
+
routes: 'virtual:cer-routes',
|
|
24
|
+
layouts: 'virtual:cer-layouts',
|
|
25
|
+
components: 'virtual:cer-components',
|
|
26
|
+
composables: 'virtual:cer-composables',
|
|
27
|
+
plugins: 'virtual:cer-plugins',
|
|
28
|
+
middleware: 'virtual:cer-middleware',
|
|
29
|
+
serverApi: 'virtual:cer-server-api',
|
|
30
|
+
serverMiddleware: 'virtual:cer-server-middleware',
|
|
31
|
+
appConfig: 'virtual:cer-app-config',
|
|
32
|
+
loading: 'virtual:cer-loading',
|
|
33
|
+
error: 'virtual:cer-error',
|
|
34
|
+
} as const
|
|
35
|
+
|
|
36
|
+
// Resolved virtual module IDs (prefixed with \0)
|
|
37
|
+
const RESOLVED_IDS = Object.fromEntries(
|
|
38
|
+
Object.entries(VIRTUAL_IDS).map(([k, v]) => [k, `\0${v}`]),
|
|
39
|
+
) as Record<keyof typeof VIRTUAL_IDS, string>
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Fills in default values for all config fields and resolves absolute paths.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveConfig(userConfig: CerAppConfig, root: string = process.cwd()): ResolvedCerConfig {
|
|
45
|
+
const mode = userConfig.mode ?? 'spa'
|
|
46
|
+
const srcDir = resolve(root, userConfig.srcDir ?? 'app')
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
mode,
|
|
50
|
+
srcDir,
|
|
51
|
+
root,
|
|
52
|
+
pagesDir: join(srcDir, 'pages'),
|
|
53
|
+
layoutsDir: join(srcDir, 'layouts'),
|
|
54
|
+
componentsDir: join(srcDir, 'components'),
|
|
55
|
+
composablesDir: join(srcDir, 'composables'),
|
|
56
|
+
pluginsDir: join(srcDir, 'plugins'),
|
|
57
|
+
middlewareDir: join(srcDir, 'middleware'),
|
|
58
|
+
serverApiDir: join(root, 'server/api'),
|
|
59
|
+
serverMiddlewareDir: join(root, 'server/middleware'),
|
|
60
|
+
port: userConfig.port ?? 3000,
|
|
61
|
+
ssr: {
|
|
62
|
+
dsd: userConfig.ssr?.dsd ?? true,
|
|
63
|
+
streaming: userConfig.ssr?.streaming ?? false,
|
|
64
|
+
},
|
|
65
|
+
ssg: {
|
|
66
|
+
routes: userConfig.ssg?.routes ?? 'auto',
|
|
67
|
+
concurrency: userConfig.ssg?.concurrency ?? 4,
|
|
68
|
+
fallback: userConfig.ssg?.fallback ?? false,
|
|
69
|
+
},
|
|
70
|
+
router: {
|
|
71
|
+
base: userConfig.router?.base,
|
|
72
|
+
scrollToFragment: userConfig.router?.scrollToFragment,
|
|
73
|
+
},
|
|
74
|
+
jitCss: {
|
|
75
|
+
content: userConfig.jitCss?.content ?? [
|
|
76
|
+
`${srcDir}/pages/**/*.ts`,
|
|
77
|
+
`${srcDir}/components/**/*.ts`,
|
|
78
|
+
`${srcDir}/layouts/**/*.ts`,
|
|
79
|
+
],
|
|
80
|
+
extendedColors: userConfig.jitCss?.extendedColors ?? false,
|
|
81
|
+
},
|
|
82
|
+
autoImports: {
|
|
83
|
+
components: userConfig.autoImports?.components ?? true,
|
|
84
|
+
composables: userConfig.autoImports?.composables ?? true,
|
|
85
|
+
directives: userConfig.autoImports?.directives ?? true,
|
|
86
|
+
runtime: userConfig.autoImports?.runtime ?? true,
|
|
87
|
+
},
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Maps a resolved virtual ID to the appropriate generator function.
|
|
93
|
+
*/
|
|
94
|
+
async function generateVirtualModule(
|
|
95
|
+
id: string,
|
|
96
|
+
config: ResolvedCerConfig,
|
|
97
|
+
): Promise<string | null> {
|
|
98
|
+
switch (id) {
|
|
99
|
+
case RESOLVED_IDS.routes:
|
|
100
|
+
return generateRoutesCode(config.pagesDir)
|
|
101
|
+
case RESOLVED_IDS.layouts:
|
|
102
|
+
return generateLayoutsCode(config.layoutsDir)
|
|
103
|
+
case RESOLVED_IDS.components:
|
|
104
|
+
return generateComponentsCode(config.componentsDir)
|
|
105
|
+
case RESOLVED_IDS.composables:
|
|
106
|
+
return generateComposablesCode(config.composablesDir)
|
|
107
|
+
case RESOLVED_IDS.plugins:
|
|
108
|
+
return generatePluginsCode(config.pluginsDir)
|
|
109
|
+
case RESOLVED_IDS.middleware:
|
|
110
|
+
return generateMiddlewareCode(config.middlewareDir)
|
|
111
|
+
case RESOLVED_IDS.serverApi:
|
|
112
|
+
return generateServerApiCode(config.serverApiDir)
|
|
113
|
+
case RESOLVED_IDS.serverMiddleware:
|
|
114
|
+
return generateServerMiddlewareCode(config.serverMiddlewareDir)
|
|
115
|
+
case RESOLVED_IDS.appConfig:
|
|
116
|
+
return generateAppConfigModule(config)
|
|
117
|
+
case RESOLVED_IDS.loading:
|
|
118
|
+
return generateLoadingCode(config.srcDir)
|
|
119
|
+
case RESOLVED_IDS.error:
|
|
120
|
+
return generateErrorCode(config.srcDir)
|
|
121
|
+
default:
|
|
122
|
+
return null
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generates a virtual module that exports the resolved app config.
|
|
128
|
+
*/
|
|
129
|
+
function generateAppConfigModule(config: ResolvedCerConfig): string {
|
|
130
|
+
const exportedConfig = {
|
|
131
|
+
mode: config.mode,
|
|
132
|
+
router: config.router,
|
|
133
|
+
ssr: config.ssr,
|
|
134
|
+
ssg: config.ssg,
|
|
135
|
+
}
|
|
136
|
+
return `// AUTO-GENERATED by vite-plugin-cer-app\nexport const appConfig = ${JSON.stringify(exportedConfig, null, 2)}\nexport default appConfig\n`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Determines which virtual module IDs should be invalidated when a file changes
|
|
141
|
+
* in a given directory.
|
|
142
|
+
*/
|
|
143
|
+
function getDirtyVirtualIds(filePath: string, config: ResolvedCerConfig): string[] {
|
|
144
|
+
const dirty: string[] = []
|
|
145
|
+
|
|
146
|
+
if (filePath.startsWith(config.pagesDir)) {
|
|
147
|
+
dirty.push(RESOLVED_IDS.routes)
|
|
148
|
+
}
|
|
149
|
+
if (filePath.startsWith(config.layoutsDir)) {
|
|
150
|
+
dirty.push(RESOLVED_IDS.layouts)
|
|
151
|
+
}
|
|
152
|
+
if (filePath.startsWith(config.componentsDir)) {
|
|
153
|
+
dirty.push(RESOLVED_IDS.components)
|
|
154
|
+
}
|
|
155
|
+
if (filePath.startsWith(config.composablesDir)) {
|
|
156
|
+
dirty.push(RESOLVED_IDS.composables)
|
|
157
|
+
}
|
|
158
|
+
if (filePath.startsWith(config.pluginsDir)) {
|
|
159
|
+
dirty.push(RESOLVED_IDS.plugins)
|
|
160
|
+
}
|
|
161
|
+
if (filePath.startsWith(config.middlewareDir)) {
|
|
162
|
+
dirty.push(RESOLVED_IDS.middleware)
|
|
163
|
+
}
|
|
164
|
+
if (filePath.startsWith(config.serverApiDir)) {
|
|
165
|
+
dirty.push(RESOLVED_IDS.serverApi)
|
|
166
|
+
}
|
|
167
|
+
if (filePath.startsWith(config.serverMiddlewareDir)) {
|
|
168
|
+
dirty.push(RESOLVED_IDS.serverMiddleware)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return dirty
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The main cerApp() Vite plugin factory.
|
|
176
|
+
* Returns an array of plugins: the cer-app orchestrator + the runtime JIT CSS plugin(s).
|
|
177
|
+
*/
|
|
178
|
+
export function cerApp(userConfig: CerAppConfig = {}): Plugin[] {
|
|
179
|
+
let config: ResolvedCerConfig
|
|
180
|
+
let composableExports = new Map<string, string>()
|
|
181
|
+
|
|
182
|
+
// Cache for generated virtual module code (invalidated on file changes)
|
|
183
|
+
const moduleCache = new Map<string, string>()
|
|
184
|
+
|
|
185
|
+
const cerAppPlugin: Plugin = {
|
|
186
|
+
name: 'vite-plugin-cer-app',
|
|
187
|
+
|
|
188
|
+
config(viteConfig) {
|
|
189
|
+
const root = viteConfig.root ? resolve(viteConfig.root) : process.cwd()
|
|
190
|
+
config = resolveConfig(userConfig, root)
|
|
191
|
+
return {
|
|
192
|
+
build: {
|
|
193
|
+
target: 'esnext',
|
|
194
|
+
rollupOptions: {
|
|
195
|
+
onwarn(warning, warn) {
|
|
196
|
+
// loader and meta are optional exports from page files — suppress noise
|
|
197
|
+
if (
|
|
198
|
+
warning.code === 'MISSING_EXPORT' &&
|
|
199
|
+
(warning.binding === 'loader' || warning.binding === 'meta')
|
|
200
|
+
) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
warn(warning)
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
configResolved(resolvedConfig) {
|
|
211
|
+
// Re-resolve with the final root
|
|
212
|
+
config = resolveConfig(userConfig, resolvedConfig.root)
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
resolveId(id: string) {
|
|
216
|
+
if ((Object.values(VIRTUAL_IDS) as string[]).includes(id)) {
|
|
217
|
+
return `\0${id}`
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
async load(id: string) {
|
|
222
|
+
const allResolved = Object.values(RESOLVED_IDS) as string[]
|
|
223
|
+
if (!allResolved.includes(id)) return null
|
|
224
|
+
|
|
225
|
+
// Return from cache if available
|
|
226
|
+
if (moduleCache.has(id)) {
|
|
227
|
+
return moduleCache.get(id)!
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Generate and cache
|
|
231
|
+
const code = await generateVirtualModule(id, config)
|
|
232
|
+
if (code !== null) {
|
|
233
|
+
moduleCache.set(id, code)
|
|
234
|
+
return code
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
transform(code: string, id: string) {
|
|
241
|
+
if (!config) return null
|
|
242
|
+
if (config.autoImports?.runtime === false) return null
|
|
243
|
+
// Skip virtual modules
|
|
244
|
+
if (id.startsWith('\0')) return null
|
|
245
|
+
|
|
246
|
+
const result = autoImportTransform(code, id, {
|
|
247
|
+
srcDir: config.srcDir,
|
|
248
|
+
composableExports: config.autoImports?.composables !== false ? composableExports : undefined,
|
|
249
|
+
})
|
|
250
|
+
if (result === null) return null
|
|
251
|
+
return { code: result, map: null }
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
async configureServer(server: ViteDevServer) {
|
|
255
|
+
if (!config) {
|
|
256
|
+
// config might not be set yet; resolve with cwd
|
|
257
|
+
config = resolveConfig(userConfig, process.cwd())
|
|
258
|
+
}
|
|
259
|
+
// Scan composables and write .d.ts + tsconfig paths on dev server start
|
|
260
|
+
composableExports = await scanComposableExports(config.composablesDir)
|
|
261
|
+
await writeAutoImportDts(config.root, config.composablesDir, composableExports)
|
|
262
|
+
writeTsconfigPaths(config.root, config.srcDir)
|
|
263
|
+
|
|
264
|
+
// Watch app/ and server/ directories for file changes
|
|
265
|
+
const watchDirs = [
|
|
266
|
+
config.pagesDir,
|
|
267
|
+
config.layoutsDir,
|
|
268
|
+
config.componentsDir,
|
|
269
|
+
config.composablesDir,
|
|
270
|
+
config.pluginsDir,
|
|
271
|
+
config.middlewareDir,
|
|
272
|
+
config.serverApiDir,
|
|
273
|
+
config.serverMiddlewareDir,
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
createWatcher(server.watcher, watchDirs, async (event, file) => {
|
|
277
|
+
if (event === 'add' || event === 'unlink') {
|
|
278
|
+
// Re-scan composables and regenerate .d.ts if a composable changed
|
|
279
|
+
if (file.startsWith(config.composablesDir)) {
|
|
280
|
+
composableExports = await scanComposableExports(config.composablesDir)
|
|
281
|
+
await writeAutoImportDts(config.root, config.composablesDir, composableExports)
|
|
282
|
+
}
|
|
283
|
+
// Invalidate relevant virtual modules
|
|
284
|
+
const dirtyIds = getDirtyVirtualIds(file, config)
|
|
285
|
+
for (const resolvedId of dirtyIds) {
|
|
286
|
+
moduleCache.delete(resolvedId)
|
|
287
|
+
const mod = server.moduleGraph.getModuleById(resolvedId)
|
|
288
|
+
if (mod) {
|
|
289
|
+
server.moduleGraph.invalidateModule(mod)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Trigger HMR
|
|
293
|
+
server.ws.send({ type: 'full-reload' })
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
// Register dev server middleware for API routes + SSR
|
|
298
|
+
configureCerDevServer(server, config)
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
async buildStart() {
|
|
302
|
+
if (!config) {
|
|
303
|
+
config = resolveConfig(userConfig, process.cwd())
|
|
304
|
+
}
|
|
305
|
+
// Scan composables and generate type declarations + tsconfig paths
|
|
306
|
+
composableExports = await scanComposableExports(config.composablesDir)
|
|
307
|
+
await writeAutoImportDts(config.root, config.composablesDir, composableExports)
|
|
308
|
+
writeTsconfigPaths(config.root, config.srcDir)
|
|
309
|
+
// Warm the virtual module cache
|
|
310
|
+
for (const resolvedId of Object.values(RESOLVED_IDS)) {
|
|
311
|
+
const code = await generateVirtualModule(resolvedId, config)
|
|
312
|
+
if (code !== null) {
|
|
313
|
+
moduleCache.set(resolvedId, code)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Include cerPlugin from the runtime for JIT CSS support
|
|
320
|
+
const jitPlugins = cerPlugin({
|
|
321
|
+
content: userConfig.jitCss?.content ?? [
|
|
322
|
+
`${userConfig.srcDir ?? 'app'}/pages/**/*.ts`,
|
|
323
|
+
`${userConfig.srcDir ?? 'app'}/components/**/*.ts`,
|
|
324
|
+
`${userConfig.srcDir ?? 'app'}/layouts/**/*.ts`,
|
|
325
|
+
],
|
|
326
|
+
ssr: userConfig.ssr?.dsd ? { dsd: true } : undefined,
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
return [cerAppPlugin, ...jitPlugins]
|
|
330
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { basename, dirname, join, relative } from 'pathe'
|
|
2
|
+
|
|
3
|
+
export interface RouteEntry {
|
|
4
|
+
filePath: string
|
|
5
|
+
routePath: string
|
|
6
|
+
tagName: string
|
|
7
|
+
isDynamic: boolean
|
|
8
|
+
isCatchAll: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Converts a file path to a route path.
|
|
13
|
+
* e.g. app/pages/blog/[slug].ts -> /blog/:slug
|
|
14
|
+
*/
|
|
15
|
+
export function fileToRoutePath(filePath: string, pagesRoot: string): string {
|
|
16
|
+
// Get path relative to pagesRoot, strip extension
|
|
17
|
+
let rel = relative(pagesRoot, filePath)
|
|
18
|
+
rel = rel.replace(/\.[jt]s$/, '')
|
|
19
|
+
|
|
20
|
+
// Split into segments
|
|
21
|
+
const segments = rel.split('/')
|
|
22
|
+
|
|
23
|
+
const transformed: string[] = []
|
|
24
|
+
for (const seg of segments) {
|
|
25
|
+
// Strip route groups: (groupName)
|
|
26
|
+
if (/^\(.*\)$/.test(seg)) {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// index becomes empty string (handled by parent)
|
|
31
|
+
if (seg === 'index') {
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// [...rest] -> :rest* (named splat so the param is accessible in components)
|
|
36
|
+
const catchAllMatch = seg.match(/^\[\.\.\.(.+)\]$/)
|
|
37
|
+
if (catchAllMatch) {
|
|
38
|
+
transformed.push(`:${catchAllMatch[1]}*`)
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// [param] -> :param
|
|
43
|
+
const dynamicMatch = seg.match(/^\[(.+)\]$/)
|
|
44
|
+
if (dynamicMatch) {
|
|
45
|
+
transformed.push(`:${dynamicMatch[1]}`)
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
transformed.push(seg)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const path = '/' + transformed.join('/')
|
|
53
|
+
// Normalize double slashes
|
|
54
|
+
return path.replace(/\/+/g, '/')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Converts a file path to a kebab-case custom element tag name.
|
|
59
|
+
* e.g. app/pages/blog/[slug].ts -> page-blog-slug
|
|
60
|
+
*/
|
|
61
|
+
export function fileToTagName(filePath: string, pagesRoot: string): string {
|
|
62
|
+
let rel = relative(pagesRoot, filePath)
|
|
63
|
+
rel = rel.replace(/\.[jt]s$/, '')
|
|
64
|
+
|
|
65
|
+
// Strip route groups
|
|
66
|
+
const segments = rel.split('/').filter((seg) => !/^\(.*\)$/.test(seg))
|
|
67
|
+
|
|
68
|
+
// Mirror fileToRoutePath: strip 'index' segments unless it's the only segment
|
|
69
|
+
// e.g. blog/index.ts -> page-blog (not page-blog-index), index.ts -> page-index
|
|
70
|
+
const tagSegments = segments
|
|
71
|
+
.map((seg) => seg.replace(/\[/g, '').replace(/\]/g, '').replace(/\./g, ''))
|
|
72
|
+
.filter((seg, _, arr) => !(seg === 'index' && arr.length > 1))
|
|
73
|
+
|
|
74
|
+
// Prefix with 'page-'
|
|
75
|
+
const name = 'page-' + tagSegments.join('-')
|
|
76
|
+
// Collapse multiple dashes and convert to lowercase
|
|
77
|
+
return name
|
|
78
|
+
.toLowerCase()
|
|
79
|
+
.replace(/-+/g, '-')
|
|
80
|
+
.replace(/^-|-$/g, '')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Converts a layout file path to a kebab-case custom element tag name.
|
|
85
|
+
* e.g. app/layouts/default.ts -> layout-default
|
|
86
|
+
*/
|
|
87
|
+
export function fileToLayoutTagName(filePath: string, layoutsRoot: string): string {
|
|
88
|
+
let rel = relative(layoutsRoot, filePath)
|
|
89
|
+
rel = rel.replace(/\.[jt]s$/, '')
|
|
90
|
+
|
|
91
|
+
const segments = rel.split('/')
|
|
92
|
+
const name = 'layout-' + segments.join('-')
|
|
93
|
+
return name
|
|
94
|
+
.toLowerCase()
|
|
95
|
+
.replace(/-+/g, '-')
|
|
96
|
+
.replace(/^-|-$/g, '')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Sorts routes: static first, then dynamic, then catch-all.
|
|
101
|
+
*/
|
|
102
|
+
export function sortRoutes(routes: RouteEntry[]): RouteEntry[] {
|
|
103
|
+
return [...routes].sort((a, b) => {
|
|
104
|
+
// Catch-all always last
|
|
105
|
+
if (a.isCatchAll && !b.isCatchAll) return 1
|
|
106
|
+
if (!a.isCatchAll && b.isCatchAll) return -1
|
|
107
|
+
|
|
108
|
+
// Dynamic after static
|
|
109
|
+
if (a.isDynamic && !b.isDynamic) return 1
|
|
110
|
+
if (!a.isDynamic && b.isDynamic) return -1
|
|
111
|
+
|
|
112
|
+
// Alphabetical among same type
|
|
113
|
+
return a.routePath.localeCompare(b.routePath)
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Determines if a route path contains dynamic segments.
|
|
119
|
+
*/
|
|
120
|
+
export function isRouteDynamic(routePath: string): boolean {
|
|
121
|
+
return routePath.includes(':')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Determines if a route path is a catch-all.
|
|
126
|
+
*/
|
|
127
|
+
export function isRouteCatchAll(routePath: string): boolean {
|
|
128
|
+
return routePath.includes('*')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Builds a full RouteEntry from a file path.
|
|
133
|
+
*/
|
|
134
|
+
export function buildRouteEntry(filePath: string, pagesRoot: string): RouteEntry {
|
|
135
|
+
const routePath = fileToRoutePath(filePath, pagesRoot)
|
|
136
|
+
const tagName = fileToTagName(filePath, pagesRoot)
|
|
137
|
+
return {
|
|
138
|
+
filePath,
|
|
139
|
+
routePath,
|
|
140
|
+
tagName,
|
|
141
|
+
isDynamic: isRouteDynamic(routePath),
|
|
142
|
+
isCatchAll: isRouteCatchAll(routePath),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Returns the layout name (key) from a layout file path.
|
|
148
|
+
* e.g. app/layouts/default.ts -> 'default'
|
|
149
|
+
*/
|
|
150
|
+
export function fileToLayoutName(filePath: string, layoutsRoot: string): string {
|
|
151
|
+
let rel = relative(layoutsRoot, filePath)
|
|
152
|
+
rel = rel.replace(/\.[jt]s$/, '')
|
|
153
|
+
return rel.replace(/\//g, '-').toLowerCase()
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Returns a safe JS identifier for a file path (for import aliases).
|
|
158
|
+
*/
|
|
159
|
+
export function fileToImportAlias(filePath: string, prefix: string = '_m'): string {
|
|
160
|
+
const base = basename(filePath).replace(/\.[jt]s$/, '')
|
|
161
|
+
const safe = base
|
|
162
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
163
|
+
.replace(/^(\d)/, '_$1')
|
|
164
|
+
return `${prefix}_${safe}`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Returns the numeric sort prefix from a filename (e.g. "01.store.ts" -> 1).
|
|
169
|
+
* Returns Infinity if no numeric prefix.
|
|
170
|
+
*/
|
|
171
|
+
export function extractPluginOrder(fileName: string): number {
|
|
172
|
+
const match = basename(fileName).match(/^(\d+)\./)
|
|
173
|
+
return match ? parseInt(match[1], 10) : Infinity
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Sorts plugin files by leading numeric prefix then alphabetically.
|
|
178
|
+
*/
|
|
179
|
+
export function sortPluginFiles(files: string[]): string[] {
|
|
180
|
+
return [...files].sort((a, b) => {
|
|
181
|
+
const aOrder = extractPluginOrder(a)
|
|
182
|
+
const bOrder = extractPluginOrder(b)
|
|
183
|
+
if (aOrder !== bOrder) return aOrder - bOrder
|
|
184
|
+
return a.localeCompare(b)
|
|
185
|
+
})
|
|
186
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fg from 'fast-glob'
|
|
2
|
+
import type { FSWatcher } from 'vite'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scans a directory for files matching the given glob pattern.
|
|
6
|
+
* Returns absolute file paths sorted alphabetically.
|
|
7
|
+
*/
|
|
8
|
+
export async function scanDirectory(pattern: string, cwd: string): Promise<string[]> {
|
|
9
|
+
const files = await fg(pattern, {
|
|
10
|
+
cwd,
|
|
11
|
+
absolute: true,
|
|
12
|
+
onlyFiles: true,
|
|
13
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
14
|
+
})
|
|
15
|
+
return files.sort()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a file watcher for the given directories using Vite's built-in watcher.
|
|
20
|
+
* Calls `onChange(event, absoluteFilePath)` on add/unlink events.
|
|
21
|
+
*
|
|
22
|
+
* @param watcher - The FSWatcher from Vite's dev server (server.watcher)
|
|
23
|
+
* @param dirs - Absolute directory paths to watch
|
|
24
|
+
* @param onChange - Callback invoked with event type and absolute file path
|
|
25
|
+
* @returns A cleanup function that removes the listeners
|
|
26
|
+
*/
|
|
27
|
+
export function createWatcher(
|
|
28
|
+
watcher: FSWatcher,
|
|
29
|
+
dirs: string[],
|
|
30
|
+
onChange: (event: string, file: string) => void,
|
|
31
|
+
): () => void {
|
|
32
|
+
// Add directories to Vite's watcher
|
|
33
|
+
for (const dir of dirs) {
|
|
34
|
+
watcher.add(dir)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleAdd = (file: string) => {
|
|
38
|
+
if (dirs.some((dir) => file.startsWith(dir))) {
|
|
39
|
+
onChange('add', file)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const handleUnlink = (file: string) => {
|
|
44
|
+
if (dirs.some((dir) => file.startsWith(dir))) {
|
|
45
|
+
onChange('unlink', file)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const handleChange = (file: string) => {
|
|
50
|
+
if (dirs.some((dir) => file.startsWith(dir))) {
|
|
51
|
+
onChange('change', file)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
watcher.on('add', handleAdd)
|
|
56
|
+
watcher.on('unlink', handleUnlink)
|
|
57
|
+
watcher.on('change', handleChange)
|
|
58
|
+
|
|
59
|
+
// Return cleanup function
|
|
60
|
+
return () => {
|
|
61
|
+
watcher.off('add', handleAdd)
|
|
62
|
+
watcher.off('unlink', handleUnlink)
|
|
63
|
+
watcher.off('change', handleChange)
|
|
64
|
+
}
|
|
65
|
+
}
|