hereya-cli 0.2.1
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/README.md +392 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +6 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/backend/common.d.ts +126 -0
- package/dist/backend/common.js +1 -0
- package/dist/backend/index.d.ts +4 -0
- package/dist/backend/index.js +5 -0
- package/dist/backend/local.d.ts +16 -0
- package/dist/backend/local.js +231 -0
- package/dist/commands/add/index.d.ts +13 -0
- package/dist/commands/add/index.js +96 -0
- package/dist/commands/bootstrap/index.d.ts +12 -0
- package/dist/commands/bootstrap/index.js +28 -0
- package/dist/commands/down/index.d.ts +10 -0
- package/dist/commands/down/index.js +78 -0
- package/dist/commands/env/index.d.ts +10 -0
- package/dist/commands/env/index.js +46 -0
- package/dist/commands/init/index.d.ts +13 -0
- package/dist/commands/init/index.js +46 -0
- package/dist/commands/remote/exec/index.d.ts +12 -0
- package/dist/commands/remote/exec/index.js +65 -0
- package/dist/commands/remove/index.d.ts +12 -0
- package/dist/commands/remove/index.js +80 -0
- package/dist/commands/run/index.d.ts +14 -0
- package/dist/commands/run/index.js +60 -0
- package/dist/commands/up/index.d.ts +10 -0
- package/dist/commands/up/index.js +121 -0
- package/dist/commands/workspace/create/index.d.ts +9 -0
- package/dist/commands/workspace/create/index.js +25 -0
- package/dist/commands/workspace/delete/index.d.ts +9 -0
- package/dist/commands/workspace/delete/index.js +24 -0
- package/dist/commands/workspace/env/index.d.ts +9 -0
- package/dist/commands/workspace/env/index.js +28 -0
- package/dist/commands/workspace/install/index.d.ts +14 -0
- package/dist/commands/workspace/install/index.js +74 -0
- package/dist/commands/workspace/uninstall/index.d.ts +14 -0
- package/dist/commands/workspace/uninstall/index.js +85 -0
- package/dist/iac/cdk.d.ts +8 -0
- package/dist/iac/cdk.js +83 -0
- package/dist/iac/common.d.ts +29 -0
- package/dist/iac/common.js +5 -0
- package/dist/iac/index.d.ts +16 -0
- package/dist/iac/index.js +18 -0
- package/dist/iac/terraform.d.ts +6 -0
- package/dist/iac/terraform.js +67 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infrastructure/aws.d.ts +8 -0
- package/dist/infrastructure/aws.js +28 -0
- package/dist/infrastructure/common.d.ts +59 -0
- package/dist/infrastructure/common.js +7 -0
- package/dist/infrastructure/index.d.ts +43 -0
- package/dist/infrastructure/index.js +82 -0
- package/dist/infrastructure/local.d.ts +14 -0
- package/dist/infrastructure/local.js +81 -0
- package/dist/lib/config/common.d.ts +34 -0
- package/dist/lib/config/common.js +1 -0
- package/dist/lib/config/index.d.ts +3 -0
- package/dist/lib/config/index.js +5 -0
- package/dist/lib/config/simple.d.ts +8 -0
- package/dist/lib/config/simple.js +41 -0
- package/dist/lib/env/index.d.ts +27 -0
- package/dist/lib/env/index.js +42 -0
- package/dist/lib/env-utils.d.ts +8 -0
- package/dist/lib/env-utils.js +20 -0
- package/dist/lib/filesystem.d.ts +1 -0
- package/dist/lib/filesystem.js +15 -0
- package/dist/lib/object-utils.d.ts +4 -0
- package/dist/lib/object-utils.js +9 -0
- package/dist/lib/package/common.d.ts +15 -0
- package/dist/lib/package/common.js +1 -0
- package/dist/lib/package/github.d.ts +4 -0
- package/dist/lib/package/github.js +32 -0
- package/dist/lib/package/index.d.ts +30 -0
- package/dist/lib/package/index.js +45 -0
- package/dist/lib/parameter/index.d.ts +34 -0
- package/dist/lib/parameter/index.js +47 -0
- package/dist/lib/shell.d.ts +9 -0
- package/dist/lib/shell.js +16 -0
- package/dist/lib/yaml-utils.d.ts +5 -0
- package/dist/lib/yaml-utils.js +27 -0
- package/oclif.manifest.json +610 -0
- package/package.json +86 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { InfrastructureType } from '../../infrastructure/common.js';
|
|
2
|
+
export declare class EnvManager {
|
|
3
|
+
addProjectEnv(input: AddEnvInput): Promise<void>;
|
|
4
|
+
getProjectEnv(input: GetProjectEnvInput): Promise<GetProjectEnvOutput>;
|
|
5
|
+
removeProjectEnv(input: RemoveEnvInput): Promise<void>;
|
|
6
|
+
private getEnvPath;
|
|
7
|
+
}
|
|
8
|
+
export declare const envManager: EnvManager;
|
|
9
|
+
export declare function getEnvManager(): EnvManager;
|
|
10
|
+
export type AddEnvInput = {
|
|
11
|
+
env: {
|
|
12
|
+
[key: string]: string;
|
|
13
|
+
};
|
|
14
|
+
infra: InfrastructureType;
|
|
15
|
+
projectRootDir?: string;
|
|
16
|
+
workspace: string;
|
|
17
|
+
};
|
|
18
|
+
export type RemoveEnvInput = AddEnvInput;
|
|
19
|
+
export type GetProjectEnvInput = {
|
|
20
|
+
projectRootDir?: string;
|
|
21
|
+
workspace: string;
|
|
22
|
+
};
|
|
23
|
+
export type GetProjectEnvOutput = {
|
|
24
|
+
env: {
|
|
25
|
+
[key: string]: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { resolveEnvValues } from '../env-utils.js';
|
|
3
|
+
import { getAnyPath } from '../filesystem.js';
|
|
4
|
+
import { load, save } from '../yaml-utils.js';
|
|
5
|
+
export class EnvManager {
|
|
6
|
+
async addProjectEnv(input) {
|
|
7
|
+
const envPath = await this.getEnvPath(input);
|
|
8
|
+
const { data: existingEnv } = await load(envPath);
|
|
9
|
+
const newEnv = Object.fromEntries(Object.entries(input.env)
|
|
10
|
+
.map(([key, value]) => [key, `${input.infra}:${value}`]));
|
|
11
|
+
const finalEnv = { ...existingEnv, ...newEnv };
|
|
12
|
+
await save(finalEnv, envPath);
|
|
13
|
+
}
|
|
14
|
+
async getProjectEnv(input) {
|
|
15
|
+
const envPath = await this.getEnvPath(input);
|
|
16
|
+
const { data: env, found } = await load(envPath);
|
|
17
|
+
if (!found) {
|
|
18
|
+
return { env: {} };
|
|
19
|
+
}
|
|
20
|
+
const resolvedEnv = await resolveEnvValues(env);
|
|
21
|
+
return { env: resolvedEnv };
|
|
22
|
+
}
|
|
23
|
+
async removeProjectEnv(input) {
|
|
24
|
+
const envPath = await this.getEnvPath(input);
|
|
25
|
+
const { data: existingEnv, found } = await load(envPath);
|
|
26
|
+
if (!found) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const envKeysToRemove = Object.keys(input.env);
|
|
30
|
+
const finalEnv = Object.fromEntries(Object
|
|
31
|
+
.entries(existingEnv)
|
|
32
|
+
.filter(([key]) => !envKeysToRemove.includes(key)));
|
|
33
|
+
await save(finalEnv, envPath);
|
|
34
|
+
}
|
|
35
|
+
async getEnvPath(input) {
|
|
36
|
+
return getAnyPath(path.join(input.projectRootDir ?? process.cwd(), '.hereya', `env.${input.workspace}.yaml`), path.join(input.projectRootDir ?? process.cwd(), '.hereya', `env.${input.workspace}.yml`));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const envManager = new EnvManager();
|
|
40
|
+
export function getEnvManager() {
|
|
41
|
+
return envManager;
|
|
42
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getInfrastructure } from '../infrastructure/index.js';
|
|
2
|
+
export function logEnv(env, logFn = console.log) {
|
|
3
|
+
for (const [key, value] of Object.entries(env)) {
|
|
4
|
+
logFn(`${key}=${value}`);
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export async function resolveEnvValues(env) {
|
|
8
|
+
return Object.fromEntries(await Promise.all(Object.entries(env)
|
|
9
|
+
.map(async ([key, value]) => {
|
|
10
|
+
const infraType = value.split(':')[0];
|
|
11
|
+
const infra$ = await getInfrastructure({ type: infraType });
|
|
12
|
+
if (!infra$.supported) {
|
|
13
|
+
throw new Error(infra$.reason);
|
|
14
|
+
}
|
|
15
|
+
const { infrastructure } = infra$;
|
|
16
|
+
const valueWithoutInfra = value.split(':').slice(1).join(':');
|
|
17
|
+
const { value: resolvedValue } = await infrastructure.resolveEnv({ value: valueWithoutInfra });
|
|
18
|
+
return [key, resolvedValue];
|
|
19
|
+
})));
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getAnyPath(...candidates: string[]): Promise<string>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { access, constants } from 'node:fs/promises';
|
|
2
|
+
export async function getAnyPath(...candidates) {
|
|
3
|
+
const checkAccess = async (index) => {
|
|
4
|
+
if (index >= candidates.length)
|
|
5
|
+
return candidates[0];
|
|
6
|
+
try {
|
|
7
|
+
await access(candidates[index], constants.W_OK);
|
|
8
|
+
return candidates[index];
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return checkAccess(index + 1);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
return checkAccess(0);
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function mapObject(obj, fn) {
|
|
2
|
+
return Object.fromEntries(Object.entries(obj).map(([key, value]) => fn(key, value)));
|
|
3
|
+
}
|
|
4
|
+
export function arrayOfStringToObject(arr, keyValueSeparator = '=') {
|
|
5
|
+
return Object.fromEntries(arr.map((item) => {
|
|
6
|
+
const [key, value] = item.split(keyValueSeparator);
|
|
7
|
+
return [key, value];
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PackageManager {
|
|
2
|
+
getRepoContent: (input: GetRepoContentInput) => Promise<GetRepoContentOutput>;
|
|
3
|
+
}
|
|
4
|
+
export type GetRepoContentInput = {
|
|
5
|
+
owner: string;
|
|
6
|
+
path: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
};
|
|
9
|
+
export type GetRepoContentOutput = {
|
|
10
|
+
content: string;
|
|
11
|
+
found: true;
|
|
12
|
+
} | {
|
|
13
|
+
found: false;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Octokit } from '@octokit/rest';
|
|
2
|
+
export class GitHubPackageManager {
|
|
3
|
+
async getRepoContent({ owner, path, repo }) {
|
|
4
|
+
const octokit = new Octokit();
|
|
5
|
+
try {
|
|
6
|
+
const response = await octokit.rest.repos.getContent({
|
|
7
|
+
headers: {
|
|
8
|
+
'Accept': 'application/vnd.github.raw+json'
|
|
9
|
+
},
|
|
10
|
+
owner,
|
|
11
|
+
path,
|
|
12
|
+
repo
|
|
13
|
+
});
|
|
14
|
+
if (response.status !== 200) {
|
|
15
|
+
return {
|
|
16
|
+
found: false,
|
|
17
|
+
reason: `Failed to fetch content: ${response.status}`
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
content: response.data,
|
|
22
|
+
found: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return {
|
|
27
|
+
found: false,
|
|
28
|
+
reason: error.message
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { IacType } from '../../iac/common.js';
|
|
3
|
+
import { InfrastructureType } from '../../infrastructure/common.js';
|
|
4
|
+
import { PackageManager } from './common.js';
|
|
5
|
+
export declare const packageManager: PackageManager;
|
|
6
|
+
export declare function getPackageManager(): PackageManager;
|
|
7
|
+
export declare function resolvePackage(input: ResolvePackageInput): Promise<ResolvePackageOutput>;
|
|
8
|
+
export declare function getPackageCanonicalName(packageName: string): string;
|
|
9
|
+
export type ResolvePackageInput = {
|
|
10
|
+
package: string;
|
|
11
|
+
};
|
|
12
|
+
export type ResolvePackageOutput = {
|
|
13
|
+
canonicalName: string;
|
|
14
|
+
found: true;
|
|
15
|
+
metadata: z.infer<typeof PackageMetadata>;
|
|
16
|
+
packageUri: string;
|
|
17
|
+
} | {
|
|
18
|
+
found: false;
|
|
19
|
+
reason: string;
|
|
20
|
+
};
|
|
21
|
+
export declare const PackageMetadata: z.ZodObject<{
|
|
22
|
+
iac: z.ZodNativeEnum<typeof IacType>;
|
|
23
|
+
infra: z.ZodNativeEnum<typeof InfrastructureType>;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
iac: IacType;
|
|
26
|
+
infra: InfrastructureType;
|
|
27
|
+
}, {
|
|
28
|
+
iac: IacType;
|
|
29
|
+
infra: InfrastructureType;
|
|
30
|
+
}>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as yaml from 'yaml';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { IacType } from '../../iac/common.js';
|
|
4
|
+
import { InfrastructureType } from '../../infrastructure/common.js';
|
|
5
|
+
import { GitHubPackageManager } from './github.js';
|
|
6
|
+
export const packageManager = new GitHubPackageManager();
|
|
7
|
+
export function getPackageManager() {
|
|
8
|
+
return packageManager;
|
|
9
|
+
}
|
|
10
|
+
export async function resolvePackage(input) {
|
|
11
|
+
const pkgParts = input.package.split('/');
|
|
12
|
+
if (pkgParts.length !== 2) {
|
|
13
|
+
return { found: false, reason: 'Invalid package format. Use owner/repository' };
|
|
14
|
+
}
|
|
15
|
+
const [owner, repo] = pkgParts;
|
|
16
|
+
const pkgUrl = `https://github.com/${input.package}`;
|
|
17
|
+
const packageManager = getPackageManager();
|
|
18
|
+
const metadataContentCandidates = (await Promise.all([
|
|
19
|
+
packageManager.getRepoContent({ owner, path: 'hereyarc.yaml', repo }),
|
|
20
|
+
packageManager.getRepoContent({ owner, path: 'hereyarc.yml', repo }),
|
|
21
|
+
])).filter(content$ => content$.found);
|
|
22
|
+
if (metadataContentCandidates.length === 0) {
|
|
23
|
+
return { found: false, reason: `No hereya metadata file found in ${pkgUrl}` };
|
|
24
|
+
}
|
|
25
|
+
const metadataContent$ = metadataContentCandidates[0];
|
|
26
|
+
try {
|
|
27
|
+
const metadata = PackageMetadata.parse(yaml.parse(metadataContent$.content));
|
|
28
|
+
return {
|
|
29
|
+
canonicalName: getPackageCanonicalName(input.package),
|
|
30
|
+
found: true,
|
|
31
|
+
metadata,
|
|
32
|
+
packageUri: pkgUrl
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return { found: false, reason: error.message };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function getPackageCanonicalName(packageName) {
|
|
40
|
+
return packageName.replace('/', '-');
|
|
41
|
+
}
|
|
42
|
+
export const PackageMetadata = z.object({
|
|
43
|
+
iac: z.nativeEnum(IacType),
|
|
44
|
+
infra: z.nativeEnum(InfrastructureType),
|
|
45
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare class ParameterManager {
|
|
2
|
+
getPackageParameters(input: GetPackageParametersInput): Promise<GetPackageParametersOutput>;
|
|
3
|
+
savePackageParameters(input: SavePackageParametersInput): Promise<SavePackageParametersOutput>;
|
|
4
|
+
}
|
|
5
|
+
export declare const parameterManager: ParameterManager;
|
|
6
|
+
export declare function getParameterManager(): ParameterManager;
|
|
7
|
+
export type GetPackageParametersInput = {
|
|
8
|
+
package: string;
|
|
9
|
+
projectRootDir?: string;
|
|
10
|
+
userSpecifiedParameters?: {
|
|
11
|
+
[key: string]: string;
|
|
12
|
+
};
|
|
13
|
+
workspace: string;
|
|
14
|
+
};
|
|
15
|
+
export type GetPackageParametersOutput = {
|
|
16
|
+
parameters: {
|
|
17
|
+
[key: string]: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export type SavePackageParametersInput = {
|
|
21
|
+
package: string;
|
|
22
|
+
parameters: {
|
|
23
|
+
[key: string]: string;
|
|
24
|
+
};
|
|
25
|
+
projectRootDir?: string;
|
|
26
|
+
workspace: string;
|
|
27
|
+
};
|
|
28
|
+
export type SavePackageParametersOutput = {
|
|
29
|
+
filePath: null;
|
|
30
|
+
saved: false;
|
|
31
|
+
} | {
|
|
32
|
+
filePath: string;
|
|
33
|
+
saved: true;
|
|
34
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getAnyPath } from '../filesystem.js';
|
|
3
|
+
import { getPackageCanonicalName } from '../package/index.js';
|
|
4
|
+
import { load, save } from '../yaml-utils.js';
|
|
5
|
+
export class ParameterManager {
|
|
6
|
+
async getPackageParameters(input) {
|
|
7
|
+
const pkgName = getPackageCanonicalName(input.package);
|
|
8
|
+
const fromFiles = await Promise.all([
|
|
9
|
+
[`${pkgName}.yaml`, `${pkgName}.yml`],
|
|
10
|
+
[`${pkgName}.${input.workspace}.yaml`, `${pkgName}.${input.workspace}.yml`],
|
|
11
|
+
].map(async ([filename, altFilename]) => {
|
|
12
|
+
const filePath = await getAnyPath(path.join(input.projectRootDir ?? process.cwd(), 'hereyavars', filename), path.join(input.projectRootDir ?? process.cwd(), 'hereyavars', altFilename));
|
|
13
|
+
const { data: parameters } = await load(filePath);
|
|
14
|
+
return parameters;
|
|
15
|
+
}));
|
|
16
|
+
const fromUser = input.userSpecifiedParameters;
|
|
17
|
+
const parameters = Object.assign({}, ...fromFiles, fromUser ?? {});
|
|
18
|
+
return { parameters };
|
|
19
|
+
}
|
|
20
|
+
async savePackageParameters(input) {
|
|
21
|
+
const pkgName = getPackageCanonicalName(input.package);
|
|
22
|
+
const paramFilesExist = await Promise.all([
|
|
23
|
+
[`${pkgName}.yaml`, `${pkgName}.yml`],
|
|
24
|
+
[`${pkgName}.${input.workspace}.yaml`, `${pkgName}.${input.workspace}.yml`],
|
|
25
|
+
].map(async ([filename, altFilename]) => {
|
|
26
|
+
const filePath = await getAnyPath(path.join(input.projectRootDir ?? process.cwd(), 'hereyavars', filename), path.join(input.projectRootDir ?? process.cwd(), 'hereyavars', altFilename));
|
|
27
|
+
const { found } = await load(filePath);
|
|
28
|
+
return found;
|
|
29
|
+
}));
|
|
30
|
+
if (paramFilesExist.some(Boolean)) {
|
|
31
|
+
return {
|
|
32
|
+
filePath: null,
|
|
33
|
+
saved: false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const filePath = path.join(input.projectRootDir ?? process.cwd(), 'hereyavars', `${pkgName}.yaml`);
|
|
37
|
+
await save(input.parameters, filePath);
|
|
38
|
+
return {
|
|
39
|
+
filePath,
|
|
40
|
+
saved: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export const parameterManager = new ParameterManager();
|
|
45
|
+
export function getParameterManager() {
|
|
46
|
+
return parameterManager;
|
|
47
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
3
|
+
import { StdioOptions, spawnSync } from 'node:child_process';
|
|
4
|
+
export type RunShellOptions = {
|
|
5
|
+
directory?: string;
|
|
6
|
+
env?: NodeJS.ProcessEnv;
|
|
7
|
+
stdio?: StdioOptions;
|
|
8
|
+
};
|
|
9
|
+
export declare function runShell(cmd: string, args: string[], options?: RunShellOptions): ReturnType<typeof spawnSync>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
export function runShell(cmd, args, options = {}) {
|
|
3
|
+
// Run the command
|
|
4
|
+
const result = spawnSync(cmd, args, {
|
|
5
|
+
cwd: options.directory ?? process.cwd(),
|
|
6
|
+
encoding: 'utf8',
|
|
7
|
+
env: { ...process.env, ...options.env },
|
|
8
|
+
shell: true,
|
|
9
|
+
stdio: options.stdio ?? 'inherit',
|
|
10
|
+
});
|
|
11
|
+
// Throw an error if the command failed
|
|
12
|
+
if (result.status !== 0) {
|
|
13
|
+
throw new Error(`Command "${cmd} ${args.join(' ')}" failed with exit code "${result.status}"`);
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { parse, stringify } from 'yaml';
|
|
4
|
+
export async function save(content, file) {
|
|
5
|
+
try {
|
|
6
|
+
await mkdir(path.dirname(file), { recursive: true });
|
|
7
|
+
await writeFile(file, stringify(content), { encoding: 'utf8' });
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
throw new Error(`could not save data to file ${file} ${error}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function load(file) {
|
|
14
|
+
let data = {};
|
|
15
|
+
let found = false;
|
|
16
|
+
try {
|
|
17
|
+
const content = await readFile(file, { encoding: 'utf8' });
|
|
18
|
+
data = parse(content);
|
|
19
|
+
found = true;
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error.code !== 'ENOENT') {
|
|
23
|
+
throw new Error(`could not load file ${file}: ${error}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { data: data || {}, found };
|
|
27
|
+
}
|