Skip to content

Commit

Permalink
feat: Add missing callbacks for function properties in BaseVisitor
Browse files Browse the repository at this point in the history
  • Loading branch information
maastrich committed Jun 12, 2024
1 parent 631e4f2 commit b59b57a
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 85 deletions.
11 changes: 1 addition & 10 deletions src/baseVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class BaseVisitor<T> implements Required<RecursiveVisitors<T>> {
}
}
AssignmentProperty<S>(n: swc.AssignmentProperty, st: S, cb: Callback<S>) {
console.error('AssignmentProperty', n)
cb(n.value, st)
}
AwaitExpression<S>(n: swc.AwaitExpression, st: S, cb: Callback<S>) {
Expand Down Expand Up @@ -779,11 +780,6 @@ export class BaseVisitor<T> implements Required<RecursiveVisitors<T>> {
TsExternalModuleReference<S>(n: swc.TsExternalModuleReference, st: S, cb: Callback<S>) {
cb(n.expression, st)
}
TsFnParameter<S>(n: swc.TsFnParameter, st: S, cb: Callback<S>) {
if (n.typeAnnotation) {
cb(n.typeAnnotation, st)
}
}
TsFunctionType<S>(n: swc.TsFunctionType, st: S, cb: Callback<S>) {
for (const param of n.params) {
cb(param, st)
Expand Down Expand Up @@ -903,11 +899,6 @@ export class BaseVisitor<T> implements Required<RecursiveVisitors<T>> {

cb(n.param, st)
}
TsParameterPropertyParameter<S>(n: swc.TsParameterPropertyParameter, st: S, cb: Callback<S>) {
if (n.typeAnnotation) {
cb(n.typeAnnotation, st)
}
}
TsParenthesizedType<S>(n: swc.TsParenthesizedType, st: S, cb: Callback<S>) {
if (n.typeAnnotation) {
cb(n.typeAnnotation, st)
Expand Down
108 changes: 39 additions & 69 deletions test/baseVisitors.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, mock } from 'node:test'
import { test } from 'node:test'
import assert from 'node:assert'

import BaseVisitor from '../src/baseVisitor.js'
Expand All @@ -13,8 +13,8 @@ const tests: Array<Test> = [
{ type: 'AssignmentPatternProperty', code: 'const {a = 1} = {}' },
{
type: 'AssignmentProperty',
code: 'const {a = 1, b} = {a: 1, b: 2};',
skip: "TODO find the correct code snippet for 'AssignmentProperty'",
code: 'const { name, age }: { name?: string, age?: number } = person;',
skip: "could not find a valid code snippet for 'AssignmentProperty'",
},
{ type: 'AwaitExpression', code: 'await foo()' },
{ type: 'BigIntLiteral', code: '1n' },
Expand All @@ -33,7 +33,7 @@ const tests: Array<Test> = [
{ type: 'Constructor', code: 'class Foo { constructor() {} }' },
{ type: 'ContinueStatement', code: 'for (const toto of []) {continue;}' },
{ type: 'DebuggerStatement', code: 'debugger;' },
{ type: 'Decorator', code: '@foo class Foo {}', skip: "current parser doesn't support decorators" },
{ type: 'Decorator', code: '@foo class Foo {}', parserOptions: { syntax: 'ecmascript', decorators: true } },
{ type: 'DoWhileStatement', code: 'do {} while (true)' },
{ type: 'EmptyStatement', code: ';' },
{ type: 'ExportAllDeclaration', code: 'export * from "foo"' },
Expand All @@ -42,8 +42,8 @@ const tests: Array<Test> = [
{ type: 'ExportDefaultExpression', code: 'export default ""' },
{
type: 'ExportDefaultSpecifier',
code: 'export {default as a}',
skip: "TODO find the correct code snippet for 'ExportDefaultSpecifier'",
code: 'export = ""',
skip: "could not find a valid code snippet for 'ExportDefaultSpecifier'",
},
{ type: 'ExportNamedDeclaration', code: 'export {a}' },
{ type: 'ExportNamespaceSpecifier', code: "export * as a from 'foo'" },
Expand All @@ -63,24 +63,20 @@ const tests: Array<Test> = [
{ type: 'ImportNamespaceSpecifier', code: 'import * as a from "foo"' },
{ type: 'ImportSpecifier', code: 'import {a} from "foo"' },
{ type: 'Invalid', code: '//', skip: "Could not find a valid code snippet for 'Invalid' node" },
{ type: 'JSXAttribute', code: '<a b="c" />', tsx: true },
{ type: 'JSXClosingElement', code: '<a></a>', tsx: true },
{ type: 'JSXClosingFragment', code: '<></>', tsx: true },
{ type: 'JSXElement', code: '<a />', tsx: true },
{ type: 'JSXEmptyExpression', code: '<a>{}</a>', tsx: true },
{ type: 'JSXExpressionContainer', code: '<a>{b}</a>', tsx: true },
{ type: 'JSXFragment', code: '<></>', tsx: true },
{ type: 'JSXMemberExpression', code: '<a.b />', tsx: true },
{ type: 'JSXNamespacedName', code: '<a:b />', tsx: true },
{ type: 'JSXOpeningElement', code: '<a />', tsx: true },
{ type: 'JSXOpeningFragment', code: '<></>', tsx: true },
{ type: 'JSXText', code: '<a>foo</a>', tsx: true },
{ type: 'JSXAttribute', code: '<a b="c" />', parserOptions: { tsx: true } },
{ type: 'JSXClosingElement', code: '<a></a>', parserOptions: { tsx: true } },
{ type: 'JSXClosingFragment', code: '<></>', parserOptions: { tsx: true } },
{ type: 'JSXElement', code: '<a />', parserOptions: { tsx: true } },
{ type: 'JSXEmptyExpression', code: '<a>{}</a>', parserOptions: { tsx: true } },
{ type: 'JSXExpressionContainer', code: '<a>{b}</a>', parserOptions: { tsx: true } },
{ type: 'JSXFragment', code: '<></>', parserOptions: { tsx: true } },
{ type: 'JSXMemberExpression', code: '<a.b />', parserOptions: { tsx: true } },
{ type: 'JSXNamespacedName', code: '<a:b />', parserOptions: { tsx: true } },
{ type: 'JSXOpeningElement', code: '<a />', parserOptions: { tsx: true } },
{ type: 'JSXOpeningFragment', code: '<></>', parserOptions: { tsx: true } },
{ type: 'JSXText', code: '<a>foo</a>', parserOptions: { tsx: true } },
{ type: 'KeyValuePatternProperty', code: 'const {a: b} = {}' },
{
type: 'KeyValueProperty',
code: 'const {a: b} = {}',
skip: "TODO find the correct code snippet for 'KeyValueProperty'",
},
{ type: 'KeyValueProperty', code: 'const foo = {a: "b"}' },
{ type: 'LabeledStatement', code: 'foo: {}' },
{ type: 'MemberExpression', code: 'a.b' },
{ type: 'MetaProperty', code: 'import.meta' },
Expand All @@ -100,22 +96,14 @@ const tests: Array<Test> = [
{ type: 'RegExpLiteral', code: '/foo/g' },
{ type: 'RestElement', code: 'function foo(...a) {}' },
{ type: 'ReturnStatement', code: '() => {return 1}' },
{ type: 'Script', code: '1', script: true, skip: 'TODO find the correct code snippet for Script' },
{ type: 'Script', code: '', parserOptions: { isModule: false } },
{ type: 'SequenceExpression', code: '1, 2' },
{ type: 'SetterProperty', code: 'const foo = { set a(b) {} }' },
{
type: 'SpreadElement',
code: 'const foo = [...a]',
skip: "TODO find the correct code snippet for 'SpreadElement'",
},
{ type: 'SpreadElement', code: 'const foo = {...bar}' },
{ type: 'StaticBlock', code: 'class Foo { static {} }' },
{ type: 'StringLiteral', code: '"foo"' },
{ type: 'Super', code: 'class Foo {constructor(){super()}}' },
{
type: 'SuperPropExpression',
code: 'class Foo {method(){super.foo}}',
skip: "TODO find the correct code snippet for 'SuperPropExpression'",
},
{ type: 'SuperPropExpression', code: 'class Foo {method(){super.foo}}' },
{ type: 'SwitchCase', code: 'switch(1){case 1: break;}' },
{ type: 'SwitchStatement', code: 'switch(1){case 1: break;}' },
{ type: 'TaggedTemplateExpression', code: 'foo`bar`' },
Expand All @@ -124,22 +112,14 @@ const tests: Array<Test> = [
{ type: 'ThisExpression', code: 'this' },
{ type: 'ThrowStatement', code: 'throw 1' },
{ type: 'TryStatement', code: 'try {} catch(e) {}' },
{
type: 'TsExpressionWithTypeArguments',
code: 'type foo<T> = Array<T>',
skip: "TODO find the correct code snippet for 'TsExpressionWithTypeArguments'",
},
{ type: 'TsExpressionWithTypeArguments', code: 'interface Foo<T> extends Baz {}' },
{ type: 'TsInterfaceDeclaration', code: 'interface Foo {}' },
{ type: 'TsInterfaceBody', code: 'interface Foo {}' },
{ type: 'TsKeywordType', code: 'type foo = string' },
{ type: 'TsPropertySignature', code: 'type foo = {a: string}' },
{ type: 'TsAsExpression', code: 'a as b' },
{
type: 'TsCallSignatureDeclaration',
code: 'type foo = () => void',
skip: "TODO find the correct code snippet for 'TsCallSignatureDeclaration'",
},
{ type: 'JSXSpreadChild', code: '<>{...a}</>', tsx: true },
{ type: 'TsCallSignatureDeclaration', code: 'interface Foo { (): void }' },
{ type: 'JSXSpreadChild', code: '<>{...a}</>', parserOptions: { tsx: true } },
{ type: 'TsArrayType', code: 'type foo = string[]' },
{ type: 'TsConditionalType', code: 'type foo = T extends U ? X : Y' },
{ type: 'TsConstAssertion', code: 'const a = 1 as const' },
Expand All @@ -149,55 +129,41 @@ const tests: Array<Test> = [
{ type: 'TsEnumMember', code: 'enum Foo {a}' },
{ type: 'TsExportAssignment', code: 'export = 1' },
{ type: 'TsExternalModuleReference', code: 'import a = require("foo")' },
{
type: 'TsFnParameter',
code: 'type foo = (a: string) => void',
skip: "TODO find the correct code snippet for 'TsFnParameter'",
},
{ type: 'TsFunctionType', code: 'type foo = () => void' },
{ type: 'TsGetterSignature', code: 'interface Foo { get a(): void }' },
{ type: 'TsImportEqualsDeclaration', code: 'import a = b' },
{
type: 'TsImportType',
code: 'import {type a} from "foo"',
skip: "TODO find the correct code snippet for 'TsImportType'",
skip: 'could not trigger the TsImportType node visitor',
},
{ type: 'TsIndexedAccessType', code: 'type Foo<T, U> = T[U]' },
{ type: 'TsIndexSignature', code: 'interface Foo { [a: string]: string }' },
{ type: 'TsInferType', code: 'type foo = infer T' },
{
type: 'TsInstantiation',
code: 'type foo = new T()',
skip: "TODO find the correct code snippet for 'TsInstantiation'",
code: 'new Foo<string>()',
skip: 'could not trigger the TsInstantiation node visitor',
},
{ type: 'TsIntersectionType', code: 'type foo = T & U' },
{ type: 'TsLiteralType', code: 'type foo = "a"' },
{ type: 'TsMappedType', code: 'type foo = { [K in keyof T]: U }' },
{ type: 'TsMethodSignature', code: 'interface Foo { a(): void }' },
{ type: 'TsModuleBlock', code: 'module "module" {}' },
{ type: 'TsModuleDeclaration', code: 'module foo {}' },
{
type: 'TsNamespaceDeclaration',
code: 'namespace foo { export const a: string }',
skip: 'TODO find the correct code snippet for TsNamespaceDeclaration',
},
{ type: 'TsNamespaceDeclaration', code: 'namespace A.B { }' },
{
type: 'TsNamespaceExportDeclaration',
code: 'export namespace foo { export const a: string }',
skip: 'TODO find the correct code snippet for TsNamespaceExportDeclaration',
code: 'export declare namespace A.B { export const a: string }',
skip: 'could not trigger the TsNamespaceExportDeclaration node visitor',
},
{ type: 'TsNonNullExpression', code: 'a!' },
{
type: 'TsOptionalType',
code: 'function foo(a?: string) {}',
skip: 'TODO find the correct code snippet for TsOptionalType',
code: 'interface MyInterface {name?: string;age?: number;}',
skip: 'could not trigger the TsOptionalType node visitor',
},
{ type: 'TsParameterProperty', code: 'class Foo { constructor(private a: string) {} }' },
{
type: 'TsParameterPropertyParameter',
code: 'class Foo { constructor(private a: string) {} }',
skip: 'TODO find the correct code snippet for TsParameterPropertyParameter',
},
{ type: 'TsParenthesizedType', code: 'type Foo = (Bar | Baz)' },
{ type: 'TsQualifiedName', code: 'const toto: Namespace.Type = ""' },
{ type: 'TsRestType', code: 'type Foo<T extends any[]> = T extends [...infer U, infer R] ? R : never' },
Expand All @@ -222,8 +188,12 @@ const tests: Array<Test> = [
{ type: 'VariableDeclaration', code: 'const a = 1' },
{ type: 'VariableDeclarator', code: 'const a = 1' },
{ type: 'WhileStatement', code: 'while (true) {}' },
{ type: 'WithStatement', code: 'with (a) {}', skip: 'TODO find the correct code snippet for WithStatement' },
{ type: 'YieldExpression', code: 'function* foo() { yield 1 }', debug: true },
{
type: 'WithStatement',
code: 'with (a) {}',
skip: 'could not find a way to run without the strict mode',
},
{ type: 'YieldExpression', code: 'function* foo() { yield 1 }' },
]

for (const options of tests) {
Expand Down
13 changes: 7 additions & 6 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import { mock } from 'node:test'
import assert from 'node:assert'
import { parseSync } from '@swc/core'
import { parseSync, type ParseOptions } from '@swc/core'
import { simple } from '../src/walk.js'
import { BaseVisitor } from '../src/baseVisitor.js'

export type Test = {
type: keyof BaseVisitor<unknown>
skip?: string
code: string
tsx?: boolean
times?: number
debug?: boolean
script?: boolean
parserOptions?: Partial<ParseOptions> & { isModule?: false }
}

const baseVisitor = new BaseVisitor()

export function given(options: Test): Test & { method: ReturnType<typeof mock.method> } {
try {
const ast = parseSync(options.code, {
tsx: options.tsx,
syntax: 'typescript',
script: options.script,
...options.parserOptions,
})

if (options.debug) {
Expand All @@ -33,7 +31,10 @@ export function given(options: Test): Test & { method: ReturnType<typeof mock.me
simple(ast, {}, baseVisitor)

return { ...options, method }
} catch {
} catch (e) {
if (options.debug) {
global.console.error(e)
}
assert.fail(`Couldn't parse the code snippet for node ${options.type}`)
}
}
Expand Down

0 comments on commit b59b57a

Please sign in to comment.