@contember/cli 1.2.0-alpha.9 → 1.2.0-beta.2

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 (98) hide show
  1. package/Dockerfile +2 -2
  2. package/dist/src/commands/deploy/DeployCommand.d.ts.map +1 -1
  3. package/dist/src/commands/deploy/DeployCommand.js +3 -10
  4. package/dist/src/commands/deploy/DeployCommand.js.map +1 -1
  5. package/dist/src/commands/migrations/MigrationAmendCommand.js +1 -1
  6. package/dist/src/commands/migrations/MigrationAmendCommand.js.map +1 -1
  7. package/dist/src/commands/migrations/MigrationDiffCommand.d.ts +1 -0
  8. package/dist/src/commands/migrations/MigrationDiffCommand.d.ts.map +1 -1
  9. package/dist/src/commands/migrations/MigrationDiffCommand.js +4 -1
  10. package/dist/src/commands/migrations/MigrationDiffCommand.js.map +1 -1
  11. package/dist/src/commands/migrations/MigrationValidationHelper.js +3 -3
  12. package/dist/src/commands/migrations/MigrationValidationHelper.js.map +1 -1
  13. package/dist/src/commands/project/ProjectGenerateDocumentation.js +3 -3
  14. package/dist/src/commands/project/ProjectGenerateDocumentation.js.map +1 -1
  15. package/dist/src/commands/transfer/ExportCommand.d.ts +17 -0
  16. package/dist/src/commands/transfer/ExportCommand.d.ts.map +1 -0
  17. package/dist/src/commands/transfer/ExportCommand.js +58 -0
  18. package/dist/src/commands/transfer/ExportCommand.js.map +1 -0
  19. package/dist/src/commands/transfer/ImportCommand.d.ts +16 -0
  20. package/dist/src/commands/transfer/ImportCommand.d.ts.map +1 -0
  21. package/dist/src/commands/transfer/ImportCommand.js +54 -0
  22. package/dist/src/commands/transfer/ImportCommand.js.map +1 -0
  23. package/dist/src/commands/transfer/TransferCommand.d.ts +17 -0
  24. package/dist/src/commands/transfer/TransferCommand.d.ts.map +1 -0
  25. package/dist/src/commands/transfer/TransferCommand.js +60 -0
  26. package/dist/src/commands/transfer/TransferCommand.js.map +1 -0
  27. package/dist/src/commands/transfer/index.d.ts +4 -0
  28. package/dist/src/commands/transfer/index.d.ts.map +1 -0
  29. package/dist/src/commands/transfer/index.js +20 -0
  30. package/dist/src/commands/transfer/index.js.map +1 -0
  31. package/dist/src/commands/transfer/utils.d.ts +26 -0
  32. package/dist/src/commands/transfer/utils.d.ts.map +1 -0
  33. package/dist/src/commands/transfer/utils.js +134 -0
  34. package/dist/src/commands/transfer/utils.js.map +1 -0
  35. package/dist/src/run.js +6 -0
  36. package/dist/src/run.js.map +1 -1
  37. package/dist/src/tsconfig.tsbuildinfo +1 -1
  38. package/dist/src/utils/admin.js +1 -1
  39. package/dist/src/utils/admin.js.map +1 -1
  40. package/dist/src/utils/commands.d.ts +2 -2
  41. package/dist/src/utils/commands.d.ts.map +1 -1
  42. package/dist/src/utils/commands.js +2 -2
  43. package/dist/src/utils/commands.js.map +1 -1
  44. package/dist/src/utils/dockerCompose.js +5 -5
  45. package/dist/src/utils/dockerCompose.js.map +1 -1
  46. package/dist/src/utils/dsn.d.ts +6 -0
  47. package/dist/src/utils/dsn.d.ts.map +1 -0
  48. package/dist/src/utils/dsn.js +17 -0
  49. package/dist/src/utils/dsn.js.map +1 -0
  50. package/dist/src/utils/npm.js +8 -8
  51. package/dist/src/utils/npm.js.map +1 -1
  52. package/dist/src/utils/project/loadSchema.js +2 -2
  53. package/dist/src/utils/project/loadSchema.js.map +1 -1
  54. package/dist/src/utils/project/projectDescribe.js +1 -1
  55. package/dist/src/utils/project/projectDescribe.js.map +1 -1
  56. package/dist/src/utils/schema.d.ts +2 -2
  57. package/dist/src/utils/schema.d.ts.map +1 -1
  58. package/dist/src/utils/schema.js +3 -3
  59. package/dist/src/utils/schema.js.map +1 -1
  60. package/dist/src/utils/stdio.d.ts +2 -0
  61. package/dist/src/utils/stdio.d.ts.map +1 -0
  62. package/dist/src/utils/stdio.js +10 -0
  63. package/dist/src/utils/stdio.js.map +1 -0
  64. package/dist/src/utils/stream.d.ts +9 -0
  65. package/dist/src/utils/stream.d.ts.map +1 -0
  66. package/dist/src/utils/stream.js +41 -0
  67. package/dist/src/utils/stream.js.map +1 -0
  68. package/dist/src/utils/workspace.d.ts.map +1 -1
  69. package/dist/src/utils/workspace.js +1 -2
  70. package/dist/src/utils/workspace.js.map +1 -1
  71. package/dist/src/utils/yaml.d.ts +1 -1
  72. package/dist/src/utils/yaml.d.ts.map +1 -1
  73. package/dist/src/utils/yaml.js +3 -3
  74. package/dist/src/utils/yaml.js.map +1 -1
  75. package/package.json +10 -11
  76. package/src/commands/deploy/DeployCommand.ts +4 -10
  77. package/src/commands/migrations/MigrationAmendCommand.ts +1 -1
  78. package/src/commands/migrations/MigrationDiffCommand.ts +6 -1
  79. package/src/commands/migrations/MigrationValidationHelper.ts +3 -3
  80. package/src/commands/project/ProjectGenerateDocumentation.ts +2 -2
  81. package/src/commands/transfer/ExportCommand.ts +72 -0
  82. package/src/commands/transfer/ImportCommand.ts +61 -0
  83. package/src/commands/transfer/TransferCommand.ts +71 -0
  84. package/src/commands/transfer/index.ts +3 -0
  85. package/src/commands/transfer/utils.ts +135 -0
  86. package/src/run.ts +6 -0
  87. package/src/utils/admin.ts +1 -1
  88. package/src/utils/commands.ts +2 -2
  89. package/src/utils/dockerCompose.ts +2 -2
  90. package/src/utils/dsn.ts +13 -0
  91. package/src/utils/npm.ts +2 -2
  92. package/src/utils/project/loadSchema.ts +1 -1
  93. package/src/utils/project/projectDescribe.tsx +1 -1
  94. package/src/utils/schema.ts +4 -4
  95. package/src/utils/stdio.ts +6 -0
  96. package/src/utils/stream.ts +44 -0
  97. package/src/utils/workspace.ts +1 -2
  98. package/src/utils/yaml.ts +2 -3
@@ -0,0 +1,72 @@
1
+ import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
2
+ import { createWriteStream } from 'node:fs'
3
+ import { dataExport, resolveProject } from './utils'
4
+ import { maskToken } from '../../utils/token'
5
+ import { pipeline } from 'node:stream/promises'
6
+ import { rewriteStdoutLine } from '../../utils/stdio'
7
+
8
+ type Args = {
9
+ source?: string
10
+ }
11
+ type Options = {
12
+ 'include-system'?: boolean
13
+ 'no-gzip'?: boolean
14
+ output?: string
15
+ }
16
+
17
+ export class ExportCommand extends Command<Args, Options> {
18
+ constructor(
19
+ private readonly workspace: Workspace,
20
+ ) {
21
+ super()
22
+ }
23
+
24
+ protected configure(configuration: CommandConfiguration<Args, Options>): void {
25
+ configuration.description('Export data from a project')
26
+ const from = configuration.argument('source')
27
+ if (this.workspace.isSingleProjectMode()) {
28
+ from.optional()
29
+ }
30
+ configuration.option('include-system').valueNone()
31
+ configuration.option('no-gzip').valueNone()
32
+ configuration.option('output').valueRequired()
33
+ }
34
+
35
+ protected async execute(input: Input<Args, Options>): Promise<void | number> {
36
+ const from = input.getArgument('source')
37
+ const project = await resolveProject({ workspace: this.workspace, projectOrDsn: from })
38
+
39
+ console.log('')
40
+ console.log('Exporting data from a following project')
41
+ console.log('')
42
+ console.log(`Project name: ${project.project}`)
43
+ console.log(`API URL: ${project.baseUrl}`)
44
+ console.log(`Token: ${maskToken(project.token)}`)
45
+
46
+ const noGzip = input.getOption('no-gzip') === true
47
+ const includeSystem = input.getOption('include-system') === true
48
+
49
+ const response = await dataExport({ project, includeSystem, noGzip })
50
+
51
+ let transferred = 0
52
+ let start = Date.now()
53
+ let lastMbReported = 0
54
+ console.log('')
55
+ response.on('data', chunk => {
56
+ transferred += chunk.length
57
+ const currentMb = Math.floor(transferred / 1024 / 1024)
58
+ if (currentMb > lastMbReported) {
59
+ const durationS = Math.floor((Date.now() - start) / 1000)
60
+ rewriteStdoutLine(`transferred ${currentMb} MiB; ${durationS} seconds`)
61
+ lastMbReported = currentMb
62
+ }
63
+ })
64
+
65
+ const fileName = input.getOption('output') ?? `${project.project}.jsonl${noGzip ? '' : '.gz'}`
66
+ const fileStream = createWriteStream(fileName)
67
+
68
+ await pipeline(response, fileStream)
69
+
70
+ console.log('\nExport done.')
71
+ }
72
+ }
@@ -0,0 +1,61 @@
1
+ import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
2
+ import { createReadStream } from 'node:fs'
3
+ import { createGunzip } from 'node:zlib'
4
+ import { confirmImport, dataImport, resolveProject } from './utils'
5
+ import { maskToken } from '../../utils/token'
6
+ import { rewriteStdoutLine } from '../../utils/stdio'
7
+ import { readStream } from '../../utils/stream'
8
+
9
+ type Args = {
10
+ file: string
11
+ target?: string
12
+ }
13
+ type Options = {
14
+ 'yes': boolean
15
+ }
16
+
17
+ export class ImportCommand extends Command<Args, Options> {
18
+ constructor(
19
+ private readonly workspace: Workspace,
20
+ ) {
21
+ super()
22
+ }
23
+
24
+ protected configure(configuration: CommandConfiguration<Args, Options>): void {
25
+ configuration.description('Import data into a project')
26
+ configuration.argument('file')
27
+ const from = configuration.argument('target')
28
+ if (this.workspace.isSingleProjectMode()) {
29
+ from.optional()
30
+ }
31
+ configuration.option('yes').valueNone()
32
+ }
33
+
34
+ protected async execute(input: Input<Args, Options>): Promise<void | number> {
35
+ const from = input.getArgument('target')
36
+ const project = await resolveProject({ projectOrDsn: from, workspace: this.workspace })
37
+ console.log('')
38
+ console.log('Importing data into a following project')
39
+ console.log('')
40
+ console.log(`Project name: ${project.project}`)
41
+ console.log(`API URL: ${project.baseUrl}`)
42
+ console.log(`Token: ${maskToken(project.token)}`)
43
+ console.log('')
44
+ if (!await confirmImport(input)) {
45
+ return 1
46
+ }
47
+ const file = input.getArgument('file')
48
+ const baseInputStream = createReadStream(file)
49
+ const stream = (file.endsWith('.gz') ? baseInputStream.pipe(createGunzip()) : baseInputStream)
50
+ const response = await dataImport({ stream: stream, project, printProgress: rewriteStdoutLine })
51
+ console.log('')
52
+ const responseData = JSON.parse((await readStream(response)).toString())
53
+ if (responseData.ok) {
54
+ console.log('Import done.')
55
+ } else {
56
+ console.error('Import failed:')
57
+ console.log(responseData)
58
+ }
59
+ }
60
+ }
61
+
@@ -0,0 +1,71 @@
1
+ import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
2
+ import { confirmImport, dataExport, dataImport, resolveProject } from './utils'
3
+ import { maskToken } from '../../utils/token'
4
+ import { rewriteStdoutLine } from '../../utils/stdio'
5
+ import { readStream } from '../../utils/stream'
6
+
7
+ type Args = {
8
+ source: string
9
+ target: string
10
+ }
11
+ type Options = {
12
+ 'include-system'?: boolean
13
+ yes: boolean
14
+ }
15
+
16
+ export class TransferCommand extends Command<Args, Options> {
17
+ constructor(
18
+ private readonly workspace: Workspace,
19
+ ) {
20
+ super()
21
+ }
22
+
23
+ protected configure(configuration: CommandConfiguration<Args, Options>): void {
24
+ configuration.description('Transfer data between projects')
25
+ configuration.argument('source')
26
+ const target = configuration.argument('target')
27
+ if (this.workspace.isSingleProjectMode()) {
28
+ target.optional()
29
+ }
30
+ configuration.option('include-system').valueNone()
31
+ configuration.option('yes').valueNone()
32
+ }
33
+
34
+ protected async execute(input: Input<Args, Options>): Promise<void | number> {
35
+ const source = input.getArgument('source')
36
+ const sourceProject = await resolveProject({ projectOrDsn: source, workspace: this.workspace })
37
+
38
+ const target = input.getArgument('target')
39
+ const targetProject = await resolveProject({ projectOrDsn: target, workspace: this.workspace })
40
+
41
+ console.log('')
42
+ console.log('Transferring data between projects:')
43
+ console.log('')
44
+ console.log(`Source project name: ${sourceProject.project}`)
45
+ console.log(`Source API URL: ${sourceProject.baseUrl}`)
46
+ console.log(`Source token: ${maskToken(sourceProject.token)}`)
47
+ console.log('')
48
+ console.log(`Target project name: ${targetProject.project}`)
49
+ console.log(`Target API URL: ${targetProject.baseUrl}`)
50
+ console.log(`Target token: ${maskToken(targetProject.token)}`)
51
+ console.log('')
52
+ if (!await confirmImport(input)) {
53
+ return 1
54
+ }
55
+
56
+ const includeSystem = input.getOption('include-system') === true
57
+ const exportResponse = (await dataExport({ project: sourceProject, includeSystem, noGzip: true }))
58
+ const importResponse = await dataImport({
59
+ stream: exportResponse, project: targetProject, printProgress: rewriteStdoutLine,
60
+ })
61
+ console.log('')
62
+
63
+ const responseData = JSON.parse((await readStream(importResponse)).toString())
64
+ if (responseData.ok) {
65
+ console.log('Transfer done.')
66
+ } else {
67
+ console.error('Transfer failed:')
68
+ console.log(responseData)
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ExportCommand'
2
+ export * from './ImportCommand'
3
+ export * from './TransferCommand'
@@ -0,0 +1,135 @@
1
+ import { Readable, Stream } from 'node:stream'
2
+ import { LineTransform, readStream } from '../../utils/stream'
3
+ import https from 'node:https'
4
+ import http from 'node:http'
5
+ import { Input, Workspace } from '@contember/cli-common'
6
+ import { parseDsn } from '../../utils/dsn'
7
+ import { interactiveResolveInstanceEnvironmentFromInput } from '../../utils/instance'
8
+ import { interactiveResolveApiToken } from '../../utils/tenant'
9
+ import prompts from 'prompts'
10
+
11
+ export type ProjectEndpoint = {
12
+ token: string
13
+ project: string
14
+ baseUrl: string
15
+ }
16
+
17
+ export const resolveProject = async ({ projectOrDsn, workspace }: { projectOrDsn?: string; workspace: Workspace }): Promise<ProjectEndpoint> => {
18
+ let endpoint: string | undefined = undefined
19
+ let token: string | undefined = undefined
20
+ let project: string | undefined
21
+ if (projectOrDsn?.includes('://')) {
22
+ ({ endpoint, project, token } = parseDsn(projectOrDsn))
23
+ } else if (projectOrDsn !== undefined && projectOrDsn !== '.') {
24
+ project = projectOrDsn
25
+ }
26
+ const instance = await interactiveResolveInstanceEnvironmentFromInput(workspace, endpoint)
27
+ token ??= await interactiveResolveApiToken({ workspace, instance })
28
+ project ??= (await workspace.projects.getSingleProject()).name
29
+ return { project, token, baseUrl: instance.baseUrl }
30
+
31
+ }
32
+
33
+ export const dataImport = async ({ printProgress, stream, project: { project, token, baseUrl } }: { stream: Readable; project: ProjectEndpoint; printProgress: (message: string) => void }) => {
34
+ let table = ''
35
+ let rowCount = 0
36
+ let rowTotal = 0
37
+ let transferred = 0
38
+ let start = Date.now()
39
+ const inputStream = stream.pipe(new LineTransform(line => {
40
+ transferred += line.length + 1
41
+ if (line === '') {
42
+ return ''
43
+ }
44
+ const data = JSON.parse(line)
45
+ switch (data[0]) {
46
+ case 'importSystemSchemaBegin':
47
+ return JSON.stringify([data[0], { ...data[1], project }])
48
+ case 'importContentSchemaBegin':
49
+ return JSON.stringify([data[0], { ...data[1], project }])
50
+ case 'insertBegin':
51
+ table = data[1].table
52
+ rowCount = 0
53
+ break
54
+ case 'insertRow':
55
+ rowCount++
56
+ rowTotal++
57
+ break
58
+ case 'insertEnd':
59
+ break
60
+ }
61
+ if ((rowCount % 1000) === 0 || data[0] !== 'insertRow') {
62
+ const mbs = Math.floor(transferred / 1024 / 1024).toString()
63
+ const durationS = Math.floor((Date.now() - start) / 1000)
64
+ printProgress(`transferred ${mbs} MiB; inserted ${rowCount} ${table} rows, ${rowTotal} total; ${durationS} seconds`)
65
+ }
66
+ return line
67
+ }))
68
+
69
+ return await executeRequest({
70
+ url: `${baseUrl}/import`,
71
+ token,
72
+ headers: {
73
+ 'Content-type': 'application/x-ndjson',
74
+ },
75
+ body: inputStream,
76
+ })
77
+ }
78
+
79
+ export const dataExport = async ({ project: { project, token, baseUrl }, noGzip, includeSystem }: { project: ProjectEndpoint; includeSystem: boolean; noGzip: boolean }) => {
80
+ return await executeRequest({
81
+ url: `${baseUrl}/export`,
82
+ token,
83
+ headers: {
84
+ 'Accept-Encoding': noGzip ? 'identity' : 'gzip',
85
+ 'Content-type': 'application/json',
86
+ },
87
+ body: JSON.stringify({
88
+ projects: [{ slug: project, system: includeSystem }],
89
+ }),
90
+ })
91
+ }
92
+
93
+ const executeRequest = async ({ url, token, headers, body }: { url: string; token: string; headers: Record<string, string >; body: any }) => {
94
+ return await new Promise<http.IncomingMessage>((resolve, reject) => {
95
+ const req = (url.startsWith('https://') ? https : http).request(url, {
96
+ method: 'POST',
97
+ headers: {
98
+ Authorization: `Bearer ${token}`,
99
+ ...headers,
100
+ },
101
+ }, async res => {
102
+ if (res.statusCode !== 200) {
103
+ const responseBody = (await readStream(res)).toString()
104
+ reject(new Error(`Invalid response: ${res.statusMessage}\n${responseBody}`))
105
+ } else {
106
+ resolve(res)
107
+ }
108
+ })
109
+
110
+ if (body instanceof Stream) {
111
+ body.pipe(req)
112
+ } else {
113
+ req.write(body)
114
+ req.end()
115
+ }
116
+ })
117
+ }
118
+
119
+ export const confirmImport = async (input: Input<{}, { yes: boolean }>): Promise<boolean> => {
120
+ if (input.getOption('yes')) {
121
+ return true
122
+ }
123
+ if (!process.stdin.isTTY) {
124
+ throw 'TTY not available. Pass --yes option to confirm execution.'
125
+ }
126
+ console.log('This will completely wipe the target project.')
127
+ console.log('(to skip this dialog, you can pass --yes option)')
128
+ console.log('')
129
+ const { ok } = await prompts({
130
+ type: 'confirm',
131
+ name: 'ok',
132
+ message: `Do you want to continue?`,
133
+ })
134
+ return ok
135
+ }
package/src/run.ts CHANGED
@@ -19,12 +19,18 @@ import {
19
19
  VersionCommand,
20
20
  WorkspaceUpdateApiCommand,
21
21
  } from './commands'
22
+ import { ExportCommand } from './commands/transfer/ExportCommand'
23
+ import { ImportCommand } from './commands/transfer/ImportCommand'
24
+ import { TransferCommand } from './commands/transfer/TransferCommand';
22
25
 
23
26
  (async () => {
24
27
  const workspace = await Workspace.get(process.cwd())
25
28
  const commands: CommandFactoryList = {
26
29
  ['deploy']: () => new DeployCommand(workspace),
27
30
  ['version']: () => new VersionCommand(),
31
+ ['data:export']: () => new ExportCommand(workspace),
32
+ ['data:import']: () => new ImportCommand(workspace),
33
+ ['data:transfer']: () => new TransferCommand(workspace),
28
34
  ['migrations:diff']: () => new MigrationDiffCommand(workspace),
29
35
  ['migrations:amend']: () => new MigrationAmendCommand(workspace),
30
36
  ['migrations:describe']: () => new MigrationDescribeCommand(workspace),
@@ -1,5 +1,5 @@
1
1
  import fetch, { Response } from 'node-fetch'
2
- import { readdir, readFile } from 'fs/promises'
2
+ import { readdir, readFile } from 'node:fs/promises'
3
3
  import { maskToken } from './token'
4
4
 
5
5
  export class AdminClient {
@@ -1,5 +1,5 @@
1
- import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
2
- import { Readable, Writable } from 'stream'
1
+ import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process'
2
+ import { Readable, Writable } from 'node:stream'
3
3
  import chalk from 'chalk'
4
4
 
5
5
  export type RunningCommand = { child: ChildProcessWithoutNullStreams; output: Promise<string> }
@@ -1,5 +1,5 @@
1
- import { join } from 'path'
2
- import { pathExists } from 'fs-extra'
1
+ import { join } from 'node:path'
2
+ import { pathExists } from '@contember/cli-common'
3
3
  import { JsonUpdateCallback, updateYaml } from './yaml'
4
4
 
5
5
  const MAIN_CONFIGS = ['docker-compose.yaml', 'docker-compose.yml']
@@ -0,0 +1,13 @@
1
+ import { URL } from 'node:url'
2
+
3
+ export const parseDsn = (dsn: string): {endpoint: string; project: string; token: string} => {
4
+ const uri = new URL(dsn)
5
+ if (uri.protocol !== 'contember:' && uri.protocol !== 'contember-unsecure:') {
6
+ throw 'Invalid DSN'
7
+ }
8
+ return {
9
+ project: uri.username,
10
+ token: uri.password,
11
+ endpoint: (uri.protocol === 'contember-unsecure:' ? 'http://' : 'https://') + uri.host,
12
+ }
13
+ }
package/src/utils/npm.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { pathExists } from 'fs-extra'
2
- import { join } from 'path'
1
+ import { pathExists } from '@contember/cli-common'
2
+ import { join } from 'node:path'
3
3
  import { runCommand } from './commands'
4
4
  import { listDirectories } from '@contember/cli-common'
5
5
 
@@ -1,7 +1,7 @@
1
1
  import { Project } from '@contember/cli-common'
2
2
  import { Schema } from '@contember/schema'
3
3
  import * as esbuild from 'esbuild'
4
- import { join } from 'path'
4
+ import { join } from 'node:path'
5
5
  import { NodeVM } from 'vm2'
6
6
  import { schemaType } from '@contember/schema-utils'
7
7
 
@@ -2,7 +2,7 @@ import { Schema } from '@contember/schema'
2
2
  import { h } from 'preact'
3
3
  import render from 'preact-render-to-string'
4
4
  import { ProjectInfo } from './components/ProjectInfo'
5
- import { readFile } from 'fs/promises'
5
+ import { readFile } from 'node:fs/promises'
6
6
 
7
7
  export const renderProjectInfoHtml = async (schema: Schema, projectName: string) => {
8
8
  const html = render(<ProjectInfo schema={schema} projectName={projectName} />)
@@ -1,4 +1,4 @@
1
- import { SchemaValidator, ValidationError } from '@contember/schema-utils'
1
+ import { SchemaValidator, SchemaValidatorSkippedErrors, ValidationError } from '@contember/schema-utils'
2
2
  import { Schema } from '@contember/schema'
3
3
 
4
4
  export const printValidationErrors = (errors: ValidationError[], message?: string) => {
@@ -7,13 +7,13 @@ export const printValidationErrors = (errors: ValidationError[], message?: strin
7
7
  }
8
8
  console.group(message || 'Schema is invalid:')
9
9
  for (const err of errors) {
10
- console.log(err.path.join('.') + ': ' + err.message)
10
+ console.log(`${err.path.join('.')}: [${err.code}] ${err.message}`)
11
11
  }
12
12
  console.groupEnd()
13
13
  }
14
14
 
15
- export const validateSchemaAndPrintErrors = (schema: Schema, message?: string): boolean => {
16
- const errors = SchemaValidator.validate(schema)
15
+ export const validateSchemaAndPrintErrors = (schema: Schema, message?: string, skippedErrors?: SchemaValidatorSkippedErrors[]): boolean => {
16
+ const errors = SchemaValidator.validate(schema, skippedErrors)
17
17
  printValidationErrors(errors, message)
18
18
  return errors.length === 0
19
19
  }
@@ -0,0 +1,6 @@
1
+ export const rewriteStdoutLine = (message: string) => {
2
+ process.stdout.clearLine(0)
3
+ process.stdout.cursorTo(0)
4
+ process.stdout.write(message)
5
+ }
6
+
@@ -0,0 +1,44 @@
1
+ import { Stream, Transform } from 'node:stream'
2
+ import { Buffer } from 'node:buffer'
3
+
4
+
5
+ export class LineTransform extends Transform {
6
+ private chunks: Buffer[] = []
7
+
8
+ constructor(transform: (line: string) => string) {
9
+ super({
10
+ transform: (chunk, encoding, done) => {
11
+ while (true) {
12
+ const eolIndex = chunk.indexOf('\n')
13
+
14
+ if (eolIndex < 0) {
15
+ this.chunks.push(chunk)
16
+ break
17
+ }
18
+
19
+ this.chunks.push(chunk.slice(0, eolIndex))
20
+ chunk = chunk.slice(eolIndex + 1)
21
+ this.push(transform(Buffer.concat(this.chunks).toString('utf8')) + '\n', 'utf8')
22
+ this.chunks = []
23
+ }
24
+
25
+ return done()
26
+ },
27
+
28
+ flush: done => {
29
+ if (this.chunks.length) {
30
+ this.push(transform(Buffer.concat(this.chunks).toString('utf8')), 'utf8')
31
+ return done()
32
+ }
33
+ },
34
+ })
35
+ }
36
+ }
37
+
38
+
39
+ export const readStream = async (stream: Stream) => new Promise<Buffer>((resolve, reject) => {
40
+ const chunks: Buffer[] = []
41
+ stream.on('error', err => reject(err))
42
+ stream.on('data', chunk => chunks.push(chunk))
43
+ stream.on('end', () => resolve(Buffer.concat(chunks)))
44
+ })
@@ -1,5 +1,4 @@
1
- import { pathExists } from 'fs-extra'
2
- import { formatWorkspaceConfigPath, Workspace } from '@contember/cli-common'
1
+ import { formatWorkspaceConfigPath, pathExists, Workspace } from '@contember/cli-common'
3
2
  import { updateYaml } from './yaml'
4
3
 
5
4
  export const updateWorkspaceApiVersion = async (workspace: Workspace, newVersion: string): Promise<string | null> => {
package/src/utils/yaml.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { JSONObject, JSONPrimitive } from '@contember/cli-common/dist/src/utils/json'
2
- import { pathExists } from 'fs-extra'
1
+ import { JSONObject, JSONPrimitive, pathExists } from '@contember/cli-common'
3
2
  import YAWN from 'yawn-yaml/cjs'
4
3
  import { Merger } from '@contember/config-loader'
5
4
  import jsyaml from 'js-yaml'
6
- import { readFile, writeFile } from 'fs/promises'
5
+ import { readFile, writeFile } from 'node:fs/promises'
7
6
 
8
7
  export type JsonUpdateCallback<Value = JSONObject> = (
9
8
  data: Partial<Value>,