@jk2908/cxx 0.3.3 → 0.3.4
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/README.md +0 -3
- package/dist/build.d.ts +10 -4
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +157 -78
- package/dist/build.js.map +6 -5
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/next/cxx-loader.cjs +55 -63
- package/dist/next/cxx-loader.cjs.map +6 -6
- package/dist/next/index.d.ts +1 -3
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +169 -104
- package/dist/next/index.js.map +7 -6
- package/dist/types.d.ts +0 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +0 -16
- package/dist/utils.d.ts.map +1 -1
- package/dist/vite/index.d.ts +1 -0
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +193 -239
- package/dist/vite/index.js.map +7 -7
- package/package.json +1 -1
- package/src/build.ts +130 -40
- package/src/logger.ts +2 -7
- package/src/next/index.ts +19 -30
- package/src/types.ts +0 -2
- package/src/utils.ts +0 -87
- package/src/vite/index.ts +49 -110
package/src/utils.ts
CHANGED
|
@@ -8,90 +8,3 @@ export function pascalise(str: string) {
|
|
|
8
8
|
.map(capitalise)
|
|
9
9
|
.join('')
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
export type Batcher<K, V> = {
|
|
13
|
-
/**
|
|
14
|
-
* Queue a value for a key. Queueing the same key again replaces the previous value.
|
|
15
|
-
*/
|
|
16
|
-
add: (key: K, value: V) => void
|
|
17
|
-
/**
|
|
18
|
-
* Run any waiting work now and resolve once it has finished.
|
|
19
|
-
*/
|
|
20
|
-
flush: () => Promise<void>
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Group bursts of work and run them together. Every key queued within `wait` is
|
|
25
|
-
* included once, and a key queued while a batch is running is handled in the next
|
|
26
|
-
* batch instead of replacing the one in progress.
|
|
27
|
-
*/
|
|
28
|
-
export function createBatcher<K, V>(
|
|
29
|
-
wait: number,
|
|
30
|
-
flush: (batch: Map<K, V>) => void | Promise<void>,
|
|
31
|
-
): Batcher<K, V> {
|
|
32
|
-
// work waiting to run, keyed so each key is only queued once
|
|
33
|
-
const pending = new Map<K, V>()
|
|
34
|
-
// waits for a burst of changes to settle before running
|
|
35
|
-
let timer: ReturnType<typeof setTimeout> | null = null
|
|
36
|
-
// the run in progress, so `flush` can wait for it and we never start a second
|
|
37
|
-
let current: Promise<void> | null = null
|
|
38
|
-
|
|
39
|
-
function drain() {
|
|
40
|
-
// a run is already going, so let it pick up the new work
|
|
41
|
-
if (current) return current
|
|
42
|
-
|
|
43
|
-
const run = (async () => {
|
|
44
|
-
// keep looping while there is work, including work added during a flush
|
|
45
|
-
while (pending.size > 0) {
|
|
46
|
-
// snapshot the queued work
|
|
47
|
-
const batch = new Map(pending)
|
|
48
|
-
// empty the queue right away, so anything added during flush waits for the next loop
|
|
49
|
-
pending.clear()
|
|
50
|
-
|
|
51
|
-
await flush(batch)
|
|
52
|
-
}
|
|
53
|
-
})()
|
|
54
|
-
|
|
55
|
-
current = run
|
|
56
|
-
run.then(
|
|
57
|
-
() => {
|
|
58
|
-
if (current === run) current = null
|
|
59
|
-
},
|
|
60
|
-
() => {
|
|
61
|
-
if (current === run) current = null
|
|
62
|
-
},
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return run
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function schedule() {
|
|
69
|
-
// cancel the previous wait, so the timer restarts from now
|
|
70
|
-
if (timer) clearTimeout(timer)
|
|
71
|
-
|
|
72
|
-
// run once the calls stop coming for `wait` ms
|
|
73
|
-
timer = setTimeout(() => {
|
|
74
|
-
// the timer has fired, so drop the stale handle
|
|
75
|
-
timer = null
|
|
76
|
-
// the flush callback handles its own errors, so just start the run
|
|
77
|
-
void drain().catch(() => {})
|
|
78
|
-
}, wait)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
add(key, value) {
|
|
83
|
-
// the newest value for this key wins
|
|
84
|
-
pending.set(key, value)
|
|
85
|
-
schedule()
|
|
86
|
-
},
|
|
87
|
-
flush() {
|
|
88
|
-
// run now instead of waiting out the timer
|
|
89
|
-
if (timer) {
|
|
90
|
-
clearTimeout(timer)
|
|
91
|
-
timer = null
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return drain()
|
|
95
|
-
},
|
|
96
|
-
}
|
|
97
|
-
}
|
package/src/vite/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { realpathSync } from 'node:fs'
|
|
2
|
-
import fs from 'node:fs/promises'
|
|
3
2
|
import path from 'node:path'
|
|
4
3
|
|
|
5
4
|
import { type Plugin, type ResolvedConfig, type ViteDevServer } from 'vite'
|
|
@@ -8,15 +7,13 @@ import type { PluginConfig } from '../types.js'
|
|
|
8
7
|
import {
|
|
9
8
|
collect,
|
|
10
9
|
flushTypes,
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
refreshFile,
|
|
11
|
+
seedTypes,
|
|
12
|
+
SOURCE_EXTENSIONS,
|
|
13
13
|
writeTypesForFile,
|
|
14
14
|
} from '../build.js'
|
|
15
15
|
import { GENERATED_DIR } from '../config.js'
|
|
16
16
|
import { Logger } from '../logger.js'
|
|
17
|
-
import { createBatcher } from '../utils.js'
|
|
18
|
-
|
|
19
|
-
const ACCEPTED_EXTENSIONS = new Set(['.js', '.jsx', '.ts', '.tsx'])
|
|
20
17
|
|
|
21
18
|
type WatchEvent = 'add' | 'change' | 'unlink'
|
|
22
19
|
|
|
@@ -24,48 +21,45 @@ function normaliseWatchPath(p: string) {
|
|
|
24
21
|
return p.replace(/\\/g, '/')
|
|
25
22
|
}
|
|
26
23
|
|
|
27
|
-
/** Tiny glob matcher for the ignore patterns we support (`**`, `*`, `?`). */
|
|
28
|
-
function globToRegExp(glob: string) {
|
|
29
|
-
const pattern = glob
|
|
30
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
31
|
-
.replace(/\*\*(\/|$)/g, '.*$1')
|
|
32
|
-
.replace(/\*/g, '[^/]*')
|
|
33
|
-
.replace(/\?/g, '[^/]')
|
|
34
|
-
|
|
35
|
-
return new RegExp(`^${pattern}$`)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
24
|
export default function cxx(pluginConfig: PluginConfig = {}) {
|
|
39
25
|
const logger = new Logger()
|
|
40
26
|
|
|
41
27
|
// resolved from Vite's root in `configResolved`, not from `process.cwd()`
|
|
42
28
|
let watchRoot = normaliseWatchPath(process.cwd())
|
|
43
|
-
|
|
29
|
+
let seeded = false
|
|
30
|
+
|
|
31
|
+
// realpath is synchronous, so cache it per directory to keep it off the transform hot path
|
|
32
|
+
const realpaths = new Map<string, string>()
|
|
44
33
|
|
|
45
34
|
function resolveWatchFile(filePath: string) {
|
|
46
35
|
const absolutePath = path.resolve(watchRoot, filePath)
|
|
47
36
|
const parentPath = path.dirname(absolutePath)
|
|
48
37
|
|
|
49
|
-
|
|
50
|
-
const resolvedParentPath = normaliseWatchPath(realpathSync.native(parentPath))
|
|
38
|
+
let resolvedParentPath = realpaths.get(parentPath)
|
|
51
39
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
40
|
+
if (resolvedParentPath === undefined) {
|
|
41
|
+
try {
|
|
42
|
+
resolvedParentPath = normaliseWatchPath(realpathSync.native(parentPath))
|
|
43
|
+
} catch {
|
|
44
|
+
resolvedParentPath = normaliseWatchPath(parentPath)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
realpaths.set(parentPath, resolvedParentPath)
|
|
57
48
|
}
|
|
49
|
+
|
|
50
|
+
return normaliseWatchPath(path.join(resolvedParentPath, path.basename(absolutePath)))
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
/**
|
|
61
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Returns the canonical, watchable path, or null when the file is irrelevant.
|
|
55
|
+
*/
|
|
56
|
+
function watchedFile(filePath: string) {
|
|
62
57
|
const resolvedPath = resolveWatchFile(filePath)
|
|
63
58
|
|
|
64
59
|
if (
|
|
65
60
|
resolvedPath.startsWith(`${watchRoot}/`) &&
|
|
66
61
|
!resolvedPath.includes(`/${GENERATED_DIR}/`) &&
|
|
67
|
-
|
|
68
|
-
!ignored.some(regexp => regexp.test(resolvedPath))
|
|
62
|
+
SOURCE_EXTENSIONS.has(path.extname(resolvedPath))
|
|
69
63
|
) {
|
|
70
64
|
return resolvedPath
|
|
71
65
|
}
|
|
@@ -73,76 +67,34 @@ export default function cxx(pluginConfig: PluginConfig = {}) {
|
|
|
73
67
|
return null
|
|
74
68
|
}
|
|
75
69
|
|
|
76
|
-
async function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
source = await fs.readFile(file, 'utf-8')
|
|
81
|
-
} catch (err) {
|
|
82
|
-
if (!isENOENT(err)) throw err
|
|
70
|
+
async function handleEvent(event: WatchEvent, file: string) {
|
|
71
|
+
if (event === 'unlink') {
|
|
83
72
|
await writeTypesForFile(file, new Map())
|
|
84
|
-
|
|
73
|
+
} else {
|
|
74
|
+
await refreshFile(file, pluginConfig)
|
|
85
75
|
}
|
|
86
76
|
|
|
87
|
-
|
|
88
|
-
? collect(source, file, pluginConfig).tags
|
|
89
|
-
: new Map()
|
|
90
|
-
await writeTypesForFile(file, tags)
|
|
77
|
+
logger.info(`updated types from ${file}`)
|
|
91
78
|
}
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
async function scan(dir: string) {
|
|
98
|
-
let entries
|
|
80
|
+
function onWatch(event: WatchEvent) {
|
|
81
|
+
return (p: string) => {
|
|
82
|
+
const file = watchedFile(p)
|
|
83
|
+
if (!file) return
|
|
99
84
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return
|
|
85
|
+
void handleEvent(event, file).catch(err => {
|
|
86
|
+
logger.error(`failed to update types from ${file}`, err)
|
|
87
|
+
})
|
|
104
88
|
}
|
|
89
|
+
}
|
|
105
90
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const full = path.join(dir, entry.name)
|
|
111
|
-
|
|
112
|
-
if (entry.isDirectory()) {
|
|
113
|
-
await scan(full)
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (!ACCEPTED_EXTENSIONS.has(path.extname(entry.name))) return
|
|
118
|
-
|
|
119
|
-
const resolved = resolveWatchFile(full)
|
|
120
|
-
if (ignored.some(regexp => regexp.test(resolved))) return
|
|
91
|
+
async function seed() {
|
|
92
|
+
if (seeded) return
|
|
93
|
+
seeded = true
|
|
121
94
|
|
|
122
|
-
|
|
123
|
-
}),
|
|
124
|
-
)
|
|
95
|
+
await seedTypes(watchRoot, pluginConfig)
|
|
125
96
|
}
|
|
126
97
|
|
|
127
|
-
// collect change events that arrive close together and handle each file once.
|
|
128
|
-
// The file path is the key, so changes to different files are all kept, and a
|
|
129
|
-
// change that arrives during a rebuild runs after that rebuild finishes.
|
|
130
|
-
const queue = createBatcher<string, WatchEvent>(75, async batch => {
|
|
131
|
-
for (const [file, event] of batch) {
|
|
132
|
-
try {
|
|
133
|
-
if (event === 'unlink') {
|
|
134
|
-
await writeTypesForFile(file, new Map())
|
|
135
|
-
} else {
|
|
136
|
-
await recordFile(file)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
logger.info(`updated types from ${file}`)
|
|
140
|
-
} catch (err) {
|
|
141
|
-
logger.error(`failed to update types from ${file}`, err)
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
|
|
146
98
|
return {
|
|
147
99
|
name: 'cxx',
|
|
148
100
|
enforce: 'pre',
|
|
@@ -154,16 +106,20 @@ export default function cxx(pluginConfig: PluginConfig = {}) {
|
|
|
154
106
|
} catch {
|
|
155
107
|
watchRoot = normaliseWatchPath(root)
|
|
156
108
|
}
|
|
109
|
+
|
|
110
|
+
realpaths.clear()
|
|
111
|
+
},
|
|
112
|
+
async buildStart() {
|
|
113
|
+
await seed()
|
|
157
114
|
},
|
|
158
115
|
transform(code, id) {
|
|
159
116
|
if (id.startsWith('\0') || id.includes('/node_modules/')) return null
|
|
160
|
-
|
|
161
117
|
// cheap bail-out before running the regex/lightningcss on every module
|
|
162
118
|
if (!code.includes('cxx')) return null
|
|
163
119
|
|
|
164
120
|
const cleanId = id.split('?')[0]
|
|
165
121
|
|
|
166
|
-
if (!
|
|
122
|
+
if (!SOURCE_EXTENSIONS.has(path.extname(cleanId))) return null
|
|
167
123
|
|
|
168
124
|
const { template, tags, map } = collect(code, cleanId, pluginConfig)
|
|
169
125
|
|
|
@@ -180,26 +136,9 @@ export default function cxx(pluginConfig: PluginConfig = {}) {
|
|
|
180
136
|
logger.info('Watching for changes...')
|
|
181
137
|
|
|
182
138
|
server.watcher
|
|
183
|
-
.on('add', (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
})
|
|
187
|
-
.on('change', (p: string) => {
|
|
188
|
-
const file = watchedFile(p)
|
|
189
|
-
if (file) queue.add(file, 'change')
|
|
190
|
-
})
|
|
191
|
-
.on('unlink', (p: string) => {
|
|
192
|
-
const file = watchedFile(p)
|
|
193
|
-
if (file) queue.add(file, 'unlink')
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
void scan(watchRoot)
|
|
197
|
-
.then(() => flushTypes())
|
|
198
|
-
.then(() => {
|
|
199
|
-
const err = takeRebuildError()
|
|
200
|
-
if (err) logger.error('failed to generate types', err)
|
|
201
|
-
})
|
|
202
|
-
.catch(err => logger.error('failed to scan for cxx templates', err))
|
|
139
|
+
.on('add', onWatch('add'))
|
|
140
|
+
.on('change', onWatch('change'))
|
|
141
|
+
.on('unlink', onWatch('unlink'))
|
|
203
142
|
},
|
|
204
143
|
async writeBundle() {
|
|
205
144
|
await flushTypes()
|