Skip to content

Commit d946326

Browse files
committed
Add template literal support to require-transform plugin
Extended transform to handle dynamic require() calls with template literals, enabling constants module coverage by transforming relative paths to absolute dist/ paths at build time.
1 parent 302412f commit d946326

File tree

1 file changed

+78
-9
lines changed

1 file changed

+78
-9
lines changed

.config/vitest-plugins/require-transform.mts

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -231,17 +231,86 @@ export function createRequireTransformPlugin(
231231
return
232232
}
233233

234-
// Step 7: Validate the require() has exactly one string argument.
235-
// require() should be called as: require('modulePath').
236-
// Invalid require() syntax, skip.
237-
if (
238-
node.arguments.length !== 1 ||
239-
!t.isStringLiteral(node.arguments[0])
240-
) {
234+
// Step 7: Handle both string literals and template literals
235+
if (node.arguments.length !== 1) {
236+
return
237+
}
238+
239+
const arg = node.arguments[0]
240+
241+
// Handle template literals like `require(\`./\${variable}\`)`
242+
if (t.isTemplateLiteral(arg)) {
243+
// Only handle simple patterns: `./prefix${variable}suffix`
244+
// Check if it starts with ./ or ../
245+
const firstQuasi = arg.quasis[0]
246+
if (
247+
!firstQuasi?.value.raw.startsWith('./') &&
248+
!firstQuasi?.value.raw.startsWith('../')
249+
) {
250+
return
251+
}
252+
253+
// Get the directory of the current file
254+
const currentDir = dirname(id)
255+
256+
// Build a template literal that resolves to dist/
257+
// For constants/index.ts with require(`./\${k}`):
258+
// Transform to require(`/abs/path/dist/lib/constants/\${k}`)
259+
260+
// Find the path relative to /registry/src/lib/
261+
const libMarker = '/registry/src/lib/'
262+
const libIndex = id.indexOf(libMarker)
263+
264+
if (libIndex === -1) {
265+
return
266+
}
267+
268+
// Get the directory path relative to lib/
269+
const relativeDir = dirname(
270+
id.substring(libIndex + libMarker.length),
271+
)
272+
273+
// Build absolute dist path
274+
const projectRoot = id.substring(0, libIndex)
275+
const distDir = resolve(
276+
projectRoot,
277+
'registry/dist/lib',
278+
relativeDir,
279+
)
280+
281+
// Reconstruct the template literal with absolute dist path
282+
// Replace the leading ./ or ../ with the absolute dist path
283+
const newQuasis = arg.quasis.map((quasi, i) => {
284+
if (i === 0) {
285+
const raw = quasi.value.raw.replace(/^\.\.?\//, `${distDir}/`)
286+
return t.templateElement({ raw, cooked: raw }, quasi.tail)
287+
}
288+
return quasi
289+
})
290+
291+
const newTemplateLiteral = t.templateLiteral(
292+
newQuasis,
293+
arg.expressions,
294+
)
295+
296+
// Replace the argument
297+
s.overwrite(
298+
arg.start!,
299+
arg.end!,
300+
code
301+
.slice(arg.start!, arg.end!)
302+
.replace(firstQuasi.value.raw, newQuasis[0].value.raw),
303+
)
304+
modified = true
305+
return
306+
}
307+
308+
// Handle string literals (existing logic)
309+
if (!t.isStringLiteral(arg)) {
241310
return
242311
}
243312

244-
const requirePath = node.arguments[0].value
313+
const requirePath = arg.value
245314

246315
// Step 8: Only handle relative requires from our code.
247316
// We don't inline:
@@ -298,7 +367,7 @@ export function createRequireTransformPlugin(
298367
// Step 11b: If we can't inline, rewrite to use compiled dist/ version.
299368
// During coverage, require() can't load TypeScript files, so we use
300369
// the compiled JavaScript files from dist/ which Node can handle.
301-
const stringNode = node.arguments[0] as t.StringLiteral
370+
const stringNode = arg as t.StringLiteral
302371

303372
// Strategy: Use the resolvedPath (absolute path to the TypeScript source)
304373
// to determine the correct dist path with preserved directory structure.

0 commit comments

Comments
 (0)