Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Formatted with prettier #2972

Open
wants to merge 4 commits into
base: prettier
Choose a base branch
from
Open

Conversation

mondoreale
Copy link
Contributor

@mondoreale mondoreale commented Jan 13, 2025

Summary

Format all the codes using Prettier and its refreshed config from prettier branch.

@teogeb, attached is the result of npm run eslint with indent enabled.

I've tweaked the branch so that it's the only outstanding rule we have to deal with.

(Note that in this PR the indent rule is on.)

Cheers!

Changes

  • Auto-formatted all the files.
  • Adjusted selected eslint rules to match Prettier's behaviour:
    • Enabled avoidEscape on the quotes rule.
    • Changed @stylistic/member-delimiter-style to semi for singleline types and interfaces (semi is typescript's default, too).

Limitations and future improvements

  • n/a

Checklist before requesting a review

  • Is this a breaking change? If it is, be clear in summary.
  • Read through code myself one more time.
  • Make sure any and all TODO comments left behind are meant to be left in.
  • Has reasonable passing test coverage?
  • Updated changelog if applicable.
  • Updated documentation if applicable.

All most of them needed was a trivial rearrangement.
@mondoreale mondoreale requested review from harbu and teogeb January 13, 2025 12:06
@github-actions github-actions bot added network Related to Network Package test-utils Related to Test Utils Package cli-tools Related to CLI Tools Package dht Related to DHT package utils proto-rpc sdk node labels Jan 13, 2025
@mondoreale mondoreale changed the title Formatted with prettier chore: Formatted with prettier Jan 13, 2025
@mondoreale mondoreale marked this pull request as ready for review January 13, 2025 15:05
- The `brace-style` eslint rule is redundant and yet conflicting.
- 120 requested by Eric.
Copy link
Contributor Author

@mondoreale mondoreale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harbu @teogeb I've reformatted the files using 120 long print width. It definitely makes certain things look much better.

That it() formatted differently is a result of passing 3rd argument to it, i.e. it(label, fn, TIMEOUT). It then places the label in its own line. Pinned down one example of it. It'll be applied consistently to all similar scenarios if needed.

Comment on lines +27 to +37
it(
'invalid value',
async () => {
const outputLines = await runCommand('stream show foobar --env invalid-environment', {
devEnvironment: false
})
expect(outputLines).toHaveLength(1)
expect(outputLines[0]).toEqual('env must be one of: "polygon", "polygonAmoy", "dev2"')
},
60 * 1000
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3rd argument (the timeout) forces Prettier to put the label onto its own line.

start: now - interval,
end: now
scheduleAtFixedRate(
async (now: number) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why this was put on a newline (but not the arguments for the callbacks below). Is it because of the async keyword?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the presence of the 2nd and the 3rd arguments given to scheduleAtFixedRate.

scheduleAtFixedRate(
    async (now: number) => { /* code */ },
    interval,
    abortSignal
)

Comment on lines +23 to +27
if (firstSlashIdx === -1) {
// legacy format
return streamIdOrPath as StreamID
} else if (firstSlashIdx === 0) { // path-only format
} else if (firstSlashIdx === 0) {
// path-only format
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this comment enforcement. I think with comments there should be freedom to put them wherever to make it very clear what is reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't touch a comment placed immediately after a statement, e.g.

if (firstSlashIdx === -1) {
    return streamIdOrPath as StreamID // something here about what just happened
}

To me placing a comment immediately after { feels a bit strange, generally, so I'm for this behaviour. FYI.

Comment on lines +88 to +92
return withTimeout(Promise.race(promises.map((promise) => promise.task)), timeout, 'raceEvents3').finally(
() => {
cancelAll()
}
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot going on in this statement. I sort of prefer the "before" here due to the complexity being broken down into lines.

Copy link
Contributor Author

@mondoreale mondoreale Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those long lines aren't ideal. Maybe smaller print width?

On the other hand... raceEvents3 could be organically async making it possible to restructure it like so:

export async function raceEvents3<T extends EventEmitter.ValidEventTypes>(
    emitter: EventEmitter<T>,
    eventNames: (keyof T)[],
    timeout: number | null = 5000
): Promise<RunAndRaceEventsReturnType<T>> {
    // …

    try {
        const taskRace = Promise.race(promises.map((promise) => promise.task))

        if (timeout !== null) {
            return await withTimeout(taskRace, timeout, 'raceEvents3')
        }

        return await taskRace
    } finally {
        promises.forEach((promise) => {
            promise.cancel()
        })
    }
}

So it's just possible if it was new code this formatting issue simply wouldn't occur.

const expected = typeof _streamr_electron_test === 'undefined'
? 'Logger.test '
: 'Logger.test'
const expected = typeof _streamr_electron_test === 'undefined' ? 'Logger.test ' : 'Logger.test'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Special case but here previous formatting is superior

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Maybe if someone wrote it *now* (accompanied by a formatter) they'd do something like this instead:

describe('name', () => {
    function pad(str: string) {
        return typeof _streamr_electron_test === 'undefined' ? str.padEnd(25) : str
    }

    it('short', () => {
        expect(Logger.createName(module)).toBe(pad('Logger.test'))
    })

    it('application id', () => {
        process.env.STREAMR_APPLICATION_ID = 'APP'
        expect(Logger.createName(module)).toBe(pad('APP:Logger.test'))
        delete process.env.STREAMR_APPLICATION_ID
    })

    it('index', () => {
        expect(
            Logger.createName({
                id: ['foo', 'bar', 'mock', 'index'].join(path.sep)
            } as any)
        ).toBe(pad('mock'))
    })
})

Comment on lines +95 to +96
const expected =
typeof _streamr_electron_test === 'undefined' ? 'APP:Logger.test ' : 'APP:Logger.test'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Special case but here previous formatting is superior

serviceId: streamPartId
})
)
otherContentDeliveryLayerNodes = range(otherNodeCount).map((i) =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistency with parameter list line break...

until(() => receivedMessageCount === 1, 10000),
nodes[1].broadcast(streamMessage)
])
await Promise.all([until(() => receivedMessageCount === 1, 10000), nodes[1].broadcast(streamMessage)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo too much going on in one line

"generated/**/*",
"test/**/*"
]
"include": ["src/**/*", "generated/**/*", "test/**/*"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of like the earlier, ngl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe one can simply write

["src", "generated", "test"]

A matter of preference. I don't mind so I'd go with the flow.

Comment on lines +7 to +8
"include": ["src/**/*", "generated/**/*", "test/**/*", "package.json"],
"references": [{ "path": "../proto-rpc/tsconfig.node.json" }, { "path": "../dht/tsconfig.node.json" }]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't find this as redable..

Comment on lines +2 to +8
"extends": "../../tsconfig.node.json",
"compilerOptions": {
"outDir": "dist",
"noImplicitOverride": false
},
"include": ["src/**/*", "generated/**/*", "test/benchmark/first-message.ts", "test/utils/utils.ts", "package.json"],
"references": [{ "path": "../dht/tsconfig.node.json" }]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't find this as readable

Copy link
Contributor Author

@mondoreale mondoreale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harbu thanks for the commentary.

I agree with a lot of it. Having said that, I believe that with a consistent formatter in place most of what's there would've been written differently.

Left a couple of notes on your notes.

Cheers!

start: now - interval,
end: now
scheduleAtFixedRate(
async (now: number) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the presence of the 2nd and the 3rd arguments given to scheduleAtFixedRate.

scheduleAtFixedRate(
    async (now: number) => { /* code */ },
    interval,
    abortSignal
)

Comment on lines +23 to +27
if (firstSlashIdx === -1) {
// legacy format
return streamIdOrPath as StreamID
} else if (firstSlashIdx === 0) { // path-only format
} else if (firstSlashIdx === 0) {
// path-only format
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't touch a comment placed immediately after a statement, e.g.

if (firstSlashIdx === -1) {
    return streamIdOrPath as StreamID // something here about what just happened
}

To me placing a comment immediately after { feels a bit strange, generally, so I'm for this behaviour. FYI.

Comment on lines +88 to +92
return withTimeout(Promise.race(promises.map((promise) => promise.task)), timeout, 'raceEvents3').finally(
() => {
cancelAll()
}
)
Copy link
Contributor Author

@mondoreale mondoreale Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those long lines aren't ideal. Maybe smaller print width?

On the other hand... raceEvents3 could be organically async making it possible to restructure it like so:

export async function raceEvents3<T extends EventEmitter.ValidEventTypes>(
    emitter: EventEmitter<T>,
    eventNames: (keyof T)[],
    timeout: number | null = 5000
): Promise<RunAndRaceEventsReturnType<T>> {
    // …

    try {
        const taskRace = Promise.race(promises.map((promise) => promise.task))

        if (timeout !== null) {
            return await withTimeout(taskRace, timeout, 'raceEvents3')
        }

        return await taskRace
    } finally {
        promises.forEach((promise) => {
            promise.cancel()
        })
    }
}

So it's just possible if it was new code this formatting issue simply wouldn't occur.

const expected = typeof _streamr_electron_test === 'undefined'
? 'Logger.test '
: 'Logger.test'
const expected = typeof _streamr_electron_test === 'undefined' ? 'Logger.test ' : 'Logger.test'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Maybe if someone wrote it *now* (accompanied by a formatter) they'd do something like this instead:

describe('name', () => {
    function pad(str: string) {
        return typeof _streamr_electron_test === 'undefined' ? str.padEnd(25) : str
    }

    it('short', () => {
        expect(Logger.createName(module)).toBe(pad('Logger.test'))
    })

    it('application id', () => {
        process.env.STREAMR_APPLICATION_ID = 'APP'
        expect(Logger.createName(module)).toBe(pad('APP:Logger.test'))
        delete process.env.STREAMR_APPLICATION_ID
    })

    it('index', () => {
        expect(
            Logger.createName({
                id: ['foo', 'bar', 'mock', 'index'].join(path.sep)
            } as any)
        ).toBe(pad('mock'))
    })
})

@@ -13,9 +13,15 @@ export interface DiscoveryLayerNodeEvents {
export interface DiscoveryLayerNode {
on<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
on<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: () => void): void
off<T extends keyof DiscoveryLayerNodeEvents>(eventName: T, listener: (peerDescriptor: PeerDescriptor) => void): void
off<T extends keyof DiscoveryLayerNodeEvents>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's the most annoying part. Prettier doesn't make sibling nodes consistent with each other.

No way around it.

return values
.filter(([id]) => !exclude.includes(id))
.map(([_id, node]) => node)
return values.filter(([id]) => !exclude.includes(id)).map(([_id, node]) => node)
Copy link
Contributor Author

@mondoreale mondoreale Jan 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, old one reads better.

On the other hand, chaining tends to get ugly quickly, and very often is also a waste of processing effort. Here for example you don't need multiple passes. Consider this

const getValuesOfIncludedKeys = (
    nodes: Map<DhtAddress, ContentDeliveryRpcRemote>,
    exclude: DhtAddress[],
    wsOnly = false
): ContentDeliveryRpcRemote[] => {
    const remotes: ContentDeliveryRpcRemote[] = []

    for (const [id, node] of nodes.entries()) {
        if (wsOnly && node.getPeerDescriptor().websocket === undefined) {
            continue
        }

        if (exclude.includes(id)) {
            continue
        }

        remotes.push(node)
    }

    return remotes
}

Few benefits

  • Single pass
  • No extra array allocation (Array.from)
  • Fairly clean
  • Very little room for the formatter to mess up

Plus, to me, it feels much cleaner.

Comment on lines +113 to +118
expect(proxiedNode.stack.getContentDeliveryManager().getStreamPartDelivery(proxiedStreamPart)!.proxied).toBe(
true
)
expect(proxiedNode.stack.getContentDeliveryManager().getStreamPartDelivery(regularStreamPart1)!.proxied).toBe(
false
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DRY-ing it up a little bit could help but yeah, I'm with you.

Comment on lines +60 to +64
const client = (
proxiedNode.stack.getContentDeliveryManager().getStreamPartDelivery(STREAM_PART_ID) as {
client: ProxyClient
}
).client
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd throw a rough guess that the type casting doesn't help. 😉

"generated/**/*",
"test/**/*"
]
"include": ["src/**/*", "generated/**/*", "test/**/*"]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe one can simply write

["src", "generated", "test"]

A matter of preference. I don't mind so I'd go with the flow.

@harbu
Copy link
Contributor

harbu commented Jan 16, 2025

So I did a bit of a readthru & analysis of the prettier formatting.

  • I went thru all the code in packages utils and trackeless-network .
  • I excluded changes related to white space, trailing commas, and interface/type semicolon (those can easily be enforced with any tool)
  • For the remaining changes, I categorized and tallied them into the three categories based on readability
    - Good: change improved readability
    - Neutral: change neither improved or worsened readability
    - Bad: change worsened readability

So for the scores:
utils

Good 24 (23%)
Neutral 74 (70%)
Bad 7 (7%)

trackerless-network

Good 51 (19%)
Neutral 198 (74%)
Bad 19 (7%)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cli-tools Related to CLI Tools Package dht Related to DHT package network Related to Network Package node proto-rpc sdk test-utils Related to Test Utils Package utils
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants