@datatruck/cli 0.21.0 → 0.22.0

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.
@@ -2,165 +2,223 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MysqlDumpTask = exports.mysqlDumpTaskDefinition = exports.mysqlDumpTaskName = void 0;
4
4
  const AppError_1 = require("../Error/AppError");
5
- const DefinitionEnum_1 = require("../JsonSchema/DefinitionEnum");
5
+ const cli_1 = require("../utils/cli");
6
+ const config_1 = require("../utils/datatruck/config");
6
7
  const fs_1 = require("../utils/fs");
7
- const process_1 = require("../utils/process");
8
+ const math_1 = require("../utils/math");
9
+ const mysql_1 = require("../utils/mysql");
10
+ const string_1 = require("../utils/string");
8
11
  const SqlDumpTaskAbstract_1 = require("./SqlDumpTaskAbstract");
9
- const fs_2 = require("fs");
10
- const fs_3 = require("fs");
12
+ const TaskAbstract_1 = require("./TaskAbstract");
13
+ const assert_1 = require("assert");
14
+ const promises_1 = require("fs/promises");
15
+ const path_1 = require("path");
11
16
  exports.mysqlDumpTaskName = "mysql-dump";
12
- exports.mysqlDumpTaskDefinition = {
13
- allOf: [(0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.sqlDumpTask)],
17
+ exports.mysqlDumpTaskDefinition = (0, SqlDumpTaskAbstract_1.sqlDumpTaskDefinition)({
18
+ dataFormat: { enum: ["csv", "sql"] },
19
+ csvSharedPath: { type: "string" },
20
+ });
21
+ const suffix = {
22
+ database: ".database.sql",
23
+ stored: ".stored-programs.sql",
24
+ table: ".table.sql",
25
+ tableData: ".table-data.csv",
26
+ tableSchema: ".table-schema.sql",
14
27
  };
15
- class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
16
- async buildConnectionArgs(database = true) {
17
- const password = await this.fetchPassword();
18
- return [
19
- `--host=${this.config.hostname}`,
20
- ...(this.config.port ? [`--port=${this.config.port}`] : []),
21
- `--user=${this.config.username}`,
22
- `--password=${password ?? ""}`,
23
- ...(database && this.config.database ? [this.config.database] : []),
24
- ];
25
- }
26
- async onDatabaseIsEmpty(name) {
27
- const [total] = await this.fetchValues(`
28
- SELECT
29
- COUNT(*) AS total
30
- FROM
31
- information_schema.tables
32
- WHERE
33
- table_schema = '${name}'
34
- `);
35
- return Number(total) ? false : true;
36
- }
37
- async onCreateDatabase(database) {
38
- const query = `
39
- CREATE DATABASE IF NOT EXISTS \`${database.name}\`
40
- CHARACTER SET ${database.charset ?? "utf8"}
41
- COLLATE ${database.charset ?? "utf8_general_ci"}
42
- `;
43
- await this.onExecQuery(query);
44
- }
45
- async onExecQuery(query) {
46
- return await (0, process_1.exec)("mysql", [
47
- ...(await this.buildConnectionArgs()),
48
- "-e",
49
- query.replace(/\s{1,}/g, " "),
50
- "-N",
51
- ], undefined, {
52
- log: this.verbose,
53
- stderr: {
54
- toExitCode: true,
55
- },
56
- stdout: {
57
- save: true,
58
- },
28
+ class MysqlDumpTask extends TaskAbstract_1.TaskAbstract {
29
+ async onBackup(data) {
30
+ const sql = (0, mysql_1.createMysqlCli)({
31
+ ...this.config,
32
+ verbose: data.options.verbose,
59
33
  });
34
+ const tableNames = await sql.fetchTableNames(this.config.database, this.config.includeTables, this.config.excludeTables);
35
+ const outputPath = data.package.path;
36
+ (0, assert_1.ok)(typeof outputPath === "string");
37
+ const dataFormat = this.config.dataFormat ?? "sql";
38
+ await (0, promises_1.mkdir)(outputPath, { recursive: true });
39
+ const sharedDir = dataFormat === "csv"
40
+ ? await sql.initSharedDir(this.config.csvSharedPath)
41
+ : undefined;
42
+ if (this.config.oneFileByTable || sharedDir) {
43
+ let current = 0;
44
+ for (const tableName of tableNames) {
45
+ await data.onProgress({
46
+ relative: {
47
+ description: "Exporting",
48
+ payload: tableName,
49
+ },
50
+ absolute: {
51
+ total: tableNames.length,
52
+ current,
53
+ percent: (0, math_1.progressPercent)(tableNames.length, current),
54
+ },
55
+ });
56
+ if (sharedDir) {
57
+ const tableSharedPath = (0, path_1.join)(sharedDir, `tmp-dtt-backup-${data.snapshot.id.slice(0, 8)}-${tableName}`);
58
+ if (data.options.verbose)
59
+ (0, cli_1.logExec)("mkdir", [tableSharedPath]);
60
+ await (0, promises_1.mkdir)(tableSharedPath, { recursive: true });
61
+ try {
62
+ await sql.csvDump({
63
+ sharedPath: tableSharedPath,
64
+ items: [tableName],
65
+ database: this.config.database,
66
+ });
67
+ const files = await (0, promises_1.readdir)(tableSharedPath);
68
+ const schemaFile = `${tableName}.sql`;
69
+ const dataFile = `${tableName}.txt`;
70
+ const successCsvDump = files.length === 2 &&
71
+ files.every((file) => file === schemaFile || file === dataFile);
72
+ if (!successCsvDump)
73
+ throw new AppError_1.AppError(`Invalid csv dump files: ${files.join(", ")}`);
74
+ await (0, promises_1.rename)((0, path_1.join)(tableSharedPath, schemaFile), (0, path_1.join)(outputPath, `${tableName}${suffix.tableSchema}`));
75
+ await (0, promises_1.rename)((0, path_1.join)(tableSharedPath, dataFile), (0, path_1.join)(outputPath, `${tableName}${suffix.tableData}`));
76
+ }
77
+ finally {
78
+ await (0, promises_1.rm)(tableSharedPath, { recursive: true });
79
+ }
80
+ }
81
+ else {
82
+ const outPath = (0, path_1.join)(outputPath, `${tableName}${suffix.table}`);
83
+ await sql.dump({
84
+ output: outPath,
85
+ items: [tableName],
86
+ database: this.config.database,
87
+ onProgress(progress) {
88
+ data.onProgress({
89
+ relative: {
90
+ description: "Exporting",
91
+ payload: tableName,
92
+ current: progress.totalBytes,
93
+ format: "size",
94
+ },
95
+ absolute: {
96
+ total: tableNames.length,
97
+ current,
98
+ percent: (0, math_1.progressPercent)(tableNames.length, current),
99
+ },
100
+ });
101
+ },
102
+ });
103
+ }
104
+ current++;
105
+ }
106
+ }
107
+ else {
108
+ await data.onProgress({
109
+ relative: { description: "Exporting" },
110
+ });
111
+ await sql.dump({
112
+ output: (0, path_1.join)(outputPath, `${this.config.database}${suffix.database}`),
113
+ items: tableNames,
114
+ database: this.config.database,
115
+ onProgress: (progress) => data.onProgress({
116
+ absolute: {
117
+ description: "Exporting in single file",
118
+ current: progress.totalBytes,
119
+ format: "size",
120
+ },
121
+ }),
122
+ });
123
+ }
124
+ if (this.config.storedPrograms ?? true) {
125
+ await data.onProgress({
126
+ relative: { description: "Exporting stored programs" },
127
+ });
128
+ await sql.dump({
129
+ database: this.config.database,
130
+ output: (0, path_1.join)(outputPath, `${this.config.database}${suffix.stored}`),
131
+ onlyStoredPrograms: true,
132
+ });
133
+ }
60
134
  }
61
- async onFetchTableNames(database) {
62
- return await this.fetchValues(`
63
- SELECT
64
- table_name
65
- FROM
66
- information_schema.tables
67
- WHERE
68
- table_schema = '${database}'
69
- ORDER BY
70
- table_name
71
- `);
72
- }
73
- async onExportTables(tableNames, output, onProgress) {
74
- const stream = (0, fs_2.createWriteStream)(output);
75
- await Promise.all([
76
- new Promise((resolve, reject) => {
77
- stream.on("close", resolve);
78
- stream.on("error", reject);
79
- }),
80
- await (0, process_1.exec)("mysqldump", [
81
- ...(await this.buildConnectionArgs()),
82
- "--lock-tables=false",
83
- "--skip-add-drop-table=false",
84
- ...tableNames,
85
- ], null, {
86
- pipe: {
87
- stream,
88
- onWriteProgress: onProgress,
89
- },
90
- log: {
91
- exec: this.verbose,
92
- stderr: this.verbose,
93
- allToStderr: true,
94
- },
95
- stderr: {
96
- toExitCode: true,
97
- },
98
- }),
99
- ]);
100
- const headerContents = await (0, fs_1.readPartialFile)(output, [0, 100]);
101
- const footerContents = await (0, fs_1.readPartialFile)(output, [-100]);
102
- const successHeader = headerContents.split(/\r?\n/).some((line) => {
103
- const firstLine = line.trim().toLowerCase();
104
- return (firstLine.startsWith("-- mysql dump") ||
105
- firstLine.startsWith("-- mariadb dump"));
135
+ async onRestore(data) {
136
+ const sql = (0, mysql_1.createMysqlCli)({
137
+ ...this.config,
138
+ verbose: data.options.verbose,
106
139
  });
107
- if (!successHeader)
108
- throw new AppError_1.AppError("No start line found");
109
- const successFooter = footerContents
110
- .split(/\r?\n/)
111
- .some((line) => line.trim().toLowerCase().startsWith("-- dump completed"));
112
- if (!successFooter)
113
- throw new AppError_1.AppError("No end line found (incomplete backup)");
114
- }
115
- async onExportStoredPrograms(output) {
116
- const stream = (0, fs_2.createWriteStream)(output);
117
- await Promise.all([
118
- new Promise((resolve, reject) => {
119
- stream.on("close", resolve);
120
- stream.on("error", reject);
121
- }),
122
- await (0, process_1.exec)("mysqldump", [
123
- ...(await this.buildConnectionArgs()),
124
- "--lock-tables=false",
125
- "--routines",
126
- "--events",
127
- "--skip-triggers",
128
- "--no-create-info",
129
- "--no-data",
130
- "--no-create-db",
131
- "--skip-opt",
132
- ], null, {
133
- pipe: { stream: stream },
134
- log: {
135
- exec: this.verbose,
136
- stderr: this.verbose,
137
- allToStderr: true,
140
+ const restorePath = data.package.restorePath;
141
+ (0, assert_1.ok)(typeof restorePath === "string");
142
+ const params = {
143
+ packageName: data.package.name,
144
+ snapshotId: data.options.snapshotId,
145
+ snapshotDate: data.snapshot.date,
146
+ action: "restore",
147
+ database: undefined,
148
+ };
149
+ const database = {
150
+ name: (0, config_1.resolveDatabaseName)(this.config.database, params),
151
+ };
152
+ if (this.config.targetDatabase)
153
+ database.name = (0, config_1.resolveDatabaseName)(this.config.targetDatabase.name, {
154
+ ...params,
155
+ database: database.name,
156
+ });
157
+ const suffixes = Object.values(suffix);
158
+ const files = (await (0, fs_1.readDir)(restorePath)).filter((f) => (0, string_1.endsWith)(f, suffixes));
159
+ // Database check
160
+ if (files.some((f) => f.endsWith(suffix.database)) &&
161
+ !(await sql.isDatabaseEmpty(database.name)))
162
+ throw new AppError_1.AppError(`Target database is not empty: ${database.name}`);
163
+ // Table check
164
+ const restoreTables = [
165
+ ...new Set(...files
166
+ .filter((f) => (0, string_1.endsWith)(f, [suffix.table, suffix.tableSchema, suffix.tableData]))
167
+ .map((f) => f.split(".")[0])),
168
+ ];
169
+ const serverTables = await sql.fetchTableNames(database.name);
170
+ const errorTables = restoreTables.filter((v) => serverTables.includes(v));
171
+ if (errorTables.length)
172
+ throw new AppError_1.AppError(`Target table already exists: ${errorTables.join(", ")}`);
173
+ // Data check
174
+ const dataFiles = files.filter((f) => f.endsWith(suffix.tableData));
175
+ const sharedDir = dataFiles.length
176
+ ? await sql.initSharedDir(this.config.csvSharedPath)
177
+ : undefined;
178
+ await sql.createDatabase(database);
179
+ if (data.options.verbose)
180
+ (0, cli_1.logExec)("readdir", [restorePath]);
181
+ let current = 0;
182
+ for (const file of files.filter((f) => !f.endsWith(suffix.tableData))) {
183
+ const path = (0, path_1.join)(restorePath, file);
184
+ data.onProgress({
185
+ relative: {
186
+ description: "Importing",
187
+ payload: file,
138
188
  },
139
- stderr: {
140
- toExitCode: true,
189
+ absolute: {
190
+ total: files.length,
191
+ current: current,
192
+ percent: (0, math_1.progressPercent)(files.length, current),
141
193
  },
142
- }),
143
- ]);
144
- }
145
- async onImport(path, database) {
146
- await (0, process_1.exec)("mysql", [...(await this.buildConnectionArgs(false)), database], null, {
147
- pipe: {
148
- stream: (0, fs_3.createReadStream)(path),
149
- onReadProgress: (data) => {
150
- if (this.verbose)
151
- (0, process_1.logExecStdout)({
152
- data: JSON.stringify(data),
153
- colorize: true,
154
- stderr: true,
155
- lineSalt: true,
156
- });
194
+ });
195
+ await sql.importFile(path, database.name);
196
+ current++;
197
+ }
198
+ for (const file of dataFiles) {
199
+ const filePath = (0, path_1.join)(restorePath, file);
200
+ const tableName = file.slice(0, suffix.tableData.length * -1);
201
+ data.onProgress({
202
+ relative: {
203
+ description: "Importing",
204
+ payload: file,
157
205
  },
158
- },
159
- log: this.verbose,
160
- stderr: {
161
- toExitCode: true,
162
- },
163
- });
206
+ absolute: {
207
+ total: files.length,
208
+ current: current,
209
+ percent: (0, math_1.progressPercent)(files.length, current),
210
+ },
211
+ });
212
+ const sharedFilePath = (0, path_1.join)(sharedDir, `tmp-dtt-restore-${data.snapshot.id.slice(0, 8)}-${tableName}.data.csv`);
213
+ try {
214
+ await (0, promises_1.rename)(filePath, sharedFilePath);
215
+ await sql.importCsvFile(sharedFilePath, database.name, tableName);
216
+ }
217
+ finally {
218
+ await (0, promises_1.rm)(sharedFilePath);
219
+ }
220
+ current++;
221
+ }
164
222
  }
165
223
  }
166
224
  exports.MysqlDumpTask = MysqlDumpTask;
@@ -81,7 +81,7 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
81
81
  stream.on("error", reject);
82
82
  }),
83
83
  (0, process_1.exec)("pg_dump", [
84
- ...(await this.buildConnectionArgs()),
84
+ ...(await this.buildConnectionArgs(this.config.database)),
85
85
  ...(tableNames?.flatMap((v) => ["-t", v]) ?? []),
86
86
  ], null, {
87
87
  pipe: { stream, onWriteProgress: onProgress },
@@ -1,6 +1,6 @@
1
1
  import { exec } from "../utils/process";
2
2
  import { BackupDataType, RestoreDataType, TaskAbstract } from "./TaskAbstract";
3
- import { JSONSchema7 } from "json-schema";
3
+ import { JSONSchema7, JSONSchema7Definition } from "json-schema";
4
4
  export type TargetDatabaseType = {
5
5
  name: string;
6
6
  charset?: string;
@@ -14,13 +14,16 @@ export type SqlDumpTaskConfigType = {
14
14
  port?: number;
15
15
  database: string;
16
16
  username: string;
17
+ /**
18
+ * @default true
19
+ */
17
20
  storedPrograms?: boolean;
18
21
  targetDatabase?: TargetDatabaseType;
19
22
  includeTables?: string[];
20
23
  excludeTables?: string[];
21
24
  oneFileByTable?: boolean;
22
25
  };
23
- export declare const sqlDumpTaskDefinition: JSONSchema7;
26
+ export declare const sqlDumpTaskDefinition: (props?: Record<string, JSONSchema7Definition>) => JSONSchema7;
24
27
  export declare abstract class SqlDumpTaskAbstract<TConfig extends SqlDumpTaskConfigType> extends TaskAbstract<TConfig> {
25
28
  protected verbose?: boolean;
26
29
  fetchPassword(): Promise<string | null>;
@@ -12,11 +12,12 @@ const assert_1 = require("assert");
12
12
  const promises_1 = require("fs/promises");
13
13
  const micromatch_1 = require("micromatch");
14
14
  const path_1 = require("path");
15
- exports.sqlDumpTaskDefinition = {
15
+ const sqlDumpTaskDefinition = (props = {}) => ({
16
16
  type: "object",
17
17
  required: ["password", "hostname", "username", "database"],
18
18
  additionalProperties: false,
19
19
  properties: {
20
+ ...props,
20
21
  password: {
21
22
  anyOf: [
22
23
  {
@@ -50,7 +51,8 @@ exports.sqlDumpTaskDefinition = {
50
51
  excludeTables: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
51
52
  oneFileByTable: { type: "boolean" },
52
53
  },
53
- };
54
+ });
55
+ exports.sqlDumpTaskDefinition = sqlDumpTaskDefinition;
54
56
  function serializeSqlFile(input) {
55
57
  if (input.database && input.table) {
56
58
  return `${input.database}.${input.table}.table.sql`;
@@ -880,11 +880,85 @@
880
880
  }
881
881
  },
882
882
  "mysql-dump-task": {
883
- "allOf": [
884
- {
885
- "$ref": "#/definitions/sqldump-task"
883
+ "type": "object",
884
+ "required": [
885
+ "password",
886
+ "hostname",
887
+ "username",
888
+ "database"
889
+ ],
890
+ "additionalProperties": false,
891
+ "properties": {
892
+ "dataFormat": {
893
+ "enum": [
894
+ "csv",
895
+ "sql"
896
+ ]
897
+ },
898
+ "csvSharedPath": {
899
+ "type": "string"
900
+ },
901
+ "password": {
902
+ "anyOf": [
903
+ {
904
+ "type": "string"
905
+ },
906
+ {
907
+ "type": "object",
908
+ "additionalProperties": false,
909
+ "required": [
910
+ "path"
911
+ ],
912
+ "properties": {
913
+ "path": {
914
+ "type": "string"
915
+ }
916
+ }
917
+ }
918
+ ]
919
+ },
920
+ "hostname": {
921
+ "type": "string"
922
+ },
923
+ "port": {
924
+ "type": "integer"
925
+ },
926
+ "username": {
927
+ "type": "string"
928
+ },
929
+ "database": {
930
+ "type": "string"
931
+ },
932
+ "targetDatabase": {
933
+ "type": "object",
934
+ "required": [
935
+ "name"
936
+ ],
937
+ "properties": {
938
+ "name": {
939
+ "type": "string"
940
+ },
941
+ "charset": {
942
+ "type": "string"
943
+ },
944
+ "collate": {
945
+ "type": "string"
946
+ }
947
+ }
948
+ },
949
+ "storedPrograms": {
950
+ "type": "boolean"
951
+ },
952
+ "includeTables": {
953
+ "$ref": "#/definitions/stringlist-util"
954
+ },
955
+ "excludeTables": {
956
+ "$ref": "#/definitions/stringlist-util"
957
+ },
958
+ "oneFileByTable": {
959
+ "type": "boolean"
886
960
  }
887
- ]
961
+ }
888
962
  },
889
963
  "postgresql-dump-task": {
890
964
  "allOf": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "dependencies": {
5
5
  "ajv": "^8.12.0",
6
6
  "async": "^3.2.4",
package/utils/cli.js CHANGED
@@ -65,7 +65,9 @@ function logExec(command, argv = [], env, logToStderr) {
65
65
  .join(" ")
66
66
  : "";
67
67
  const text = `+ ${envText ? envText + " " : ""}${chalk_1.default.yellow(`${command} ${argv.join(" ")}`)}`;
68
- logToStderr ? process.stderr.write(`${text}\n`) : console.info(text);
68
+ logToStderr /* && process.env.VITEST !== "true"*/
69
+ ? process.stderr.write(`${text}\n`)
70
+ : console.info(text);
69
71
  }
70
72
  exports.logExec = logExec;
71
73
  function resultColumn(error, state) {
@@ -15,7 +15,7 @@ type ResolvePackagePathParamsType = ResolvePackageParamsType & {
15
15
  path: string | undefined;
16
16
  };
17
17
  export declare function resolvePackagePath(value: string, params: ResolvePackagePathParamsType): string;
18
- type ResolveDatabaseNameParamsType = ResolvePackageParamsType & {
18
+ export type ResolveDatabaseNameParamsType = ResolvePackageParamsType & {
19
19
  packageName: string;
20
20
  database: string | undefined;
21
21
  };
package/utils/fs.d.ts CHANGED
@@ -96,12 +96,13 @@ export declare function createFileScanner(options: {
96
96
  disposed: boolean;
97
97
  total: number;
98
98
  current: number;
99
- progress: (description: string, path?: string) => Promise<void>;
99
+ progress: (description: string, path?: string, increment?: boolean) => Promise<void>;
100
100
  end: () => Promise<void>;
101
101
  start: (cb?: ((entry: Required<Entry>) => any) | undefined) => Promise<void>;
102
102
  }>;
103
103
  type StreamItem = {
104
104
  key: string;
105
+ lines: number;
105
106
  stream: WriteStream;
106
107
  finished: boolean;
107
108
  error?: Error;
@@ -113,8 +114,10 @@ export declare function createWriteStreamPool(options: {
113
114
  }): {
114
115
  pool: Record<string, StreamItem>;
115
116
  path(key: string | number): string | undefined;
117
+ lines(key: string | number): number;
116
118
  writeLine(key: string | number, v: string): boolean;
117
119
  end(): Promise<void>;
118
120
  };
119
121
  export declare function countFileLines(path: string): Promise<number>;
122
+ export declare function fetchData<T>(input: T, onPath?: (input: Exclude<T, string>) => string | undefined): Promise<string | null>;
120
123
  export {};
package/utils/fs.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
6
+ exports.fetchData = exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
7
7
  const globalData_1 = __importDefault(require("../globalData"));
8
8
  const math_1 = require("./math");
9
9
  const path_1 = require("./path");
@@ -43,9 +43,8 @@ function isLocalDir(path) {
43
43
  }
44
44
  exports.isLocalDir = isLocalDir;
45
45
  async function mkdirIfNotExists(path) {
46
- await (0, promises_1.mkdir)(path, {
47
- recursive: true,
48
- });
46
+ if (!(await existsDir(path)))
47
+ await (0, promises_1.mkdir)(path, { recursive: true });
49
48
  return path;
50
49
  }
51
50
  exports.mkdirIfNotExists = mkdirIfNotExists;
@@ -410,10 +409,10 @@ async function createFileScanner(options) {
410
409
  disposed: false,
411
410
  total: 0,
412
411
  current: 0,
413
- progress: async (description, path) => {
412
+ progress: async (description, path, increment = true) => {
414
413
  if (object.disposed)
415
414
  return;
416
- if (path)
415
+ if (path && increment)
417
416
  object.current++;
418
417
  await options.onProgress({
419
418
  relative: {
@@ -463,6 +462,7 @@ function createWriteStreamPool(options) {
463
462
  const create = (key) => {
464
463
  const item = {
465
464
  key,
465
+ lines: 0,
466
466
  stream: (0, fs_2.createWriteStream)((0, path_2.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
467
467
  finished: false,
468
468
  };
@@ -484,16 +484,22 @@ function createWriteStreamPool(options) {
484
484
  throw new Error(`Stream path is not defined: ${key}`);
485
485
  return item.stream.path;
486
486
  },
487
+ lines(key) {
488
+ const item = pool[key];
489
+ return item?.lines;
490
+ },
487
491
  writeLine(key, v) {
488
492
  const item = pool[key] || create(key.toString());
489
493
  if (item.finished) {
490
494
  return false;
491
495
  }
492
496
  else if (item.written) {
497
+ item.lines++;
493
498
  return item.stream.write(`\n${v}`);
494
499
  }
495
500
  else {
496
501
  item.written = true;
502
+ item.lines++;
497
503
  return item.stream.write(`${v}`);
498
504
  }
499
505
  },
@@ -529,3 +535,12 @@ function countFileLines(path) {
529
535
  });
530
536
  }
531
537
  exports.countFileLines = countFileLines;
538
+ async function fetchData(input, onPath) {
539
+ if (typeof input === "string")
540
+ return input;
541
+ const path = onPath?.(input);
542
+ if (typeof path === "string")
543
+ return (await (0, promises_1.readFile)(path)).toString();
544
+ return null;
545
+ }
546
+ exports.fetchData = fetchData;