@nuasite/cms 0.23.0 → 0.24.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/dist/editor.js +329 -331
- package/package.json +1 -1
- package/src/build-processor.ts +2 -4
- package/src/content-invalidator.ts +134 -0
- package/src/dev-middleware.ts +42 -1
- package/src/editor/index.tsx +2 -3
- package/src/handlers/api-routes.ts +17 -3
- package/src/source-finder/collection-finder.ts +74 -6
- package/src/source-finder/cross-file-tracker.ts +2 -4
- package/src/source-finder/snippet-utils.ts +146 -35
- package/src/utils.ts +13 -0
- package/src/vite-plugin.ts +27 -13
package/src/utils.ts
CHANGED
|
@@ -133,6 +133,19 @@ export function escapeReplacement(str: string): string {
|
|
|
133
133
|
return str.replace(/\$/g, '$$$$')
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// ============================================================================
|
|
137
|
+
// Path Resolution
|
|
138
|
+
// ============================================================================
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Resolve a source path to an absolute filesystem path.
|
|
142
|
+
* If the path is already absolute it is returned as-is; otherwise it is
|
|
143
|
+
* joined with the project root directory.
|
|
144
|
+
*/
|
|
145
|
+
export function resolveSourcePath(sourcePath: string): string {
|
|
146
|
+
return path.isAbsolute(sourcePath) ? sourcePath : path.join(getProjectRoot(), sourcePath)
|
|
147
|
+
}
|
|
148
|
+
|
|
136
149
|
// ============================================================================
|
|
137
150
|
// Path Validation
|
|
138
151
|
// ============================================================================
|
package/src/vite-plugin.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { watch } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import type { Plugin } from 'vite'
|
|
4
|
+
import { invalidateContentCache, notifyContentStoreUpdated, type ViteServerLike } from './content-invalidator'
|
|
4
5
|
import { expectedDeletions } from './dev-middleware'
|
|
5
6
|
import type { ManifestWriter } from './manifest-writer'
|
|
6
7
|
import { markFileDirty } from './source-finder'
|
|
@@ -85,6 +86,11 @@ export function createVitePlugin(context: VitePluginContext): Plugin[] {
|
|
|
85
86
|
// Without this, content collection edits update the data store on disk but the
|
|
86
87
|
// browser never receives a full-reload because Vite's watcher never fires "change"
|
|
87
88
|
// for that file. We use native fs.watch as a reliable fallback.
|
|
89
|
+
//
|
|
90
|
+
// Caveat: native fs.watch on Linux tracks the inode, not the path. Astro writes
|
|
91
|
+
// data-store.json via atomic rename (writeFile-tmp + rename), which replaces the
|
|
92
|
+
// inode and silently kills the existing watcher. We re-attach on every event to
|
|
93
|
+
// keep tracking the live file across atomic writes.
|
|
88
94
|
const dataStoreWatchPlugin: Plugin = {
|
|
89
95
|
name: 'cms-data-store-watch',
|
|
90
96
|
configureServer(server) {
|
|
@@ -93,25 +99,32 @@ export function createVitePlugin(context: VitePluginContext): Plugin[] {
|
|
|
93
99
|
const dataStorePath = join(root, '.astro', 'data-store.json')
|
|
94
100
|
let fsWatcher: ReturnType<typeof watch> | undefined
|
|
95
101
|
let debounce: ReturnType<typeof setTimeout> | undefined
|
|
102
|
+
let closed = false
|
|
96
103
|
|
|
97
104
|
const invalidate = () => {
|
|
98
|
-
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
invalidateContentCache(server as unknown as ViteServerLike)
|
|
106
|
+
// Wake any CMS API middleware call that is currently blocked
|
|
107
|
+
// waiting for the data store to reflect a just-written file.
|
|
108
|
+
// This keeps the invalidation on a single path (here) and lets
|
|
109
|
+
// the middleware respond only after the SSR module graph is fresh.
|
|
110
|
+
notifyContentStoreUpdated()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const onEvent = () => {
|
|
114
|
+
clearTimeout(debounce)
|
|
115
|
+
debounce = setTimeout(invalidate, 80)
|
|
116
|
+
// Re-attach: native fs.watch dies after the inode is replaced by an
|
|
117
|
+
// atomic rename. Close current and restart so subsequent writes are
|
|
118
|
+
// observed.
|
|
119
|
+
fsWatcher?.close()
|
|
120
|
+
fsWatcher = undefined
|
|
121
|
+
if (!closed) startWatching()
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
const startWatching = () => {
|
|
125
|
+
if (closed) return
|
|
110
126
|
try {
|
|
111
|
-
fsWatcher = watch(dataStorePath,
|
|
112
|
-
clearTimeout(debounce)
|
|
113
|
-
debounce = setTimeout(invalidate, 80)
|
|
114
|
-
})
|
|
127
|
+
fsWatcher = watch(dataStorePath, onEvent)
|
|
115
128
|
} catch {
|
|
116
129
|
// File doesn't exist yet — retry when it appears
|
|
117
130
|
setTimeout(startWatching, 2000)
|
|
@@ -123,6 +136,7 @@ export function createVitePlugin(context: VitePluginContext): Plugin[] {
|
|
|
123
136
|
|
|
124
137
|
const origClose = server.close.bind(server)
|
|
125
138
|
server.close = async () => {
|
|
139
|
+
closed = true
|
|
126
140
|
fsWatcher?.close()
|
|
127
141
|
clearTimeout(debounce)
|
|
128
142
|
return origClose()
|