Repository: rollup/rollup
File: src/ast/nodes/shared/Node.ts (TypeScript)

Code before:
	shouldBeIncluded(context: InclusionContext): boolean;
}

export type StatementNode = Node;

export const IS_SKIPPED_CHAIN = Symbol('IS_SKIPPED_CHAIN');
export type SkippedChain = typeof IS_SKIPPED_CHAIN;

export interface ExpressionNode extends ExpressionEntity, Node, Partial<ChainElement> {}

export interface ChainElement {
	getLiteralValueAtPathAsChainElement(
		path: ObjectPath,
		recursionTracker: EntityPathTracker,
		origin: DeoptimizableEntity
	): LiteralValueOrUnknown | SkippedChain;
	hasEffectsAsChainElement(context: HasEffectsContext): boolean | SkippedChain;
}

export class NodeBase extends ExpressionEntity implements ExpressionNode {
	declare annotations?: readonly RollupAnnotation[];
	end!: number;
	parent: Node | { context: AstContext; type: string };
	scope!: ChildScope;
	start!: number;
	type!: keyof typeof NodeType;
	/**
	 * This will be populated during initialise if setAssignedValue is called.
	 */
	protected assignmentInteraction!: NodeInteractionAssigned;

	/**
	 * Nodes can apply custom deoptimizations once they become part of the
	 * executed code. To do this, they must initialize this as false, implement
	 * applyDeoptimizations and call this from include and hasEffects if they have
	 * custom handlers
	 */
	protected get deoptimized(): boolean {
		return isFlagSet(this.flags, Flag.deoptimized);
	}

	protected set deoptimized(value: boolean) {
		this.flags = setFlag(this.flags, Flag.deoptimized, value);
	}

	constructor(parent: Node | { context: AstContext; type: string }, parentScope: ChildScope) {
		super();
		this.parent = parent;
		this.scope = parentScope;
		this.createScope(parentScope);
	}

Review comment (bug):
`NodeBase.parseNode` skips copying any ESTree properties that already exist as own-properties (`if (this.hasOwnProperty(key)) continue;`). Switching these core fields (`start/end/type/scope`) from `declare` to real class fields makes them own-properties (initialized to `undefined`), so `parseNode` will skip them and nodes will end up with `start/end/type` etc. unset. This will break parsing and all downstream logic. Keep these as `declare` (type-only) or adjust `parseNode` to only skip when a property has been explicitly set (e.g. descriptor exists and value !== undefined) and ensure any prototype flags/getters still work.

Code after:
	shouldBeIncluded(context: InclusionContext): boolean;
}

export type StatementNode = Node;

export const IS_SKIPPED_CHAIN = Symbol('IS_SKIPPED_CHAIN');
export type SkippedChain = typeof IS_SKIPPED_CHAIN;

export interface ExpressionNode extends ExpressionEntity, Node, Partial<ChainElement> {}

export interface ChainElement {
	getLiteralValueAtPathAsChainElement(
		path: ObjectPath,
		recursionTracker: EntityPathTracker,
		origin: DeoptimizableEntity
	): LiteralValueOrUnknown | SkippedChain;
	hasEffectsAsChainElement(context: HasEffectsContext): boolean | SkippedChain;
}

export class NodeBase extends ExpressionEntity implements ExpressionNode {
	declare annotations?: readonly RollupAnnotation[];
	declare end: number;
	parent: Node | { context: AstContext; type: string };
	declare scope: ChildScope;
	declare start: number;
	declare type: keyof typeof NodeType;
	/**
	 * This will be populated during initialise if setAssignedValue is called.
	 */
	declare protected assignmentInteraction: NodeInteractionAssigned;

	/**
	 * Nodes can apply custom deoptimizations once they become part of the
	 * executed code. To do this, they must initialize this as false, implement
	 * applyDeoptimizations and call this from include and hasEffects if they have
	 * custom handlers
	 */
	protected get deoptimized(): boolean {
		return isFlagSet(this.flags, Flag.deoptimized);
	}

	protected set deoptimized(value: boolean) {
		this.flags = setFlag(this.flags, Flag.deoptimized, value);
	}

	constructor(parent: Node | { context: AstContext; type: string }, parentScope: ChildScope) {
		super();
		this.parent = parent;
		this.scope = parentScope;
		this.createScope(parentScope);
	}