eslint-codemod-utils
Version:
A collection of AST helper functions for more complex ESLint rule fixes.
1,204 lines (1,203 loc) • 37.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.literal = exports.nullLiteral = exports.numberLiteral = exports.booleanLiteral = exports.stringLiteral = exports.regExpLiteral = exports.bigIntLiteral = exports.importDeclaration = exports.templateElement = exports.importNamespaceSpecifier = exports.variableDeclaration = exports.variableDeclarator = exports.logicalExpression = exports.memberExpression = exports.emptyStatement = exports.objectExpression = exports.restElement = exports.spreadElement = exports.objectPattern = exports.property = exports.newExpression = exports.expressionStatement = exports.updateExpression = exports.arrayPattern = exports.arrayExpression = exports.yieldExpression = exports.importSpecifier = exports.exportSpecifier = exports.exportAllDeclaration = exports.exportDefaultDeclaration = exports.exportNamedDeclaration = exports.importDefaultSpecifier = exports.importExpression = exports.withStatement = exports.tryStatement = exports.catchClause = exports.ifStatement = exports.thisExpression = exports.unaryExpression = exports.throwStatement = exports.returnStatement = exports.blockStatement = exports.functionExpression = exports.taggedTemplateExpression = exports.arrowFunctionExpression = exports.sequenceExpression = exports.binaryExpression = exports.chainExpression = exports.superCallExpression = exports.callExpression = void 0;
exports.decorator = exports.privateIdentifier = exports.program = exports.classExpression = exports.classDeclaration = exports.classBody = exports.propertyDefinition = exports.methodDefinition = exports.methodOrPropertyFn = exports.functionDeclaration = exports.staticBlock = exports.awaitExpression = exports.assignmentExpression = exports.conditionalExpression = exports.debuggerStatement = exports.breakStatement = exports.continueStatement = exports.forOfStatement = exports.forInStatement = exports.forStatement = exports.templateLiteral = exports.switchStatement = exports.switchCase = exports.whileStatement = exports.doWhileStatement = exports.identifier = void 0;
const types_1 = require("@typescript-eslint/types");
const node_1 = require("./utils/node");
const constants_1 = require("./constants");
const utils_1 = require("./utils");
/**
* __CallExpression__
*
* @example
*
* Usage
* ```
* const call = callExpression({ callee: identifier({ name: 'normalCallExpression' }) })
* ```
*
* Produces
*
* @example
*
* ```js
* normalCallExpression()
* ```
*
* @returns {ESTree.CallExpression}
*/
const callExpression = ({ arguments: calleeArgs, callee, optional = false, ...other }) => {
return {
...other,
arguments: calleeArgs,
callee,
optional,
type: types_1.AST_NODE_TYPES.CallExpression,
toString: () => `${(0, node_1.node)(callee)}${optional ? '?.' : ''}(${calleeArgs
.map(node_1.node)
.join(', ')})`,
};
};
exports.callExpression = callExpression;
/**
* __Super__
*
* @example
*
* ```
* // note the whole expression is a `CallExpression`
* // super is simply the callee / identifier
* super()
* ^^^^^
* ```
*
* @returns {ESTree.Super}
*/
const superCallExpression = ({ ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.Super,
toString: () => `super`,
};
};
exports.superCallExpression = superCallExpression;
const chainExpression = ({ expression, ...other }) => {
return {
...other,
expression,
type: types_1.AST_NODE_TYPES.ChainExpression,
toString: () => `${(0, node_1.node)(expression)}`,
};
};
exports.chainExpression = chainExpression;
/**
* __BinaryExpression__
*
* @example
* ```ts
* const x = 'left' + 'right'
* ^^^^^^^^^^^^^^^^
* ```
*/
const binaryExpression = ({ left, right, operator, ...other }) => {
return {
...other,
left,
right,
operator,
type: types_1.AST_NODE_TYPES.BinaryExpression,
toString: () => `${(0, node_1.node)(left)} ${operator} ${(0, node_1.node)(right)}`,
};
};
exports.binaryExpression = binaryExpression;
/**
* __SequenceExpression__
*
* @example
* ```ts
* const x = (4, 8)
* ^^^^^^
* ```
*/
const sequenceExpression = ({ expressions, ...other }) => {
return {
...other,
expressions,
type: types_1.AST_NODE_TYPES.SequenceExpression,
toString: () => `(${expressions.map(node_1.node).map(String).join(', ')})`,
};
};
exports.sequenceExpression = sequenceExpression;
/**
* __ArrowFunctionExpression__
*
* @example
* ```js
* const arrow = () => 42
* ⌃⌃⌃⌃⌃⌃⌃⌃
* ```
* @returns {ESTree.ArrowFunctionExpression}
*/
const arrowFunctionExpression = ({ async = false, generator = false, body, expression, params, ...other }) => {
return {
...other,
generator,
async,
expression,
body,
params,
type: types_1.AST_NODE_TYPES.ArrowFunctionExpression,
toString: () => `${async ? 'async ' : ''}(${params
.map(node_1.node)
.map(String)
.join(', ')}) => ${(0, node_1.node)(body)}`,
};
};
exports.arrowFunctionExpression = arrowFunctionExpression;
/**
* __TaggedTemplateExpression__
*
* @example
* ```ts
* const style = css`color: red;`
* ^^^^^^^^^^^
* ```
*/
const taggedTemplateExpression = ({ quasi, tag, ...other }) => {
return {
...other,
quasi,
tag,
type: types_1.AST_NODE_TYPES.TaggedTemplateExpression,
toString: () => `${(0, node_1.node)(tag)}${(0, node_1.node)(quasi)}`,
};
};
exports.taggedTemplateExpression = taggedTemplateExpression;
const functionExpression = ({ async = false, generator = false, body, params, id, ...other }) => {
return {
...other,
id,
async,
generator,
body,
params,
type: types_1.AST_NODE_TYPES.FunctionExpression,
toString: () => `${async ? 'async ' : ''}function ${id ? (0, node_1.node)(id) : ''}(${params
.map(node_1.node)
.join(', ')}) ${(0, node_1.node)(body)}`,
};
};
exports.functionExpression = functionExpression;
const blockStatement = ({ body, ...other }) => {
return {
...other,
body,
type: types_1.AST_NODE_TYPES.BlockStatement,
toString: () => `{${body.length
? constants_1.DEFAULT_WHITESPACE +
body.map(node_1.node).map(String).join(constants_1.DEFAULT_WHITESPACE) +
'\n'
: ''}}`,
};
};
exports.blockStatement = blockStatement;
const returnStatement = ({ argument, ...other }) => {
return {
...other,
argument,
type: types_1.AST_NODE_TYPES.ReturnStatement,
toString: () => `return${argument
? argument.type === 'JSXElement'
? ` (${constants_1.DEFAULT_WHITESPACE}${(0, node_1.node)(argument)}${constants_1.DEFAULT_WHITESPACE})`
: ` ${(0, node_1.node)(argument)}`
: ''};`,
};
};
exports.returnStatement = returnStatement;
const throwStatement = ({ argument, ...other }) => {
return {
...other,
argument,
type: types_1.AST_NODE_TYPES.ThrowStatement,
toString: () => {
if (!argument) {
return `throw;`;
}
// `TSESTree.ThrowStatement.argument` is typed as
// `Statement | TSAsExpression | null` in `@typescript-eslint/types@5.45`
// which notably excludes JSX nodes, even though `espree` happily emits
// `JSXElement`/`JSXFragment` here at runtime for `throw (<el />)`. Cast
// the discriminant to the full `AST_NODE_TYPES` enum so the comparison
// reflects the real parser output rather than the narrow declared type.
const argumentType = argument.type;
const needsParens = argumentType === types_1.AST_NODE_TYPES.JSXElement ||
argumentType === types_1.AST_NODE_TYPES.JSXFragment;
return `throw${needsParens
? ` (${constants_1.DEFAULT_WHITESPACE}${(0, node_1.node)(argument)}${constants_1.DEFAULT_WHITESPACE})`
: ` ${(0, node_1.node)(argument)}`};`;
},
};
};
exports.throwStatement = throwStatement;
/**
* __UnaryExpression__
*
* @example
*
* ```ts
* const y = typeof x
* ^^^^^^
* ++x
* ^^
* ```
*
* @returns {ESTree.UnaryExpression}
*/
const unaryExpression = ({ operator, argument, prefix, ...other }) => {
return {
...other,
operator,
prefix,
argument,
type: types_1.AST_NODE_TYPES.UnaryExpression,
toString: () => `${operator} ${(0, node_1.node)(argument)}`,
};
};
exports.unaryExpression = unaryExpression;
/**
* __ThisExpression__
*
* @example
*
* ```js
* // In `this.self` 'this' is a ThisExpression.
* this.self
* ⌃⌃⌃⌃
* ```
*
* @returns {ESTree.ThisExpression}
*/
const thisExpression = (node) => ({
...node,
type: types_1.AST_NODE_TYPES.ThisExpression,
toString: () => `this`,
});
exports.thisExpression = thisExpression;
/**
* __IfStatement__
*
* @example
*
* ```ts
* if (test) {
* // consequant
* } else {
* // alternate
* }
* ⌃⌃⌃⌃^^^^^^^^
* ```
*
* @returns {ESTree.IfStatement}
*/
const ifStatement = ({ test, alternate, consequent, ...other }) => ({
...other,
test,
alternate,
consequent,
type: types_1.AST_NODE_TYPES.IfStatement,
toString: () => `if (${(0, node_1.node)(test)}) ${(0, node_1.node)(consequent)} ${alternate ? `else ${(0, node_1.node)(alternate)}` : ''}`,
});
exports.ifStatement = ifStatement;
/**
* __CatchClause__
*
* @example
*
* ```ts
* // always inside a try statement
* catch (e) {}
* ⌃⌃⌃⌃^^^^^^^^
* ```
*
* @returns {ESTree.CatchClause}
*/
const catchClause = ({ body, param, ...other }) => ({
...other,
body,
param,
type: types_1.AST_NODE_TYPES.CatchClause,
toString: () => `catch${param ? ` (${(0, node_1.node)(param)})` : ''} ${(0, node_1.node)(body)}`,
});
exports.catchClause = catchClause;
/**
* __TryStatement__
*
* @example
*
* ```ts
* try {
* // block
* } catch(e) { // <--- handler
*
* } finally {} // <--- finalizer
* ⌃⌃⌃⌃^^^^^^^^
* ```
*
* @returns {ESTree.TryStatement}
*/
const tryStatement = ({ block, finalizer, handler, ...other }) => ({
...other,
block,
finalizer,
handler,
type: types_1.AST_NODE_TYPES.TryStatement,
toString: () => `try ${(0, node_1.node)(block)} ${handler ? (0, node_1.node)(handler) : ''} ${finalizer ? `finally ${(0, node_1.node)(finalizer)}` : ''}`,
});
exports.tryStatement = tryStatement;
/**
* __WithStatement__
*
* @example
*
* ```ts
* with (Math) {
* a = PI * r * r;
* x = r * cos(PI);
* y = r * sin(PI / 2);
* }
* ```
*
* @returns {ESTree.WithStatement}
*/
const withStatement = ({ object, body, ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.WithStatement,
object,
body,
toString: () => `with (${(0, node_1.node)(object)}) ${(0, node_1.node)(body)}`,
});
exports.withStatement = withStatement;
/**
* __ImportExpression__
*
* @example
*
* ```ts
* import('some-path')
* ⌃⌃⌃⌃^^^^^^^^^^^^^^^
* ```
*
* @returns {ESTree.ImportExpression}
*/
const importExpression = ({ source, ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.ImportExpression,
source,
toString: () => `import(${(0, node_1.node)(source)})`,
});
exports.importExpression = importExpression;
/**
* __ImportDefaultSpecifier__
*
* @example
*
* ```ts
* import Hello from 'world'
* ^^^^^
* ```
*
* @returns {ESTree.ImportDefaultSpecifier}
*/
const importDefaultSpecifier = ({ local, ...other }) => ({
...other,
local,
type: types_1.AST_NODE_TYPES.ImportDefaultSpecifier,
toString: () => local.name,
});
exports.importDefaultSpecifier = importDefaultSpecifier;
/**
* __ExportNamedDeclaration__
*
* @example
*
* ```ts
* export { Hello } from 'world'
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* ```
*
* @returns {ESTree.ExportNamedDeclaration}
*/
const exportNamedDeclaration = ({ declaration, specifiers, source, assertions = [], exportKind = 'value', ...other }) => {
// `ExportNamedDeclaration` is a discriminated union with mutually-exclusive
// `source`/`declaration`/`specifiers` invariants that depend on runtime
// values. TypeScript can't narrow to one variant here, so fall back to a
// structural cast — the runtime shape is always valid for exactly one
// variant depending on the inputs the caller provided.
return {
...other,
declaration,
specifiers,
source,
assertions,
exportKind,
type: types_1.AST_NODE_TYPES.ExportNamedDeclaration,
toString: () => `export ${declaration ? (0, node_1.node)(declaration) : ''}${specifiers.length
? `{ ${specifiers.map(node_1.node).map(String).join(', ')} }`
: ''}${source ? `from ${(0, node_1.node)(source)}` : ''}`,
};
};
exports.exportNamedDeclaration = exportNamedDeclaration;
/**
* __ExportDefaultDeclaration__
*
* @example
*
* ```ts
* export default HelloWorld
* ^^^^^^^^^^^^^^^^^^^^^^^^^
* ```
*
* @returns {ESTree.ExportDefaultDeclaration}
*/
const exportDefaultDeclaration = ({ declaration, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ExportDefaultDeclaration,
declaration,
toString: () => `export default ${(0, node_1.node)(declaration)}`,
};
};
exports.exportDefaultDeclaration = exportDefaultDeclaration;
/**
* __ExportAllDeclaration__
*
* @example
*
* ```ts
* export * from 'world'
* ^^^^^^^^^^^^^^^^^^^^^^^^^
* ```
* ```ts
* export * as Hello from 'world'
* ^^^^^^^^^^^^^^^^^^^^^^^^^
* ```
*
* @returns {ESTree.ExportAllDeclaration}
*/
/**
* `exported` is made optional here (defaults to `null`) via a narrowed input
* type so callers don't have to write `exported: null` for the common
* `export * from '…'` case. It stays required on the underlying TSESTree type
* (and on `ExportSpecifier`, which is why it's deliberately excluded from the
* shared `DefaultableFields` list).
*/
const exportAllDeclaration = ({ source, exported = null, assertions = [], exportKind = 'value', ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ExportAllDeclaration,
source,
exported,
assertions,
exportKind,
toString: () => `export * ${exported ? `as ${(0, node_1.node)(exported)} ` : ''}from ${(0, node_1.node)(source)}`,
};
};
exports.exportAllDeclaration = exportAllDeclaration;
const exportSpecifier = ({ exported, local, exportKind = 'value', ...other }) => {
return {
...other,
exported,
local,
exportKind,
type: types_1.AST_NODE_TYPES.ExportSpecifier,
toString: () => local.name !== exported.name
? `${(0, node_1.node)(exported)} as ${(0, node_1.node)(local)}`
: String((0, node_1.node)(local)),
};
};
exports.exportSpecifier = exportSpecifier;
const importSpecifier = ({ imported, local, importKind = 'value', ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.ImportSpecifier,
imported,
importKind,
local,
toString: () => `${importKind === 'type' ? 'type ' : ''}${local.name === imported.name
? imported.name
: `${imported.name} as ${local.name}`}`,
});
exports.importSpecifier = importSpecifier;
/**
* __YieldExpression__
*
* @example
*
* ```ts
* const thing = yield someYieldExpression
* ⌃⌃⌃⌃⌃⌃⌃⌃⌃⌃⌃⌃^^^^^^^^^^^^^
* ```
*
* @returns {ESTree.YieldExpression}
*/
const yieldExpression = ({ argument, delegate, ...other }) => {
return {
...other,
argument,
delegate,
type: types_1.AST_NODE_TYPES.YieldExpression,
toString: () => `yield ${argument ? (0, node_1.node)(argument) : ''}`,
};
};
exports.yieldExpression = yieldExpression;
const arrayExpression = ({ elements, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ArrayExpression,
elements,
toString: () => `[${elements
.filter((n) => Boolean(n))
.map(node_1.node)
.map(String)
.join(', ')}]`,
};
};
exports.arrayExpression = arrayExpression;
const arrayPattern = ({ elements, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ArrayPattern,
elements,
toString: () => `[${elements
.filter((node) => node !== null)
.map(node_1.node)
.map(String)
.join(', ')}]`,
};
};
exports.arrayPattern = arrayPattern;
const updateExpression = ({ argument, operator, prefix, ...other }) => {
return {
...other,
argument,
operator,
prefix,
type: types_1.AST_NODE_TYPES.UpdateExpression,
toString: () => `${prefix ? `${operator}${(0, node_1.node)(argument)}` : `${(0, node_1.node)(argument)}${operator}`}`,
};
};
exports.updateExpression = updateExpression;
const expressionStatement = ({ expression, ...other }) => ({
...other,
expression,
type: types_1.AST_NODE_TYPES.ExpressionStatement,
toString: () => String((0, node_1.node)(expression)),
});
exports.expressionStatement = expressionStatement;
/**
* __NewExpression__
*
* @example
* ```ts
* new SomeThing()
* ^^^^^^^^^^^^^^^
* ```
*/
const newExpression = ({ callee, arguments: argumentsParam, ...other }) => ({
...other,
callee,
arguments: argumentsParam,
type: types_1.AST_NODE_TYPES.NewExpression,
toString: () => `new ${(0, node_1.node)(callee)}(${argumentsParam.map(node_1.node).join(', ')})`,
});
exports.newExpression = newExpression;
const property = ({ kind = 'init', key, value, method = false, computed = false, shorthand = false, ...other }) => {
// `Property` is a union discriminated on `computed: boolean`. The runtime
// value of `computed` chooses the variant, but TS can't narrow a runtime
// boolean so structurally cast the return.
return {
...other,
key,
kind,
value,
method,
shorthand,
computed,
type: types_1.AST_NODE_TYPES.Property,
toString: () => `${kind === 'init' ? '' : kind + ' '}${computed ? '[' : ''}${(0, node_1.node)(key)}${computed ? ']' : ''}${kind !== 'init' ? '' : ': '}${kind !== 'init' &&
(0, utils_1.isNodeOfType)(value, types_1.AST_NODE_TYPES.FunctionExpression)
? (0, exports.methodOrPropertyFn)(value)
: (0, node_1.node)(value)}`,
};
};
exports.property = property;
/**
* __ObjectPattern__
*
* @example
* ```ts
* function App({ a }) {}
* ^^^^^
* ```
* @returns
*/
const objectPattern = ({ properties, ...other }) => {
return {
...other,
properties,
type: types_1.AST_NODE_TYPES.ObjectPattern,
toString: () => `{${properties.map(node_1.node).map(String).join(', ')}}`,
};
};
exports.objectPattern = objectPattern;
/**
* __SpreadElement__
*
* @example
* ```ts
* const obj = {
* ...spread
* ^^^^^^^^^
* }
* ```
*
* @returns {ESTree.SpreadElement}
*/
const spreadElement = ({ argument, ...other }) => {
return {
...other,
argument,
type: types_1.AST_NODE_TYPES.SpreadElement,
toString: () => `...${(0, node_1.node)(argument)}`,
};
};
exports.spreadElement = spreadElement;
/**
* __RestElement__
*
* @example
* ```ts
* const [a, ...b] = c
* ^^^^
* ```
*
* * @example
* ```ts
* const { a, ...b } = c
* ^^^^
* ```
*
* @returns {ESTree.RestElement}
*/
const restElement = ({ argument, ...other }) => {
return {
...other,
argument,
type: types_1.AST_NODE_TYPES.RestElement,
toString: () => `...${(0, node_1.node)(argument)}`,
};
};
exports.restElement = restElement;
/**
* __ObjectExpression__
* @example
* ```ts
* const x = {
* key: value,
* get x() { return 1 },
* }
* ^^^^^^^^^^^^
* ```
*/
const objectExpression = ({ properties, ...other }) => {
return {
...other,
properties,
type: types_1.AST_NODE_TYPES.ObjectExpression,
toString: () => `{${constants_1.DEFAULT_WHITESPACE}${properties
.map(node_1.node)
.map(String)
.join(`,${constants_1.DEFAULT_WHITESPACE}`)}\n}`,
};
};
exports.objectExpression = objectExpression;
const emptyStatement = ({ ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.EmptyStatement,
toString: () => `;`,
});
exports.emptyStatement = emptyStatement;
const memberExpression = ({ object, property, computed = false, optional = false, ...other }) =>
// `MemberExpression` is a union discriminated on `computed` — see the note
// on `property` above for why a structural cast is needed here.
({
...other,
type: types_1.AST_NODE_TYPES.MemberExpression,
computed,
optional,
object,
property,
toString: () => {
const translatedNode = (0, node_1.node)(property);
return `${(0, node_1.node)(object)}${computed ? `[${translatedNode}]` : `.${translatedNode}`}`;
},
});
exports.memberExpression = memberExpression;
const logicalExpression = ({ left, right, operator, ...other }) => {
return {
...other,
left,
right,
operator,
type: types_1.AST_NODE_TYPES.LogicalExpression,
toString: () => `${(0, node_1.node)(left)} ${operator} ${(0, node_1.node)(right)}`,
};
};
exports.logicalExpression = logicalExpression;
const variableDeclarator = ({ id, init, ...other }) => {
return {
...other,
id,
init,
type: types_1.AST_NODE_TYPES.VariableDeclarator,
toString: () => `${(0, node_1.node)(id)}${init ? ` = ${(0, node_1.node)(init)}` : ''}`,
};
};
exports.variableDeclarator = variableDeclarator;
const variableDeclaration = ({ declarations, kind, ...other }) => {
return {
...other,
declarations,
kind,
type: types_1.AST_NODE_TYPES.VariableDeclaration,
toString: () => `${kind ? `${kind} ` : ''}${declarations
.map(exports.variableDeclarator)
.map(String)
.join()}`,
};
};
exports.variableDeclaration = variableDeclaration;
const importNamespaceSpecifier = ({ local, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ImportNamespaceSpecifier,
local,
toString: () => `* as ${local.name}`,
};
};
exports.importNamespaceSpecifier = importNamespaceSpecifier;
const templateElement = ({ value, ...other }) => {
return {
...other,
value,
type: types_1.AST_NODE_TYPES.TemplateElement,
toString: () => `${value.raw}`,
};
};
exports.templateElement = templateElement;
const importDeclaration = ({ specifiers, source, importKind = 'value', assertions = [], ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.ImportDeclaration,
specifiers,
source,
importKind,
assertions,
toString: () => {
if (!specifiers.length) {
return `import '${source.value}'`;
}
const defaultSpecifier = specifiers.find((spec) => (0, utils_1.isNodeOfType)(spec, types_1.AST_NODE_TYPES.ImportDefaultSpecifier));
const otherSpecifiers = specifiers.filter((spec) => (0, utils_1.isNodeOfType)(spec, types_1.AST_NODE_TYPES.ImportSpecifier));
const nameSpaceSpecifier = specifiers.find((node) => (0, utils_1.isNodeOfType)(node, types_1.AST_NODE_TYPES.ImportNamespaceSpecifier));
const seperator = otherSpecifiers.length > 4 ? `,${constants_1.DEFAULT_WHITESPACE}` : ', ';
const leadOrEndSpecifier = otherSpecifiers.length > 4 ? '\n' : ' ';
return `import ${importKind === 'type' ? 'type ' : ''}${defaultSpecifier ? defaultSpecifier.local.name : ''}${otherSpecifiers.length
? defaultSpecifier
? `, {${leadOrEndSpecifier}${otherSpecifiers
.map(exports.importSpecifier)
.join(seperator)}${leadOrEndSpecifier}}`
: `{${leadOrEndSpecifier}${otherSpecifiers
.map(exports.importSpecifier)
.join(seperator)}${leadOrEndSpecifier}}`
: ''}${(otherSpecifiers.length || defaultSpecifier) && nameSpaceSpecifier
? ', '
: ''}${nameSpaceSpecifier ? (0, exports.importNamespaceSpecifier)(nameSpaceSpecifier) : ''} from '${source.value}'`;
},
});
exports.importDeclaration = importDeclaration;
const bigIntLiteral = ({ value, raw, bigint, ...other }) => ({
...other,
value,
raw,
bigint,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => raw || String(value),
});
exports.bigIntLiteral = bigIntLiteral;
const regExpLiteral = ({ value, raw, regex, ...other }) => ({
...other,
value,
raw,
regex,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => raw || String(value),
});
exports.regExpLiteral = regExpLiteral;
const stringLiteral = ({ raw, ...node }) => {
return {
...node,
raw,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => raw,
};
};
exports.stringLiteral = stringLiteral;
const booleanLiteral = (n) => {
if (typeof n === 'boolean') {
return {
value: n,
raw: String(n),
type: types_1.AST_NODE_TYPES.Literal,
toString: () => String(n),
};
}
else {
return {
...n,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => n.raw || String(n.value),
};
}
};
exports.booleanLiteral = booleanLiteral;
const numberLiteral = (n) => {
if (typeof n === 'number') {
return {
value: n,
raw: String(n),
type: types_1.AST_NODE_TYPES.Literal,
toString: () => String(n),
};
}
return {
...n,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => n.raw || String(n.value),
};
};
exports.numberLiteral = numberLiteral;
const nullLiteral = (n) => {
if (n === null) {
return {
raw: 'null',
value: null,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => 'null',
};
}
return {
...n,
type: types_1.AST_NODE_TYPES.Literal,
toString: () => 'null',
};
};
exports.nullLiteral = nullLiteral;
function literal(n) {
if (typeof n === 'string') {
return (0, exports.stringLiteral)({ value: n, raw: `'${n}'` });
}
if (typeof n === 'boolean') {
return (0, exports.booleanLiteral)(n);
}
if (typeof n === 'number') {
return (0, exports.numberLiteral)(n);
}
if (n === null) {
return (0, exports.nullLiteral)(n);
}
// At this point `n` is `WithoutType<Literal>` — a discriminated union across
// the concrete literal variants. Dispatch by the discriminator field. The
// `in` narrowing loses `WithoutType<…>` through the distributive intersection
// produced by `Loose<…>`, so re-assert the concrete shape.
if ('bigint' in n) {
return (0, exports.bigIntLiteral)(n);
}
if ('regex' in n) {
return (0, exports.regExpLiteral)(n);
}
if (typeof n.value === 'string') {
return (0, exports.stringLiteral)(n);
}
if (typeof n.value === 'number') {
return (0, exports.numberLiteral)(n);
}
if (typeof n.value === 'boolean') {
return (0, exports.booleanLiteral)(n);
}
if (n.value === null) {
return (0, exports.nullLiteral)(n);
}
// Unreachable in well-typed callers — fall back to stringifying as a string
// literal so this degrades gracefully at runtime.
return (0, exports.stringLiteral)(n);
}
exports.literal = literal;
const identifier = (param) => {
const name = typeof param === 'string' ? param : param.name;
const other = typeof param === 'object' ? param : {};
return {
...other,
type: types_1.AST_NODE_TYPES.Identifier,
name,
toString: () => name,
};
};
exports.identifier = identifier;
const doWhileStatement = ({ test, body, ...other }) => ({
...other,
test,
body,
type: types_1.AST_NODE_TYPES.DoWhileStatement,
toString() {
return `do ${(0, node_1.node)(body)} while (${(0, node_1.node)(test)})`;
},
});
exports.doWhileStatement = doWhileStatement;
const whileStatement = ({ test, body, ...other }) => ({
...other,
test,
body,
type: types_1.AST_NODE_TYPES.WhileStatement,
toString() {
return `while (${(0, node_1.node)(test)}) ${(0, node_1.node)(body)}`;
},
});
exports.whileStatement = whileStatement;
const switchCase = ({ consequent, test, ...other }) => {
return {
...other,
consequent,
test,
type: types_1.AST_NODE_TYPES.SwitchCase,
toString: () => `${!test ? 'default' : `case ${(0, node_1.node)(test)}`}: ${consequent
.map(node_1.node)
.map(String)
.join('; ')};`,
};
};
exports.switchCase = switchCase;
const switchStatement = ({ cases, discriminant, ...other }) => ({
...other,
toString: () => `switch (${(0, node_1.node)(discriminant)}) {
${cases.map(exports.switchCase).join(constants_1.DEFAULT_WHITESPACE)}\n}`,
cases,
discriminant,
type: types_1.AST_NODE_TYPES.SwitchStatement,
});
exports.switchStatement = switchStatement;
const templateLiteral = ({ expressions, quasis, ...other }) => {
if (quasis.length < expressions.length) {
throw new Error('invariant: quasis should always outnumber expressions in a TemplateLiteral');
}
return {
...other,
type: types_1.AST_NODE_TYPES.TemplateLiteral,
quasis,
expressions,
toString: () => {
const range = Array.from({ length: quasis.length + expressions.length });
return ('`' +
range
.map((_, index) => {
if (index % 2 === 0) {
return (0, node_1.node)(quasis[Math.floor(index / 2)]);
}
else {
return `\${${(0, node_1.node)(expressions[Math.floor(index / 2)])}}`;
}
})
.map(String)
.join('') +
'`');
},
};
};
exports.templateLiteral = templateLiteral;
const forStatement = ({ body, init, test, update, ...other }) => ({
...other,
init,
body,
test,
update,
type: types_1.AST_NODE_TYPES.ForStatement,
toString: () => `for (${init ? (0, node_1.node)(init) : ''};${test ? (0, node_1.node)(test) : ''};${update ? (0, node_1.node)(update) : ''}) ${(0, node_1.node)(body)}`,
});
exports.forStatement = forStatement;
const forInStatement = ({ body, left, right, ...other }) => ({
...other,
body,
left,
right,
type: types_1.AST_NODE_TYPES.ForInStatement,
toString: () => `for (${(0, node_1.node)(left)} in ${(0, node_1.node)(right)}) ${(0, node_1.node)(body)}`,
});
exports.forInStatement = forInStatement;
const forOfStatement = ({ body, left, right, ...other }) => ({
...other,
body,
left,
right,
type: types_1.AST_NODE_TYPES.ForOfStatement,
toString: () => `for (${(0, node_1.node)(left)} of ${(0, node_1.node)(right)}) ${(0, node_1.node)(body)}`,
});
exports.forOfStatement = forOfStatement;
const continueStatement = ({ label, ...other }) => ({
...other,
toString: () => `continue${label ? ` ${(0, node_1.node)(label)}` : ''}`,
label,
type: types_1.AST_NODE_TYPES.ContinueStatement,
});
exports.continueStatement = continueStatement;
const breakStatement = ({ label, ...other }) => ({
...other,
toString: () => `break${label ? ` ${(0, node_1.node)(label)}` : ''}`,
label,
type: types_1.AST_NODE_TYPES.BreakStatement,
});
exports.breakStatement = breakStatement;
const debuggerStatement = (node) => ({
...node,
toString: () => `debugger`,
type: types_1.AST_NODE_TYPES.DebuggerStatement,
});
exports.debuggerStatement = debuggerStatement;
const conditionalExpression = ({ consequent, alternate, test, ...other }) => ({
...other,
toString: () => `${(0, node_1.node)(test)} ? ${(0, node_1.node)(consequent)} : ${(0, node_1.node)(alternate)}`,
consequent,
alternate,
test,
type: types_1.AST_NODE_TYPES.ConditionalExpression,
});
exports.conditionalExpression = conditionalExpression;
const assignmentExpression = ({ left, right, operator, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.AssignmentExpression,
left,
right,
operator,
toString: () => `${(0, node_1.node)(left)}${operator}${(0, node_1.node)(right)}`,
};
};
exports.assignmentExpression = assignmentExpression;
const awaitExpression = ({ argument, ...other }) => ({
...other,
toString: () => `await ${(0, node_1.node)(argument)}`,
argument,
type: types_1.AST_NODE_TYPES.AwaitExpression,
});
exports.awaitExpression = awaitExpression;
/**
* __StaticBlock__
*
* @example
* ```ts
* class A {
* // only applicable inside a class
* static { }
* ^^^^^^^^^^
* }
* ```
*/
const staticBlock = ({ body, ...other }) => {
return {
...other,
body,
type: types_1.AST_NODE_TYPES.StaticBlock,
toString: () => `static {\n${body.map(node_1.node).map(String).join(constants_1.DEFAULT_WHITESPACE)}\n}`,
};
};
exports.staticBlock = staticBlock;
const functionDeclaration = ({ body, async = false, id, generator = false, params, ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.FunctionDeclaration,
body,
async,
id,
generator,
params,
toString: () => `${async ? 'async ' : ''}function ${id ? (0, node_1.node)(id) : ''}(${params
.map(node_1.node)
.map(String)
.join(', ')}) ${(0, node_1.node)(body)}`,
});
exports.functionDeclaration = functionDeclaration;
const methodOrPropertyFn = ({ params, body, }) => {
return `(${params.map(node_1.node).join(', ')}) ${body ? (0, node_1.node)(body) : `{}`}`;
};
exports.methodOrPropertyFn = methodOrPropertyFn;
const methodDefinition = ({ computed, key, kind, value, ...other }) => {
// `MethodDefinition` is a union discriminated on `computed: boolean` — TS
// can't narrow a runtime boolean to one variant, so cast the result.
return {
...other,
computed,
key,
kind,
value,
type: types_1.AST_NODE_TYPES.MethodDefinition,
toString: () => `${computed ? `[${(0, node_1.node)(key)}]` : (0, node_1.node)(key)} ${(0, exports.methodOrPropertyFn)(value)}`,
};
};
exports.methodDefinition = methodDefinition;
const propertyDefinition = ({ computed, key, static: staticKeyWord, value, ...other }) => {
// `PropertyDefinition` is a union discriminated on `computed: boolean` —
// cast the structural return to satisfy the union.
return {
...other,
computed,
key,
static: staticKeyWord,
value,
type: types_1.AST_NODE_TYPES.PropertyDefinition,
toString: () => `UNIMPLEMENTED`,
};
};
exports.propertyDefinition = propertyDefinition;
const classBody = ({ body, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ClassBody,
body,
toString: () => body.length
? `${constants_1.DEFAULT_WHITESPACE}${body
.map(node_1.node)
.map(String)
.join(constants_1.DEFAULT_WHITESPACE)}\n`
: '',
};
};
exports.classBody = classBody;
const classDeclaration = ({ superClass, id, body, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ClassDeclaration,
superClass,
body,
id,
toString: () => `class${id ? ` ${(0, node_1.node)(id)}` : ''}${superClass ? ` extends ${(0, node_1.node)(superClass)}` : ''} {${(0, node_1.node)(body)}}`,
};
};
exports.classDeclaration = classDeclaration;
const classExpression = ({ superClass, id, body, ...other }) => {
return {
...other,
type: types_1.AST_NODE_TYPES.ClassExpression,
superClass,
body,
id,
toString: () => String((0, exports.classDeclaration)({ superClass, id: id || null, body, ...other })),
};
};
exports.classExpression = classExpression;
const program = ({ body, ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.Program,
toString: () => body.map(node_1.node).map(String).join('\n'),
body,
});
exports.program = program;
const privateIdentifier = ({ name, ...other }) => ({
...other,
type: types_1.AST_NODE_TYPES.PrivateIdentifier,
name,
toString: () => `#${name}`,
});
exports.privateIdentifier = privateIdentifier;
const decorator = ({ expression, ...other }) => ({
expression,
...other,
type: types_1.AST_NODE_TYPES.Decorator,
toString: () => `@${(0, node_1.node)(expression)}`,
});
exports.decorator = decorator;