From 65075dfeebcb56245f9aaa82dcd3278be342b986 Mon Sep 17 00:00:00 2001 From: Andrey Sitnik Date: Tue, 6 Feb 2024 22:00:37 +0100 Subject: [PATCH 1/2] Allow to pass undefined to adding methods to simplify type check --- lib/container.d.ts | 42 ++++++++++++++++++++++++++++++++++----- lib/container.js | 2 ++ lib/node.d.ts | 6 +++--- test/container.test.ts | 45 ++++++++++++++++++++++++++---------------- 4 files changed, 70 insertions(+), 25 deletions(-) diff --git a/lib/container.d.ts b/lib/container.d.ts index 92961f496..f6835f53a 100644 --- a/lib/container.d.ts +++ b/lib/container.d.ts @@ -66,7 +66,15 @@ declare abstract class Container_ extends Node { * @return This node for methods chain. */ append( - ...nodes: (ChildProps | ChildProps[] | Node | Node[] | string | string[])[] + ...nodes: ( + | ChildProps + | ChildProps[] + | Node + | Node[] + | string + | string[] + | undefined + )[] ): this assign(overrides: Container.ContainerProps | object): this @@ -146,7 +154,14 @@ declare abstract class Container_ extends Node { */ insertAfter( oldNode: Child | number, - newNode: Child | Child[] | ChildProps | ChildProps[] | string | string[] + newNode: + | Child + | Child[] + | ChildProps + | ChildProps[] + | string + | string[] + | undefined ): this /** * Insert new node before old node within the container. @@ -161,7 +176,14 @@ declare abstract class Container_ extends Node { */ insertBefore( oldNode: Child | number, - newNode: Child | Child[] | ChildProps | ChildProps[] | string | string[] + newNode: + | Child + | Child[] + | ChildProps + | ChildProps[] + | string + | string[] + | undefined ): this /** @@ -202,7 +224,15 @@ declare abstract class Container_ extends Node { * @return This node for methods chain. */ prepend( - ...nodes: (ChildProps | ChildProps[] | Node | Node[] | string | string[])[] + ...nodes: ( + | ChildProps + | ChildProps[] + | Node + | Node[] + | string + | string[] + | undefined + )[] ): this /** * Add child to the end of the node. @@ -447,6 +477,8 @@ declare abstract class Container_ extends Node { get last(): Child | undefined } -declare class Container extends Container_ {} +declare class Container< + Child extends Node = ChildNode +> extends Container_ {} export = Container diff --git a/lib/container.js b/lib/container.js index 914c05308..462e3f0d3 100644 --- a/lib/container.js +++ b/lib/container.js @@ -173,6 +173,8 @@ class Container extends Node { normalize(nodes, sample) { if (typeof nodes === 'string') { nodes = cleanSource(parse(nodes).nodes) + } else if (typeof nodes === 'undefined') { + nodes = [] } else if (Array.isArray(nodes)) { nodes = nodes.slice(0) for (let i of nodes) { diff --git a/lib/node.d.ts b/lib/node.d.ts index 71b30159a..597165645 100644 --- a/lib/node.d.ts +++ b/lib/node.d.ts @@ -246,7 +246,7 @@ declare abstract class Node_ { * @param newNode New node. * @return This node for methods chain. */ - after(newNode: Node | Node.ChildProps | Node[] | string): this + after(newNode: Node | Node.ChildProps | Node[] | string | undefined): this /** * It assigns properties to an existing node instance. @@ -273,7 +273,7 @@ declare abstract class Node_ { * @param newNode New node. * @return This node for methods chain. */ - before(newNode: Node | Node.ChildProps | Node[] | string): this + before(newNode: Node | Node.ChildProps | Node[] | string | undefined): this /** * Clear the code style properties for the node and its children. @@ -531,6 +531,6 @@ declare abstract class Node_ { warn(result: Result, message: string, options?: WarningOptions): Warning } -declare class Node extends Node_ { } +declare class Node extends Node_ {} export = Node diff --git a/test/container.test.ts b/test/container.test.ts index 567bda009..0a4b2d69a 100755 --- a/test/container.test.ts +++ b/test/container.test.ts @@ -638,18 +638,18 @@ test('insertBefore() receives array', () => { test('insertBefore() receives pre-existing child node - a', () => { let a = parse('a{ align-items: start; color: red; z-index: 1 }') - let declA = (a.first as Rule).nodes[0]; - let declC = (a.first as Rule).nodes[2]; - declC.before(declA); + let declA = (a.first as Rule).nodes[0] + let declC = (a.first as Rule).nodes[2] + declC.before(declA) is(a.toString(), 'a{ color: red; align-items: start; z-index: 1 }') }) test('insertBefore() receives pre-existing child node - b', () => { let a = parse('a{ align-items: start; color: red; z-index: 1 }') - let declA = (a.first as Rule).nodes[0]; - let declC = (a.first as Rule).nodes[2]; - declA.before(declC); + let declA = (a.first as Rule).nodes[0] + let declC = (a.first as Rule).nodes[2] + declA.before(declC) is(a.toString(), 'a{ z-index: 1; align-items: start; color: red }') }) @@ -708,18 +708,18 @@ test('insertAfter() receives array', () => { test('insertAfter() receives pre-existing child node - a', () => { let a = parse('a{ align-items: start; color: red; z-index: 1 }') - let declA = (a.first as Rule).nodes[0]; - let declC = (a.first as Rule).nodes[2]; - declC.after(declA); + let declA = (a.first as Rule).nodes[0] + let declC = (a.first as Rule).nodes[2] + declC.after(declA) is(a.toString(), 'a{ color: red; z-index: 1; align-items: start }') }) test('insertAfter() receives pre-existing child node - b', () => { let a = parse('a{ align-items: start; color: red; z-index: 1 }') - let declA = (a.first as Rule).nodes[0]; - let declC = (a.first as Rule).nodes[2]; - declA.after(declC); + let declA = (a.first as Rule).nodes[0] + let declC = (a.first as Rule).nodes[2] + declA.after(declC) is(a.toString(), 'a{ align-items: start; z-index: 1; color: red }') }) @@ -874,7 +874,7 @@ test('allows to clone nodes', () => { test('container.nodes can be sorted', () => { let root = parse('@b; @c; @a;') - let b = root.nodes[0]; + let b = root.nodes[0] root.nodes.sort((x, y) => { return (x as AtRule).name.localeCompare((y as AtRule).name) @@ -884,10 +884,10 @@ test('container.nodes can be sorted', () => { is(root.toString(), ' @a;@b; @c;') // Sorted nodes are reflected in "walk". - let result: string[] = []; - root.walkAtRules((atRule) => { + let result: string[] = [] + root.walkAtRules(atRule => { result.push(atRule.name.trim()) - }); + }) is(result.join(' '), 'a b c') @@ -895,8 +895,19 @@ test('container.nodes can be sorted', () => { is(root.index(b), 1) // Inserting after a sorted node results in the correct order. - b.after('@d;'); + b.after('@d;') is(root.toString(), ' @a;@b;@d; @c;') }) +test('ignores undefined on adding', () => { + let rule = parse('a { a: 1; b: 2 }').first as Rule + rule.append({ prop: 'c', value: '3' }, undefined) + rule.prepend(undefined) + rule.insertAfter(0, undefined) + rule.insertBefore(0, undefined) + rule.after(undefined) + rule.before(undefined) + is(rule.parent!.toString(), 'a { a: 1; b: 2; c: 3 }') +}) + test.run() From 5e7449fe7e0186b7318fa85817863a09c8e2b72d Mon Sep 17 00:00:00 2001 From: Andrey Sitnik Date: Tue, 6 Feb 2024 22:20:04 +0100 Subject: [PATCH 2/2] Fix node.parent.nodes type --- lib/at-rule.d.ts | 7 +++++-- lib/container.d.ts | 6 ++++++ lib/declaration.d.ts | 4 ++-- lib/rule.d.ts | 7 +++++-- test/types.ts | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/at-rule.d.ts b/lib/at-rule.d.ts index 3315d1ec5..5bdee38be 100644 --- a/lib/at-rule.d.ts +++ b/lib/at-rule.d.ts @@ -1,4 +1,7 @@ -import Container, { ContainerProps } from './container.js' +import Container, { + ContainerProps, + ContainerWithChildren +} from './container.js' declare namespace AtRule { export interface AtRuleRaws extends Record { @@ -116,7 +119,7 @@ declare class AtRule_ extends Container { * ``` */ params: string - parent: Container | undefined + parent: ContainerWithChildren | undefined raws: AtRule.AtRuleRaws diff --git a/lib/container.d.ts b/lib/container.d.ts index f6835f53a..d16b85d08 100644 --- a/lib/container.d.ts +++ b/lib/container.d.ts @@ -5,6 +5,12 @@ import Node, { ChildNode, ChildProps, NodeProps } from './node.js' import Rule from './rule.js' declare namespace Container { + export class ContainerWithChildren< + Child extends Node = ChildNode + > extends Container_ { + nodes: Child[] + } + export interface ValueOptions { /** * String that’s used to narrow down values and speed up the regexp search. diff --git a/lib/declaration.d.ts b/lib/declaration.d.ts index 655deea58..a5db984a4 100644 --- a/lib/declaration.d.ts +++ b/lib/declaration.d.ts @@ -1,4 +1,4 @@ -import Container from './container.js' +import { ContainerWithChildren } from './container.js' import Node from './node.js' declare namespace Declaration { @@ -79,7 +79,7 @@ declare class Declaration_ extends Node { */ important: boolean - parent: Container | undefined + parent: ContainerWithChildren | undefined /** * The property name for a CSS declaration. diff --git a/lib/rule.d.ts b/lib/rule.d.ts index 8b3db5f1d..fc5dd721a 100644 --- a/lib/rule.d.ts +++ b/lib/rule.d.ts @@ -1,4 +1,7 @@ -import Container, { ContainerProps } from './container.js' +import Container, { + ContainerProps, + ContainerWithChildren +} from './container.js' declare namespace Rule { export interface RuleRaws extends Record { @@ -70,7 +73,7 @@ declare namespace Rule { */ declare class Rule_ extends Container { nodes: NonNullable - parent: Container | undefined + parent: ContainerWithChildren | undefined raws: Rule.RuleRaws /** * The rule’s full selector represented as a string. diff --git a/test/types.ts b/test/types.ts index 4b7cbccf1..316be4ee0 100644 --- a/test/types.ts +++ b/test/types.ts @@ -4,7 +4,7 @@ const plugin: PluginCreator = prop => { return { Declaration: (decl, { Comment, result }) => { if (decl.prop === prop) { - decl.warn(result, `${decl.prop} found`) + decl.warn(result, `${decl.prop} found in ${decl.parent?.nodes.length}`) decl.replaceWith(new Comment({ text: `${decl.prop} removed` })) } },