@stacksjs/storage 0.58.47 → 0.58.49
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/package.json +10 -9
- package/src/copy.ts +42 -0
- package/src/delete.ts +131 -0
- package/src/drivers/aws.ts +15 -0
- package/src/drivers/index.ts +1 -0
- package/src/files.ts +190 -0
- package/src/folders.ts +51 -0
- package/src/fs.ts +9 -0
- package/src/glob.ts +1 -0
- package/src/hash.ts +39 -0
- package/src/helpers.ts +29 -0
- package/src/index.ts +11 -0
- package/src/move.ts +84 -0
- package/src/storage.ts +10 -0
- package/src/visibility.ts +6 -0
- package/src/zip.ts +55 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/storage",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.58.
|
|
4
|
+
"version": "0.58.49",
|
|
5
5
|
"description": "The Stacks file system.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"license": "MIT",
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
],
|
|
43
43
|
"files": [
|
|
44
44
|
"README.md",
|
|
45
|
-
"dist"
|
|
45
|
+
"dist",
|
|
46
|
+
"src"
|
|
46
47
|
],
|
|
47
48
|
"scripts": {
|
|
48
49
|
"build": "bun --bun build.ts",
|
|
@@ -50,12 +51,12 @@
|
|
|
50
51
|
"prepublishOnly": "bun --bun run build"
|
|
51
52
|
},
|
|
52
53
|
"peerDependencies": {
|
|
53
|
-
"@stacksjs/arrays": "
|
|
54
|
-
"@stacksjs/cli": "
|
|
55
|
-
"@stacksjs/error-handling": "
|
|
56
|
-
"@stacksjs/path": "
|
|
57
|
-
"@stacksjs/strings": "
|
|
58
|
-
"@stacksjs/types": "
|
|
54
|
+
"@stacksjs/arrays": "latest",
|
|
55
|
+
"@stacksjs/cli": "latest",
|
|
56
|
+
"@stacksjs/error-handling": "latest",
|
|
57
|
+
"@stacksjs/path": "latest",
|
|
58
|
+
"@stacksjs/strings": "latest",
|
|
59
|
+
"@stacksjs/types": "latest",
|
|
59
60
|
"@types/archiver": "^6.0.2",
|
|
60
61
|
"archiver": "^6.0.1"
|
|
61
62
|
},
|
|
@@ -72,6 +73,6 @@
|
|
|
72
73
|
"unstorage": "^1.10.1"
|
|
73
74
|
},
|
|
74
75
|
"devDependencies": {
|
|
75
|
-
"@stacksjs/development": "
|
|
76
|
+
"@stacksjs/development": "latest"
|
|
76
77
|
}
|
|
77
78
|
}
|
package/src/copy.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { contains } from '@stacksjs/arrays'
|
|
2
|
+
import { join } from '@stacksjs/path'
|
|
3
|
+
import { fs } from './fs'
|
|
4
|
+
|
|
5
|
+
export function copy(src: string | string[], dest: string, exclude: string[] = []): void {
|
|
6
|
+
if (Array.isArray(src)) {
|
|
7
|
+
src.forEach((file) => {
|
|
8
|
+
copy(file, dest, exclude)
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
if (fs.statSync(src).isDirectory())
|
|
13
|
+
copyFolder(src, dest, exclude)
|
|
14
|
+
|
|
15
|
+
else
|
|
16
|
+
copyFile(src, dest)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function copyFile(src: string, dest: string): void {
|
|
21
|
+
fs.copyFileSync(src, dest)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function copyFolder(src: string, dest: string, exclude: string[] = []): void {
|
|
25
|
+
if (!fs.existsSync(dest))
|
|
26
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
27
|
+
|
|
28
|
+
if (fs.existsSync(src)) {
|
|
29
|
+
fs.readdirSync(src).forEach((file) => {
|
|
30
|
+
if (!contains(join(src, file), exclude)) {
|
|
31
|
+
const srcPath = join(src, file)
|
|
32
|
+
const destPath = join(dest, file)
|
|
33
|
+
|
|
34
|
+
if (fs.statSync(srcPath).isDirectory())
|
|
35
|
+
copyFolder(srcPath, destPath, exclude)
|
|
36
|
+
|
|
37
|
+
else
|
|
38
|
+
fs.copyFileSync(srcPath, destPath)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/delete.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Result } from '@stacksjs/error-handling'
|
|
2
|
+
import { err, ok } from '@stacksjs/error-handling'
|
|
3
|
+
import { italic, log } from '@stacksjs/cli'
|
|
4
|
+
import { isFile } from './files'
|
|
5
|
+
import { isFolder } from './folders'
|
|
6
|
+
import { glob } from './glob'
|
|
7
|
+
import { fs } from './fs'
|
|
8
|
+
|
|
9
|
+
export function deleteFolder(path: string): Promise<Result<string, Error>> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
try {
|
|
12
|
+
if (isFolder(path)) {
|
|
13
|
+
fs.rmSync(path, { recursive: true, force: true })
|
|
14
|
+
return resolve(ok(`Deleted ${path}`))
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return resolve(ok(`Path ${path} was not a directory`))
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
return reject(err(error))
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function isDirectoryEmpty(path: string): Promise<Result<boolean, Error>> {
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
try {
|
|
28
|
+
if (fs.statSync(path).isDirectory()) {
|
|
29
|
+
if (fs.readdirSync(path).length === 0)
|
|
30
|
+
return resolve(ok(true))
|
|
31
|
+
else
|
|
32
|
+
return resolve(ok(false))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return resolve(ok(false))
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return reject(err(error))
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function deleteEmptyFolder(path: string): Promise<Result<string, Error>> {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
try {
|
|
46
|
+
if (fs.statSync(path).isDirectory()) {
|
|
47
|
+
if (fs.readdirSync(path).length === 0) {
|
|
48
|
+
fs.rmSync(path, { recursive: true, force: true })
|
|
49
|
+
return resolve(ok(`Deleted ${path}`))
|
|
50
|
+
}
|
|
51
|
+
else { return resolve(ok(`Path ${path} was not empty`)) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return resolve(ok(`Path ${path} was not a directory`))
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return reject(err(error))
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function deleteEmptyFolders(dir: string): Promise<Result<string, Error>> {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs.existsSync(dir))
|
|
65
|
+
return ok(`Path ${dir} does not exist`)
|
|
66
|
+
|
|
67
|
+
const files = fs.readdirSync(dir)
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
const p = join(dir, file)
|
|
70
|
+
if (isFolder(p)) {
|
|
71
|
+
if (fs.readdirSync(p).length === 0)
|
|
72
|
+
fs.rmSync(p, { recursive: true, force: true })
|
|
73
|
+
else
|
|
74
|
+
await deleteEmptyFolders(p)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return ok(`Deleted empty folders located in ${dir}`)
|
|
79
|
+
}
|
|
80
|
+
catch (error: any) {
|
|
81
|
+
return err(error)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function deleteFile(path: string): Promise<Result<string, Error>> {
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
try {
|
|
88
|
+
if (isFile(path)) {
|
|
89
|
+
fs.rmSync(path, { recursive: true, force: true })
|
|
90
|
+
return resolve(ok(`Deleted ${path}`))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return resolve(ok(`Path ${path} was not a file`))
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return reject(err(error))
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function deleteGlob(path: string): Promise<Result<string, Error>> {
|
|
102
|
+
if (!path.includes('*'))
|
|
103
|
+
return err(handleError(`Path ${path} does not contain a glob`))
|
|
104
|
+
|
|
105
|
+
const directories = await glob([path], { onlyDirectories: true })
|
|
106
|
+
|
|
107
|
+
for (const directory of directories) {
|
|
108
|
+
const result = await deleteFolder(directory)
|
|
109
|
+
if (result.isErr()) {
|
|
110
|
+
log.error(result.error)
|
|
111
|
+
return result
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
log.info(`Deleted ${italic(directory)}`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return ok(`Deleted ${directories.length} directories`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function del(path: string): Promise<Result<string, Error>> {
|
|
121
|
+
if (isFile(path))
|
|
122
|
+
return await deleteFile(path)
|
|
123
|
+
|
|
124
|
+
if (isFolder(path))
|
|
125
|
+
return await deleteFolder(path)
|
|
126
|
+
|
|
127
|
+
if (path.includes('*'))
|
|
128
|
+
return await deleteGlob(path)
|
|
129
|
+
|
|
130
|
+
return err(handleError(`Path ${path} cannot be deleted due to an unhandled condition. Please report this issue.`))
|
|
131
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NestedStackProps } from 'aws-cdk-lib'
|
|
2
|
+
import { NestedStack } from 'aws-cdk-lib'
|
|
3
|
+
import type { Construct } from 'constructs'
|
|
4
|
+
|
|
5
|
+
export class StorageStack extends NestedStack {
|
|
6
|
+
constructor(scope: Construct, id: string, props?: NestedStackProps) {
|
|
7
|
+
super(scope, id, props)
|
|
8
|
+
|
|
9
|
+
// if (!storage.name)
|
|
10
|
+
// throw new Error('./config storage.name is not defined')
|
|
11
|
+
|
|
12
|
+
// // eslint-disable-next-line no-new
|
|
13
|
+
// new s3.Bucket(this, storage.name)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as aws from './aws'
|
package/src/files.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type { BunFile, PathLike } from 'bun'
|
|
2
|
+
import type { JsonFile, PackageJson, TextFile } from '@stacksjs/types'
|
|
3
|
+
import { detectIndent, detectNewline } from '@stacksjs/strings'
|
|
4
|
+
import { dirname, join, path as p } from '@stacksjs/path'
|
|
5
|
+
import { contains } from '@stacksjs/arrays'
|
|
6
|
+
import { existsSync, fs } from './fs'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reads a JSON file and returns the parsed data.
|
|
10
|
+
*/
|
|
11
|
+
export async function readJsonFile(name: string, cwd?: string): Promise<JsonFile> {
|
|
12
|
+
const file = await readTextFile(name, cwd)
|
|
13
|
+
const data = JSON.parse(file.data)
|
|
14
|
+
const indent = detectIndent(file.data).indent
|
|
15
|
+
const newline = detectNewline(file.data)
|
|
16
|
+
|
|
17
|
+
return { ...file, data, indent, newline }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Reads a package.json file and returns the parsed data.
|
|
22
|
+
*/
|
|
23
|
+
export async function readPackageJson(name: string, cwd?: string) {
|
|
24
|
+
const file = await readJsonFile(name, cwd)
|
|
25
|
+
return file.data as PackageJson
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Path = BunFile | PathLike
|
|
29
|
+
type Data = Blob | TypedArray | ArrayBufferLike | string | BlobPart[]
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Writes the given text to the specified file.
|
|
33
|
+
*/
|
|
34
|
+
export async function writeFile(path: Path, data: Data): Promise<number> {
|
|
35
|
+
if (typeof path === 'string') {
|
|
36
|
+
const dirPath = dirname(path)
|
|
37
|
+
if (!await existsSync(dirPath))
|
|
38
|
+
await createFolder(dirPath)
|
|
39
|
+
|
|
40
|
+
return await Bun.write(Bun.file(path), data)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return await Bun.write(path, data)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Writes the given data to the specified JSON file.
|
|
48
|
+
*/
|
|
49
|
+
export async function writeJsonFile(file: JsonFile): Promise<number> {
|
|
50
|
+
let json = JSON.stringify(file.data, undefined, file.indent)
|
|
51
|
+
|
|
52
|
+
if (file.newline)
|
|
53
|
+
json += file.newline
|
|
54
|
+
|
|
55
|
+
return writeTextFile({ ...file, data: json })
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Reads a text file and returns its contents.
|
|
60
|
+
*/
|
|
61
|
+
export function readTextFile(name: string, cwd?: string): Promise<TextFile> {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
let filePath: string
|
|
64
|
+
|
|
65
|
+
if (cwd)
|
|
66
|
+
filePath = join(cwd, name)
|
|
67
|
+
else
|
|
68
|
+
filePath = name
|
|
69
|
+
|
|
70
|
+
fs.readFile(filePath, 'utf8', (err, text) => {
|
|
71
|
+
if (err) {
|
|
72
|
+
reject(err)
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
resolve({
|
|
76
|
+
path: filePath,
|
|
77
|
+
data: text,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Writes the given text to the specified file.
|
|
86
|
+
*/
|
|
87
|
+
export async function writeTextFile(file: TextFile): Promise<number> {
|
|
88
|
+
return await Bun.write(file.path, file.data)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Determine whether a path exists.
|
|
93
|
+
*/
|
|
94
|
+
export function isFile(path: string): boolean {
|
|
95
|
+
return fs.existsSync(path)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function doesExist(path: string): boolean {
|
|
99
|
+
return !isFile(path) || !isFolder(path)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function doesNotExist(path: string): boolean {
|
|
103
|
+
return !isFile(path) && !isFolder(path)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Determine whether a folder has any files in it.
|
|
108
|
+
*/
|
|
109
|
+
export function hasFiles(folder: string): boolean {
|
|
110
|
+
try {
|
|
111
|
+
return fs.readdirSync(folder).length > 0
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function hasComponents(): boolean {
|
|
119
|
+
return hasFiles(p.componentsPath())
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function hasFunctions(): boolean {
|
|
123
|
+
return hasFiles(p.functionsPath())
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function deleteFiles(dir: string, exclude: string[] = []) {
|
|
127
|
+
if (fs.existsSync(dir)) {
|
|
128
|
+
fs.readdirSync(dir).forEach((file) => {
|
|
129
|
+
const p = join(dir, file)
|
|
130
|
+
if (fs.statSync(p).isDirectory()) {
|
|
131
|
+
if (fs.readdirSync(p).length === 0)
|
|
132
|
+
fs.rmSync(p, { recursive: true, force: true })
|
|
133
|
+
|
|
134
|
+
else
|
|
135
|
+
deleteFiles(p, exclude)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
else if (!contains(p, exclude)) {
|
|
139
|
+
fs.rmSync(p)
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function getFiles(dir: string, exclude: string[] = []): string[] {
|
|
146
|
+
let results: string[] = []
|
|
147
|
+
const list = fs.readdirSync(dir)
|
|
148
|
+
|
|
149
|
+
list.forEach((file) => {
|
|
150
|
+
file = join(dir, file)
|
|
151
|
+
const stat = fs.statSync(file)
|
|
152
|
+
|
|
153
|
+
if (stat && stat.isDirectory())
|
|
154
|
+
results = results.concat(getFiles(file, exclude))
|
|
155
|
+
|
|
156
|
+
else if (!contains(file, exclude))
|
|
157
|
+
results.push(file)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
return results
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function put(path: string, contents: string) {
|
|
164
|
+
const dirPath = dirname(path)
|
|
165
|
+
|
|
166
|
+
if (!fs.existsSync(dirPath))
|
|
167
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
|
168
|
+
|
|
169
|
+
fs.writeFileSync(path, contents, 'utf-8')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function get(path: string): Promise<string> {
|
|
173
|
+
return Bun.file(path).text()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export const files = {
|
|
177
|
+
readJsonFile,
|
|
178
|
+
readPackageJson,
|
|
179
|
+
readTextFile,
|
|
180
|
+
writeJsonFile,
|
|
181
|
+
writeTextFile,
|
|
182
|
+
isFile,
|
|
183
|
+
hasFiles,
|
|
184
|
+
hasComponents,
|
|
185
|
+
hasFunctions,
|
|
186
|
+
deleteFiles,
|
|
187
|
+
getFiles,
|
|
188
|
+
put,
|
|
189
|
+
get,
|
|
190
|
+
}
|
package/src/folders.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { join } from '@stacksjs/path'
|
|
2
|
+
import { fs } from './fs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Determine whether a path is a folder.
|
|
6
|
+
*/
|
|
7
|
+
export function isFolder(path: string): boolean {
|
|
8
|
+
try {
|
|
9
|
+
return fs.statSync(path).isDirectory()
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isDirectory(path: string): boolean {
|
|
17
|
+
return isFolder(path)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isDir(path: string): boolean {
|
|
21
|
+
return isFolder(path)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function doesFolderExist(path: string) {
|
|
25
|
+
return fs.existsSync(path)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function createFolder(dir: string): Promise<void> {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
fs.mkdirs(dir, (err: any) => {
|
|
31
|
+
if (err)
|
|
32
|
+
reject(err)
|
|
33
|
+
|
|
34
|
+
else
|
|
35
|
+
resolve()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getFolders(dir: string): string[] {
|
|
41
|
+
return fs.readdirSync(dir).filter((file) => {
|
|
42
|
+
return fs.statSync(join(dir, file)).isDirectory()
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const folders = {
|
|
47
|
+
isFolder,
|
|
48
|
+
doesFolderExist,
|
|
49
|
+
createFolder,
|
|
50
|
+
getFolders,
|
|
51
|
+
}
|
package/src/fs.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import * as fs from 'fs-extra'
|
|
3
|
+
import { pathExists as existsSync } from 'fs-extra'
|
|
4
|
+
|
|
5
|
+
export async function exists(path: string): Promise<boolean> {
|
|
6
|
+
return await existsSync(path)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { fs, existsSync, mkdirSync, writeFileSync }
|
package/src/glob.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as glob } from 'fast-glob'
|
package/src/hash.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Hash } from 'node:crypto'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { path as p } from '@stacksjs/path'
|
|
4
|
+
import { fs } from './fs'
|
|
5
|
+
|
|
6
|
+
export function hashFileOrDirectory(path: string, hash: Hash): void {
|
|
7
|
+
if (fs.statSync(path).isDirectory()) {
|
|
8
|
+
const files = fs.readdirSync(path)
|
|
9
|
+
for (const file of files) {
|
|
10
|
+
const filePath = p.join(path, file)
|
|
11
|
+
hashFileOrDirectory(filePath, hash)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
hash.update(fs.readFileSync(path))
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function hashDirectory(directory: string): string {
|
|
20
|
+
const hash = createHash('sha256')
|
|
21
|
+
hashFileOrDirectory(directory, hash)
|
|
22
|
+
return hash.digest('hex')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function hashPath(path: string): string {
|
|
26
|
+
const hash = createHash('sha256')
|
|
27
|
+
hashFileOrDirectory(path, hash)
|
|
28
|
+
return hash.digest('hex')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function hashPaths(paths: string | string[]): string {
|
|
32
|
+
const hash = createHash('sha256')
|
|
33
|
+
const pathsArray = Array.isArray(paths) ? paths : [paths]
|
|
34
|
+
|
|
35
|
+
for (const path of pathsArray)
|
|
36
|
+
hashFileOrDirectory(path, hash)
|
|
37
|
+
|
|
38
|
+
return hash.digest('hex')
|
|
39
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url'
|
|
2
|
+
import { dirname } from '@stacksjs/path'
|
|
3
|
+
import { fs } from './fs'
|
|
4
|
+
|
|
5
|
+
export const _dirname = typeof __dirname !== 'undefined'
|
|
6
|
+
? __dirname
|
|
7
|
+
: dirname(fileURLToPath(import.meta.url))
|
|
8
|
+
|
|
9
|
+
export function updateConfigFile(filePath: string, newConfig: Record<string, unknown>): Promise<void> {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const config = JSON.parse(fs.readFileSync(filePath, 'utf8')) as Record<string, unknown>
|
|
12
|
+
|
|
13
|
+
for (const key in newConfig)
|
|
14
|
+
config[key] = newConfig[key]
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
fs.writeFileSync(filePath, JSON.stringify(config, null, 2))
|
|
18
|
+
resolve()
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
reject(error)
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const helpers = {
|
|
27
|
+
_dirname,
|
|
28
|
+
updateConfigFile,
|
|
29
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './files'
|
|
2
|
+
export * from './folders'
|
|
3
|
+
export * from './hash'
|
|
4
|
+
export * from './helpers'
|
|
5
|
+
export * from './fs'
|
|
6
|
+
export * from './copy'
|
|
7
|
+
export * from './glob'
|
|
8
|
+
export * from './delete'
|
|
9
|
+
export * from './zip'
|
|
10
|
+
|
|
11
|
+
export * as storage from './storage'
|
package/src/move.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type Result, err, ok } from '@stacksjs/error-handling'
|
|
2
|
+
import { log } from '@stacksjs/logging'
|
|
3
|
+
import { path } from '@stacksjs/path'
|
|
4
|
+
import { fs } from './fs'
|
|
5
|
+
|
|
6
|
+
interface MoveOptions {
|
|
7
|
+
// glob?: glob.Options
|
|
8
|
+
overwrite?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function move(
|
|
12
|
+
src: string | string[],
|
|
13
|
+
dest: string,
|
|
14
|
+
options?: MoveOptions,
|
|
15
|
+
): Promise<Result<{ message: string }, Error>> {
|
|
16
|
+
try {
|
|
17
|
+
if (Array.isArray(src)) {
|
|
18
|
+
const operations = src.map(async (file) => {
|
|
19
|
+
const from = file
|
|
20
|
+
const to = path.resolve(dest, path.basename(file))
|
|
21
|
+
const result = await rename(from, to, options)
|
|
22
|
+
|
|
23
|
+
if (result.isErr()) {
|
|
24
|
+
log.error(result.error)
|
|
25
|
+
return err(handleError(result.error.message, result.error))
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
await Promise.all(operations)
|
|
30
|
+
|
|
31
|
+
return ok({ message: 'Files moved successfully' })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const from = src
|
|
35
|
+
const to = dest
|
|
36
|
+
const result = await rename(from, to, options)
|
|
37
|
+
|
|
38
|
+
if (result.isErr()) {
|
|
39
|
+
log.error(result.error)
|
|
40
|
+
return err(handleError(result.error))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return ok({ message: 'File moved successfully' })
|
|
44
|
+
}
|
|
45
|
+
catch (error: any) {
|
|
46
|
+
return err(handleError(error))
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function rename(from: string, to: string, options?: MoveOptions): Promise<Result<{ message: string }, Error>> {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
try {
|
|
53
|
+
// Check if the "to" directory exists
|
|
54
|
+
const dir = path.dirname(to)
|
|
55
|
+
if (!fs.existsSync(dir))
|
|
56
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
57
|
+
|
|
58
|
+
// Ensure the "from" file exists
|
|
59
|
+
if (!fs.existsSync(from))
|
|
60
|
+
return reject(err(new Error(`File or directory does not exist: ${from}`)))
|
|
61
|
+
|
|
62
|
+
// Ensure the "to" file does not exist
|
|
63
|
+
if (fs.existsSync(to)) {
|
|
64
|
+
if (!options?.overwrite)
|
|
65
|
+
return reject(err(new Error(`File or directory already exists: ${to}`)))
|
|
66
|
+
|
|
67
|
+
fs.unlinkSync(to)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// "Move" the file
|
|
71
|
+
fs.renameSync(from, to)
|
|
72
|
+
|
|
73
|
+
return resolve(ok({ message: 'File moved successfully' }))
|
|
74
|
+
}
|
|
75
|
+
catch (error: any) {
|
|
76
|
+
if (error.code === 'ENOENT')
|
|
77
|
+
log.error('File or directory does not exist\n\n', error)
|
|
78
|
+
else
|
|
79
|
+
log.error(error)
|
|
80
|
+
|
|
81
|
+
return reject(err(new Error(error)))
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
}
|
package/src/storage.ts
ADDED
package/src/zip.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ZlibCompressionOptions } from 'bun'
|
|
2
|
+
import { runCommand } from '@stacksjs/cli'
|
|
3
|
+
|
|
4
|
+
interface ZipOptions {
|
|
5
|
+
cwd?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export async function zip(from: string | string[], to?: string, options?: ZipOptions) {
|
|
9
|
+
const toPath = to || 'archive.zip'
|
|
10
|
+
const fromPath = Array.isArray(from) ? from.join(' ') : from
|
|
11
|
+
|
|
12
|
+
if (Array.isArray(from))
|
|
13
|
+
return runCommand(`zip -r ${toPath} ${fromPath}`, options)
|
|
14
|
+
|
|
15
|
+
return runCommand(`zip -r ${to} ${from}`, options)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function unzip(paths: string | string[]) {
|
|
19
|
+
if (Array.isArray(paths))
|
|
20
|
+
return runCommand(`unzip ${paths.join(' ')}`)
|
|
21
|
+
|
|
22
|
+
return runCommand(`unzip ${paths}`)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function archive(paths: string | string[]) {
|
|
26
|
+
return zip(paths)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function unarchive(paths: string | string[]) {
|
|
30
|
+
return unzip(paths)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function compress(paths: string[]) {
|
|
34
|
+
return zip(paths)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function decompress(paths: string | string[]) {
|
|
38
|
+
return unzip(paths)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function gzipSync(data: Uint8Array, options?: ZlibCompressionOptions) {
|
|
42
|
+
return Bun.gzipSync(data, options) // buffer extends Uint8Array
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function gunzipSync(data: Uint8Array) {
|
|
46
|
+
return Bun.gunzipSync(data) // decompressed
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function deflateSync(data: Uint8Array, options?: ZlibCompressionOptions) {
|
|
50
|
+
return Bun.deflateSync(data, options) // compressed
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function inflateSync(data: Uint8Array) {
|
|
54
|
+
return Bun.inflateSync(data) // decompressed
|
|
55
|
+
}
|