rman 0.0.3 → 0.2.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.
Files changed (84) hide show
  1. package/README.md +22 -90
  2. package/bin/rman.js +2 -2
  3. package/cjs/cli.js +57 -0
  4. package/cjs/commands/changed-command.js +38 -0
  5. package/cjs/commands/execute-command.js +79 -0
  6. package/cjs/commands/info-command.js +66 -0
  7. package/cjs/commands/list-command.js +141 -0
  8. package/cjs/commands/multi-task-command.js +70 -0
  9. package/cjs/commands/publish-command.js +74 -0
  10. package/cjs/commands/run-command.js +103 -0
  11. package/cjs/commands/version-command.js +155 -0
  12. package/cjs/core/command.js +110 -0
  13. package/cjs/core/config.js +73 -0
  14. package/cjs/core/logger.js +8 -0
  15. package/cjs/core/package.js +33 -0
  16. package/cjs/core/repository.js +133 -0
  17. package/{dist/workspace → cjs/core}/types.js +0 -0
  18. package/cjs/debug.js +5 -0
  19. package/{dist → cjs}/index.js +1 -1
  20. package/cjs/package.json +3 -0
  21. package/cjs/utils/constants.js +5 -0
  22. package/cjs/utils/exec.js +115 -0
  23. package/cjs/utils/git-utils.js +70 -0
  24. package/cjs/utils/npm-run-path.js +63 -0
  25. package/cjs/utils.js +36 -0
  26. package/esm/cli.d.ts +4 -0
  27. package/esm/cli.mjs +50 -0
  28. package/esm/commands/changed-command.d.ts +16 -0
  29. package/esm/commands/changed-command.mjs +34 -0
  30. package/esm/commands/execute-command.d.ts +18 -0
  31. package/esm/commands/execute-command.mjs +72 -0
  32. package/esm/commands/info-command.d.ts +10 -0
  33. package/esm/commands/info-command.mjs +59 -0
  34. package/esm/commands/list-command.d.ts +38 -0
  35. package/esm/commands/list-command.mjs +134 -0
  36. package/esm/commands/multi-task-command.d.ts +20 -0
  37. package/esm/commands/multi-task-command.mjs +63 -0
  38. package/esm/commands/publish-command.d.ts +17 -0
  39. package/esm/commands/publish-command.mjs +67 -0
  40. package/esm/commands/run-command.d.ts +22 -0
  41. package/esm/commands/run-command.mjs +96 -0
  42. package/esm/commands/version-command.d.ts +24 -0
  43. package/esm/commands/version-command.mjs +148 -0
  44. package/esm/core/command.d.ts +35 -0
  45. package/esm/core/command.mjs +103 -0
  46. package/esm/core/config.d.ts +9 -0
  47. package/esm/core/config.mjs +66 -0
  48. package/esm/core/logger.d.ts +10 -0
  49. package/esm/core/logger.mjs +3 -0
  50. package/esm/core/package.d.ts +11 -0
  51. package/esm/core/package.mjs +26 -0
  52. package/esm/core/repository.d.ts +18 -0
  53. package/esm/core/repository.mjs +126 -0
  54. package/esm/core/types.d.ts +14 -0
  55. package/esm/core/types.mjs +1 -0
  56. package/esm/debug.d.ts +1 -0
  57. package/esm/debug.mjs +3 -0
  58. package/esm/index.d.ts +1 -0
  59. package/esm/index.mjs +1 -0
  60. package/esm/utils/constants.d.ts +2 -0
  61. package/esm/utils/constants.mjs +2 -0
  62. package/{dist/workspace/executor.d.ts → esm/utils/exec.d.ts} +5 -8
  63. package/esm/utils/exec.mjs +108 -0
  64. package/esm/utils/git-utils.d.ts +25 -0
  65. package/esm/utils/git-utils.mjs +63 -0
  66. package/esm/utils/npm-run-path.d.ts +67 -0
  67. package/esm/utils/npm-run-path.mjs +55 -0
  68. package/esm/utils.d.ts +2 -0
  69. package/esm/utils.mjs +28 -0
  70. package/package.json +64 -31
  71. package/bin/debug.ts +0 -4
  72. package/dist/cli.d.ts +0 -1
  73. package/dist/cli.js +0 -71
  74. package/dist/index.d.ts +0 -1
  75. package/dist/workspace/executor.js +0 -120
  76. package/dist/workspace/package.d.ts +0 -27
  77. package/dist/workspace/package.js +0 -43
  78. package/dist/workspace/providers/npm-provider.d.ts +0 -4
  79. package/dist/workspace/providers/npm-provider.js +0 -16
  80. package/dist/workspace/types.d.ts +0 -10
  81. package/dist/workspace/utils.d.ts +0 -6
  82. package/dist/workspace/utils.js +0 -31
  83. package/dist/workspace/workspace.d.ts +0 -17
  84. package/dist/workspace/workspace.js +0 -240
@@ -0,0 +1,103 @@
1
+ import { AsyncEventEmitter, TypedEventEmitterClass } from 'strict-typed-events';
2
+ import npmlog from 'npmlog';
3
+ import isCi from 'is-ci';
4
+ import merge from 'putil-merge';
5
+ import './logger.mjs';
6
+ import { isTTY } from './../utils/constants.mjs';
7
+ const noOp = () => void (0);
8
+ export class Command extends TypedEventEmitterClass(AsyncEventEmitter) {
9
+ constructor(options) {
10
+ super();
11
+ this._started = false;
12
+ this._finished = false;
13
+ this.logger = npmlog;
14
+ this._options = options || {};
15
+ if (isCi)
16
+ this.options.ci = true;
17
+ }
18
+ get options() {
19
+ return this._options;
20
+ }
21
+ get commandName() {
22
+ return Object.getPrototypeOf(this).constructor.commandName;
23
+ }
24
+ async execute() {
25
+ if (this._finished) {
26
+ this._finished = false;
27
+ this._started = false;
28
+ }
29
+ if (!this._started) {
30
+ this._started = true;
31
+ await this.emitAsync('start');
32
+ }
33
+ this._started = true;
34
+ try {
35
+ this.logger.level = this.options.logLevel ||
36
+ (this.options.ci ? 'error' : 'info');
37
+ if (this.options.ci || !isTTY) {
38
+ this.logger.disableColor();
39
+ this.logger.disableUnicode();
40
+ }
41
+ else {
42
+ this.logger.enableColor();
43
+ this.logger.enableUnicode();
44
+ }
45
+ if (this._preExecute)
46
+ await this._preExecute();
47
+ const v = await this._execute();
48
+ if (this._postExecute)
49
+ await this._postExecute();
50
+ await this.emitAsync('finish', undefined, v).catch(noOp);
51
+ this.disableProgress();
52
+ this.logger.resume();
53
+ this.logger.success('', 'Command completed');
54
+ return v;
55
+ }
56
+ catch (e) {
57
+ this.disableProgress();
58
+ await this.emitAsync('finish', e).catch(noOp);
59
+ if (this.listenerCount('error'))
60
+ await this.emitAsync('error', e).catch(noOp);
61
+ this.logger.resume();
62
+ throw e;
63
+ }
64
+ finally {
65
+ this._finished = true;
66
+ }
67
+ }
68
+ async enableProgress() {
69
+ if (this.options.ci || !isTTY || (this._screen && this._screen.visible))
70
+ return;
71
+ }
72
+ disableProgress() {
73
+ }
74
+ }
75
+ (function (Command) {
76
+ Command.globalOptions = {
77
+ 'log-level': {
78
+ defaultDescription: "info",
79
+ describe: "Set log level",
80
+ choices: ['silly', 'verbose', 'info', 'output', 'notice', 'success', 'warn', 'error', 'silent'],
81
+ requiresArg: true
82
+ },
83
+ 'json': {
84
+ alias: 'j',
85
+ describe: '# Stream log as json',
86
+ type: 'boolean'
87
+ },
88
+ 'ci': {
89
+ hidden: true,
90
+ type: "boolean"
91
+ }
92
+ };
93
+ function composeOptions(commandName, yargArgs, config) {
94
+ const result = merge({}, yargArgs);
95
+ merge(result, config, { filter: (_, key) => key !== 'command' });
96
+ const cfgCmd = config.commans && typeof config.command === 'object' ?
97
+ config.command : undefined;
98
+ if (cfgCmd && typeof cfgCmd === 'object')
99
+ merge(result, cfgCmd);
100
+ return result;
101
+ }
102
+ Command.composeOptions = composeOptions;
103
+ })(Command || (Command = {}));
@@ -0,0 +1,9 @@
1
+ export declare class Config {
2
+ data: any;
3
+ constructor(data: any);
4
+ getObject(key: string, def?: any): any | undefined;
5
+ getString(key: string, def?: string): string | undefined;
6
+ getNumber(key: string, def?: number): number | undefined;
7
+ get(key: string): any;
8
+ static resolve(dirname: string): Config;
9
+ }
@@ -0,0 +1,66 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import yaml from 'js-yaml';
4
+ import merge from 'putil-merge';
5
+ import { getPackageJson } from './../utils.mjs';
6
+ export class Config {
7
+ constructor(data) {
8
+ this.data = data;
9
+ }
10
+ getObject(key, def) {
11
+ const v = this.get(key);
12
+ if (v != null && typeof v === 'object')
13
+ return v;
14
+ return def;
15
+ }
16
+ getString(key, def) {
17
+ const v = this.get(key);
18
+ if (v != null && typeof v !== 'object')
19
+ return '' + v;
20
+ return def;
21
+ }
22
+ getNumber(key, def) {
23
+ const v = this.get(key);
24
+ if (v != null && typeof v !== 'object') {
25
+ const n = parseFloat(v);
26
+ if (!isNaN(n))
27
+ return n;
28
+ }
29
+ return def;
30
+ }
31
+ get(key) {
32
+ const keys = key.split('.');
33
+ let o = this.data;
34
+ for (let i = 0; i < keys.length; i++) {
35
+ const k = keys[i];
36
+ if (o.hasOwnProperty(k)) {
37
+ if (i === keys.length - 1)
38
+ return o[k];
39
+ if (o[k] && typeof o[k] === 'object') {
40
+ o = o[k];
41
+ }
42
+ else
43
+ return;
44
+ }
45
+ }
46
+ }
47
+ static resolve(dirname) {
48
+ const data = {};
49
+ const pkgJson = getPackageJson(dirname);
50
+ if (pkgJson && typeof pkgJson.rman === 'object')
51
+ merge(data, pkgJson.rman, { deep: true });
52
+ let filename = path.resolve(dirname, '.rman.yml');
53
+ if (fs.existsSync(filename)) {
54
+ const obj = yaml.load(fs.readFileSync(filename, 'utf-8'));
55
+ if (obj && typeof obj === 'object')
56
+ merge(data, obj, { deep: true });
57
+ }
58
+ filename = path.resolve(dirname, '.rmanrc');
59
+ if (fs.existsSync(filename)) {
60
+ const obj = JSON.parse(fs.readFileSync(filename, 'utf-8'));
61
+ if (obj && typeof obj === 'object')
62
+ merge(data, obj, { deep: true });
63
+ }
64
+ return new Config(data);
65
+ }
66
+ }
@@ -0,0 +1,10 @@
1
+ declare module 'npmlog' {
2
+ interface Logger {
3
+ output(prefix: string, message: string, ...args: any[]): void;
4
+ success(prefix: string, message: string, ...args: any[]): void;
5
+ disp: Record<string, string>;
6
+ showProgress(): any;
7
+ hideProgress(): any;
8
+ }
9
+ }
10
+ export {};
@@ -0,0 +1,3 @@
1
+ import logger from 'npmlog';
2
+ logger.addLevel("success", 1500, { fg: "green", bold: true });
3
+ logger.addLevel('output', 3300, {}, '');
@@ -0,0 +1,11 @@
1
+ export declare class Package {
2
+ readonly dirname: string;
3
+ private _json;
4
+ dependencies: string[];
5
+ constructor(dirname: string);
6
+ get name(): string;
7
+ get version(): string;
8
+ get json(): any;
9
+ get isPrivate(): boolean;
10
+ reloadJson(): any;
11
+ }
@@ -0,0 +1,26 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ export class Package {
4
+ constructor(dirname) {
5
+ this.dirname = dirname;
6
+ this.dependencies = [];
7
+ this.reloadJson();
8
+ }
9
+ get name() {
10
+ return this._json.name;
11
+ }
12
+ get version() {
13
+ return this._json.version;
14
+ }
15
+ get json() {
16
+ return this._json;
17
+ }
18
+ get isPrivate() {
19
+ return !!this._json.private;
20
+ }
21
+ reloadJson() {
22
+ const f = path.join(this.dirname, 'package.json');
23
+ this._json = JSON.parse(fs.readFileSync(f, 'utf-8'));
24
+ return this._json;
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ import { Package } from './package';
2
+ export declare class Repository extends Package {
3
+ readonly dirname: string;
4
+ readonly config: any;
5
+ readonly packages: Package[];
6
+ protected constructor(dirname: string, config: any, packages: Package[]);
7
+ getPackages(options?: {
8
+ scope?: string | string[];
9
+ toposort?: boolean;
10
+ }): Package[];
11
+ getPackage(name: string): Package | undefined;
12
+ protected _updateDependencies(): void;
13
+ static create(root?: string, options?: {
14
+ deep?: number;
15
+ }): Repository;
16
+ protected static _resolvePackages(dirname: string, patterns: string[]): Package[];
17
+ protected static _readConfig(dirname: string): any;
18
+ }
@@ -0,0 +1,126 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import glob from 'fast-glob';
4
+ import merge from 'putil-merge';
5
+ import yaml from 'js-yaml';
6
+ import { Package } from './package.mjs';
7
+ import { getPackageJson } from './../utils.mjs';
8
+ export class Repository extends Package {
9
+ constructor(dirname, config, packages) {
10
+ super(dirname);
11
+ this.dirname = dirname;
12
+ this.config = config;
13
+ this.packages = packages;
14
+ }
15
+ getPackages(options) {
16
+ const result = [...this.packages];
17
+ if (options?.toposort)
18
+ topoSortPackages(result);
19
+ return result;
20
+ }
21
+ getPackage(name) {
22
+ return this.packages.find(p => p.name === name);
23
+ }
24
+ _updateDependencies() {
25
+ const deps = {};
26
+ for (const pkg of this.packages) {
27
+ const o = {
28
+ ...pkg.json.dependencies,
29
+ ...pkg.json.devDependencies,
30
+ ...pkg.json.peerDependencies,
31
+ ...pkg.json.optionalDependencies
32
+ };
33
+ const dependencies = [];
34
+ for (const k of Object.keys(o)) {
35
+ const p = this.getPackage(k);
36
+ if (p)
37
+ dependencies.push(k);
38
+ }
39
+ deps[pkg.name] = dependencies;
40
+ pkg.dependencies = dependencies;
41
+ }
42
+ let circularCheck;
43
+ const deepFindDependencies = (pkg, target) => {
44
+ if (circularCheck.includes(pkg.name))
45
+ return;
46
+ circularCheck.push(pkg.name);
47
+ for (const s of pkg.dependencies) {
48
+ if (!target.includes(s)) {
49
+ target.push(s);
50
+ const p = this.getPackage(s);
51
+ if (p) {
52
+ deepFindDependencies(p, target);
53
+ }
54
+ }
55
+ }
56
+ };
57
+ for (const pkg of this.packages) {
58
+ circularCheck = [];
59
+ deepFindDependencies(pkg, pkg.dependencies);
60
+ }
61
+ }
62
+ static create(root, options) {
63
+ let dirname = root || process.cwd();
64
+ let deep = options?.deep ?? 10;
65
+ while (deep-- >= 0 && fs.existsSync(dirname)) {
66
+ const f = path.join(dirname, 'package.json');
67
+ if (fs.existsSync(f)) {
68
+ const pkgJson = JSON.parse(fs.readFileSync(f, 'utf-8'));
69
+ if (Array.isArray(pkgJson.workspaces)) {
70
+ const packages = this._resolvePackages(dirname, pkgJson.workspaces);
71
+ const config = this._readConfig(dirname);
72
+ const repo = new Repository(dirname, config, packages);
73
+ repo._updateDependencies();
74
+ return repo;
75
+ }
76
+ }
77
+ dirname = path.resolve(dirname, '..');
78
+ }
79
+ throw new Error('No monorepo project detected');
80
+ }
81
+ static _resolvePackages(dirname, patterns) {
82
+ const packages = [];
83
+ for (const pattern of patterns) {
84
+ const dirs = glob.sync(pattern, {
85
+ cwd: dirname,
86
+ absolute: true,
87
+ deep: 0,
88
+ onlyDirectories: true
89
+ });
90
+ for (const dir of dirs) {
91
+ const f = path.join(dir, 'package.json');
92
+ if (fs.existsSync(f))
93
+ packages.push(new Package(dir));
94
+ }
95
+ }
96
+ return packages;
97
+ }
98
+ static _readConfig(dirname) {
99
+ const result = {};
100
+ const pkgJson = getPackageJson(dirname);
101
+ if (pkgJson && typeof pkgJson.rman === 'object')
102
+ merge(result, pkgJson.rman, { deep: true });
103
+ let filename = path.resolve(dirname, '.rman.yml');
104
+ if (fs.existsSync(filename)) {
105
+ const obj = yaml.load(fs.readFileSync(filename, 'utf-8'));
106
+ if (obj && typeof obj === 'object')
107
+ merge(result, obj, { deep: true });
108
+ }
109
+ filename = path.resolve(dirname, '.rmanrc');
110
+ if (fs.existsSync(filename)) {
111
+ const obj = JSON.parse(fs.readFileSync(filename, 'utf-8'));
112
+ if (obj && typeof obj === 'object')
113
+ merge(result, obj, { deep: true });
114
+ }
115
+ return result;
116
+ }
117
+ }
118
+ function topoSortPackages(packages) {
119
+ packages.sort((a, b) => {
120
+ if (b.dependencies.includes(a.name))
121
+ return -1;
122
+ if (a.dependencies.includes(b.name))
123
+ return 1;
124
+ return 0;
125
+ });
126
+ }
@@ -0,0 +1,14 @@
1
+ export interface IWorkspaceProvider {
2
+ parse: (root: string) => IWorkspaceInfo | undefined;
3
+ }
4
+ export interface IWorkspaceInfo {
5
+ dirname: string;
6
+ pkgJson: any;
7
+ packages: string[];
8
+ }
9
+ export interface Logger {
10
+ info: (message?: any, ...optionalParams: any[]) => void;
11
+ error: (message?: any, ...optionalParams: any[]) => void;
12
+ log: (message?: any, ...optionalParams: any[]) => void;
13
+ warn: (message?: any, ...optionalParams: any[]) => void;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
package/esm/debug.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/esm/debug.mjs ADDED
@@ -0,0 +1,3 @@
1
+ import { runCli } from './cli.mjs';
2
+ runCli({ cwd: '/Users/ehanoglu/Yazilim/js/sqb' })
3
+ .catch(() => 0);
package/esm/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './core/repository';
package/esm/index.mjs ADDED
@@ -0,0 +1 @@
1
+ export * from './core/repository.mjs';
@@ -0,0 +1,2 @@
1
+ export declare const DOTS: string[];
2
+ export declare const isTTY: boolean;
@@ -0,0 +1,2 @@
1
+ export const DOTS = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
2
+ export const isTTY = process.stdout.isTTY && process.env.TERM !== 'dumb';
@@ -6,18 +6,15 @@ export interface IExecutorOptions {
6
6
  argv?: string[];
7
7
  env?: Record<string, string | undefined>;
8
8
  shell?: boolean;
9
- color?: boolean;
10
9
  onSpawn?: (childProcess: ChildProcess) => void;
11
10
  onLine?: (line: string, stdio: 'stderr' | 'stdout') => void;
12
11
  onData?: (data: string, stdio: 'stderr' | 'stdout') => void;
13
- }
14
- export interface IRunScriptOptions {
15
- gauge?: boolean;
12
+ throwOnError?: boolean;
16
13
  }
17
14
  export interface ExecuteCommandResult {
18
15
  code?: number;
19
- error?: any;
20
- stderr: string;
21
- stdout: string;
16
+ error?: Error;
17
+ stderr?: string;
18
+ stdout?: string;
22
19
  }
23
- export declare function executeCommand(command: string, options?: IExecutorOptions): Promise<ExecuteCommandResult>;
20
+ export declare function exec(command: string, options?: IExecutorOptions): Promise<ExecuteCommandResult>;
@@ -0,0 +1,108 @@
1
+ import { spawn } from 'child_process';
2
+ import onExit from 'signal-exit';
3
+ import { npmRunPathEnv } from './npm-run-path.mjs';
4
+ const runningChildren = new Map();
5
+ export async function exec(command, options) {
6
+ const opts = {
7
+ shell: true,
8
+ throwOnError: true,
9
+ ...options,
10
+ };
11
+ opts.env = {
12
+ ...npmRunPathEnv({ cwd: opts.cwd }),
13
+ ...opts.env
14
+ };
15
+ opts.cwd = opts.cwd || process.cwd();
16
+ const spawnOptions = {
17
+ stdio: opts.stdio || 'pipe',
18
+ env: opts.env,
19
+ cwd: opts.cwd,
20
+ shell: opts.shell,
21
+ windowsHide: true
22
+ };
23
+ const result = {
24
+ code: undefined,
25
+ stderr: '',
26
+ stdout: ''
27
+ };
28
+ const buffer = {
29
+ stdout: '',
30
+ stderr: ''
31
+ };
32
+ const processData = (data, stdio) => {
33
+ buffer[stdio] += data;
34
+ result[stdio] += data;
35
+ if (opts.onData)
36
+ opts.onData(data, stdio);
37
+ };
38
+ const processLines = (stdio, flush) => {
39
+ let chunk = buffer[stdio];
40
+ let i;
41
+ if (flush && !chunk.endsWith('\n'))
42
+ chunk += '\n';
43
+ while ((i = chunk.indexOf('\n')) >= 0) {
44
+ const line = chunk.substring(0, i);
45
+ chunk = chunk.substring(i + 1);
46
+ if (opts.onLine)
47
+ opts.onLine(line, stdio);
48
+ }
49
+ buffer[stdio] = chunk;
50
+ };
51
+ const child = spawn(command, opts.argv || [], spawnOptions);
52
+ if (child.pid) {
53
+ runningChildren.set(child.pid, child);
54
+ if (opts.onSpawn)
55
+ opts.onSpawn(child);
56
+ }
57
+ child.stdout?.on('data', (data) => {
58
+ processData(data, 'stdout');
59
+ processLines('stdout');
60
+ });
61
+ child.stderr?.on('data', (data) => {
62
+ processData(data, 'stderr');
63
+ processLines('stderr');
64
+ });
65
+ return new Promise((resolve, reject) => {
66
+ let resolved;
67
+ child.on('error', (err) => {
68
+ if (child.pid)
69
+ runningChildren.delete(child.pid);
70
+ processLines('stdout', true);
71
+ processLines('stderr', true);
72
+ if (resolved)
73
+ return;
74
+ result.code = err.code || 1;
75
+ if (!err) {
76
+ const text = `Command failed (${result.code})`;
77
+ err = new Error(text);
78
+ }
79
+ if (typeof err === 'string')
80
+ err = new Error(err);
81
+ result.error = err;
82
+ resolved = true;
83
+ if (opts.throwOnError)
84
+ return reject(result.error);
85
+ resolve(result);
86
+ });
87
+ child.on('close', (code) => {
88
+ if (child.pid)
89
+ runningChildren.delete(child.pid);
90
+ processLines('stdout', true);
91
+ processLines('stderr', true);
92
+ if (resolved)
93
+ return;
94
+ result.code = code;
95
+ resolved = true;
96
+ if (code) {
97
+ const text = `Command failed (${result.code})`;
98
+ result.error = new Error(text);
99
+ }
100
+ return resolve(result);
101
+ });
102
+ });
103
+ }
104
+ onExit(() => {
105
+ runningChildren.forEach((child) => {
106
+ child.kill();
107
+ });
108
+ });
@@ -0,0 +1,25 @@
1
+ export declare namespace GitHelper {
2
+ interface FileStatus {
3
+ filename: string;
4
+ status: string;
5
+ }
6
+ }
7
+ export interface GitOptions {
8
+ cwd?: string;
9
+ }
10
+ export declare class GitHelper {
11
+ cwd: string;
12
+ constructor(options?: GitOptions);
13
+ listDirtyFileStatus(options?: {
14
+ absolute?: boolean;
15
+ }): Promise<GitHelper.FileStatus[]>;
16
+ listDirtyFiles(options?: {
17
+ absolute?: boolean;
18
+ }): Promise<string[]>;
19
+ listCommitSha(): Promise<string[]>;
20
+ listCommittedFiles(options?: {
21
+ absolute?: boolean;
22
+ commits?: string | string[];
23
+ }): Promise<string[]>;
24
+ readFileLastPublished(filePath: string, commitSha?: string): Promise<string>;
25
+ }
@@ -0,0 +1,63 @@
1
+ import { exec } from './exec.mjs';
2
+ import path from 'path';
3
+ export class GitHelper {
4
+ constructor(options) {
5
+ this.cwd = options?.cwd || process.cwd();
6
+ }
7
+ async listDirtyFileStatus(options) {
8
+ const x = await exec('git', {
9
+ cwd: this.cwd,
10
+ argv: ['status', '--porcelain']
11
+ });
12
+ const result = [];
13
+ const files = x.stdout ? x.stdout.trim().split(/\s*\n\s*/) : [];
14
+ for (const f of files) {
15
+ const m = f.match(/^(\w+) (.+)$/);
16
+ if (m) {
17
+ result.push({
18
+ filename: options?.absolute ? path.join(this.cwd, m[2]) : m[2],
19
+ status: m[1]
20
+ });
21
+ }
22
+ }
23
+ return result;
24
+ }
25
+ async listDirtyFiles(options) {
26
+ return (await this.listDirtyFileStatus(options)).map(x => x.filename);
27
+ }
28
+ async listCommitSha() {
29
+ const x = await exec('git', {
30
+ cwd: this.cwd,
31
+ argv: ['cherry']
32
+ });
33
+ const matches = x.stdout ? x.stdout.matchAll(/([a-f0-9]+)/gi) : [];
34
+ const result = [];
35
+ for (const m of matches) {
36
+ result.push(m[1]);
37
+ }
38
+ return result;
39
+ }
40
+ async listCommittedFiles(options) {
41
+ const shaArr = options?.commits
42
+ ? (Array.isArray(options?.commits) ? options?.commits : [options?.commits])
43
+ : await this.listCommitSha();
44
+ let result = [];
45
+ for (const s of shaArr) {
46
+ const x = await exec('git', {
47
+ cwd: this.cwd,
48
+ argv: ['show', s, '--name-only', '--pretty="format:"']
49
+ });
50
+ result.push(...(x.stdout ? x.stdout.trim().split(/\s*\n\s*/) : []));
51
+ }
52
+ if (options?.absolute)
53
+ result = result.map(f => path.join(this.cwd, f));
54
+ return result;
55
+ }
56
+ async readFileLastPublished(filePath, commitSha) {
57
+ const x = await exec('git', {
58
+ cwd: this.cwd,
59
+ argv: ['show', (commitSha || 'HEAD') + ':"' + filePath + '"']
60
+ });
61
+ return x.stdout || '';
62
+ }
63
+ }