polen 0.10.0-next.13 → 0.10.0-next.14
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/build/api/vite/plugins/build.d.ts.map +1 -1
- package/build/api/vite/plugins/build.js +11 -3
- package/build/api/vite/plugins/build.js.map +1 -1
- package/build/api/vite/plugins/core.d.ts.map +1 -1
- package/build/api/vite/plugins/core.js +12 -10
- package/build/api/vite/plugins/core.js.map +1 -1
- package/build/api/vite/plugins/pages.d.ts.map +1 -1
- package/build/api/vite/plugins/pages.js +6 -7
- package/build/api/vite/plugins/pages.js.map +1 -1
- package/build/api/vite/plugins/serve.d.ts.map +1 -1
- package/build/api/vite/plugins/serve.js +47 -7
- package/build/api/vite/plugins/serve.js.map +1 -1
- package/build/lib/file-router/diagnostic-reporter.js +2 -2
- package/build/lib/file-router/diagnostic-reporter.js.map +1 -1
- package/build/lib/graphql-document/components/GraphQLDocument.d.ts.map +1 -1
- package/build/lib/graphql-document/components/GraphQLDocument.js +23 -11
- package/build/lib/graphql-document/components/GraphQLDocument.js.map +1 -1
- package/build/lib/graphql-document/positioning-simple.d.ts +0 -5
- package/build/lib/graphql-document/positioning-simple.d.ts.map +1 -1
- package/build/lib/graphql-document/positioning-simple.js +78 -90
- package/build/lib/graphql-document/positioning-simple.js.map +1 -1
- package/build/lib/kit-temp.d.ts +103 -0
- package/build/lib/kit-temp.d.ts.map +1 -1
- package/build/lib/kit-temp.js +236 -2
- package/build/lib/kit-temp.js.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts +1 -8
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.d.ts.map +1 -1
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js +48 -53
- package/build/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.js.map +1 -1
- package/build/package-paths.js +3 -3
- package/build/package-paths.js.map +1 -1
- package/build/template/components/Link.d.ts +1 -1
- package/build/template/components/Link.d.ts.map +1 -1
- package/build/template/components/Link.js +14 -5
- package/build/template/components/Link.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWithSchema.js +0 -3
- package/build/template/components/content/GraphQLDocumentWithSchema.js.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.d.ts.map +1 -1
- package/build/template/components/content/GraphQLDocumentWrapper.js +8 -7
- package/build/template/components/content/GraphQLDocumentWrapper.js.map +1 -1
- package/build/template/components/sidebar/SidebarItem.js +2 -2
- package/build/template/entry.client.d.ts.map +1 -1
- package/build/template/entry.client.js +0 -3
- package/build/template/entry.client.js.map +1 -1
- package/build/template/hooks/useClientOnly.d.ts +9 -0
- package/build/template/hooks/useClientOnly.d.ts.map +1 -0
- package/build/template/hooks/useClientOnly.js +16 -0
- package/build/template/hooks/useClientOnly.js.map +1 -0
- package/build/template/routes/root.d.ts.map +1 -1
- package/build/template/routes/root.js +2 -150
- package/build/template/routes/root.js.map +1 -1
- package/build/template/server/app.d.ts +8 -1
- package/build/template/server/app.d.ts.map +1 -1
- package/build/template/server/app.js +21 -21
- package/build/template/server/app.js.map +1 -1
- package/build/template/server/create-page-html-response.d.ts +7 -0
- package/build/template/server/create-page-html-response.d.ts.map +1 -0
- package/build/template/server/{render-page.js → create-page-html-response.js} +11 -16
- package/build/template/server/create-page-html-response.js.map +1 -0
- package/build/template/server/main.js +2 -1
- package/build/template/server/main.js.map +1 -1
- package/build/template/server/middleware/page.d.ts +4 -0
- package/build/template/server/middleware/page.d.ts.map +1 -0
- package/build/template/server/middleware/page.js +15 -0
- package/build/template/server/middleware/page.js.map +1 -0
- package/build/template/server/middleware/unsupported-assets.d.ts +10 -0
- package/build/template/server/middleware/unsupported-assets.d.ts.map +1 -0
- package/build/template/server/middleware/unsupported-assets.js +21 -0
- package/build/template/server/middleware/unsupported-assets.js.map +1 -0
- package/build/template/server/ssg/generate.d.ts.map +1 -1
- package/build/template/server/ssg/generate.js +33 -34
- package/build/template/server/ssg/generate.js.map +1 -1
- package/build/template/styles/code-block.css +218 -0
- package/package.json +3 -2
- package/src/api/singletons/markdown/markdown.test.ts +1 -1
- package/src/api/vite/plugins/build.ts +97 -89
- package/src/api/vite/plugins/core.ts +15 -10
- package/src/api/vite/plugins/pages.ts +9 -7
- package/src/api/vite/plugins/serve.ts +62 -9
- package/src/lib/file-router/diagnostic-reporter.ts +2 -2
- package/src/lib/graphql-document/components/GraphQLDocument.tsx +23 -11
- package/src/lib/graphql-document/positioning-simple.test.ts +18 -22
- package/src/lib/graphql-document/positioning-simple.ts +97 -108
- package/src/lib/kit-temp.test.ts +15 -3
- package/src/lib/kit-temp.ts +304 -4
- package/src/lib/vite-plugin-reactive-data/vite-plugin-reactive-data.ts +52 -58
- package/src/package-paths.ts +3 -3
- package/src/template/components/Link.tsx +20 -12
- package/src/template/components/content/GraphQLDocumentWithSchema.tsx +0 -5
- package/src/template/components/content/GraphQLDocumentWrapper.tsx +14 -7
- package/src/template/components/sidebar/SidebarItem.tsx +2 -2
- package/src/template/entry.client.tsx +0 -3
- package/src/template/hooks/useClientOnly.ts +21 -0
- package/src/template/routes/root.tsx +0 -159
- package/src/template/server/app.ts +33 -23
- package/src/template/server/{render-page.tsx → create-page-html-response.ts} +19 -16
- package/src/template/server/main.ts +2 -1
- package/src/template/server/middleware/page.ts +19 -0
- package/src/template/server/middleware/unsupported-assets.ts +25 -0
- package/src/template/server/ssg/generate.ts +68 -72
- package/build/template/server/render-page.d.ts +0 -3
- package/build/template/server/render-page.d.ts.map +0 -1
- package/build/template/server/render-page.js.map +0 -1
@@ -1,29 +1,80 @@
|
|
1
1
|
import type { Config } from '#api/config/index'
|
2
2
|
import { reportError } from '#api/server/report-error'
|
3
|
-
import
|
3
|
+
import { Hono } from '#dep/hono/index'
|
4
4
|
import type { Vite } from '#dep/vite/index'
|
5
5
|
import { ResponseInternalServerError } from '#lib/kit-temp'
|
6
6
|
import { debugPolen } from '#singletons/debug'
|
7
7
|
import * as HonoNodeServer from '@hono/node-server'
|
8
|
-
import { Err } from '@wollybeard/kit'
|
8
|
+
import { Err, Obj } from '@wollybeard/kit'
|
9
9
|
|
10
10
|
type App = Hono.Hono
|
11
11
|
|
12
|
+
interface AppOptions {
|
13
|
+
hooks?: {
|
14
|
+
transformHtml?: Array<(html: string, ctx: Hono.Context) => Promise<string> | string>
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
12
18
|
interface AppServerModule {
|
13
|
-
|
19
|
+
createApp: (options: AppOptions) => App
|
14
20
|
}
|
15
21
|
|
16
22
|
export const Serve = (
|
17
23
|
config: Config.Config,
|
18
24
|
): Vite.PluginOption => {
|
19
25
|
const debug = debugPolen.sub(`serve`)
|
26
|
+
debug('construct')
|
27
|
+
const appModulePath = config.paths.framework.template.server.app
|
28
|
+
|
20
29
|
let appPromise: Promise<App | Error>
|
21
30
|
|
22
|
-
const
|
31
|
+
const isNeedAppLoadOrReload = (server: Vite.ViteDevServer): boolean => {
|
32
|
+
const appModule = server.moduleGraph.getModuleById(appModulePath)
|
33
|
+
if (!appModule) return true // Not loaded yet
|
34
|
+
|
35
|
+
// Check if the module or any of its dependencies are invalidated
|
36
|
+
const checkInvalidated = (mod: Vite.ModuleNode, visited = new Set<string>()): boolean => {
|
37
|
+
console.log(Obj.pick(mod, ['ssrInvalidationState', 'invalidationState', 'lastInvalidationTimestamp']))
|
38
|
+
// if (!mod.id || visited.has(mod.id)) return false
|
39
|
+
// visited.add(mod.id)
|
40
|
+
|
41
|
+
// // Check if this module is invalidated
|
42
|
+
// if (mod.transformResult === null) return true
|
43
|
+
|
44
|
+
// // Check all imported modules recursively
|
45
|
+
// for (const imported of mod.importedModules) {
|
46
|
+
// if (checkInvalidated(imported, visited)) return true
|
47
|
+
// }
|
48
|
+
|
49
|
+
return false
|
50
|
+
}
|
51
|
+
|
52
|
+
return checkInvalidated(appModule)
|
53
|
+
}
|
54
|
+
|
55
|
+
const reloadApp = async (server: Vite.ViteDevServer): Promise<App | Error> => {
|
23
56
|
debug('reloadApp')
|
57
|
+
|
24
58
|
return server.ssrLoadModule(config.paths.framework.template.server.app)
|
25
59
|
.then(module => module as AppServerModule)
|
26
|
-
.then(module =>
|
60
|
+
.then(module => {
|
61
|
+
return module.createApp({
|
62
|
+
hooks: {
|
63
|
+
transformHtml: [
|
64
|
+
// Inject entry client script for development
|
65
|
+
(html: string, _ctx) => {
|
66
|
+
const entryClientPath = config.paths.framework.template.client.entrypoint
|
67
|
+
const entryClientScript = `<script type="module" src="${entryClientPath}"></script>`
|
68
|
+
return html.replace('</body>', `${entryClientScript}</body>`)
|
69
|
+
},
|
70
|
+
// Apply Vite's transformations
|
71
|
+
async (html: string, ctx) => {
|
72
|
+
return await server.transformIndexHtml(ctx.req.url, html)
|
73
|
+
},
|
74
|
+
],
|
75
|
+
},
|
76
|
+
})
|
77
|
+
})
|
27
78
|
.catch(async (error) => {
|
28
79
|
if (Err.is(error)) {
|
29
80
|
// ━ Clean Stack Trace
|
@@ -57,12 +108,12 @@ export const Serve = (
|
|
57
108
|
handleHotUpdate({ server }) {
|
58
109
|
debug('handleHotUpdate')
|
59
110
|
// Reload app server immediately in the background
|
60
|
-
appPromise = reloadApp(
|
111
|
+
appPromise = reloadApp(server)
|
61
112
|
},
|
62
113
|
async configureServer(server) {
|
63
114
|
debug('configureServer')
|
64
115
|
// Initial load
|
65
|
-
appPromise = reloadApp(
|
116
|
+
appPromise = reloadApp(server)
|
66
117
|
|
67
118
|
return () => {
|
68
119
|
// Remove index.html serving middleware.
|
@@ -74,6 +125,8 @@ export const Serve = (
|
|
74
125
|
|
75
126
|
// Add middleware that runs our entry server
|
76
127
|
server.middlewares.use((req, res, ___next) => {
|
128
|
+
debug('request')
|
129
|
+
// isNeedAppLoadOrReload(server)
|
77
130
|
void HonoNodeServer.getRequestListener(async request => {
|
78
131
|
// Always await the current app promise
|
79
132
|
const app = await appPromise
|
@@ -81,8 +134,8 @@ export const Serve = (
|
|
81
134
|
// Err.log(app)
|
82
135
|
return ResponseInternalServerError()
|
83
136
|
}
|
84
|
-
|
85
|
-
return
|
137
|
+
|
138
|
+
return await app.fetch(request)
|
86
139
|
})(req, res)
|
87
140
|
})
|
88
141
|
}
|
@@ -13,10 +13,10 @@ export const reportDiagnostics = (diagnostics: Diagnostic[]) => {
|
|
13
13
|
infos.length > 0 && `${infos.length} info${infos.length === 1 ? '' : 's'}`,
|
14
14
|
].filter(Boolean).join(', ')
|
15
15
|
|
16
|
-
console.warn(`\
|
16
|
+
console.warn(`\nPolen found ${summary}:\n`)
|
17
17
|
|
18
18
|
diagnostics.forEach((diagnostic, index) => {
|
19
|
-
const icon = diagnostic.severity === 'error' ? '
|
19
|
+
const icon = diagnostic.severity === 'error' ? '✗' : diagnostic.severity === 'warning' ? '⚠' : 'ⓘ'
|
20
20
|
console.warn(`${icon} ${index + 1}. ${diagnostic.message}\n`)
|
21
21
|
})
|
22
22
|
}
|
@@ -59,7 +59,7 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
|
|
59
59
|
onNavigate,
|
60
60
|
validate = true,
|
61
61
|
className = '',
|
62
|
-
} = options
|
62
|
+
} = options
|
63
63
|
|
64
64
|
const navigate = useNavigate()
|
65
65
|
const handleNavigate = onNavigate || ((url: string) => navigate(url))
|
@@ -93,8 +93,9 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
|
|
93
93
|
const analysisResult = ReactHooks.useMemo(() => {
|
94
94
|
if (plain) return null
|
95
95
|
const result = analyze(children, { schema })
|
96
|
+
// Debug logging handled by debug prop
|
96
97
|
return result
|
97
|
-
}, [children, plain, schema])
|
98
|
+
}, [children, plain, schema, debug])
|
98
99
|
|
99
100
|
// Layer 2: Schema resolution
|
100
101
|
const resolver = ReactHooks.useMemo(() => {
|
@@ -130,6 +131,7 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
|
|
130
131
|
// Prepare code block and calculate positions after render
|
131
132
|
ReactHooks.useEffect(() => {
|
132
133
|
if (!containerRef.current || !analysisResult || !positionCalculator || plain) {
|
134
|
+
// Skip position calculation - debug handled by debug prop
|
133
135
|
return
|
134
136
|
}
|
135
137
|
|
@@ -138,11 +140,13 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
|
|
138
140
|
|| containerRef.current.querySelector('pre code')
|
139
141
|
|| containerRef.current.querySelector('code')
|
140
142
|
if (!codeElement) {
|
143
|
+
// No code element found - skip
|
141
144
|
return
|
142
145
|
}
|
143
146
|
|
144
147
|
// Prepare the code block (wrap identifiers)
|
145
148
|
const identifiers = Array.from(analysisResult.identifiers.byPosition.values())
|
149
|
+
// Prepare code block with identifiers
|
146
150
|
positionCalculator.prepareCodeBlock(codeElement as Element, identifiers)
|
147
151
|
|
148
152
|
// Get positions after DOM update
|
@@ -150,28 +154,36 @@ export const GraphQLDocument: React.FC<GraphQLDocumentProps> = ({
|
|
150
154
|
// Pass containerRef.current as the reference element for positioning
|
151
155
|
if (containerRef.current) {
|
152
156
|
const newPositions = positionCalculator.getIdentifierPositions(codeElement as Element, containerRef.current)
|
157
|
+
// Position calculation complete
|
153
158
|
setPositions(newPositions)
|
154
159
|
setIsReady(true)
|
155
160
|
}
|
156
161
|
})
|
157
162
|
}, [analysisResult, positionCalculator, plain, highlightedHtml])
|
158
163
|
|
159
|
-
// Handle resize events
|
164
|
+
// Handle resize events with debouncing
|
160
165
|
ReactHooks.useEffect(() => {
|
161
166
|
if (!containerRef.current || !positionCalculator || plain) return
|
162
167
|
|
168
|
+
let resizeTimer: NodeJS.Timeout
|
163
169
|
const handleResize = () => {
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
170
|
+
clearTimeout(resizeTimer)
|
171
|
+
resizeTimer = setTimeout(() => {
|
172
|
+
const codeElement = containerRef.current?.querySelector('pre.shiki code')
|
173
|
+
|| containerRef.current?.querySelector('pre code')
|
174
|
+
|| containerRef.current?.querySelector('code')
|
175
|
+
if (codeElement && containerRef.current) {
|
176
|
+
const newPositions = positionCalculator.getIdentifierPositions(codeElement as Element, containerRef.current)
|
177
|
+
setPositions(newPositions)
|
178
|
+
}
|
179
|
+
}, 100) // Debounce resize events
|
171
180
|
}
|
172
181
|
|
173
182
|
window.addEventListener('resize', handleResize)
|
174
|
-
return () =>
|
183
|
+
return () => {
|
184
|
+
clearTimeout(resizeTimer)
|
185
|
+
window.removeEventListener('resize', handleResize)
|
186
|
+
}
|
175
187
|
}, [positionCalculator, plain])
|
176
188
|
|
177
189
|
// Validation errors
|
@@ -33,13 +33,11 @@ describe('Simple Positioning Engine', () => {
|
|
33
33
|
const container = document.createElement('div')
|
34
34
|
container.innerHTML = `
|
35
35
|
<pre class="shiki">
|
36
|
-
<code>
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
<span class="line">}</span>
|
42
|
-
</code>
|
36
|
+
<code>query GetUser {
|
37
|
+
user {
|
38
|
+
name
|
39
|
+
}
|
40
|
+
}</code>
|
43
41
|
</pre>
|
44
42
|
`
|
45
43
|
|
@@ -49,7 +47,8 @@ describe('Simple Positioning Engine', () => {
|
|
49
47
|
createTestIdentifier('name', 3, 5, 'Field'),
|
50
48
|
]
|
51
49
|
|
52
|
-
|
50
|
+
const codeElement = container.querySelector('code')!
|
51
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
53
52
|
|
54
53
|
// Check that identifiers were wrapped
|
55
54
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -66,9 +65,7 @@ describe('Simple Positioning Engine', () => {
|
|
66
65
|
const container = document.createElement('div')
|
67
66
|
container.innerHTML = `
|
68
67
|
<pre class="shiki">
|
69
|
-
<code>
|
70
|
-
<span class="line">query GetUserById($id: ID!) {</span>
|
71
|
-
</code>
|
68
|
+
<code>query GetUserById($id: ID!) {</code>
|
72
69
|
</pre>
|
73
70
|
`
|
74
71
|
|
@@ -79,7 +76,8 @@ describe('Simple Positioning Engine', () => {
|
|
79
76
|
createTestIdentifier('ID', 1, 24, 'Type'),
|
80
77
|
]
|
81
78
|
|
82
|
-
|
79
|
+
const codeElement = container.querySelector('code')!
|
80
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
83
81
|
|
84
82
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
85
83
|
expect(wrappedElements.length).toBe(4)
|
@@ -140,9 +138,7 @@ describe('Simple Positioning Engine', () => {
|
|
140
138
|
const container = document.createElement('div')
|
141
139
|
container.innerHTML = `
|
142
140
|
<pre class="shiki">
|
143
|
-
<code>
|
144
|
-
<span class="line"><span data-graphql-id="existing">user</span> {</span>
|
145
|
-
</code>
|
141
|
+
<code><span data-graphql-id="existing">user</span> {</code>
|
146
142
|
</pre>
|
147
143
|
`
|
148
144
|
|
@@ -150,7 +146,8 @@ describe('Simple Positioning Engine', () => {
|
|
150
146
|
createTestIdentifier('user', 1, 1),
|
151
147
|
]
|
152
148
|
|
153
|
-
|
149
|
+
const codeElement = container.querySelector('code')!
|
150
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
154
151
|
|
155
152
|
// Should still only have one wrapped element
|
156
153
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -162,11 +159,9 @@ describe('Simple Positioning Engine', () => {
|
|
162
159
|
const container = document.createElement('div')
|
163
160
|
container.innerHTML = `
|
164
161
|
<pre class="shiki">
|
165
|
-
<code>
|
166
|
-
|
167
|
-
|
168
|
-
<span class="line"> user</span>
|
169
|
-
</code>
|
162
|
+
<code>query {
|
163
|
+
|
164
|
+
user</code>
|
170
165
|
</pre>
|
171
166
|
`
|
172
167
|
|
@@ -175,8 +170,9 @@ describe('Simple Positioning Engine', () => {
|
|
175
170
|
createTestIdentifier('user', 3, 3),
|
176
171
|
]
|
177
172
|
|
173
|
+
const codeElement = container.querySelector('code')!
|
178
174
|
expect(() => {
|
179
|
-
calculator.prepareCodeBlock(
|
175
|
+
calculator.prepareCodeBlock(codeElement, identifiers)
|
180
176
|
}).not.toThrow()
|
181
177
|
|
182
178
|
const wrappedElements = container.querySelectorAll('[data-graphql-id]')
|
@@ -50,21 +50,104 @@ export class SimplePositionCalculator {
|
|
50
50
|
containerElement: Element,
|
51
51
|
identifiers: Identifier[],
|
52
52
|
): void {
|
53
|
-
//
|
54
|
-
const
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
// Get the full text content of the container
|
54
|
+
const fullText = containerElement.textContent || ''
|
55
|
+
const lines = fullText.split('\n')
|
56
|
+
|
57
|
+
// Build a map of line start positions in the full text
|
58
|
+
const lineStartPositions: number[] = [0]
|
59
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
60
|
+
const lineLength = lines[i]?.length ?? 0
|
61
|
+
lineStartPositions.push(lineStartPositions[i]! + lineLength + 1) // +1 for newline
|
62
|
+
}
|
63
|
+
|
64
|
+
// Process identifiers by line
|
65
|
+
for (const identifier of identifiers) {
|
66
|
+
const lineIndex = identifier.position.line - 1
|
67
|
+
if (lineIndex >= lines.length || lineIndex < 0) continue
|
68
|
+
|
69
|
+
const lineText = lines[lineIndex]
|
70
|
+
if (!lineText) continue
|
71
|
+
|
72
|
+
const columnIndex = identifier.position.column - 1
|
73
|
+
|
74
|
+
// Check if the identifier exists at the expected position
|
75
|
+
if (lineText.substring(columnIndex).startsWith(identifier.name)) {
|
76
|
+
// Calculate the absolute position in the full text
|
77
|
+
const lineStartPosition = lineStartPositions[lineIndex] ?? 0
|
78
|
+
const absolutePosition = lineStartPosition + columnIndex
|
79
|
+
|
80
|
+
// Check if already wrapped at this specific position
|
81
|
+
const existingWrapped = containerElement.querySelectorAll(`[data-graphql-id]`)
|
82
|
+
let alreadyWrapped = false
|
83
|
+
for (const wrapped of existingWrapped) {
|
84
|
+
if (wrapped.textContent === identifier.name) {
|
85
|
+
const startPos = parseInt(wrapped.getAttribute('data-graphql-start') || '0')
|
86
|
+
if (startPos === identifier.position.start) {
|
87
|
+
alreadyWrapped = true
|
88
|
+
break
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
if (alreadyWrapped) continue
|
93
|
+
|
94
|
+
// Create wrapper span
|
95
|
+
const wrapper = document.createElement('span')
|
96
|
+
const id = `${identifier.position.start}-${identifier.name}-${identifier.kind}`
|
97
|
+
wrapper.setAttribute('data-graphql-id', id)
|
98
|
+
wrapper.setAttribute('data-graphql-name', identifier.name)
|
99
|
+
wrapper.setAttribute('data-graphql-kind', identifier.kind)
|
100
|
+
wrapper.setAttribute('data-graphql-start', String(identifier.position.start))
|
101
|
+
wrapper.setAttribute('data-graphql-end', String(identifier.position.end))
|
102
|
+
wrapper.setAttribute('data-graphql-line', String(identifier.position.line))
|
103
|
+
wrapper.setAttribute('data-graphql-column', String(identifier.position.column))
|
104
|
+
wrapper.setAttribute('data-graphql-path', identifier.schemaPath.join(','))
|
105
|
+
|
106
|
+
// Find the position in the container and wrap the text
|
107
|
+
const walker = document.createTreeWalker(
|
108
|
+
containerElement,
|
109
|
+
NodeFilter.SHOW_TEXT,
|
110
|
+
null,
|
111
|
+
)
|
112
|
+
|
113
|
+
let currentPos = 0
|
114
|
+
let node: Node | null
|
115
|
+
|
116
|
+
while (node = walker.nextNode()) {
|
117
|
+
const textNode = node as Text
|
118
|
+
const text = textNode.textContent || ''
|
119
|
+
|
120
|
+
// Check if this text node contains our identifier
|
121
|
+
if (currentPos <= absolutePosition && absolutePosition < currentPos + text.length) {
|
122
|
+
const relativePos = absolutePosition - currentPos
|
123
|
+
|
124
|
+
if (text.substring(relativePos).startsWith(identifier.name)) {
|
125
|
+
// Split the text node
|
126
|
+
const before = text.substring(0, relativePos)
|
127
|
+
const identifierText = identifier.name
|
128
|
+
const after = text.substring(relativePos + identifierText.length)
|
129
|
+
|
130
|
+
const parent = textNode.parentNode!
|
131
|
+
|
132
|
+
if (before) {
|
133
|
+
parent.insertBefore(document.createTextNode(before), textNode)
|
134
|
+
}
|
135
|
+
|
136
|
+
wrapper.textContent = identifierText
|
137
|
+
parent.insertBefore(wrapper, textNode)
|
138
|
+
|
139
|
+
if (after) {
|
140
|
+
parent.insertBefore(document.createTextNode(after), textNode)
|
141
|
+
}
|
142
|
+
|
143
|
+
parent.removeChild(textNode)
|
144
|
+
break
|
145
|
+
}
|
146
|
+
}
|
62
147
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
const wrapped = this.wrapIdentifier(containerElement, identifier)
|
67
|
-
if (wrapped) wrappedCount++
|
148
|
+
currentPos += text.length
|
149
|
+
}
|
150
|
+
}
|
68
151
|
}
|
69
152
|
}
|
70
153
|
|
@@ -117,100 +200,6 @@ export class SimplePositionCalculator {
|
|
117
200
|
|
118
201
|
return results
|
119
202
|
}
|
120
|
-
|
121
|
-
/**
|
122
|
-
* Wrap an identifier in a span for positioning
|
123
|
-
* Returns true if the identifier was successfully wrapped
|
124
|
-
*/
|
125
|
-
private wrapIdentifier(containerElement: Element, identifier: Identifier): boolean {
|
126
|
-
const walker = document.createTreeWalker(
|
127
|
-
containerElement,
|
128
|
-
NodeFilter.SHOW_TEXT,
|
129
|
-
null,
|
130
|
-
)
|
131
|
-
|
132
|
-
let currentLine = 1
|
133
|
-
let currentColumn = 1
|
134
|
-
let node: Node | null
|
135
|
-
|
136
|
-
while (node = walker.nextNode()) {
|
137
|
-
const textNode = node as Text
|
138
|
-
const text = textNode.textContent || ''
|
139
|
-
|
140
|
-
// Check if already wrapped
|
141
|
-
if (textNode.parentElement?.hasAttribute('data-graphql-id')) {
|
142
|
-
// Update position tracking and continue
|
143
|
-
for (const char of text) {
|
144
|
-
if (char === '\n') {
|
145
|
-
currentLine++
|
146
|
-
currentColumn = 1
|
147
|
-
} else {
|
148
|
-
currentColumn++
|
149
|
-
}
|
150
|
-
}
|
151
|
-
continue
|
152
|
-
}
|
153
|
-
|
154
|
-
// Track position in the text
|
155
|
-
let textIndex = 0
|
156
|
-
while (textIndex < text.length) {
|
157
|
-
// Check if we're at the identifier's position
|
158
|
-
if (
|
159
|
-
currentLine === identifier.position.line
|
160
|
-
&& currentColumn === identifier.position.column
|
161
|
-
) {
|
162
|
-
// Verify it's actually our identifier
|
163
|
-
const remainingText = text.substring(textIndex)
|
164
|
-
if (remainingText.startsWith(identifier.name)) {
|
165
|
-
// Create a unique ID for this identifier
|
166
|
-
const id = `${identifier.position.start}-${identifier.name}-${identifier.kind}`
|
167
|
-
|
168
|
-
// Split the text node and wrap the identifier
|
169
|
-
const before = text.substring(0, textIndex)
|
170
|
-
const after = text.substring(textIndex + identifier.name.length)
|
171
|
-
|
172
|
-
const span = document.createElement('span')
|
173
|
-
span.setAttribute('data-graphql-id', id)
|
174
|
-
span.setAttribute('data-graphql-name', identifier.name)
|
175
|
-
span.setAttribute('data-graphql-kind', identifier.kind)
|
176
|
-
span.setAttribute('data-graphql-start', String(identifier.position.start))
|
177
|
-
span.setAttribute('data-graphql-end', String(identifier.position.end))
|
178
|
-
span.setAttribute('data-graphql-line', String(identifier.position.line))
|
179
|
-
span.setAttribute('data-graphql-column', String(identifier.position.column))
|
180
|
-
span.setAttribute('data-graphql-path', identifier.schemaPath.join(','))
|
181
|
-
span.textContent = identifier.name
|
182
|
-
|
183
|
-
const parent = textNode.parentNode!
|
184
|
-
|
185
|
-
if (before) {
|
186
|
-
parent.insertBefore(document.createTextNode(before), textNode)
|
187
|
-
}
|
188
|
-
|
189
|
-
parent.insertBefore(span, textNode)
|
190
|
-
|
191
|
-
if (after) {
|
192
|
-
parent.insertBefore(document.createTextNode(after), textNode)
|
193
|
-
}
|
194
|
-
|
195
|
-
parent.removeChild(textNode)
|
196
|
-
return true
|
197
|
-
}
|
198
|
-
}
|
199
|
-
|
200
|
-
// Update position tracking
|
201
|
-
const char = text[textIndex]
|
202
|
-
if (char === '\n') {
|
203
|
-
currentLine++
|
204
|
-
currentColumn = 1
|
205
|
-
} else {
|
206
|
-
currentColumn++
|
207
|
-
}
|
208
|
-
textIndex++
|
209
|
-
}
|
210
|
-
}
|
211
|
-
|
212
|
-
return false // Identifier not found
|
213
|
-
}
|
214
203
|
}
|
215
204
|
|
216
205
|
/**
|
package/src/lib/kit-temp.test.ts
CHANGED
@@ -74,8 +74,13 @@ describe('property-based tests', () => {
|
|
74
74
|
expect(inAllowed).toBe(!inDenied)
|
75
75
|
})
|
76
76
|
|
77
|
-
// Combined they reconstruct the original object
|
78
|
-
|
77
|
+
// Combined they reconstruct the original object (only own properties)
|
78
|
+
const reconstructed = { ...allowed, ...denied }
|
79
|
+
const ownPropsObj = Object.keys(obj).reduce((acc, key) => {
|
80
|
+
acc[key] = obj[key]
|
81
|
+
return acc
|
82
|
+
}, {} as any)
|
83
|
+
expect(reconstructed).toEqual(ownPropsObj)
|
79
84
|
},
|
80
85
|
),
|
81
86
|
)
|
@@ -87,7 +92,14 @@ describe('property-based tests', () => {
|
|
87
92
|
fc.object(),
|
88
93
|
(obj) => {
|
89
94
|
const filtered = objFilter(obj, () => true)
|
90
|
-
|
95
|
+
|
96
|
+
// Object.keys doesn't include __proto__, so we need to handle it specially
|
97
|
+
const objWithoutProto = Object.keys(obj).reduce((acc, key) => {
|
98
|
+
acc[key] = obj[key]
|
99
|
+
return acc
|
100
|
+
}, {} as any)
|
101
|
+
|
102
|
+
expect(filtered).toEqual(objWithoutProto)
|
91
103
|
|
92
104
|
// Values are the same reference
|
93
105
|
Object.keys(filtered).forEach(key => {
|