TS Macro
This is a VSCode plugin for define TS(X) macro powered by Volar.js.
Usage
Install the VSCode Plugin, or use a REPL that supports ts-macro.
Create tsm.config.ts at the same level of tsconfig.json .
TS Macro supports automatic registration of Volar plugins from vite.config.ts, similar to xxx.d.ts .
For plugin authors, you need to export a volar file, and TS Macro will automatically load the plugin and share userOptions with the vite plugin. Example.
For plugin users, you only need to install the TS 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 of node.forEachChild . Relation issue: https://github.com/volarjs/volar.js/issues/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
TSC
Use tsmc instead of tsc to compile TS.
Install
pnpm add @ts-macro/tsc -D
Usage in package.json.
{
"scripts": {
"typecheck": "tsmc --noEmit"
}
}
References
Thanks for these great projects, I have learned a lot from them.
Who are using ts-macro
| |