codify-plugin-lib 1.0.182-beta4 → 1.0.182-beta6

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.
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export * from './resource/parsed-resource-settings.js';
10
10
  export * from './resource/resource.js';
11
11
  export * from './resource/resource-settings.js';
12
12
  export * from './stateful-parameter/stateful-parameter.js';
13
+ export * from './utils/functions.js';
13
14
  export * from './utils/index.js';
14
15
  export * from './utils/verbosity-level.js';
15
- export * from './utils/functions.js';
16
16
  export declare function runPlugin(plugin: Plugin): Promise<void>;
package/dist/index.js CHANGED
@@ -10,9 +10,9 @@ export * from './resource/parsed-resource-settings.js';
10
10
  export * from './resource/resource.js';
11
11
  export * from './resource/resource-settings.js';
12
12
  export * from './stateful-parameter/stateful-parameter.js';
13
+ export * from './utils/functions.js';
13
14
  export * from './utils/index.js';
14
15
  export * from './utils/verbosity-level.js';
15
- export * from './utils/functions.js';
16
16
  export async function runPlugin(plugin) {
17
17
  const messageHandler = new MessageHandler(plugin);
18
18
  process.on('message', (message) => messageHandler.onMessage(message));
@@ -35,7 +35,7 @@ export class SequentialPty {
35
35
  // Initial terminal dimensions
36
36
  const initialCols = process.stdout.columns ?? 80;
37
37
  const initialRows = process.stdout.rows ?? 24;
38
- const args = (options?.interactive ?? false) ? ['-i', '-c', `"${cmd}"`] : ['-c', `"${cmd}"`];
38
+ const args = (options?.interactive ?? false) ? ['-i', '-c', cmd] : ['-c', cmd];
39
39
  // Run the command in a pty for interactivity
40
40
  const mPty = pty.spawn(this.getDefaultShell(), args, {
41
41
  ...options,
@@ -0,0 +1,16 @@
1
+ export declare class FileUtils {
2
+ static downloadFile(url: string, destination: string): Promise<void>;
3
+ static addToStartupFile(line: string): Promise<void>;
4
+ static addAllToStartupFile(lines: string[]): Promise<void>;
5
+ static addPathToPrimaryShellRc(value: string, prepend: boolean): Promise<void>;
6
+ static dirExists(path: string): Promise<boolean>;
7
+ static fileExists(path: string): Promise<boolean>;
8
+ static exists(path: string): Promise<boolean>;
9
+ static checkDirExistsOrThrowIfFile(path: string): Promise<boolean>;
10
+ static createDirIfNotExists(path: string): Promise<void>;
11
+ static removeFromFile(filePath: string, search: string): Promise<void>;
12
+ static removeLineFromFile(filePath: string, search: RegExp | string): Promise<void>;
13
+ static removeLineFromPrimaryShellRc(search: RegExp | string): Promise<void>;
14
+ static appendToFileWithSpacing(file: string, textToInsert: string): string;
15
+ private static calculateEndingNewLines;
16
+ }
@@ -0,0 +1,172 @@
1
+ import * as fsSync from 'node:fs';
2
+ import * as fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { Readable } from 'node:stream';
5
+ import { finished } from 'node:stream/promises';
6
+ import { Utils } from './index.js';
7
+ const SPACE_REGEX = /^\s*$/;
8
+ export class FileUtils {
9
+ static async downloadFile(url, destination) {
10
+ console.log(`Downloading file from ${url} to ${destination}`);
11
+ const { body } = await fetch(url);
12
+ const dirname = path.dirname(destination);
13
+ if (!await fs.stat(dirname).then((s) => s.isDirectory()).catch(() => false)) {
14
+ await fs.mkdir(dirname, { recursive: true });
15
+ }
16
+ const ws = fsSync.createWriteStream(destination);
17
+ // Different type definitions here for readable stream (NodeJS vs DOM). Small hack to fix that
18
+ await finished(Readable.fromWeb(body).pipe(ws));
19
+ console.log(`Finished downloading to ${destination}`);
20
+ }
21
+ static async addToStartupFile(line) {
22
+ const lineToInsert = addLeadingSpacer(addTrailingSpacer(line));
23
+ await fs.appendFile(Utils.getPrimaryShellRc(), lineToInsert);
24
+ function addLeadingSpacer(line) {
25
+ return line.startsWith('\n')
26
+ ? line
27
+ : '\n' + line;
28
+ }
29
+ function addTrailingSpacer(line) {
30
+ return line.endsWith('\n')
31
+ ? line
32
+ : line + '\n';
33
+ }
34
+ }
35
+ static async addAllToStartupFile(lines) {
36
+ const formattedLines = '\n' + lines.join('\n') + '\n';
37
+ const shellRc = Utils.getPrimaryShellRc();
38
+ console.log(`Adding to ${path.basename(shellRc)}:
39
+ ${lines.join('\n')}`);
40
+ await fs.appendFile(shellRc, formattedLines);
41
+ }
42
+ static async addPathToPrimaryShellRc(value, prepend) {
43
+ const shellRc = Utils.getPrimaryShellRc();
44
+ console.log(`Saving path: ${value} to ${shellRc}`);
45
+ if (prepend) {
46
+ await fs.appendFile(shellRc, `\nexport PATH=$PATH:${value};`, { encoding: 'utf8' });
47
+ return;
48
+ }
49
+ await fs.appendFile(shellRc, `\nexport PATH=${value}:$PATH;`, { encoding: 'utf8' });
50
+ }
51
+ static async dirExists(path) {
52
+ let stat;
53
+ try {
54
+ stat = await fs.stat(path);
55
+ return stat.isDirectory();
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ static async fileExists(path) {
62
+ let stat;
63
+ try {
64
+ stat = await fs.stat(path);
65
+ return stat.isFile();
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ static async exists(path) {
72
+ try {
73
+ await fs.stat(path);
74
+ return true;
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }
80
+ static async checkDirExistsOrThrowIfFile(path) {
81
+ let stat;
82
+ try {
83
+ stat = await fs.stat(path);
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ if (stat.isDirectory()) {
89
+ return true;
90
+ }
91
+ throw new Error(`Directory ${path} already exists and is a file`);
92
+ }
93
+ static async createDirIfNotExists(path) {
94
+ if (!fsSync.existsSync(path)) {
95
+ await fs.mkdir(path, { recursive: true });
96
+ }
97
+ }
98
+ static async removeFromFile(filePath, search) {
99
+ const contents = await fs.readFile(filePath, 'utf8');
100
+ const newContents = contents.replaceAll(search, '');
101
+ await fs.writeFile(filePath, newContents, 'utf8');
102
+ }
103
+ static async removeLineFromFile(filePath, search) {
104
+ const file = await fs.readFile(filePath, 'utf8');
105
+ const lines = file.split('\n');
106
+ let searchRegex;
107
+ let searchString;
108
+ if (typeof search === 'object') {
109
+ const startRegex = /^([\t ]*)?/;
110
+ const endRegex = /([\t ]*)?/;
111
+ // Augment regex with spaces criteria to make sure this function is not deleting lines that are comments or has other content.
112
+ searchRegex = search
113
+ ? new RegExp(startRegex.source + search.source + endRegex.source, search.flags)
114
+ : search;
115
+ }
116
+ if (typeof search === 'string') {
117
+ searchString = search;
118
+ }
119
+ for (let counter = lines.length; counter >= 0; counter--) {
120
+ if (!lines[counter]) {
121
+ continue;
122
+ }
123
+ if (searchString && lines[counter].includes(searchString)) {
124
+ lines.splice(counter, 1);
125
+ continue;
126
+ }
127
+ if (searchRegex && lines[counter].search(searchRegex) !== -1) {
128
+ lines.splice(counter, 1);
129
+ }
130
+ }
131
+ await fs.writeFile(filePath, lines.join('\n'));
132
+ console.log(`Removed line: ${search} from ${filePath}`);
133
+ }
134
+ static async removeLineFromPrimaryShellRc(search) {
135
+ return FileUtils.removeLineFromFile(Utils.getPrimaryShellRc(), search);
136
+ }
137
+ // Append the string to the end of a file ensuring at least 1 lines of space between.
138
+ // Ex result:
139
+ // something something;
140
+ //
141
+ // newline;
142
+ static appendToFileWithSpacing(file, textToInsert) {
143
+ const lines = file.trimEnd().split(/\n/);
144
+ if (lines.length === 0) {
145
+ return textToInsert;
146
+ }
147
+ const endingNewLines = FileUtils.calculateEndingNewLines(lines);
148
+ const numNewLines = endingNewLines === -1
149
+ ? 0
150
+ : Math.max(0, 2 - endingNewLines);
151
+ return lines.join('\n') + '\n'.repeat(numNewLines) + textToInsert;
152
+ }
153
+ // This is overly complicated but it can be used to insert into any
154
+ // position in the future
155
+ static calculateEndingNewLines(lines) {
156
+ let counter = 0;
157
+ while (true) {
158
+ const line = lines.at(-counter - 1);
159
+ if (!line) {
160
+ return -1;
161
+ }
162
+ if (!SPACE_REGEX.test(line)) {
163
+ return counter;
164
+ }
165
+ counter++;
166
+ // Short circuit here because we don't need to check over 2;
167
+ if (counter > 2) {
168
+ return counter;
169
+ }
170
+ }
171
+ }
172
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codify-plugin-lib",
3
- "version": "1.0.182-beta4",
3
+ "version": "1.0.182-beta6",
4
4
  "description": "Library plugin library",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -12,9 +12,9 @@ export * from './resource/parsed-resource-settings.js';
12
12
  export * from './resource/resource.js'
13
13
  export * from './resource/resource-settings.js'
14
14
  export * from './stateful-parameter/stateful-parameter.js'
15
+ export * from './utils/functions.js'
15
16
  export * from './utils/index.js'
16
17
  export * from './utils/verbosity-level.js'
17
- export * from './utils/functions.js'
18
18
 
19
19
  export async function runPlugin(plugin: Plugin) {
20
20
  const messageHandler = new MessageHandler(plugin);
@@ -46,7 +46,7 @@ export class SequentialPty implements IPty {
46
46
  const initialCols = process.stdout.columns ?? 80;
47
47
  const initialRows = process.stdout.rows ?? 24;
48
48
 
49
- const args = (options?.interactive ?? false) ? ['-i', '-c', `"${cmd}"`] : ['-c', `"${cmd}"`]
49
+ const args = (options?.interactive ?? false) ? ['-i', '-c', cmd] : ['-c', cmd]
50
50
 
51
51
  // Run the command in a pty for interactivity
52
52
  const mPty = pty.spawn(this.getDefaultShell(), args, {
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest';
2
2
  import { SequentialPty } from './seqeuntial-pty.js';
3
- import { VerbosityLevel } from '../utils/functions.js';
3
+ import { VerbosityLevel } from '../utils/verbosity-level.js';
4
4
 
5
5
  describe('SequentialPty tests', () => {
6
6
  it('Can launch a simple command', async () => {
@@ -52,7 +52,7 @@ describe('SequentialPty tests', () => {
52
52
  it('It can launch a command in interactive mode', async () => {
53
53
  const pty = new SequentialPty();
54
54
 
55
- const resultSuccess = await pty.spawnSafe('ls', { interactive: false });
55
+ const resultSuccess = await pty.spawnSafe('ls', { interactive: true });
56
56
  expect(resultSuccess).toMatchObject({
57
57
  status: 'success',
58
58
  exitCode: 0,
@@ -0,0 +1,216 @@
1
+ import * as fsSync from 'node:fs';
2
+ import * as fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { Readable } from 'node:stream';
5
+ import { finished } from 'node:stream/promises';
6
+
7
+ import { Utils } from './index.js';
8
+
9
+ const SPACE_REGEX = /^\s*$/
10
+
11
+ export class FileUtils {
12
+ static async downloadFile(url: string, destination: string): Promise<void> {
13
+ console.log(`Downloading file from ${url} to ${destination}`);
14
+ const { body } = await fetch(url)
15
+
16
+ const dirname = path.dirname(destination);
17
+ if (!await fs.stat(dirname).then((s) => s.isDirectory()).catch(() => false)) {
18
+ await fs.mkdir(dirname, { recursive: true });
19
+ }
20
+
21
+ const ws = fsSync.createWriteStream(destination)
22
+ // Different type definitions here for readable stream (NodeJS vs DOM). Small hack to fix that
23
+ await finished(Readable.fromWeb(body as never).pipe(ws));
24
+
25
+ console.log(`Finished downloading to ${destination}`);
26
+ }
27
+
28
+ static async addToStartupFile(line: string): Promise<void> {
29
+ const lineToInsert = addLeadingSpacer(
30
+ addTrailingSpacer(line)
31
+ );
32
+
33
+ await fs.appendFile(Utils.getPrimaryShellRc(), lineToInsert)
34
+
35
+ function addLeadingSpacer(line: string): string {
36
+ return line.startsWith('\n')
37
+ ? line
38
+ : '\n' + line;
39
+ }
40
+
41
+ function addTrailingSpacer(line: string): string {
42
+ return line.endsWith('\n')
43
+ ? line
44
+ : line + '\n';
45
+ }
46
+ }
47
+
48
+ static async addAllToStartupFile(lines: string[]): Promise<void> {
49
+ const formattedLines = '\n' + lines.join('\n') + '\n';
50
+ const shellRc = Utils.getPrimaryShellRc();
51
+
52
+ console.log(`Adding to ${path.basename(shellRc)}:
53
+ ${lines.join('\n')}`)
54
+
55
+ await fs.appendFile(shellRc, formattedLines)
56
+ }
57
+
58
+ static async addPathToPrimaryShellRc(value: string, prepend: boolean): Promise<void> {
59
+ const shellRc = Utils.getPrimaryShellRc();
60
+ console.log(`Saving path: ${value} to ${shellRc}`);
61
+
62
+ if (prepend) {
63
+ await fs.appendFile(shellRc, `\nexport PATH=$PATH:${value};`, { encoding: 'utf8' });
64
+ return;
65
+ }
66
+
67
+ await fs.appendFile(shellRc, `\nexport PATH=${value}:$PATH;`, { encoding: 'utf8' });
68
+ }
69
+
70
+ static async dirExists(path: string): Promise<boolean> {
71
+ let stat;
72
+ try {
73
+ stat = await fs.stat(path);
74
+ return stat.isDirectory();
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+
80
+ static async fileExists(path: string): Promise<boolean> {
81
+ let stat;
82
+ try {
83
+ stat = await fs.stat(path);
84
+ return stat.isFile();
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ static async exists(path: string): Promise<boolean> {
91
+ try {
92
+ await fs.stat(path);
93
+ return true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ static async checkDirExistsOrThrowIfFile(path: string): Promise<boolean> {
100
+ let stat;
101
+ try {
102
+ stat = await fs.stat(path);
103
+ } catch {
104
+ return false;
105
+ }
106
+
107
+ if (stat.isDirectory()) {
108
+ return true;
109
+ }
110
+
111
+ throw new Error(`Directory ${path} already exists and is a file`);
112
+ }
113
+
114
+ static async createDirIfNotExists(path: string): Promise<void> {
115
+ if (!fsSync.existsSync(path)) {
116
+ await fs.mkdir(path, { recursive: true });
117
+ }
118
+ }
119
+
120
+ static async removeFromFile(filePath: string, search: string): Promise<void> {
121
+ const contents = await fs.readFile(filePath, 'utf8');
122
+ const newContents = contents.replaceAll(search, '');
123
+
124
+ await fs.writeFile(filePath, newContents, 'utf8');
125
+ }
126
+
127
+
128
+ static async removeLineFromFile(filePath: string, search: RegExp | string): Promise<void> {
129
+ const file = await fs.readFile(filePath, 'utf8')
130
+ const lines = file.split('\n');
131
+
132
+ let searchRegex;
133
+ let searchString;
134
+
135
+ if (typeof search === 'object') {
136
+ const startRegex = /^([\t ]*)?/;
137
+ const endRegex = /([\t ]*)?/;
138
+
139
+ // Augment regex with spaces criteria to make sure this function is not deleting lines that are comments or has other content.
140
+ searchRegex = search
141
+ ? new RegExp(
142
+ startRegex.source + search.source + endRegex.source,
143
+ search.flags
144
+ )
145
+ : search;
146
+ }
147
+
148
+ if (typeof search === 'string') {
149
+ searchString = search;
150
+ }
151
+
152
+ for (let counter = lines.length; counter >= 0; counter--) {
153
+ if (!lines[counter]) {
154
+ continue;
155
+ }
156
+
157
+ if (searchString && lines[counter].includes(searchString)) {
158
+ lines.splice(counter, 1);
159
+ continue;
160
+ }
161
+
162
+ if (searchRegex && lines[counter].search(searchRegex) !== -1) {
163
+ lines.splice(counter, 1);
164
+ }
165
+ }
166
+
167
+ await fs.writeFile(filePath, lines.join('\n'));
168
+ console.log(`Removed line: ${search} from ${filePath}`)
169
+ }
170
+
171
+ static async removeLineFromPrimaryShellRc(search: RegExp | string): Promise<void> {
172
+ return FileUtils.removeLineFromFile(Utils.getPrimaryShellRc(), search);
173
+ }
174
+
175
+ // Append the string to the end of a file ensuring at least 1 lines of space between.
176
+ // Ex result:
177
+ // something something;
178
+ //
179
+ // newline;
180
+ static appendToFileWithSpacing(file: string, textToInsert: string): string {
181
+ const lines = file.trimEnd().split(/\n/);
182
+ if (lines.length === 0) {
183
+ return textToInsert;
184
+ }
185
+
186
+ const endingNewLines = FileUtils.calculateEndingNewLines(lines);
187
+ const numNewLines = endingNewLines === -1
188
+ ? 0
189
+ : Math.max(0, 2 - endingNewLines);
190
+ return lines.join('\n') + '\n'.repeat(numNewLines) + textToInsert
191
+ }
192
+
193
+ // This is overly complicated but it can be used to insert into any
194
+ // position in the future
195
+ private static calculateEndingNewLines(lines: string[]): number {
196
+ let counter = 0;
197
+ while (true) {
198
+ const line = lines.at(-counter - 1);
199
+
200
+ if (!line) {
201
+ return -1
202
+ }
203
+
204
+ if (!SPACE_REGEX.test(line)) {
205
+ return counter;
206
+ }
207
+
208
+ counter++;
209
+
210
+ // Short circuit here because we don't need to check over 2;
211
+ if (counter > 2) {
212
+ return counter;
213
+ }
214
+ }
215
+ }
216
+ }