@jk2908/cxx 0.2.6 → 0.3.1
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 +18 -2
- package/dist/build.d.ts +13 -4
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +180 -54
- package/dist/build.js.map +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -7
- package/dist/index.js.map +3 -3
- package/dist/logger.d.ts +0 -2
- package/dist/logger.d.ts.map +1 -1
- package/dist/next/cxx-loader.cjs +214 -132
- package/dist/next/cxx-loader.cjs.map +7 -7
- package/dist/next/index.d.ts +32 -11
- package/dist/next/index.d.ts.map +1 -1
- package/dist/next/index.js +272 -123
- package/dist/next/index.js.map +5 -5
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +16 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/vite/index.d.ts +11 -3
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +273 -115
- package/dist/vite/index.js.map +6 -6
- package/package.json +83 -79
- package/src/build.ts +258 -93
- package/src/index.ts +26 -9
- package/src/logger.ts +5 -13
- package/src/next/cxx-loader.js +1 -5
- package/src/next/index.ts +143 -100
- package/src/types.ts +5 -4
- package/src/utils.ts +72 -7
- package/src/vite/index.ts +109 -78
package/src/build.ts
CHANGED
|
@@ -3,17 +3,28 @@ import fs from 'node:fs/promises'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
|
|
5
5
|
import { transform } from 'lightningcss'
|
|
6
|
+
import MagicString, { type SourceMap } from 'magic-string'
|
|
6
7
|
|
|
7
8
|
import type { BuildContext, PluginConfig, Tags } from './types.js'
|
|
8
9
|
import { AUTOGEN_MSG, GENERATED_DIR, PKG_NAME } from './config.js'
|
|
9
10
|
import { LruCache } from './lru.js'
|
|
10
11
|
import { pascalise } from './utils.js'
|
|
11
12
|
|
|
13
|
+
// decode bytes to a string, then stringify into a safe literal. `${bytes}` only
|
|
14
|
+
// works by accident for Buffer; a plain Uint8Array stringifies as `97,123,125`
|
|
15
|
+
const decoder = new TextDecoder()
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
|
-
* LRU cache of `collect` results by file id
|
|
14
|
-
* reuse the same parsed template/tags.
|
|
18
|
+
* LRU cache of `collect` results by file id + last seen source + config so watcher and transform
|
|
19
|
+
* can reuse the same parsed template/tags.
|
|
15
20
|
*/
|
|
16
|
-
const collectCache = new LruCache<{
|
|
21
|
+
const collectCache = new LruCache<{
|
|
22
|
+
source: string
|
|
23
|
+
config: string
|
|
24
|
+
template: string
|
|
25
|
+
tags: Tags
|
|
26
|
+
map: SourceMap | null
|
|
27
|
+
}>()
|
|
17
28
|
|
|
18
29
|
/**
|
|
19
30
|
* LRU cache for file content. Stores the last written content per file path so `maybeWrite`
|
|
@@ -21,6 +32,12 @@ const collectCache = new LruCache<{ source: string; template: string; tags: Tags
|
|
|
21
32
|
*/
|
|
22
33
|
const fileCache = new LruCache<string>()
|
|
23
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Serialises type file writes inside a single process so overlapping `writeTypes` calls can't
|
|
37
|
+
* interleave or land out of order.
|
|
38
|
+
*/
|
|
39
|
+
let writeChain: Promise<void> = Promise.resolve()
|
|
40
|
+
|
|
24
41
|
/**
|
|
25
42
|
* Returns whether the error was caused by a missing file or directory.
|
|
26
43
|
*/
|
|
@@ -29,20 +46,44 @@ export function isENOENT(err: unknown) {
|
|
|
29
46
|
}
|
|
30
47
|
|
|
31
48
|
/**
|
|
32
|
-
* Write a file
|
|
49
|
+
* Write a file by first writing to a unique temporary file and then renaming it into place, so
|
|
50
|
+
* concurrent writers (e.g. Turbopack loader workers) never observe a partial file.
|
|
33
51
|
*/
|
|
34
|
-
|
|
35
|
-
const
|
|
52
|
+
async function writeFileAtomic(filePath: string, content: string) {
|
|
53
|
+
const tmp = `${filePath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`
|
|
36
54
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
fileCache.set(filePath, content)
|
|
55
|
+
await fs.writeFile(tmp, content)
|
|
56
|
+
await fs.rename(tmp, filePath)
|
|
57
|
+
}
|
|
41
58
|
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Run a write task after all previously queued writes have settled. A rejection doesn't break the
|
|
61
|
+
* chain for later tasks.
|
|
62
|
+
*/
|
|
63
|
+
function enqueueWrite<T>(task: () => Promise<T>) {
|
|
64
|
+
// wait for the previous write to finish, then run this one, even if the previous one failed
|
|
65
|
+
const run = writeChain.then(task, task)
|
|
66
|
+
|
|
67
|
+
// move the queue on to this write, but turn either outcome into success so it never rejects
|
|
68
|
+
writeChain = run.then(
|
|
69
|
+
() => undefined,
|
|
70
|
+
() => undefined,
|
|
71
|
+
)
|
|
44
72
|
|
|
45
|
-
//
|
|
73
|
+
// return the real promise, so the caller still sees this write's value or error
|
|
74
|
+
return run
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Write a file only if the content actually differs from what is already on disk.
|
|
79
|
+
*/
|
|
80
|
+
export async function maybeWrite(filePath: string, content: string) {
|
|
81
|
+
const cached = fileCache.get(filePath)
|
|
82
|
+
// fast path: already wrote this exact content in this process
|
|
83
|
+
if (cached === content) return false
|
|
84
|
+
|
|
85
|
+
// verify against disk on a cold start (or when the cache is stale) so we
|
|
86
|
+
// don't needlessly bump the mtime and invalidate downstream watchers
|
|
46
87
|
try {
|
|
47
88
|
if ((await fs.readFile(filePath, 'utf-8')) === content) {
|
|
48
89
|
fileCache.set(filePath, content)
|
|
@@ -52,17 +93,19 @@ export async function maybeWrite(filePath: string, content: string) {
|
|
|
52
93
|
if (!isENOENT(err)) throw err
|
|
53
94
|
}
|
|
54
95
|
|
|
55
|
-
await
|
|
96
|
+
await writeFileAtomic(filePath, content)
|
|
56
97
|
fileCache.set(filePath, content)
|
|
57
98
|
|
|
58
99
|
return true
|
|
59
100
|
}
|
|
60
101
|
|
|
61
102
|
// matches `[css, classes, href] = cxx\`...\`` and `cxx.tag<T>('name')\`...\``
|
|
62
|
-
// capture groups: 1
|
|
63
|
-
//
|
|
103
|
+
// capture groups: 1 optional `export `, 2-4 assigned variable names, 5 plain
|
|
104
|
+
// css body, 6 quote kind, 7 tag name, 8 tagged css body
|
|
64
105
|
const regex =
|
|
65
|
-
/(?:const|var|let)\s*\[(\w
|
|
106
|
+
/((?:export\s+)?)(?:const|var|let)\s*\[(\w*)\s*,?\s*(\w*)\s*,?\s*(\w*)\s*\]\s*=\s*cxx(?:\s*`([\s\S]*?)`|\s*\.\s*tag(?:\s*<[^>]+>)?\s*\(\s*(['"])(.*?)\6\s*\)\s*`([\s\S]*?)`)/gm
|
|
107
|
+
|
|
108
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/
|
|
66
109
|
|
|
67
110
|
/**
|
|
68
111
|
* Hash transformed CSS into a stable href for style deduplication.
|
|
@@ -79,115 +122,237 @@ export class DuplicateTagError extends Error {
|
|
|
79
122
|
}
|
|
80
123
|
}
|
|
81
124
|
|
|
125
|
+
export class InvalidTagError extends Error {
|
|
126
|
+
name = 'InvalidTagError'
|
|
127
|
+
|
|
128
|
+
constructor(tag: string) {
|
|
129
|
+
super(
|
|
130
|
+
`Tag "${tag}" does not map to a valid TypeScript identifier. ` +
|
|
131
|
+
`Use letters, numbers, spaces, "_" or "-"`,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
82
136
|
/**
|
|
83
137
|
* Transform `cxx` templates in one source file and collect any exported tag types.
|
|
84
138
|
*/
|
|
85
139
|
export function collect(source: string, id: string, config: PluginConfig) {
|
|
86
|
-
const
|
|
140
|
+
const configKey = JSON.stringify(config)
|
|
87
141
|
|
|
142
|
+
// reuse the last result when the source and config are unchanged
|
|
88
143
|
const cached = collectCache.get(id)
|
|
89
|
-
if (cached?.source === source) return cached
|
|
90
|
-
|
|
91
|
-
const template = source.replace(
|
|
92
|
-
regex,
|
|
93
|
-
(_match, varOne, varTwo, varThree, plainCss, _quote, tag, taggedCss) => {
|
|
94
|
-
const css = plainCss ?? taggedCss
|
|
95
|
-
|
|
96
|
-
const { code, exports = {} } = transform({
|
|
97
|
-
minify: true,
|
|
98
|
-
cssModules: true,
|
|
99
|
-
filename: id,
|
|
100
|
-
code: new TextEncoder().encode(css),
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
const href = hashCss(code)
|
|
104
|
-
const styles = Object.fromEntries(
|
|
105
|
-
Object.entries(exports).map(([k, v]) => [k, v.name]),
|
|
106
|
-
)
|
|
144
|
+
if (cached?.source === source && cached.config === configKey) return cached
|
|
107
145
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
146
|
+
const tags: Tags = new Map()
|
|
147
|
+
const magic = new MagicString(source)
|
|
148
|
+
let matched = false
|
|
149
|
+
|
|
150
|
+
// the regex is global, so reset its cursor before scanning
|
|
151
|
+
regex.lastIndex = 0
|
|
152
|
+
|
|
153
|
+
let match: RegExpExecArray | null
|
|
154
|
+
|
|
155
|
+
// walk every cxx template and replace it in place
|
|
156
|
+
while ((match = regex.exec(source))) {
|
|
157
|
+
matched = true
|
|
158
|
+
|
|
159
|
+
// capture groups: export, three bindings, plain css, (quote), tag, tagged css
|
|
160
|
+
const [, exp, varOne, varTwo, varThree, plainCss, , tag, taggedCss] = match
|
|
161
|
+
const css = plainCss ?? taggedCss
|
|
162
|
+
|
|
163
|
+
// compile the CSS to minified CSS Modules
|
|
164
|
+
const { code, exports = {} } = transform({
|
|
165
|
+
minify: true,
|
|
166
|
+
cssModules: true,
|
|
167
|
+
filename: id,
|
|
168
|
+
code: new TextEncoder().encode(css),
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const href = hashCss(code)
|
|
172
|
+
// map each class name to its generated (scoped) name
|
|
173
|
+
const styles = Object.fromEntries(
|
|
174
|
+
Object.entries(exports).map(([k, v]) => [k, v.name]),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if (tag) {
|
|
178
|
+
const suffix =
|
|
179
|
+
typeof config.typeSuffix === 'string'
|
|
180
|
+
? config.typeSuffix
|
|
181
|
+
: config.typeSuffix === false
|
|
182
|
+
? ''
|
|
183
|
+
: 'Classes'
|
|
184
|
+
const tagWithSuffix = pascalise(`${tag}${suffix}`)
|
|
185
|
+
|
|
186
|
+
if (!IDENTIFIER.test(tagWithSuffix)) throw new InvalidTagError(tag)
|
|
187
|
+
if (tags.has(tagWithSuffix)) throw new DuplicateTagError(tagWithSuffix)
|
|
188
|
+
|
|
189
|
+
// side effect but necessary
|
|
190
|
+
tags.set(tagWithSuffix, Object.keys(styles))
|
|
191
|
+
}
|
|
132
192
|
|
|
133
|
-
|
|
134
|
-
collectCache.set(id, { source, template, tags })
|
|
193
|
+
const statements: string[] = []
|
|
135
194
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
195
|
+
if (varOne) {
|
|
196
|
+
statements.push(`${exp}const ${varOne} = ${JSON.stringify(decoder.decode(code))}`)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (varTwo) {
|
|
200
|
+
statements.push(`${exp}const ${varTwo} = ${JSON.stringify(styles)}`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (varThree) {
|
|
204
|
+
statements.push(`${exp}const ${varThree} = ${JSON.stringify(href)}`)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// swap the template for the generated declarations, keeping the export keyword
|
|
208
|
+
magic.overwrite(match.index, match.index + match[0].length, statements.join('\n'))
|
|
139
209
|
}
|
|
210
|
+
|
|
211
|
+
const template = magic.toString()
|
|
212
|
+
// only build a map when something was actually rewritten
|
|
213
|
+
const map = matched
|
|
214
|
+
? magic.generateMap({ source: id, includeContent: true, hires: true })
|
|
215
|
+
: null
|
|
216
|
+
const result = { source, config: configKey, template, tags, map }
|
|
217
|
+
|
|
218
|
+
collectCache.set(id, result)
|
|
219
|
+
|
|
220
|
+
return result
|
|
140
221
|
}
|
|
141
222
|
|
|
142
223
|
/**
|
|
143
|
-
*
|
|
224
|
+
* Render the generated declaration file contents for a set of collected tags.
|
|
144
225
|
*/
|
|
145
|
-
|
|
226
|
+
function renderTypes(tags: Tags) {
|
|
227
|
+
return [
|
|
228
|
+
AUTOGEN_MSG,
|
|
229
|
+
'',
|
|
230
|
+
`import '${PKG_NAME}'`,
|
|
231
|
+
'',
|
|
232
|
+
`declare module '${PKG_NAME}' {`,
|
|
233
|
+
tagsToType(tags),
|
|
234
|
+
'}',
|
|
235
|
+
'',
|
|
236
|
+
].join('\n')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Write (or remove) the generated `index.d.ts` for a full tag surface.
|
|
241
|
+
*/
|
|
242
|
+
async function writeRenderedTypes(tags: Tags) {
|
|
146
243
|
const filePath = path.join(GENERATED_DIR, 'index.d.ts')
|
|
147
244
|
|
|
148
245
|
if (!tags.size) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
} catch (err) {
|
|
153
|
-
if (!isENOENT(err)) throw err
|
|
154
|
-
}
|
|
246
|
+
// `force` makes this a no-op when the file is already absent
|
|
247
|
+
await fs.rm(filePath, { force: true })
|
|
248
|
+
fileCache.delete(filePath)
|
|
155
249
|
|
|
156
250
|
return
|
|
157
251
|
}
|
|
158
252
|
|
|
159
253
|
await fs.mkdir(GENERATED_DIR, { recursive: true })
|
|
254
|
+
await maybeWrite(filePath, renderTypes(tags))
|
|
255
|
+
}
|
|
160
256
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Write the generated declaration file for the current set of collected tags.
|
|
259
|
+
*/
|
|
260
|
+
export function writeTypes(tags: Tags) {
|
|
261
|
+
return enqueueWrite(() => writeRenderedTypes(tags))
|
|
262
|
+
}
|
|
164
263
|
|
|
165
|
-
|
|
264
|
+
const PARTIAL_DIR = path.join(GENERATED_DIR, 'tags')
|
|
166
265
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
`,
|
|
266
|
+
function partialPath(filePath: string) {
|
|
267
|
+
return path.join(
|
|
268
|
+
PARTIAL_DIR,
|
|
269
|
+
`${createHash('sha1').update(filePath).digest('hex')}.json`,
|
|
171
270
|
)
|
|
172
271
|
}
|
|
173
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Read every per-file partial written by the Next loader workers and merge them into one tag map.
|
|
275
|
+
* Partials whose source file no longer exists are pruned so deleted files drop out of the types.
|
|
276
|
+
*/
|
|
277
|
+
async function readPartials() {
|
|
278
|
+
let entries: string[]
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
entries = await fs.readdir(PARTIAL_DIR)
|
|
282
|
+
} catch (err) {
|
|
283
|
+
if (isENOENT(err)) return new Map()
|
|
284
|
+
throw err
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const tags: Tags = new Map()
|
|
288
|
+
|
|
289
|
+
for (const entry of entries) {
|
|
290
|
+
if (!entry.endsWith('.json')) continue
|
|
291
|
+
|
|
292
|
+
const partial = path.join(PARTIAL_DIR, entry)
|
|
293
|
+
let parsed: { file: string; tags: Record<string, string[]> }
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
parsed = JSON.parse(await fs.readFile(partial, 'utf-8'))
|
|
297
|
+
} catch {
|
|
298
|
+
continue
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
await fs.access(parsed.file)
|
|
303
|
+
} catch {
|
|
304
|
+
await fs.rm(partial, { force: true })
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for (const [name, classes] of Object.entries(parsed.tags)) {
|
|
309
|
+
if (tags.has(name)) throw new DuplicateTagError(name)
|
|
310
|
+
tags.set(name, classes)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return tags
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Next-specific write path. Turbopack spreads loaders across worker processes, so each worker
|
|
319
|
+
* persists its own per-file partial and rebuilds the shared declaration file from all of them.
|
|
320
|
+
*/
|
|
321
|
+
export function writeTypesForFile(filePath: string, tags: Tags) {
|
|
322
|
+
return enqueueWrite(async () => {
|
|
323
|
+
await fs.mkdir(PARTIAL_DIR, { recursive: true })
|
|
324
|
+
|
|
325
|
+
const partial = partialPath(filePath)
|
|
326
|
+
|
|
327
|
+
if (!tags.size) {
|
|
328
|
+
await fs.rm(partial, { force: true })
|
|
329
|
+
} else {
|
|
330
|
+
await writeFileAtomic(
|
|
331
|
+
partial,
|
|
332
|
+
JSON.stringify({ file: filePath, tags: Object.fromEntries(tags) }),
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await writeRenderedTypes(await readPartials())
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
|
|
174
340
|
/**
|
|
175
341
|
* Render collected tags as a module augmentation for the package entrypoint.
|
|
176
342
|
*/
|
|
177
343
|
export function tagsToType(tags: Tags) {
|
|
178
344
|
return [...tags]
|
|
179
|
-
.map(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
)
|
|
345
|
+
.map(([tag, classes]) => {
|
|
346
|
+
const union =
|
|
347
|
+
classes.length > 0
|
|
348
|
+
? classes
|
|
349
|
+
.toSorted((a, b) => a.length - b.length)
|
|
350
|
+
.map(c => `'${c}'`)
|
|
351
|
+
.join(' | ')
|
|
352
|
+
: 'never'
|
|
353
|
+
|
|
354
|
+
return `\texport type ${tag} = ${union}`
|
|
355
|
+
})
|
|
191
356
|
.join('\n')
|
|
192
357
|
}
|
|
193
358
|
|
package/src/index.ts
CHANGED
|
@@ -2,15 +2,32 @@ import type { Cxx, Result } from './types.js'
|
|
|
2
2
|
|
|
3
3
|
export type { Cxx, Result, Tag } from './types.js'
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Hit when a `cxx` template reaches runtime, which means the build transform did
|
|
7
|
+
* not run. Throws so the problem is obvious instead of rendering empty styles.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* // no cxx plugin configured
|
|
12
|
+
* const [css, styles, href] = cxx`.a { color: red; }`
|
|
13
|
+
* // Error: cxx template was not transformed ...
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function untransformed(): never {
|
|
17
|
+
throw new Error(
|
|
18
|
+
'cxx template was not transformed. Make sure the cxx Vite plugin or Next integration is configured and applies to this file.',
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
5
22
|
/**
|
|
6
23
|
* Build-time template tag placeholder used by the transform to emit CSS, classes and href values.
|
|
7
24
|
*/
|
|
8
|
-
export const cxx: Cxx = Object.assign(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
export const cxx: Cxx = Object.assign(
|
|
26
|
+
(_: TemplateStringsArray): Result => untransformed(),
|
|
27
|
+
{
|
|
28
|
+
tag:
|
|
29
|
+
<ClassName extends string = string>(_: string) =>
|
|
30
|
+
(_strings: TemplateStringsArray): Result<ClassName> =>
|
|
31
|
+
untransformed(),
|
|
32
|
+
},
|
|
33
|
+
)
|
package/src/logger.ts
CHANGED
|
@@ -8,6 +8,8 @@ const LEVELS = {
|
|
|
8
8
|
|
|
9
9
|
export type LogLevel = keyof typeof LEVELS
|
|
10
10
|
|
|
11
|
+
const defaultLevel: LogLevel = process.env['NODE_ENV'] === 'production' ? 'error' : 'info'
|
|
12
|
+
|
|
11
13
|
type LogEntry = {
|
|
12
14
|
ts: number
|
|
13
15
|
level: LogLevel
|
|
@@ -25,23 +27,13 @@ type LogEntry = {
|
|
|
25
27
|
* Log messages with different severity levels.
|
|
26
28
|
*/
|
|
27
29
|
export class Logger {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#level?: LogLevel
|
|
30
|
+
#level: LogLevel
|
|
31
31
|
|
|
32
32
|
constructor(
|
|
33
33
|
level?: LogLevel,
|
|
34
34
|
public name?: string,
|
|
35
35
|
) {
|
|
36
|
-
this.#level = level
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
static set defaultLevel(level: LogLevel) {
|
|
40
|
-
Logger.#defaultLevel = level
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
static get defaultLevel() {
|
|
44
|
-
return Logger.#defaultLevel
|
|
36
|
+
this.#level = level ?? defaultLevel
|
|
45
37
|
}
|
|
46
38
|
|
|
47
39
|
/**
|
|
@@ -79,7 +71,7 @@ export class Logger {
|
|
|
79
71
|
}
|
|
80
72
|
|
|
81
73
|
get level() {
|
|
82
|
-
return this.#level
|
|
74
|
+
return this.#level
|
|
83
75
|
}
|
|
84
76
|
|
|
85
77
|
/**
|
package/src/next/cxx-loader.js
CHANGED
|
@@ -3,11 +3,7 @@ import { processFile } from './index.js'
|
|
|
3
3
|
|
|
4
4
|
const loader = function (source) {
|
|
5
5
|
const { pluginConfig } = this.getOptions()
|
|
6
|
-
const logger = new Logger(
|
|
7
|
-
(pluginConfig?.logger?.level ?? process.env.NODE_ENV === 'production')
|
|
8
|
-
? 'error'
|
|
9
|
-
: 'debug',
|
|
10
|
-
)
|
|
6
|
+
const logger = new Logger()
|
|
11
7
|
|
|
12
8
|
const { template } = processFile(source, this.resourcePath, pluginConfig, logger)
|
|
13
9
|
|