From d8010197710f283f9226a55caa2736600ada7260 Mon Sep 17 00:00:00 2001 From: orta Date: Mon, 22 Jan 2024 11:56:46 +0000 Subject: [PATCH 1/2] Initial import on CLI --- packages/twoslash-cli/LICENSE | 40 ++ packages/twoslash-cli/README.md | 22 + packages/twoslash-cli/build.config.ts | 12 + packages/twoslash-cli/package.json | 61 +++ packages/twoslash-cli/src/cli.ts | 81 ++++ packages/twoslash-cli/src/index.ts | 208 +++++++++ packages/twoslash-cli/test/fixtures.test.ts | 72 ++++ .../twoslash-cli/test/fixtures/another.js | 1 + packages/twoslash-cli/test/fixtures/basic.md | 8 + .../twoslash-cli/test/fixtures/codefence.ts | 7 + .../test/fixtures/file-with-settings.md | 9 + .../test/fixtures/single-with-settings.ts | 3 + packages/twoslash-cli/test/fixtures/single.ts | 2 + .../twoslash-cli/test/results/completion.json | 252 +++++++++++ .../twoslash-cli/test/results/example.json | 273 ++++++++++++ .../test/results/query-basic.json | 227 ++++++++++ .../test/results/renderer/example.raw.html | 398 ++++++++++++++++++ .../test/results/renderer/example.vue.html | 68 +++ 18 files changed, 1744 insertions(+) create mode 100644 packages/twoslash-cli/LICENSE create mode 100644 packages/twoslash-cli/README.md create mode 100644 packages/twoslash-cli/build.config.ts create mode 100644 packages/twoslash-cli/package.json create mode 100644 packages/twoslash-cli/src/cli.ts create mode 100644 packages/twoslash-cli/src/index.ts create mode 100644 packages/twoslash-cli/test/fixtures.test.ts create mode 100644 packages/twoslash-cli/test/fixtures/another.js create mode 100644 packages/twoslash-cli/test/fixtures/basic.md create mode 100644 packages/twoslash-cli/test/fixtures/codefence.ts create mode 100644 packages/twoslash-cli/test/fixtures/file-with-settings.md create mode 100644 packages/twoslash-cli/test/fixtures/single-with-settings.ts create mode 100644 packages/twoslash-cli/test/fixtures/single.ts create mode 100644 packages/twoslash-cli/test/results/completion.json create mode 100644 packages/twoslash-cli/test/results/example.json create mode 100644 packages/twoslash-cli/test/results/query-basic.json create mode 100644 packages/twoslash-cli/test/results/renderer/example.raw.html create mode 100644 packages/twoslash-cli/test/results/renderer/example.vue.html diff --git a/packages/twoslash-cli/LICENSE b/packages/twoslash-cli/LICENSE new file mode 100644 index 0000000..40cdc0f --- /dev/null +++ b/packages/twoslash-cli/LICENSE @@ -0,0 +1,40 @@ +MIT License + +Copyright (c) 2024-PRESENT Orta Therox + Anthony Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +The MIT License (MIT) +Copyright (c) Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/twoslash-cli/README.md b/packages/twoslash-cli/README.md new file mode 100644 index 0000000..3d4484f --- /dev/null +++ b/packages/twoslash-cli/README.md @@ -0,0 +1,22 @@ +# twoslash-vue + +[![npm version][npm-version-src]][npm-version-href] +[![npm downloads][npm-downloads-src]][npm-downloads-href] + +Extended Twoslash for Vue SFC support. + +> [!IMPORTANT] +> Working in Progress. + +[📚 Documentation](https://twoslash.netlify.app/packages/vue) + +## License + +[MIT](./LICENSE) License © 2023-PRESENT [Anthony Fu](https://github.com/antfu) + + + +[npm-version-src]: https://img.shields.io/npm/v/twoslash-vue?style=flat&colorA=080f12&colorB=1fa669 +[npm-version-href]: https://npmjs.com/package/twoslash-vue +[npm-downloads-src]: https://img.shields.io/npm/dm/twoslash-vue?style=flat&colorA=080f12&colorB=1fa669 +[npm-downloads-href]: https://npmjs.com/package/twoslash-vue diff --git a/packages/twoslash-cli/build.config.ts b/packages/twoslash-cli/build.config.ts new file mode 100644 index 0000000..0bd008c --- /dev/null +++ b/packages/twoslash-cli/build.config.ts @@ -0,0 +1,12 @@ +import { defineBuildConfig } from 'unbuild' + +export default defineBuildConfig({ + entries: [ + 'src/index', + ], + declaration: true, + clean: true, + rollup: { + emitCJS: true, + }, +}) diff --git a/packages/twoslash-cli/package.json b/packages/twoslash-cli/package.json new file mode 100644 index 0000000..eb34ce5 --- /dev/null +++ b/packages/twoslash-cli/package.json @@ -0,0 +1,61 @@ +{ + "name": "twoslash-cli", + "type": "module", + "version": "0.1.0", + "description": "Twoslash for your file-system", + "author": "Anthony Fu ", + "license": "MIT", + "funding": "https://github.com/sponsors/antfu", + "homepage": "https://github.com/twoslashes/twoslash#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/twoslashes/twoslash.git", + "directory": "packages/twoslash-cli" + }, + "bugs": "https://github.com/twoslashes/twoslash/issues", + "keywords": [ + "twoslash", + "cli", + "shiki" + ], + "sideEffects": false, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + } + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "typesVersions": { + "*": { + "*": [ + "./dist/*", + "./dist/index.d.ts" + ] + } + }, + "bin": { + "twoslash": "./dist/cli.js", + "twoslash-cli": "./dist/cli.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "unbuild", + "dev": "unbuild --stub", + "prepublishOnly": "nr build", + "start": "esno src/index.ts" + }, + "peerDependencies": { + "typescript": "*" + }, + "dependencies": { + "chokidar": "^3.5.3", + "commander": "^11.1.0", + "twoslash": "workspace:*" + } +} diff --git a/packages/twoslash-cli/src/cli.ts b/packages/twoslash-cli/src/cli.ts new file mode 100644 index 0000000..eb0c0e5 --- /dev/null +++ b/packages/twoslash-cli/src/cli.ts @@ -0,0 +1,81 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ + +'use strict' + +import { readdirSync, statSync } from 'node:fs' +import { join } from 'node:path' +import process from 'node:process' +import { Command } from 'commander' +import chokidar from 'chokidar' + +import { canConvert, runOnFile } from './' + +const program = new Command() + +program + .description( + `Converts md/ts/js/tsx/jsx files into HTML by running them through Shikiji Twoslash. + +Examples: + + Converts a bunch of ts files in the samples dir and creates .html files in renders + + $ twoslash samples/*.ts renders + + Render a few markdown files to .html files in the build folder + + $ twoslash pages/one.md pages/two.md build`, + ) + .option('-w, --watch', 'Watch for file updates and rerun Twoslash if necessary.') + .option('-s, --samples', 'Instead of rendering to HTML, spit out individual code blocks as files.') + .option('--sourceAlso', 'Also include a render of the source input. Only works on ts/tsx/js/jsx files.') + .option('--reactAlso', 'Also include a tsx file with the code embedded.') + .option('--lint', 'Don\'t actually render output files, just verify they work.') + + .on('--help', () => { + console.log('\n') + console.log(' Reference:') + console.log(' - CLI Info:') + console.log(' https://github.com/twoslash/twoslash/tree/main/packages/twoslash-cli') + }) + +program.parse(process.argv) + +const options = program.opts() +if (options.debug) + console.log(options) + +const to = program.args.pop()! +if (!to) + throw new Error('Missing output folder') + +const possibleFiles = program.args + .flatMap((from) => { + const stat = statSync(from) + return stat.isDirectory() ? readdirSync(from).map(p => join(from, p)) : [from] + }) + .filter(canConvert) + +if (possibleFiles.length === 0) + throw new Error('Could not find any md/ts/js/tsx/jsx files in the input') + +const s = possibleFiles.length === 1 ? '' : 's' +console.log(`Twoslashifying ${possibleFiles.length} file${s} ${options.watch ? '(watch mode)' : ''}:\n`) + +function run(from: string) { + runOnFile({ + from, + to, + splitOutCodeSamples: options.samples, + alsoRenderSource: options.sourceAlso, + lint: options.lint, + reactAlso: options.reactAlso, + }) +} + +if (options.watch) + chokidar.watch(possibleFiles).on('all', (_, from) => run(from)) + +else + possibleFiles.forEach(run) diff --git a/packages/twoslash-cli/src/index.ts b/packages/twoslash-cli/src/index.ts new file mode 100644 index 0000000..b78e824 --- /dev/null +++ b/packages/twoslash-cli/src/index.ts @@ -0,0 +1,208 @@ +/* eslint-disable no-console */ + +import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs' +import { basename, dirname, extname, join, sep } from 'node:path' +import { tmpdir } from 'node:os' + +// The args which come from the CLI +interface Args { + // The source filepath + from: string + // The result filepath + to: string + // Convert each tile to a unique file + splitOutCodeSamples: boolean + // Also make a shiki'd of the source code + alsoRenderSource: boolean + // Don't output anything, just check for errors + lint: boolean + // With things like code samples, or virtual markdown files, describe the "original source" + realFrom?: string + // Make the code sample runnable in React + reactAlso?: boolean +} + +// import remark from 'remark' +// import toHAST from 'mdast-util-to-hast' +// import hastToHTML from 'hast-util-to-html' +// import { visit } from 'unist-util-visit' +// import remarkShikiTwoslash from 'remark-shiki-twoslash' + +export async function runOnFile(args: Args) { + const { from } = args + if (!canConvert(from)) + return + + switch (extname(from)) { + case '.md': + return renderMarkdown(args) + default: + return renderJS(args) + } +} + +export function canConvert(path: string) { + const usable = ['.md', '.ts', '.js', '.tsx', '.jsx'] + if (!usable.includes(extname(path))) + return false + + const filename = basename(path) + if (filename.startsWith('.')) + return false + + const stat = statSync(path) + if (stat.isDirectory()) + return false + + return true +} + +// Render a JS/TS file to just the code sample equivalent + +function renderJS(args: Args) { + // Basically write to a tmp file as a markdown file and go through that pipeline + const { from } = args + + let fileContent = readFileSync(from, 'utf8') + + // Support forwarding the Twoslash Config from the ts to the md + let prefix = '' + if (fileContent.startsWith('// twoslash: {')) { + const js = fileContent.split('\n')[0].replace('// twoslash: ', '') + prefix = `` + fileContent = fileContent.replace(`// twoslash: ${js}\n`, '') + } + + // Support forwarding codefence info for highlighting + const classes = [] + if (fileContent.startsWith('// codefence: ')) { + const highlightOpts = fileContent.split('\n')[0].replace('// codefence: ', '') + classes.push(highlightOpts) + fileContent = fileContent.replace(`// codefence: ${highlightOpts}\n`, '') + } + + const newFileName = `${tmpdir() + sep + basename(from)}.md` + const code = toCode(prefix, extname(from).replace('.', ''), [...classes, 'twoslash'], fileContent) + writeFileSync(newFileName, code) + + renderMarkdown({ ...args, from: newFileName, realFrom: from }) + + // Also allow for showing a before/after by supporting a flag which renders the src + if (args.alsoRenderSource) { + const newFileName = `${tmpdir() + sep + basename(from)}_src.md` + const code = toCode(prefix, extname(from).replace('.', ''), classes, fileContent) + writeFileSync(newFileName, code) + renderMarkdown({ ...args, from: newFileName, realFrom: from }) + } +} + +// Parse out the codeblocks from a markdown doc, and return a rendered version of the whole file + +async function renderMarkdown(args: Args) { + const { from, to, splitOutCodeSamples, realFrom } = args + + const fileContent = readFileSync(from, 'utf8') + const settings = getSettingsFromMarkdown(fileContent, from) || {} + const markdownAST = remark().parse(fileContent) + + try { + // @ts-expect-error ignore + await remarkShikiTwoslash.default(settings)(markdownAST) + } + catch (error) { + console.error(`Failed to render: ${from}`) + console.error(error) + } + + // Bail before writing the new versions if we're linting + if (args.lint) + return + + // Render directly to one file + if (!splitOutCodeSamples) { + // The dangerous bit is that we include the HTML + const hAST = toHAST(markdownAST, { allowDangerousHtml: true }) + const html = hastToHTML(hAST, { allowDangerousHtml: true }) + + // Assume folder unless you write .html + const lastIsHTML = to.endsWith('.html') + if (!existsSync(to)) { + const hostFolder = lastIsHTML ? dirname(to) : to + mkdirSync(hostFolder, { recursive: true }) + } + + // Write it + const writePath = lastIsHTML ? to : join(to, basename(from).replace('.md', '.html')) + writeFileSync(writePath, html) + + // Log it + console.log(` - ${realFrom || from} -> ${writePath} `) + + // Also allow rendering the output into a TSX components + if (args.reactAlso) { + const twoslash = hAST.children.find(node => node.type === 'raw' && node.value.includes('class="shiki')) + if (!twoslash) + throw new Error(`Could not find a twoslash code sample in '${from}' for the TSX component`) + + const prefix = `// Auto-generated by the twoslash-cli from ${basename(from)}` + const code = toTSX(prefix, twoslash.value) + + const writePath = lastIsHTML ? to : join(to, basename(from).replace('.tsx', '').replace('.ts', '').replace('.md', '.tsx')) + writeFileSync(writePath, code) + + console.log(` ${' '.repeat((realFrom || from).length)} + ${writePath} \n`) + } + } + else { + if (!existsSync(to)) + mkdirSync(to, { recursive: true }) + if (!existsSync(join(to, 'mds'))) + mkdirSync(join(to, 'mds')) + + let index = 1 + visit(markdownAST, 'html', (c) => { + const hAST = toHAST(c, { allowDangerousHtml: true }) + const html = hastToHTML(hAST, { allowDangerousHtml: true }) + writeFileSync(join(to, 'mds', `code-${index}.html`), html) + index++ + }) + + console.log(` -> Wrote ${index} files to ${to}`) + } +} + +function getSettingsFromMarkdown(fileContent: string, from: string) { + if (fileContent.startsWith('')[0] + try { + // eslint-disable-next-line no-eval + return eval(`const res = ${code}; res`) + } + catch (error) { + console.error( + `Twoslash CLI: Setting custom theme settings in ${from} failed. The eval'd code is '${code}' which bailed:`, + ) + throw error + } + } +} + +function toCode(prefix: string, lang: string, classes: string[], content: string) { + return `${prefix} +\`\`\`${lang} ${classes.join(' ')} +${content} +\`\`\` +` +} + +function toTSX(prefix: string, content: string) { + return `${prefix} +import React from "react" + +const innerHTML = \` +${content} +\` + +export const Code = () =>
+` +} diff --git a/packages/twoslash-cli/test/fixtures.test.ts b/packages/twoslash-cli/test/fixtures.test.ts new file mode 100644 index 0000000..397e37a --- /dev/null +++ b/packages/twoslash-cli/test/fixtures.test.ts @@ -0,0 +1,72 @@ +/// + +import { extname } from 'node:path' +import process from 'node:process' +import { expect, it } from 'vitest' +import { createTwoslasher } from '../src/index' + +// To add a test, create a file in the fixtures folder and it will will run through +// as though it was the codeblock. + +const fixtures = import.meta.glob('./fixtures/**/*.*', { as: 'raw' }) + +// A temporary list of regex to match with the path of the file to test +const filters: RegExp[] = [ + // /completions-files/, +] + +if (process.env.CI && filters.length) + throw new Error('Should not filters fixture tests in CI, did you forget to remove them?') + +const twoslasher = createTwoslasher() + +Object.entries(fixtures).forEach(([path, fixture]) => { + path = path.replace(/\\/g, '/') + const expectThrows = path.includes('/throws/') + const inExt = extname(path).slice(1) + const outExt = expectThrows ? '.txt' : '.json' + const outPath = path.replace('/fixtures/', '/results/').replace(/\.[^/.]+$/, outExt) + + it.skipIf(filters.length && !filters.some(f => path.match(f)))( + path, + async () => { + let result: TwoslashReturn = undefined! + try { + result = twoslasher( + await fixture(), + inExt, + { + customTags: ['annotate'], + }, + ) + } + catch (err: any) { + if (expectThrows) { + expect(err.message).toMatchFileSnapshot(outPath) + return + } + else { + throw err + } + } + + if (expectThrows) { + throw new Error('Expected to throw') + } + + else { + expect(cleanFixture(result)) + .toMatchFileSnapshot(outPath) + } + }, + ) +}) + +function cleanFixture(result: TwoslashReturn) { + return JSON.stringify({ + code: result.code, + nodes: result.nodes, + flags: result.meta.flagNotations, + // compilerOptions: ts.meta.compilerOptions + }, null, 2).replaceAll(process.cwd(), '[home]') +} diff --git a/packages/twoslash-cli/test/fixtures/another.js b/packages/twoslash-cli/test/fixtures/another.js new file mode 100644 index 0000000..00a80b4 --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/another.js @@ -0,0 +1 @@ +const asd = "1231241" \ No newline at end of file diff --git a/packages/twoslash-cli/test/fixtures/basic.md b/packages/twoslash-cli/test/fixtures/basic.md new file mode 100644 index 0000000..624a1cc --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/basic.md @@ -0,0 +1,8 @@ +## Hello + +There's some text + +```ts twoslash +const a = 123 +const b = 3456 +``` \ No newline at end of file diff --git a/packages/twoslash-cli/test/fixtures/codefence.ts b/packages/twoslash-cli/test/fixtures/codefence.ts new file mode 100644 index 0000000..a1ff2bc --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/codefence.ts @@ -0,0 +1,7 @@ +// twoslash: { theme: "dark-plus" } +// codefence: {1} +function greet(person: string, date: Date) { + console.log(`Hello ${person}, today is ${date.toDateString()}!`) +} + +greet("Maddison", new Date()) diff --git a/packages/twoslash-cli/test/fixtures/file-with-settings.md b/packages/twoslash-cli/test/fixtures/file-with-settings.md new file mode 100644 index 0000000..db55340 --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/file-with-settings.md @@ -0,0 +1,9 @@ + +## Hello + +There's some text + +```ts twoslash +const a = 123 +const b = 3456 +``` \ No newline at end of file diff --git a/packages/twoslash-cli/test/fixtures/single-with-settings.ts b/packages/twoslash-cli/test/fixtures/single-with-settings.ts new file mode 100644 index 0000000..34c0eff --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/single-with-settings.ts @@ -0,0 +1,3 @@ +// twoslash: { themes: ["nord", "light-plus"] } +const a = 123 +const b = 3456 \ No newline at end of file diff --git a/packages/twoslash-cli/test/fixtures/single.ts b/packages/twoslash-cli/test/fixtures/single.ts new file mode 100644 index 0000000..031b6f7 --- /dev/null +++ b/packages/twoslash-cli/test/fixtures/single.ts @@ -0,0 +1,2 @@ +const abc = 123 +const cvfd = "1231" \ No newline at end of file diff --git a/packages/twoslash-cli/test/results/completion.json b/packages/twoslash-cli/test/results/completion.json new file mode 100644 index 0000000..c6b0302 --- /dev/null +++ b/packages/twoslash-cli/test/results/completion.json @@ -0,0 +1,252 @@ +{ + "code": "\n\n\n", + "nodes": [ + { + "type": "hover", + "text": "(alias) function ref(value: T): Ref> (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 34, + "length": 3, + "target": "ref", + "line": 1, + "character": 9 + }, + { + "type": "hover", + "text": "const count1: Ref", + "start": 58, + "length": 6, + "target": "count1", + "line": 3, + "character": 6 + }, + { + "type": "hover", + "text": "(alias) ref(value: number): Ref (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 67, + "length": 3, + "target": "ref", + "line": 3, + "character": 15 + }, + { + "type": "hover", + "text": "function count2(): number", + "start": 84, + "length": 6, + "target": "count2", + "line": 5, + "character": 9 + }, + { + "type": "hover", + "text": "const count1: Ref", + "start": 104, + "length": 6, + "target": "count1", + "line": 6, + "character": 9 + }, + { + "type": "hover", + "text": "(property) Ref.value: number", + "start": 111, + "length": 5, + "target": "value", + "line": 6, + "character": 16 + }, + { + "type": "hover", + "text": "(property) div: HTMLAttributes & ReservedProps", + "start": 148, + "length": 3, + "target": "div", + "line": 11, + "character": 3 + }, + { + "type": "hover", + "text": "const count1: Ref", + "start": 160, + "length": 6, + "target": "count1", + "line": 12, + "character": 7 + }, + { + "type": "completion", + "start": 161, + "length": 0, + "completions": [ + { + "name": "count1", + "kind": "const", + "kindModifiers": "", + "sortText": "11" + }, + { + "name": "count2", + "kind": "function", + "kindModifiers": "", + "sortText": "11" + }, + { + "name": "caches", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "cancelAnimationFrame", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "cancelIdleCallback", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "case", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15" + }, + { + "name": "catch", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15" + }, + { + "name": "class", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15" + }, + { + "name": "clearInterval", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "clearTimeout", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "close", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "closed", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "confirm", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "console", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "const", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15" + }, + { + "name": "continue", + "kind": "keyword", + "kindModifiers": "", + "sortText": "15" + }, + { + "name": "createImageBitmap", + "kind": "function", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "crossOriginIsolated", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "crypto", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "customElements", + "kind": "var", + "kindModifiers": "declare", + "sortText": "15" + }, + { + "name": "captureEvents", + "kind": "function", + "kindModifiers": "deprecated,declare", + "sortText": "z15" + }, + { + "name": "clientInformation", + "kind": "var", + "kindModifiers": "deprecated,declare", + "sortText": "z15" + } + ], + "completionsPrefix": "c", + "line": 12, + "character": 8, + "target": "" + }, + { + "type": "hover", + "text": "(property) div: HTMLAttributes & ReservedProps", + "start": 174, + "length": 3, + "target": "div", + "line": 13, + "character": 4 + } + ], + "flags": [] +} \ No newline at end of file diff --git a/packages/twoslash-cli/test/results/example.json b/packages/twoslash-cli/test/results/example.json new file mode 100644 index 0000000..ff539f3 --- /dev/null +++ b/packages/twoslash-cli/test/results/example.json @@ -0,0 +1,273 @@ +{ + "code": "\n\n\n\n\n", + "nodes": [ + { + "type": "hover", + "text": "(alias) function ref(value: T): Ref> (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 34, + "length": 3, + "target": "ref", + "line": 1, + "character": 9 + }, + { + "type": "hover", + "text": "(alias) const computed: {\n (getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef;\n (options: WritableComputedOptions, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>;\n}\nimport computed", + "start": 39, + "length": 8, + "target": "computed", + "line": 1, + "character": 14 + }, + { + "type": "query", + "text": "(alias) const computed: {\n (getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef;\n (options: WritableComputedOptions, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>;\n}\nimport computed", + "start": 39, + "length": 8, + "target": "computed", + "line": 1, + "character": 14 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 72, + "length": 5, + "target": "count", + "line": 7, + "character": 6 + }, + { + "type": "hover", + "text": "(alias) ref(value: number): Ref (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 80, + "length": 3, + "target": "ref", + "line": 7, + "character": 14 + }, + { + "type": "hover", + "text": "const double: ComputedRef", + "start": 94, + "length": 6, + "target": "double", + "line": 9, + "character": 6 + }, + { + "type": "query", + "text": "const double: ComputedRef", + "start": 94, + "length": 6, + "target": "double", + "line": 9, + "character": 6 + }, + { + "type": "hover", + "text": "(alias) computed(getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef (+1 overload)\nimport computed", + "docs": "Takes a getter function and returns a readonly reactive ref object for the\nreturned value from the getter. It can also take an object with get and set\nfunctions to create a writable ref object.", + "tags": [ + [ + "example", + "```js\n// Creating a readonly computed ref:\nconst count = ref(1)\nconst plusOne = computed(() => count.value + 1)\n\nconsole.log(plusOne.value) // 2\nplusOne.value++ // error\n```\n\n```js\n// Creating a writable computed ref:\nconst count = ref(1)\nconst plusOne = computed({\n get: () => count.value + 1,\n set: (val) => {\n count.value = val - 1\n }\n})\n\nplusOne.value = 1\nconsole.log(count.value) // 0\n```" + ], + [ + "param", + "getter - Function that produces the next value." + ], + [ + "param", + "debugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#computed}" + ] + ], + "start": 103, + "length": 8, + "target": "computed", + "line": 9, + "character": 15 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 118, + "length": 5, + "target": "count", + "line": 9, + "character": 30 + }, + { + "type": "hover", + "text": "(property) Ref.value: number", + "start": 124, + "length": 5, + "target": "value", + "line": 9, + "character": 36 + }, + { + "type": "hover", + "text": "(property) ComponentOptionsBase = {}>.name?: string | undefined", + "start": 174, + "length": 4, + "target": "name", + "line": 14, + "character": 2 + }, + { + "type": "hover", + "text": "(property) LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.data?: ((this: CreateComponentPublicInstance<...>, vm: CreateComponentPublicInstance<{}, {}, {}, {}, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, {}, {}, {}, false, ... 8 more ..., {}>) => {\n ...;\n}) | undefined", + "start": 196, + "length": 4, + "target": "data", + "line": 15, + "character": 2 + }, + { + "type": "hover", + "text": "(property) msg: string", + "start": 224, + "length": 3, + "target": "msg", + "line": 17, + "character": 6 + }, + { + "type": "hover", + "text": "(property) LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.methods?: {\n greet(): void;\n} | undefined", + "start": 251, + "length": 7, + "target": "methods", + "line": 20, + "character": 2 + }, + { + "type": "hover", + "text": "(method) greet(): void", + "start": 266, + "length": 5, + "target": "greet", + "line": 21, + "character": 4 + }, + { + "type": "hover", + "text": "var console: Console", + "start": 282, + "length": 7, + "target": "console", + "line": 22, + "character": 6 + }, + { + "type": "hover", + "text": "(method) Console.log(...data: any[]): void", + "docs": "[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)", + "start": 290, + "length": 3, + "target": "log", + "line": 22, + "character": 14 + }, + { + "type": "hover", + "text": "(property) msg: string", + "start": 299, + "length": 3, + "target": "msg", + "line": 22, + "character": 23 + }, + { + "type": "hover", + "text": "(property) button: ButtonHTMLAttributes & ReservedProps", + "start": 341, + "length": 6, + "target": "button", + "line": 29, + "character": 3 + }, + { + "type": "hover", + "text": "(property) 'click': ((payload: MouseEvent) => void) | undefined", + "start": 349, + "length": 5, + "target": "click", + "line": 29, + "character": 11 + }, + { + "type": "query", + "text": "(property) onClick?: ((payload: MouseEvent) => void) | undefined", + "start": 349, + "length": 5, + "target": "click", + "line": 29, + "character": 11 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 356, + "length": 5, + "target": "count", + "line": 29, + "character": 18 + }, + { + "type": "hover", + "text": "(property) msg: string", + "start": 368, + "length": 3, + "target": "msg", + "line": 29, + "character": 30 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 388, + "length": 5, + "target": "count", + "line": 29, + "character": 50 + }, + { + "type": "hover", + "text": "(property) button: ButtonHTMLAttributes & ReservedProps", + "start": 398, + "length": 6, + "target": "button", + "line": 29, + "character": 60 + } + ], + "flags": [] +} \ No newline at end of file diff --git a/packages/twoslash-cli/test/results/query-basic.json b/packages/twoslash-cli/test/results/query-basic.json new file mode 100644 index 0000000..6907a31 --- /dev/null +++ b/packages/twoslash-cli/test/results/query-basic.json @@ -0,0 +1,227 @@ +{ + "code": "\n\n\n", + "nodes": [ + { + "type": "hover", + "text": "(alias) function ref(value: T): Ref> (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 34, + "length": 3, + "target": "ref", + "line": 1, + "character": 9 + }, + { + "type": "hover", + "text": "(alias) const computed: {\n (getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef;\n (options: WritableComputedOptions, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>;\n}\nimport computed", + "start": 39, + "length": 8, + "target": "computed", + "line": 1, + "character": 14 + }, + { + "type": "query", + "text": "(alias) const computed: {\n (getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef;\n (options: WritableComputedOptions, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>;\n}\nimport computed", + "start": 39, + "length": 8, + "target": "computed", + "line": 1, + "character": 14 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 68, + "length": 5, + "target": "count", + "line": 3, + "character": 6 + }, + { + "type": "hover", + "text": "(alias) ref(value: number): Ref (+1 overload)\nimport ref", + "docs": "Takes an inner value and returns a reactive and mutable ref object, which\nhas a single property `.value` that points to the inner value.", + "tags": [ + [ + "param", + "value - The object to wrap in the ref." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#ref}" + ] + ], + "start": 76, + "length": 3, + "target": "ref", + "line": 3, + "character": 14 + }, + { + "type": "hover", + "text": "const double: ComputedRef", + "start": 89, + "length": 6, + "target": "double", + "line": 4, + "character": 6 + }, + { + "type": "query", + "text": "const double: ComputedRef", + "start": 89, + "length": 6, + "target": "double", + "line": 4, + "character": 6 + }, + { + "type": "hover", + "text": "(alias) computed(getter: ComputedGetter, debugOptions?: DebuggerOptions | undefined): ComputedRef (+1 overload)\nimport computed", + "docs": "Takes a getter function and returns a readonly reactive ref object for the\nreturned value from the getter. It can also take an object with get and set\nfunctions to create a writable ref object.", + "tags": [ + [ + "example", + "```js\n// Creating a readonly computed ref:\nconst count = ref(1)\nconst plusOne = computed(() => count.value + 1)\n\nconsole.log(plusOne.value) // 2\nplusOne.value++ // error\n```\n\n```js\n// Creating a writable computed ref:\nconst count = ref(1)\nconst plusOne = computed({\n get: () => count.value + 1,\n set: (val) => {\n count.value = val - 1\n }\n})\n\nplusOne.value = 1\nconsole.log(count.value) // 0\n```" + ], + [ + "param", + "getter - Function that produces the next value." + ], + [ + "param", + "debugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}." + ], + [ + "see", + "{@link https://vuejs.org/api/reactivity-core.html#computed}" + ] + ], + "start": 98, + "length": 8, + "target": "computed", + "line": 4, + "character": 15 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 113, + "length": 5, + "target": "count", + "line": 4, + "character": 30 + }, + { + "type": "hover", + "text": "(property) Ref.value: number", + "start": 119, + "length": 5, + "target": "value", + "line": 4, + "character": 36 + }, + { + "type": "hover", + "text": "(property) button: ButtonHTMLAttributes & ReservedProps", + "start": 155, + "length": 6, + "target": "button", + "line": 8, + "character": 3 + }, + { + "type": "hover", + "text": "(property) 'click': ((payload: MouseEvent) => void) | undefined", + "start": 163, + "length": 5, + "target": "click", + "line": 8, + "character": 11 + }, + { + "type": "query", + "text": "(property) onClick?: ((payload: MouseEvent) => void) | undefined", + "start": 163, + "length": 5, + "target": "click", + "line": 8, + "character": 11 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 170, + "length": 5, + "target": "count", + "line": 8, + "character": 18 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 192, + "length": 5, + "target": "count", + "line": 8, + "character": 40 + }, + { + "type": "hover", + "text": "(property) button: ButtonHTMLAttributes & ReservedProps", + "start": 202, + "length": 6, + "target": "button", + "line": 8, + "character": 50 + }, + { + "type": "hover", + "text": "(property) p: HTMLAttributes & ReservedProps", + "start": 213, + "length": 1, + "target": "p", + "line": 9, + "character": 3 + }, + { + "type": "hover", + "text": "const count: Ref", + "start": 228, + "length": 5, + "target": "count", + "line": 9, + "character": 18 + }, + { + "type": "query", + "text": "(property) count: number", + "start": 228, + "length": 5, + "target": "count", + "line": 9, + "character": 18 + }, + { + "type": "hover", + "text": "(property) p: HTMLAttributes & ReservedProps", + "start": 238, + "length": 1, + "target": "p", + "line": 9, + "character": 28 + } + ], + "flags": [] +} \ No newline at end of file diff --git a/packages/twoslash-cli/test/results/renderer/example.raw.html b/packages/twoslash-cli/test/results/renderer/example.raw.html new file mode 100644 index 0000000..105e867 --- /dev/null +++ b/packages/twoslash-cli/test/results/renderer/example.raw.html @@ -0,0 +1,398 @@ + + + + +
/* __placeholder__ */
+
+import { function ref<T>(value: T): Ref<UnwrapRef<T>> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which +has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
,
const computed: { + <T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions | undefined): ComputedRef<T>; + <T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>; +}
computed
} from 'vue'
+ + + + + + + + +export default await (async () => { +const { const defineProps: { + <PropNames extends string = string>(props: PropNames[]): { [K in keyof Readonly<{ [key in PropNames]?: any; }>]: Readonly<{ [key in PropNames]?: any; }>[K]; }; + <PP extends ComponentObjectPropsOptions<...> = ComponentObjectPropsOptions<...>>(props: PP): { [K in keyof Readonly<...>]: Readonly<...>[K]; }; + <TypeProps>(): DefineProps<...>; +}
Vue `<script setup>` compiler macro for declaring component props. The +expected argument is the same as the component `props` option. + +Example runtime declaration: +```js +// using Array syntax +const props = defineProps(['foo', 'bar']) +// using Object syntax +const props = defineProps({ + foo: String, + bar: { + type: Number, + required: true + } +}) +``` + +Equivalent type-based declaration: +```ts +// will be compiled into equivalent runtime declarations +const props = defineProps<{ + foo?: string + bar: number +}>() +```
defineProps
, const defineSlots: <S extends Record<string, any> = Record<string, any>>() => Readonly<S & {}> & SdefineSlots, const defineEmits: { + <EE extends string = string>(emitOptions: EE[]): (event: EE, ...args: any[]) => void; + <E extends EmitsOptions = EmitsOptions>(emitOptions: E): EmitFn<E, keyof E>; + <T extends Record<...> | ((...args: any[]) => any)>(): T extends (...args: any[]) => any ? T : UnionToIntersection<...>; +}
Vue `<script setup>` compiler macro for declaring a component's emitted +events. The expected argument is the same as the component `emits` option. + +Example runtime declaration: +```js +const emit = defineEmits(['change', 'update']) +``` + +Example type-based declaration: +```ts +const emit = defineEmits<{ + // <eventName>: <expected arguments> + change: [] + update: [value: string] // named tuple syntax +}>() + +emit('change') +emit('update', 1) +``` + +This is only usable inside `<script setup>`, is compiled away in the +output and should **not** be actually called at runtime.
defineEmits
, const defineExpose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed | undefined) => void
Vue `<script setup>` compiler macro for declaring a component's exposed +instance properties when it is accessed by a parent component via template +refs. + +`<script setup>` components are closed by default - i.e. variables inside +the `<script setup>` scope is not exposed to parent unless explicitly exposed +via `defineExpose`. + +This is only usable inside `<script setup>`, is compiled away in the +output and should **not** be actually called at runtime.
defineExpose
, const defineModel: { + <T, M extends string | number | symbol = string>(options: { + required: true; + } & PropOptions<T, T> & DefineModelOptions<T>): ModelRef<T, M>; + <T, M extends string | ... 1 more ... | symbol = string>(options: { + ...; + } & ... 1 more ... & DefineModelOptions<...>): ModelRef<...>; + <T, M extends string | ... 1 more ... | symbol = string>(options?: (PropOptions<...> & DefineModelOptions<...>) | undefined): ModelRef<...>; + <T, M extends string | ... 1 more ... | symbol = string>(name: string, options: { + ...; + } & ... 1 more ... & DefineModelOptions<...>): ModelRef<...>; + <T, M extends string | ... 1 more ... | symbol = string>(name: string, options: { + ...; + } & ... 1 more ... & DefineModelOptions<...>): ModelRef<...>; + <T, M extends string | ... 1 more ... | symbol = string>(name: string, options?: (PropOptions<...> & DefineModelOptions<...>) | undefined): ModelRef<...>; +}
Vue `<script setup>` compiler macro for declaring a +two-way binding prop that can be consumed via `v-model` from the parent +component. This will declare a prop with the same name and a corresponding +`update:propName` event. + +If the first argument is a string, it will be used as the prop name; +Otherwise the prop name will default to "modelValue". In both cases, you +can also pass an additional object which will be used as the prop's options. + +The the returned ref behaves differently depending on whether the parent +provided the corresponding v-model props or not: +- If yes, the returned ref's value will always be in sync with the parent + prop. +- If not, the returned ref will behave like a normal local ref.
defineModel
, const defineOptions: <RawBindings = {}, D = {}, C extends ComputedOptions = {}, M extends MethodOptions = {}, Mixin extends ComponentOptionsMixin = ComponentOptionsMixin, Extends extends ComponentOptionsMixin = ComponentOptionsMixin>(options?: (ComponentOptionsBase<...> & ... 2 more ... & { + ...; +}) | undefined) => void
Vue `<script setup>` compiler macro for declaring a component's additional +options. This should be used only for options that cannot be expressed via +Composition API - e.g. `inheritAttrs`.
defineOptions
, const withDefaults: <T, BKeys extends keyof T, Defaults extends InferDefaults<T>>(props: DefineProps<T, BKeys>, defaults: Defaults) => PropsWithDefaults<T, Defaults, BKeys>
Vue `<script setup>` compiler macro for providing props default values when +using type-based `defineProps` declaration. + +Example usage: +```ts +withDefaults(defineProps<{ + size?: number + labels?: string[] +}>(), { + size: 3, + labels: () => ['default label'] +}) +``` + +This is only usable inside `<script setup>`, is compiled away in the output +and should **not** be actually called at runtime.
withDefaults
, } = await import('vue');
+const const count: Ref<number>count = ref<number>(value: number): Ref<number> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which +has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
(0)
+ +const
const double: ComputedRef<number>
double
= computed<number>(getter: ComputedGetter<number>, debugOptions?: DebuggerOptions | undefined): ComputedRef<number> (+1 overload)
Takes a getter function and returns a readonly reactive ref object for the +returned value from the getter. It can also take an object with get and set +functions to create a writable ref object.
@example```js +// Creating a readonly computed ref: +const count = ref(1) +const plusOne = computed(() => count.value + 1) + +console.log(plusOne.value) // 2 +plusOne.value++ // error +``` + +```js +// Creating a writable computed ref: +const count = ref(1) +const plusOne = computed({ + get: () => count.value + 1, + set: (val) => { + count.value = val - 1 + } +}) + +plusOne.value = 1 +console.log(count.value) // 0 +```@paramgetter - Function that produces the next value.@paramdebugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}.@see{@link https://vuejs.org/api/reactivity-core.html#computed}
computed
(() => const count: Ref<number>count.Ref<number>.value: numbervalue * 2)
+ + +const __VLS_componentsOption = {}; + +const __VLS_name = 'HelloWorld' as type const = "HelloWorld"const; +function __VLS_template() { +let __VLS_ctx!: type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any
Obtain the return type of a constructor function type
InstanceType
<__VLS_PickNotAny<typeof __VLS_internalComponent, new () => {}>> & {
+}; +/* Components */ +let __VLS_otherComponents!: type NonNullable<T> = T & {}
Exclude null and undefined from T
NonNullable
<typeof __VLS_internalComponent extends { components: Ccomponents: infer function (type parameter) CC } ? function (type parameter) CC : {}> & typeof __VLS_componentsOption;
+let __VLS_own!: __VLS_SelfComponent<typeof __VLS_name, typeof __VLS_internalComponent & (new () => { $slots: {}$slots: typeof __VLS_slots })>; +let __VLS_localComponents!: typeof __VLS_otherComponents & type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
Construct a type with the properties of T except for those in type K.
Omit
<typeof __VLS_own, keyof typeof __VLS_otherComponents>;
+let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx; +/* Style Scoped */ +type __VLS_StyleScopedClasses = {}; +let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[]; +/* CSS variable injection */ +/* CSS variable injection end */ +let __VLS_resolvedLocalAndGlobalComponents!: {} +; +__VLS_intrinsicElements.button: ButtonHTMLAttributes & ReservedPropsbutton;__VLS_intrinsicElements.button: ButtonHTMLAttributes & ReservedPropsbutton; +{ +const __VLS_0 = __VLS_intrinsicElements["button"]; +const __VLS_1 = __VLS_elementAsFunctionalComponent(__VLS_0); +const __VLS_2 = __VLS_1({ ...{ onClick?: ((payload: MouseEvent) => void) | undefinedonClick: {} as any, }, }, ...__VLS_functionalComponentArgsRest(__VLS_1)); +({} as (props: ButtonHTMLAttributes & ReservedProps & Record<string, unknown>props: __VLS_FunctionalComponentProps<typeof __VLS_0, typeof __VLS_2> & type Record<K extends string | number | symbol, T> = { [P in K]: T; }
Construct a type with a set of properties K of type T
Record
<string, unknown>) => void)({ ...{ onClick?: ((payload: MouseEvent) => void) | undefinedonClick: {} as any, }, });
+const __VLS_3 = __VLS_pickFunctionalComponentCtx(__VLS_0, __VLS_2)!; +let __VLS_4!: __VLS_NormalizeEmits<typeof __VLS_3.emit?: anyemit>; +let __VLS_5 = { 'click': __VLS_pickEvent(__VLS_4['click'], ({} as __VLS_FunctionalComponentProps<typeof __VLS_1, typeof __VLS_2>).
onClick?: ((payload: MouseEvent) => void) | undefined
onClick
) };
+__VLS_5 = { 'click': ((payload: MouseEvent) => void) | undefinedclick: $event: MouseEvent$event => { +__VLS_ctx.count: numbercount++; +// @ts-ignore +[const count: Ref<number>count,]; +} + }; +( __VLS_ctx.msg: stringmsg ); +( __VLS_ctx.count: numbercount ); +(__VLS_3.slots?: anyslots!).default; +} +if (typeof __VLS_styleScopedClasses === 'object' && !var Array: ArrayConstructorArray.ArrayConstructor.isArray(arg: any): arg is any[]isArray(__VLS_styleScopedClasses)) { +} +var __VLS_slots!:{ +}; +// @ts-ignore +[msg,const count: Ref<number>count,]; +return __VLS_slots; +} +const __VLS_internalComponent = (await import('vue')).function defineComponent<{}, { + count: Ref<number>; +}, { + msg: string; +}, {}, { + greet(): void; +}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, {}, {}, string>(options: ComponentOptionsWithoutProps<...>): DefineComponent<...> (+4 overloads)defineComponent({ +ComponentOptionsBase<{}, { count: Ref<number>; }, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, ... 4 more ..., {}>.setup?: ((this: void, props: LooseRequired<{} & {}>, ctx: { + attrs: Data; + slots: Readonly<InternalSlots>; + emit: (event: string, ...args: any[]) => void; + expose: (exposed?: Record<...> | undefined) => void; +}) => void | ... 2 more ... | Promise<...>) | undefinedsetup() { +return { +count: Ref<number>count: const count: Ref<number>count as typeof const count: Ref<number>count, +}; +}, + + ComponentOptionsBase<Props, RawBindings, D, C extends ComputedOptions, M extends MethodOptions, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, Defaults = {}, I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType<...> = {}>.name?: string | undefinedname: 'HelloWorld', + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.data?: ((this: CreateComponentPublicInstance<...>, vm: CreateComponentPublicInstance<{}, {}, {}, {}, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, {}, {}, {}, false, ... 8 more ..., {}>) => { + ...; +}) | undefineddata() { + return { + msg: stringmsg: 'Hello!' + } + }, + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.methods?: { + greet(): void; +} | undefinedmethods: { + function greet(): voidgreet() { + var console: Consoleconsole.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
log
(this.msg: stringmsg)
+ } + } +}); +return (await import('vue')).function defineComponent<{}, {}, { + msg: string; +}, {}, { + greet(): void; +}, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, {}, {}, string>(options: ComponentOptionsWithoutProps<{}, {}, { + msg: string; +}, ... 9 more ..., {}>): DefineComponent<...> (+4 overloads)defineComponent({ +ComponentOptionsBase<{}, {}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, {}, {}, string, {}>.setup?: ((this: void, props: LooseRequired<{} & {}>, ctx: { + attrs: Data; + slots: Readonly<InternalSlots>; + emit: (event: string, ...args: any[]) => void; + expose: (exposed?: Record<...> | undefined) => void; +}) => void | ... 2 more ... | Promise<...>) | undefinedsetup() { +return { +}; +}, + + ComponentOptionsBase<Props, RawBindings, D, C extends ComputedOptions, M extends MethodOptions, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, Defaults = {}, I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType<...> = {}>.name?: string | undefinedname: 'HelloWorld', + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.data?: ((this: CreateComponentPublicInstance<...>, vm: CreateComponentPublicInstance<{}, {}, {}, {}, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, {}, {}, {}, false, ... 8 more ..., {}>) => { + ...; +}) | undefineddata() { + return { + msg: stringmsg: 'Hello!' + } + }, + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.methods?: { + greet(): void; +} | undefinedmethods: { + function greet(): voidgreet() { + var console: Consoleconsole.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
log
(this.msg: stringmsg)
+ } + } +}); +})() + +// ---cut-after--- +// @ts-nocheck + +type __VLS_IntrinsicElements = __VLS_PickNotAny<import('vue/jsx-runtime').JSX.IntrinsicElements, __VLS_PickNotAny<JSX.IntrinsicElements, Record<string, any>>>; +type __VLS_Element = __VLS_PickNotAny<import('vue/jsx-runtime').JSX.Element, JSX.Element>; + +type __VLS_IsAny<T> = 0 extends 1 & T ? true : false; +type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A; + +type __VLS_Prettify<T> = { [K in keyof T]: T[K]; } & {}; + +type __VLS_OmitKeepDiscriminatedUnion<T, K extends keyof any> = + T extends any + ? Pick<T, Exclude<keyof T, K>> + : never; + +type __VLS_GlobalComponents = + __VLS_PickNotAny<import('vue').GlobalComponents, {}> + & __VLS_PickNotAny<import('@vue/runtime-core').GlobalComponents, {}> + & __VLS_PickNotAny<import('@vue/runtime-dom').GlobalComponents, {}> + & Pick<typeof import('vue'), + 'Transition' + | 'TransitionGroup' + | 'KeepAlive' + | 'Suspense' + | 'Teleport' + >; + +declare const __VLS_intrinsicElements: __VLS_IntrinsicElements; + +// v-for +declare function __VLS_getVForSourceType(source: number): [number, number, number][]; +declare function __VLS_getVForSourceType(source: string): [string, number, number][]; +declare function __VLS_getVForSourceType<T extends any[]>(source: T): [ + T[number], // item + number, // key + number, // index +][]; +declare function __VLS_getVForSourceType<T extends { [Symbol.iterator](): Iterator<any> }>(source: T): [ + T extends { [Symbol.iterator](): Iterator<infer T1> } ? T1 : never, // item + number, // key + undefined, // index +][]; +declare function __VLS_getVForSourceType<T>(source: T): [ + T[keyof T], // item + keyof T, // key + number, // index +][]; + +declare function __VLS_getSlotParams<T>(slot: T): Parameters<__VLS_PickNotAny<NonNullable<T>, (...args: any[]) => any>>; +declare function __VLS_getSlotParam<T>(slot: T): Parameters<__VLS_PickNotAny<NonNullable<T>, (...args: any[]) => any>>[0]; +declare function __VLS_directiveFunction<T>(dir: T): + T extends import('vue').ObjectDirective<infer E, infer V> | import('vue').FunctionDirective<infer E, infer V> ? (value: V) => void + : T; +declare function __VLS_withScope<T, K>(ctx: T, scope: K): ctx is T & K; +declare function __VLS_makeOptional<T>(t: T): { [K in keyof T]?: T[K] }; + +type __VLS_SelfComponent<N, C> = string extends N ? {} : N extends string ? { [P in N]: C } : {}; +type __VLS_WithComponent<N0 extends string, LocalComponents, N1 extends string, N2 extends string, N3 extends string> = + N1 extends keyof LocalComponents ? N1 extends N0 ? Pick<LocalComponents, N0> : { [K in N0]: LocalComponents[N1] } : + N2 extends keyof LocalComponents ? N2 extends N0 ? Pick<LocalComponents, N0> : { [K in N0]: LocalComponents[N2] } : + N3 extends keyof LocalComponents ? N3 extends N0 ? Pick<LocalComponents, N0> : { [K in N0]: LocalComponents[N3] } : + N1 extends keyof __VLS_GlobalComponents ? N1 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N1] } : + N2 extends keyof __VLS_GlobalComponents ? N2 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N2] } : + N3 extends keyof __VLS_GlobalComponents ? N3 extends N0 ? Pick<__VLS_GlobalComponents, N0> : { [K in N0]: __VLS_GlobalComponents[N3] } : + { [K in N0]: unknown } + +type __VLS_FillingEventArg_ParametersLength<E extends (...args: any) => any> = __VLS_IsAny<Parameters<E>> extends true ? -1 : Parameters<E>['length']; +type __VLS_FillingEventArg<E> = E extends (...args: any) => any ? __VLS_FillingEventArg_ParametersLength<E> extends 0 ? ($event?: undefined) => ReturnType<E> : E : E; +declare function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K): + T extends new (...args: any) => any + ? (props: (K extends { $props: infer Props } ? Props : any) & Record<string, unknown>, ctx?: { + attrs?: any, + slots?: K extends { $slots: infer Slots } ? Slots : any, + emit?: K extends { $emit: infer Emit } ? Emit : any + }) => __VLS_Element & { __ctx?: typeof ctx & { props?: typeof props; expose?(exposed: K): void; } } + : T extends () => any ? (props: {}, ctx?: any) => ReturnType<T> + : T extends (...args: any) => any ? T + : (_: {} & Record<string, unknown>, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: {} & Record<string, unknown> } }; +declare function __VLS_elementAsFunctionalComponent<T>(t: T): (_: T & Record<string, unknown>, ctx?: any) => { __ctx?: { attrs?: any, expose?: any, slots?: any, emit?: any, props?: T & Record<string, unknown> } }; +declare function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): Parameters<T>['length'] extends 2 ? [any] : []; +declare function __VLS_pickEvent<E1, E2>(emitEvent: E1, propEvent: E2): __VLS_FillingEventArg< + __VLS_PickNotAny< + __VLS_AsFunctionOrAny<E2>, + __VLS_AsFunctionOrAny<E1> + > +> | undefined; +declare function __VLS_pickFunctionalComponentCtx<T, K>(comp: T, compInstance: K): __VLS_PickNotAny< + '__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: infer Ctx } ? Ctx : never : any + , T extends (props: any, ctx: infer Ctx) => any ? Ctx : any +>; +type __VLS_FunctionalComponentProps<T, K> = + '__ctx' extends keyof __VLS_PickNotAny<K, {}> ? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never + : T extends (props: infer P, ...args: any) => any ? P : + {}; +type __VLS_AsFunctionOrAny<F> = unknown extends F ? any : ((...args: any) => any) extends F ? F : any; + +declare function __VLS_normalizeSlot<S>(s: S): S extends () => infer R ? (props: {}) => R : S; + +/** + * emit + */ +// fix https://github.com/vuejs/language-tools/issues/926 +type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; +type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R + ? U extends T + ? never + : __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R) + : never; +type __VLS_OverloadUnion<T> = Exclude< + __VLS_OverloadUnionInner<(() => never) & T>, + T extends () => never ? never : () => never +>; +type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F + ? F extends (event: infer E, ...args: infer A) => any + ? { [K in E & string]: (...args: A) => void; } + : never + : never; +type __VLS_NormalizeEmits<T> = __VLS_Prettify< + __VLS_UnionToIntersection< + __VLS_ConstructorOverloads<T> & { + [K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never + } + > +>;
\ No newline at end of file diff --git a/packages/twoslash-cli/test/results/renderer/example.vue.html b/packages/twoslash-cli/test/results/renderer/example.vue.html new file mode 100644 index 0000000..6b271ac --- /dev/null +++ b/packages/twoslash-cli/test/results/renderer/example.vue.html @@ -0,0 +1,68 @@ + + + + +
<script setup lang="ts">
+import { function ref<T>(value: T): Ref<UnwrapRef<T>> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which +has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
,
const computed: { + <T>(getter: ComputedGetter<T>, debugOptions?: DebuggerOptions | undefined): ComputedRef<T>; + <T>(options: WritableComputedOptions<T>, debugOptions?: DebuggerOptions | undefined): WritableComputedRef<...>; +}
computed
} from 'vue'
+ + + + + +const const count: Ref<number>count = ref<number>(value: number): Ref<number> (+1 overload)
Takes an inner value and returns a reactive and mutable ref object, which +has a single property `.value` that points to the inner value.
@paramvalue - The object to wrap in the ref.@see{@link https://vuejs.org/api/reactivity-core.html#ref}
ref
(0)
+ +const
const double: ComputedRef<number>
double
= computed<number>(getter: ComputedGetter<number>, debugOptions?: DebuggerOptions | undefined): ComputedRef<number> (+1 overload)
Takes a getter function and returns a readonly reactive ref object for the +returned value from the getter. It can also take an object with get and set +functions to create a writable ref object.
@example```js +// Creating a readonly computed ref: +const count = ref(1) +const plusOne = computed(() => count.value + 1) + +console.log(plusOne.value) // 2 +plusOne.value++ // error +``` + +```js +// Creating a writable computed ref: +const count = ref(1) +const plusOne = computed({ + get: () => count.value + 1, + set: (val) => { + count.value = val - 1 + } +}) + +plusOne.value = 1 +console.log(count.value) // 0 +```@paramgetter - Function that produces the next value.@paramdebugOptions - For debugging. See {@link https://vuejs.org/guide/extras/reactivity-in-depth.html#computed-debugging}.@see{@link https://vuejs.org/api/reactivity-core.html#computed}
computed
(() => const count: Ref<number>count.Ref<number>.value: numbervalue * 2)
+</script> + +<script> +export default { + ComponentOptionsBase<Props, RawBindings, D, C extends ComputedOptions, M extends MethodOptions, Mixin extends ComponentOptionsMixin, Extends extends ComponentOptionsMixin, E extends EmitsOptions, EE extends string = string, Defaults = {}, I extends ComponentInjectOptions = {}, II extends string = string, S extends SlotsType<...> = {}>.name?: string | undefinedname: 'HelloWorld', + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.data?: ((this: CreateComponentPublicInstance<...>, vm: CreateComponentPublicInstance<{}, {}, {}, {}, MethodOptions, ComponentOptionsMixin, ComponentOptionsMixin, {}, {}, {}, false, ... 8 more ..., {}>) => { + ...; +}) | undefineddata() { + return { + msg: stringmsg: 'Hello!' + } + }, + LegacyOptions<{}, { msg: string; }, {}, { greet(): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string>.methods?: { + greet(): void; +} | undefinedmethods: { + function greet(): voidgreet() { + var console: Consoleconsole.Console.log(...data: any[]): void
[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
log
(this.msg: stringmsg)
+ } + } +} +</script> + +<template> + <button: ButtonHTMLAttributes & ReservedPropsbutton @
onClick?: ((payload: MouseEvent) => void) | undefined
click
="const count: Ref<number>count++">{{ msg: stringmsg }} Count is: {{ const count: Ref<number>count }}</button: ButtonHTMLAttributes & ReservedPropsbutton>
+</template> +
\ No newline at end of file From 9c2075a22272c104387525012de5505812ae433c Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 8 Feb 2024 14:52:34 +0100 Subject: [PATCH 2/2] chore: update --- packages/twoslash-cli/src/cli.ts | 4 ---- pnpm-lock.yaml | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/twoslash-cli/src/cli.ts b/packages/twoslash-cli/src/cli.ts index eb0c0e5..60120c8 100644 --- a/packages/twoslash-cli/src/cli.ts +++ b/packages/twoslash-cli/src/cli.ts @@ -1,8 +1,4 @@ -#!/usr/bin/env node /* eslint-disable no-console */ - -'use strict' - import { readdirSync, statSync } from 'node:fs' import { join } from 'node:path' import process from 'node:process' diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fe8fd9..2bfbd7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,6 +214,21 @@ importers: specifier: ^5.1.0 version: 5.1.0(@types/node@20.11.16)(sass@1.70.0) + packages/twoslash-cli: + dependencies: + chokidar: + specifier: ^3.5.3 + version: 3.5.3 + commander: + specifier: ^11.1.0 + version: 11.1.0 + twoslash: + specifier: workspace:* + version: link:../twoslash + typescript: + specifier: '*' + version: 5.3.3 + packages/twoslash-protocol: {} packages/twoslash-vue: @@ -2784,7 +2799,6 @@ packages: /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - dev: true /commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}