This is a VSCode plugin for define TS(X) macro powered by Volar.js.
-
Install the VSCode Plugin, or use a REPL that supports ts-macro.
-
Create
tsm.config.ts
at the same level oftsconfig.json
.TS Macro
supports automatic registration of Volar plugins from vite.config.ts, similar toxxx.d.ts
.
For plugin authors, you need to export avolar
file, andTS Macro
will automatically load the plugin and share userOptions with the vite plugin. Example.
For plugin users, you only need to install theTS Macro
VSCode plugin, and there's no need to write tsm.config.ts. -
Writing your first plugin.
// tsm.config.ts export default { plugins: [ { name: 'ts-macro-define-style', resolveVirtualCode({ codes }) { // declare the `defineStyle` function type for every TS(X) files. codes.push('declare function defineStyle<T>(style: string): T ') }, }, ], }
Or You can use
createPlugin
to define plugin. also compatibility with @vue/language-tools plugin.// tsm.config.ts import { createPlugin, replaceRange } from 'ts-macro' const defineStylePlugin = createPlugin<{ macro: string } | undefined>( ( { ts, compilerOptions }, userOptions = vueCompilerOptions?.defineStyle ?? { macro: 'defineStyle', }, // Default options ) => { return { name: 'volar-plugin-define-style', resolveVirtualCode({ ast, codes }) { codes.push( `declare function ${userOptions.macro}<T>(style: string): T `, ) ast.forEachChild(function walk(node) { if ( ts.isCallExpression(node) && node.expression.getText(ast) === userOptions.macro ) { // Add generic type for defineStyle. replaceRange( codes, node.arguments.pos - 1, node.arguments.pos - 1, // For simple cases, use strings instead of regex to generate types. '<{ foo: string }>', ) } node.forEachChild(walk) }) }, } }, ) export default { plugins: [ defineStylePlugin({ macro: 'defineStyle', }), ], }
Compatible with Vue
For Vue, your should import getText and getStart from ts-macro, and use
ts.forEachChild
instead ofnode.forEachChild
. Relation issue: volarjs/volar.js#165// tsm.config.ts import { createPlugin, getText, replaceSourceRange } from 'ts-macro' const defineStylePlugin = createPlugin<{ macro: string } | undefined>( ( { ts, compilerOptions, vueCompilerOptions }, userOptions = vueCompilerOptions?.defineStyle ?? { macro: 'defineStyle', }, // Default options ) => { return { name: 'volar-plugin-define-style', resolveVirtualCode({ ast, codes, source }) { codes.push( `declare function ${userOptions.macro}<T>(style: string): T `, ) ts.forEachChild(ast, function walk(node) { if ( ts.isCallExpression(node) && getText(node.expression, ast, ts) === userOptions.macro ) { // Add generic type for defineStyle. replaceSourceRange( codes, // In vue file will be 'script' | 'scriptSetup', in ts file will be undefined. source, node.arguments.pos - 1, node.arguments.pos - 1, // For simple cases, use strings instead of regex to generate types. '<{ foo: string }>', ) } ts.forEachChild(node, walk) }) }, } }, ) export default { plugins: [ defineStylePlugin({ macro: 'defineStyle', }), ], }
-
Result
Full implementation: define-style
Use tsmc
instead of tsc
to compile TS.
Install
pnpm add @ts-macro/tsc -D
Usage in package.json.
{
"scripts": {
"typecheck": "tsmc --noEmit"
}
}
Thanks for these great projects, I have learned a lot from them.