@dcl/sdk-commands 7.0.0-4293371227.commit-54082c6 → 7.0.0-4294380152.commit-0d08b10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/components/analytics.js +5 -1
  2. package/package.json +6 -3
  3. package/src/commands/build/index.ts +0 -77
  4. package/src/commands/export-static/index.ts +0 -150
  5. package/src/commands/init/index.ts +0 -68
  6. package/src/commands/init/repos.ts +0 -17
  7. package/src/commands/start/index.ts +0 -221
  8. package/src/commands/start/server/endpoints.ts +0 -471
  9. package/src/commands/start/server/file-watch-notifier.ts +0 -45
  10. package/src/commands/start/server/realm.ts +0 -63
  11. package/src/commands/start/server/routes.ts +0 -36
  12. package/src/commands/start/server/ws.ts +0 -24
  13. package/src/commands/start/types.ts +0 -26
  14. package/src/components/analytics.ts +0 -92
  15. package/src/components/dcl-info-config.ts +0 -63
  16. package/src/components/eth.ts +0 -3
  17. package/src/components/fetch.ts +0 -11
  18. package/src/components/fs.ts +0 -62
  19. package/src/components/index.ts +0 -26
  20. package/src/components/log.ts +0 -48
  21. package/src/index.ts +0 -90
  22. package/src/logic/args.ts +0 -19
  23. package/src/logic/beautiful-logs.ts +0 -26
  24. package/src/logic/catalyst-requests.ts +0 -31
  25. package/src/logic/commands.ts +0 -28
  26. package/src/logic/config.ts +0 -45
  27. package/src/logic/coordinates.ts +0 -95
  28. package/src/logic/dcl-ignore.ts +0 -50
  29. package/src/logic/error.ts +0 -1
  30. package/src/logic/exec.ts +0 -36
  31. package/src/logic/fs.ts +0 -76
  32. package/src/logic/get-free-port.ts +0 -15
  33. package/src/logic/project-files.ts +0 -92
  34. package/src/logic/project-validations.ts +0 -61
  35. package/src/logic/realm.ts +0 -28
  36. package/src/logic/scene-validations.ts +0 -81
  37. package/tsconfig.json +0 -28
@@ -1,92 +0,0 @@
1
- import { v4 as uuidv4 } from 'uuid'
2
- import { Analytics } from '@segment/analytics-node'
3
- import future from 'fp-future'
4
-
5
- import { CliComponents } from '.'
6
- import { colors } from './log'
7
-
8
- export type IAnalyticsComponent = {
9
- get(): Analytics
10
- identify(): Promise<void>
11
- track<T extends keyof Events>(eventName: T, eventProps: Events[T]): Promise<void>
12
- }
13
-
14
- type Events = {
15
- 'Scene created': {
16
- projectType: string
17
- url: string
18
- }
19
- 'Preview started': {
20
- projectHash: string
21
- coords: { x: number; y: number }
22
- isWorkspace: boolean
23
- }
24
- 'Build scene': {
25
- projectHash: string
26
- coords: { x: number; y: number }
27
- isWorkspace: boolean
28
- }
29
- 'Export static': {
30
- projectHash: string
31
- coords: { x: number; y: number }
32
- }
33
- }
34
-
35
- export async function createAnalyticsComponent({
36
- dclInfoConfig
37
- }: Pick<CliComponents, 'dclInfoConfig'>): Promise<IAnalyticsComponent> {
38
- const USER_ID = 'sdk-commands-user'
39
- const config = await dclInfoConfig.get()
40
- const analytics: Analytics = new Analytics({ writeKey: config.segmentKey ?? '' })
41
- return {
42
- get() {
43
- return analytics
44
- },
45
- async identify() {
46
- if (!config.userId) {
47
- console.log(
48
- `Decentraland CLI sends anonymous usage stats to improve their products, if you want to disable it change the configuration at ${colors.bold(
49
- '~/.dclinfo'
50
- )}\n`
51
- )
52
-
53
- const userId = uuidv4()
54
- await dclInfoConfig.updateDCLInfo({ userId, trackStats: true })
55
- analytics.identify({
56
- userId: USER_ID,
57
- traits: {
58
- devId: userId,
59
- createdAt: new Date()
60
- }
61
- })
62
- }
63
- },
64
- async track<T extends keyof Events>(eventName: T, eventProps: Events[T]) {
65
- const trackFuture = future<void>()
66
- const { userId, trackStats } = await dclInfoConfig.get()
67
- if (!trackStats) {
68
- return trackFuture.resolve()
69
- }
70
- const trackInfo = {
71
- userId: USER_ID,
72
- event: eventName,
73
- properties: {
74
- ...eventProps,
75
- os: process.platform,
76
- nodeVersion: process.version,
77
- cliVersion: await dclInfoConfig.getVersion(),
78
- isCI: dclInfoConfig.isCI(),
79
- isEditor: dclInfoConfig.isEditor(),
80
- devId: userId
81
- }
82
- }
83
- analytics.track(trackInfo, () => {
84
- trackFuture.resolve()
85
- })
86
- if (!dclInfoConfig.isProduction()) {
87
- console.log(trackInfo)
88
- }
89
- return trackFuture
90
- }
91
- }
92
- }
@@ -1,63 +0,0 @@
1
- import path, { resolve } from 'path'
2
- import { CliComponents } from '.'
3
- import { DCLInfo, getDCLInfoConfig, getDclInfoPath, getEnvConfig } from '../logic/config'
4
- import { readJSON, writeJSON } from '../logic/fs'
5
-
6
- export type IDCLInfoConfigComponent = {
7
- get(): Promise<DCLInfo>
8
- updateDCLInfo(value: Partial<DCLInfo>): Promise<Partial<DCLInfo>>
9
- getVersion(): Promise<string>
10
- isCI(): boolean
11
- isEditor(): boolean
12
- isProduction(): boolean
13
- }
14
-
15
- export async function createDCLInfoConfigComponent({
16
- fs
17
- }: Pick<CliComponents, 'fs'>): Promise<IDCLInfoConfigComponent> {
18
- function isProduction() {
19
- return process.env.NODE_ENV === 'production' || __filename.includes('node_modules')
20
- }
21
- const defaultConfig: Partial<DCLInfo> = {
22
- userId: '',
23
- trackStats: false,
24
- segmentKey: isProduction() ? 'sFdziRVDJo0taOnGzTZwafEL9nLIANZ3' : 'mjCV5Dc4VAKXLJAH5g7LyHyW1jrIR3to'
25
- }
26
-
27
- return {
28
- async get() {
29
- const dclInfoConfig = await getDCLInfoConfig({ fs })
30
- const envConfig = getEnvConfig()
31
- const config = { ...defaultConfig, ...dclInfoConfig, ...envConfig }
32
- return config
33
- },
34
- updateDCLInfo(value: Partial<DCLInfo>) {
35
- return writeJSON({ fs }, getDclInfoPath(), value)
36
- },
37
- async getVersion() {
38
- try {
39
- /* istanbul ignore next */
40
- const sdkPath = path.dirname(
41
- require.resolve('@dcl/sdk/package.json', {
42
- paths: [process.cwd()]
43
- })
44
- )
45
- /* istanbul ignore next */
46
- const packageJson = await readJSON<{ version: string }>({ fs }, resolve(sdkPath, 'package.json'))
47
- /* istanbul ignore next */
48
- return packageJson.version ?? 'unknown'
49
- } catch (e) {
50
- return 'unknown'
51
- }
52
- },
53
- isEditor() {
54
- return process.env.EDITOR === 'true'
55
- },
56
- isCI() {
57
- return process.env.CI === 'true' || process.argv.includes('--ci') || process.argv.includes('--c')
58
- },
59
- isProduction() {
60
- return isProduction()
61
- }
62
- }
63
- }
@@ -1,3 +0,0 @@
1
- import { HTTPProvider } from 'eth-connect'
2
-
3
- export const providerInstance = new HTTPProvider('https://rpc.decentraland.org/mainnet?project=sdk-commands')
@@ -1,11 +0,0 @@
1
- import * as undici from 'undici'
2
-
3
- export type IFetchComponent = {
4
- fetch(url: string, init?: undici.RequestInit): Promise<undici.Response>
5
- }
6
-
7
- export function createFetchComponent(): IFetchComponent {
8
- return {
9
- fetch: undici.fetch
10
- }
11
- }
@@ -1,62 +0,0 @@
1
- import * as fs from 'fs'
2
- import * as fsPromises from 'fs/promises'
3
-
4
- /**
5
- * @public
6
- *
7
- * This may be moved to well-known-components in the future
8
- */
9
- export type IFileSystemComponent = Pick<typeof fs, 'createReadStream'> &
10
- Pick<typeof fs, 'createWriteStream'> &
11
- Pick<
12
- typeof fsPromises,
13
- 'access' | 'opendir' | 'stat' | 'unlink' | 'mkdir' | 'readFile' | 'writeFile' | 'rename' | 'rmdir'
14
- > & {
15
- constants: Pick<typeof fs.constants, 'F_OK' | 'R_OK'>
16
- } & {
17
- fileExists(path: string): Promise<boolean>
18
- directoryExists(path: string): Promise<boolean>
19
- readdir(path: string): Promise<string[]>
20
- }
21
-
22
- async function fileExists(path: string): Promise<boolean> {
23
- try {
24
- await fs.promises.access(path, fs.constants.F_OK | fs.constants.R_OK)
25
- return true
26
- } catch (error) {
27
- return false
28
- }
29
- }
30
- async function directoryExists(path: string): Promise<boolean> {
31
- try {
32
- return (await fs.promises.lstat(path)).isDirectory()
33
- } catch (error) {
34
- return false
35
- }
36
- }
37
-
38
- /**
39
- * @public
40
- */
41
- export function createFsComponent(): IFileSystemComponent {
42
- return {
43
- createReadStream: fs.createReadStream,
44
- createWriteStream: fs.createWriteStream,
45
- access: fsPromises.access,
46
- writeFile: fsPromises.writeFile,
47
- opendir: fsPromises.opendir,
48
- stat: fsPromises.stat,
49
- unlink: fsPromises.unlink,
50
- mkdir: fsPromises.mkdir,
51
- rmdir: fsPromises.rmdir,
52
- readdir: fsPromises.readdir,
53
- readFile: fsPromises.readFile,
54
- constants: {
55
- F_OK: fs.constants.F_OK,
56
- R_OK: fs.constants.R_OK
57
- },
58
- rename: fsPromises.rename,
59
- fileExists,
60
- directoryExists
61
- }
62
- }
@@ -1,26 +0,0 @@
1
- import { ILoggerComponent } from '@well-known-components/interfaces'
2
- import { createAnalyticsComponent, IAnalyticsComponent } from './analytics'
3
- import { createDCLInfoConfigComponent, IDCLInfoConfigComponent } from './dcl-info-config'
4
- import { createFetchComponent, IFetchComponent } from './fetch'
5
- import { createFsComponent, IFileSystemComponent } from './fs'
6
- import { createStdoutCliLogger } from './log'
7
-
8
- export type CliComponents = {
9
- fs: IFileSystemComponent
10
- fetch: IFetchComponent
11
- logger: ILoggerComponent.ILogger
12
- dclInfoConfig: IDCLInfoConfigComponent
13
- analytics: IAnalyticsComponent
14
- }
15
-
16
- export async function initComponents(): Promise<CliComponents> {
17
- const fsComponent = createFsComponent()
18
- const dclInfoConfig = await createDCLInfoConfigComponent({ fs: fsComponent })
19
- return {
20
- fs: fsComponent,
21
- fetch: createFetchComponent(),
22
- logger: createStdoutCliLogger(),
23
- dclInfoConfig,
24
- analytics: await createAnalyticsComponent({ dclInfoConfig })
25
- }
26
- }
@@ -1,48 +0,0 @@
1
- import { ILoggerComponent } from '@well-known-components/interfaces'
2
- import { createColors } from 'colorette'
3
- import { CliError } from '../logic/error'
4
-
5
- /**
6
- * This file imitates "cargo" logs. The words are aligned with the colon like this:
7
- * V
8
- * Error: some text provided as argumen
9
- * Info: some text provided as argumen
10
- * Success: some text provided as argumen
11
- * Warning: some text provided as argumen
12
- * ^
13
- */
14
-
15
- const stderr = (...parameters: readonly unknown[]) => {
16
- process.stderr.write(`${parameters.filter(($) => $ !== undefined).join('')}\n`)
17
- }
18
-
19
- // @see https://no-color.org
20
- // @see https://www.npmjs.com/package/chalk
21
- export const colors = createColors({
22
- useColor: process.env.FORCE_COLOR !== '0' && !process.env.NO_COLOR
23
- })
24
-
25
- export function createStdoutCliLogger(): ILoggerComponent.ILogger {
26
- return {
27
- log(message, extra) {
28
- stderr(message, extra && JSON.stringify(extra))
29
- },
30
- debug(message, extra) {
31
- stderr(colors.blueBright('debug: '), message, extra && JSON.stringify(extra))
32
- },
33
- error(error, extra) {
34
- stderr(colors.redBright('error: '), error, extra && JSON.stringify(extra))
35
- /* istanbul ignore next */
36
- if (!(error instanceof CliError) || process.env.DEBUG) {
37
- // print the stacktrace if it is not a CliError
38
- console.error(error)
39
- }
40
- },
41
- info(message, extra) {
42
- stderr(colors.blueBright('info: '), message, extra && JSON.stringify(extra))
43
- },
44
- warn(message, extra) {
45
- stderr(colors.yellow('warning: '), message, extra && JSON.stringify(extra))
46
- }
47
- }
48
- }
package/src/index.ts DELETED
@@ -1,90 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /* istanbul ignore file */
4
-
5
- import { getArgs } from './logic/args'
6
- import { CliError } from './logic/error'
7
- import { COMMANDS_PATH, getCommands } from './logic/commands'
8
- import { CliComponents, initComponents } from './components'
9
- import { printCommand } from './logic/beautiful-logs'
10
- import { colors } from './components/log'
11
-
12
- export interface Options {
13
- args: ReturnType<typeof getArgs>
14
- components: CliComponents
15
- }
16
-
17
- // leaving args as "any" since we don't know yet if we will use them
18
- type FileFn = (options: Options) => Promise<void>
19
-
20
- interface FileExports {
21
- help?: FileFn
22
- main?: FileFn
23
- args?: ReturnType<typeof getArgs>
24
- }
25
-
26
- const listCommandsStr = (commands: string[]) => commands.map(($) => `\t *sdk-commands ${$} \n`).join('')
27
-
28
- const commandFnsAreValid = (fns: FileExports): fns is Required<FileExports> => {
29
- const { help, main } = fns
30
- if (!help || !main) {
31
- throw new CliError(`Command does not follow implementation rules:
32
- * Requires a "help" function
33
- * Requires a "main" function
34
- `)
35
- }
36
- return true
37
- }
38
-
39
- async function main() {
40
- const helpMessage = (commands: string[]) => `Here is the list of commands:\n${listCommandsStr(commands)}`
41
- const args = getArgs()
42
- const command = process.argv[2]
43
- const needsHelp = args['--help']
44
- const components: CliComponents = await initComponents()
45
-
46
- const commands = await getCommands(components)
47
-
48
- if (!commands.includes(command)) {
49
- if (needsHelp) {
50
- components.logger.info(helpMessage(commands))
51
- return
52
- }
53
- throw new CliError(`Command ${command} is invalid. ${helpMessage(commands)}`)
54
- }
55
- // eslint-disable-next-line @typescript-eslint/no-var-requires
56
- const cmd = require(`${COMMANDS_PATH}/${command}`)
57
-
58
- if (commandFnsAreValid(cmd)) {
59
- await components.analytics.identify()
60
- const options = { args: cmd.args, components }
61
- if (needsHelp) {
62
- await cmd.help(options)
63
- } else {
64
- printCommand(components.logger, command)
65
-
66
- const ret = await cmd.main(options)
67
- // print the result of the evaluation as json in the standard output
68
- if (cmd.args['--json']) {
69
- process.stdout.write(JSON.stringify(ret, null, 2))
70
- }
71
- }
72
- }
73
-
74
- // rollup watcher leaves many open FSWatcher even in build mode. we must call
75
- // process.exit at this point to prevent the program halting forever
76
- process.exit(process.exitCode || 0)
77
- }
78
-
79
- main().catch(function handleError(err: Error) {
80
- if (err instanceof CliError) {
81
- console.error(colors.redBright('Error: ') + err.message)
82
- } else {
83
- // log with console to show stacktrace and debug information
84
- console.error(err)
85
- console.warn(`Developer: All errors thrown must be an instance of "CliError"`)
86
- }
87
-
88
- // set an exit code but not finish the program immediately to close any pending work
89
- process.exitCode = 1
90
- })
package/src/logic/args.ts DELETED
@@ -1,19 +0,0 @@
1
- import arg, { Result } from 'arg'
2
-
3
- export type Args = {
4
- [key: string]: string | StringConstructor | NumberConstructor | BooleanConstructor
5
- }
6
-
7
- // updating to TS 4.9 will prevent losing types when
8
- // enforcing type to be "Args" by using "satisfies Args"
9
- export const DEFAULT_ARGS = {
10
- '--help': Boolean,
11
- '--json': Boolean,
12
- '-h': '--help'
13
- }
14
-
15
- export function getArgs(): Result<typeof DEFAULT_ARGS>
16
- export function getArgs<T extends Args>(args: T): Result<typeof DEFAULT_ARGS & T>
17
- export function getArgs<T extends Args>(args?: T) {
18
- return arg({ ...DEFAULT_ARGS, ...args }, { permissive: true })
19
- }
@@ -1,26 +0,0 @@
1
- import { ILoggerComponent } from '@well-known-components/interfaces'
2
- import { colors } from '../components/log'
3
-
4
- // eslint-disable-next-line @typescript-eslint/no-var-requires
5
- const { name, version } = require('../../package.json')
6
-
7
- export function printProgressStep(logger: ILoggerComponent.ILogger, log: string, currentStep: number, maxStep: number) {
8
- logger.log(colors.dim(`[${currentStep}/${maxStep}]`) + ' ' + log)
9
- }
10
-
11
- export function printProgressInfo(logger: ILoggerComponent.ILogger, log: string) {
12
- logger.log(colors.dim(log))
13
- }
14
-
15
- export function printCommand(logger: ILoggerComponent.ILogger, commandName: string) {
16
- logger.log(colors.bold(`${name} ${commandName} v${version}`))
17
- }
18
-
19
- export function printSuccess(logger: ILoggerComponent.ILogger, operationSuccessfulMessage: string, summary: string) {
20
- // print a space before the success callout
21
- logger.log('')
22
- logger.log(colors.greenBright(operationSuccessfulMessage))
23
- if (typeof summary === 'string') {
24
- logger.log(summary)
25
- }
26
- }
@@ -1,31 +0,0 @@
1
- import { Entity } from '@dcl/schemas'
2
- import { fetch } from 'undici'
3
-
4
- export async function fetchEntityByPointer(
5
- baseUrl: string,
6
- pointers: string[]
7
- ): Promise<{
8
- baseUrl: string
9
- deployments: Entity[]
10
- }> {
11
- if (pointers.length === 0)
12
- return {
13
- baseUrl,
14
- deployments: []
15
- }
16
-
17
- const activeEntities = baseUrl + '/content/entities/active'
18
-
19
- const response = await fetch(activeEntities, {
20
- method: 'post',
21
- headers: { 'content-type': 'application/json', connection: 'close' },
22
- body: JSON.stringify({ pointers })
23
- })
24
-
25
- const deployments: Entity[] = response.ok ? ((await response.json()) as Entity[]) : []
26
-
27
- return {
28
- baseUrl,
29
- deployments
30
- }
31
- }
@@ -1,28 +0,0 @@
1
- import { resolve } from 'path'
2
- import { CliComponents } from '../components'
3
- import { CliError } from './error'
4
-
5
- export const COMMANDS_PATH = resolve(__dirname, '../commands')
6
-
7
- export async function getCommands({ fs }: Pick<CliComponents, 'fs'>): Promise<string[]> {
8
- const commandDirs = await fs.readdir(COMMANDS_PATH)
9
-
10
- const commands = commandDirs.map(async (dir) => {
11
- const path = resolve(COMMANDS_PATH, dir)
12
-
13
- const statDir = await fs.stat(path)
14
-
15
- if (!statDir.isDirectory()) {
16
- throw new CliError('Developer: All commands must be inside a folder')
17
- }
18
-
19
- const statIndex = await fs.stat(`${path}/index.js`)
20
- if (!statIndex.isFile()) {
21
- throw new CliError('Developer: All commands must have an "index.js" file inside')
22
- }
23
-
24
- return dir
25
- })
26
-
27
- return Promise.all(commands)
28
- }
@@ -1,45 +0,0 @@
1
- import path from 'path'
2
- import { CliComponents } from '../components'
3
-
4
- export interface DCLInfo {
5
- segmentKey?: string
6
- userId?: string
7
- trackStats?: boolean
8
- }
9
-
10
- /* istanbul ignore next */
11
- export const getDclInfoPath = () =>
12
- path.resolve(process.env[process.platform === 'win32' ? 'USERPROFILE' : 'HOME'] ?? '', '.dclinfo')
13
-
14
- /**
15
- * Reads the contents of the `.dclinfo` file
16
- */
17
- export async function getDCLInfoConfig(components: Pick<CliComponents, 'fs'>): Promise<DCLInfo> {
18
- try {
19
- const content = await components.fs.readFile(getDclInfoPath(), 'utf8')
20
- return JSON.parse(content) as DCLInfo
21
- } catch (e) {
22
- return {}
23
- }
24
- }
25
-
26
- /**
27
- * Config that can be override via ENV variables
28
- */
29
- export function getEnvConfig(): Partial<DCLInfo> {
30
- const { SEGMENT_KEY } = process.env
31
-
32
- const envConfig = {
33
- segmentKey: SEGMENT_KEY
34
- }
35
-
36
- return removeEmptyKeys(envConfig)
37
- }
38
-
39
- export function removeEmptyKeys(obj: Record<string, unknown>) {
40
- const result: Record<string, unknown> = {}
41
- Object.keys(obj)
42
- .filter((k) => !!obj[k])
43
- .forEach((k) => (result[k] = obj[k]))
44
- return result
45
- }
@@ -1,95 +0,0 @@
1
- export interface IBounds {
2
- minX: number
3
- minY: number
4
- maxX: number
5
- maxY: number
6
- }
7
-
8
- export type Coords = {
9
- x: number
10
- y: number
11
- }
12
-
13
- /**
14
- * Returns metaverse coordinates bounds.
15
- * TODO: use functions from @dcl/schemas
16
- */
17
- export function getBounds(): IBounds {
18
- return {
19
- minX: -150,
20
- minY: -150,
21
- maxX: 165,
22
- maxY: 165
23
- }
24
- }
25
-
26
- /**
27
- * Parses a string-based set of coordinates.
28
- * - All spaces are removed
29
- * - Leading zeroes are removed
30
- * - `-0` is converted to `0`
31
- * @param coordinates An string containing coordinates in the `x,y; x,y; ...` format
32
- */
33
- export function parse(coordinates: string): string[] {
34
- return coordinates.split(';').map((coord: string) => {
35
- const [x, y] = coord.split(',').map(($) => {
36
- return parseInt($, 10)
37
- .toString() // removes spaces :)
38
- .replace('-0', '0')
39
- .replace(/undefined|NaN/g, '0')
40
- })
41
- return `${x},${y}`
42
- })
43
- }
44
-
45
- /**
46
- * Converts a string-based set of coordinates to an object
47
- * @param coords A string containing a set of coordinates
48
- */
49
- export function getObject(coords: string): Coords {
50
- const [x, y] = parse(coords)[0].split(',')
51
- return { x: parseInt(x.toString(), 10), y: parseInt(y.toString(), 10) }
52
- }
53
-
54
- /**
55
- * Returns true if the given coordinates are in metaverse bounds
56
- */
57
- export function inBounds(x: number, y: number): boolean {
58
- const { minX, minY, maxX, maxY } = getBounds()
59
- return x >= minX && x <= maxX && y >= minY && y <= maxY
60
- }
61
-
62
- /**
63
- * Returns true if the given parcels array are connected
64
- */
65
- export function areConnected(parcels: Coords[]): boolean {
66
- if (parcels.length === 0) {
67
- return false
68
- }
69
- const visited = visitParcel(parcels[0], parcels)
70
- return visited.length === parcels.length
71
- }
72
-
73
- function visitParcel(parcel: Coords, allParcels: Coords[], visited: Coords[] = []): Coords[] {
74
- const isVisited = visited.some((visitedParcel) => isEqual(visitedParcel, parcel))
75
- if (!isVisited) {
76
- visited.push(parcel)
77
- const neighbours = getNeighbours(parcel.x, parcel.y, allParcels)
78
- neighbours.forEach((neighbours) => visitParcel(neighbours, allParcels, visited))
79
- }
80
- return visited
81
- }
82
-
83
- function getIsNeighbourMatcher(x: number, y: number) {
84
- return (coords: Coords) =>
85
- (coords.x === x && (coords.y + 1 === y || coords.y - 1 === y)) ||
86
- (coords.y === y && (coords.x + 1 === x || coords.x - 1 === x))
87
- }
88
-
89
- function getNeighbours(x: number, y: number, parcels: Coords[]): Coords[] {
90
- return parcels.filter(getIsNeighbourMatcher(x, y))
91
- }
92
-
93
- export function isEqual(p1: Coords, p2: Coords): boolean {
94
- return p1.x === p2.x && p1.y === p2.y
95
- }