syncodemayo 1.0.8 → 2.0.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.
- package/CHANGELOG.md +81 -0
- package/README.md +99 -30
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +66 -0
- package/dist/cli.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.js +17 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/Args.d.ts +21 -0
- package/dist/lib/Args.js +99 -0
- package/dist/lib/Args.js.map +1 -0
- package/{lib → dist/lib}/Cli.d.ts +5 -0
- package/dist/lib/Cli.js +104 -0
- package/dist/lib/Cli.js.map +1 -0
- package/{lib → dist/lib}/Client.d.ts +5 -3
- package/dist/lib/Client.js +73 -0
- package/dist/lib/Client.js.map +1 -0
- package/dist/lib/Config.d.ts +48 -0
- package/dist/lib/Config.js +92 -0
- package/dist/lib/Config.js.map +1 -0
- package/dist/lib/ConfigSchema.d.ts +3 -0
- package/dist/lib/ConfigSchema.js +132 -0
- package/dist/lib/ConfigSchema.js.map +1 -0
- package/dist/lib/Connection.d.ts +41 -0
- package/dist/lib/Connection.js +176 -0
- package/dist/lib/Connection.js.map +1 -0
- package/dist/lib/Filelist.d.ts +42 -0
- package/dist/lib/Filelist.js +156 -0
- package/dist/lib/Filelist.js.map +1 -0
- package/dist/lib/Log.d.ts +12 -0
- package/dist/lib/Log.js +46 -0
- package/dist/lib/Log.js.map +1 -0
- package/dist/lib/Prompt.d.ts +15 -0
- package/dist/lib/Prompt.js +31 -0
- package/dist/lib/Prompt.js.map +1 -0
- package/dist/lib/RemoteConnection.d.ts +37 -0
- package/dist/lib/RemoteConnection.js +2 -0
- package/dist/lib/RemoteConnection.js.map +1 -0
- package/dist/lib/Sync.d.ts +18 -0
- package/dist/lib/Sync.js +127 -0
- package/dist/lib/Sync.js.map +1 -0
- package/dist/lib/TargetServer.d.ts +31 -0
- package/dist/lib/TargetServer.js +76 -0
- package/dist/lib/TargetServer.js.map +1 -0
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/vendor/strip-json-comments.d.ts +23 -0
- package/dist/vendor/strip-json-comments.js +126 -0
- package/dist/vendor/strip-json-comments.js.map +1 -0
- package/package.json +24 -26
- package/.npmignore +0 -9
- package/cli.d.ts +0 -0
- package/cli.js +0 -93
- package/index.d.ts +0 -1
- package/index.js +0 -7
- package/lib/Cli.js +0 -122
- package/lib/Client.js +0 -81
- package/lib/Config.d.ts +0 -32
- package/lib/Config.js +0 -115
- package/lib/ConfigSchema.d.ts +0 -7
- package/lib/ConfigSchema.js +0 -65
- package/lib/Connection.d.ts +0 -14
- package/lib/Connection.js +0 -111
- package/lib/Filelist.d.ts +0 -11
- package/lib/Filelist.js +0 -59
- package/lib/Sync.d.ts +0 -18
- package/lib/Sync.js +0 -141
- package/lib/TargetServer.d.ts +0 -22
- package/lib/TargetServer.js +0 -73
- package/lib/index.d.ts +0 -7
- package/lib/index.js +0 -13
- package/missing-types.d.ts +0 -14
- package/tsconfig.json +0 -48
- package/yarn.lock +0 -975
package/dist/lib/Sync.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as Log from './Log.js';
|
|
3
|
+
import { resolveTarget } from './Config.js';
|
|
4
|
+
import { getRemoteFilelist, buildLocalFilelist, remotePathForKey } from './Filelist.js';
|
|
5
|
+
import { confirmUpload } from './Prompt.js';
|
|
6
|
+
export class Sync {
|
|
7
|
+
constructor(conn, config, target) {
|
|
8
|
+
this._conn = conn;
|
|
9
|
+
this._config = config;
|
|
10
|
+
this._target = resolveTarget(config, target);
|
|
11
|
+
}
|
|
12
|
+
async run(isDryRun = false, forceConfirmation = false) {
|
|
13
|
+
const { localFiles, remoteFiles } = await this._getFilelists(this._config.local, this._target, this._conn);
|
|
14
|
+
const changeset = this._buildChangeset(localFiles, remoteFiles);
|
|
15
|
+
const filesToUpload = [...changeset.added, ...changeset.changed];
|
|
16
|
+
if (isDryRun || (filesToUpload.length === 0 && changeset.removed.length === 0)) {
|
|
17
|
+
return changeset;
|
|
18
|
+
}
|
|
19
|
+
const _verifiedDirs = new Map();
|
|
20
|
+
const verifyDirExists = async (remoteDir) => {
|
|
21
|
+
if (!_verifiedDirs.has(remoteDir)) {
|
|
22
|
+
const isVerified = await this._conn.remoteDirectoryExists(remoteDir);
|
|
23
|
+
_verifiedDirs.set(remoteDir, isVerified);
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
Log.info("\n%s files to upload...", filesToUpload.length);
|
|
28
|
+
Log.info("%s files to remove...\n", changeset.removed.length);
|
|
29
|
+
const confirmed = forceConfirmation === true
|
|
30
|
+
? true
|
|
31
|
+
: await this._confirmUpload(this._target, filesToUpload.length);
|
|
32
|
+
if (!confirmed) {
|
|
33
|
+
Log.info("Canceled upload.");
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
Log.log("Uploading %s files...", filesToUpload.length);
|
|
37
|
+
// The remote path for a cache key, or null (with a warning) when the key is outside the
|
|
38
|
+
// current local.path.
|
|
39
|
+
const remotePathFor = (filePath) => {
|
|
40
|
+
const remotePath = remotePathForKey(this._config.local.path, this._target.path, filePath);
|
|
41
|
+
if (remotePath === null) {
|
|
42
|
+
Log.urgent(` -> Skipping ${filePath}: it isn't under local.path (${this._config.local.path}), so its remote path is unknown.`);
|
|
43
|
+
}
|
|
44
|
+
return remotePath;
|
|
45
|
+
};
|
|
46
|
+
let sequentialUploader = Promise.resolve(true);
|
|
47
|
+
filesToUpload.forEach((filePath) => {
|
|
48
|
+
sequentialUploader = sequentialUploader.then(async () => {
|
|
49
|
+
const remotePath = remotePathFor(filePath);
|
|
50
|
+
if (remotePath === null)
|
|
51
|
+
return true;
|
|
52
|
+
const remoteDir = path.posix.dirname(remotePath);
|
|
53
|
+
const localPath = path.resolve(filePath);
|
|
54
|
+
await verifyDirExists(remoteDir);
|
|
55
|
+
await this._conn.putFile(localPath, remotePath);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
if (this._config.local.deleteRemoteFiles && changeset.removed.length > 0) {
|
|
60
|
+
changeset.removed.forEach((filePath) => {
|
|
61
|
+
sequentialUploader = sequentialUploader.then(async () => {
|
|
62
|
+
const remotePath = remotePathFor(filePath);
|
|
63
|
+
if (remotePath === null)
|
|
64
|
+
return true;
|
|
65
|
+
const didDelete = await this._conn.deleteRemoteFile(remotePath);
|
|
66
|
+
if (!didDelete)
|
|
67
|
+
Log.urgent(" -> Error deleting file");
|
|
68
|
+
return true;
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
await sequentialUploader;
|
|
73
|
+
const remoteCachePath = `${this._target.path}/${this._target.cache}`;
|
|
74
|
+
await verifyDirExists(path.dirname(remoteCachePath));
|
|
75
|
+
await this._conn.putContent(JSON.stringify(localFiles, null, 2), remoteCachePath);
|
|
76
|
+
return changeset;
|
|
77
|
+
}
|
|
78
|
+
async _confirmUpload(target, fileCount, streams) {
|
|
79
|
+
// If no prompt is specified, auto-confirm upload!
|
|
80
|
+
if (!target.prompt)
|
|
81
|
+
return true;
|
|
82
|
+
const message = typeof target.prompt === 'string'
|
|
83
|
+
? target.prompt
|
|
84
|
+
: "Confirm upload?";
|
|
85
|
+
return confirmUpload(message, streams);
|
|
86
|
+
}
|
|
87
|
+
async _getFilelists(local, target, conn) {
|
|
88
|
+
// Load/build Filesets in parallel.
|
|
89
|
+
const [localFiles, remoteFiles] = await Promise.all([
|
|
90
|
+
buildLocalFilelist(local),
|
|
91
|
+
getRemoteFilelist(target, conn),
|
|
92
|
+
]);
|
|
93
|
+
return { localFiles, remoteFiles };
|
|
94
|
+
}
|
|
95
|
+
_buildChangeset(localFiles, remoteFiles) {
|
|
96
|
+
const changeset = {
|
|
97
|
+
added: [],
|
|
98
|
+
removed: [],
|
|
99
|
+
changed: []
|
|
100
|
+
};
|
|
101
|
+
// Build list of files that need to be uploaded...
|
|
102
|
+
Object.keys(localFiles).forEach((localFilename) => {
|
|
103
|
+
const localHash = localFiles[localFilename];
|
|
104
|
+
const remoteHash = remoteFiles[localFilename];
|
|
105
|
+
// Local hash not in remote cache
|
|
106
|
+
if (!(localFilename in remoteFiles)) {
|
|
107
|
+
changeset.added.push(localFilename);
|
|
108
|
+
}
|
|
109
|
+
// Local hash unequal to remote cache
|
|
110
|
+
else if (remoteHash != localHash) {
|
|
111
|
+
changeset.changed.push(localFilename);
|
|
112
|
+
}
|
|
113
|
+
// Local hash and remote hash are equal!
|
|
114
|
+
});
|
|
115
|
+
// Build list of files to be removed from the server...
|
|
116
|
+
Object.keys(remoteFiles).forEach((remoteFilename) => {
|
|
117
|
+
if (!(remoteFilename in localFiles)) {
|
|
118
|
+
changeset.removed.push(remoteFilename);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return changeset;
|
|
122
|
+
}
|
|
123
|
+
static create(conn, config, target) {
|
|
124
|
+
return new Sync(conn, config, target);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=Sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Sync.js","sourceRoot":"./","sources":["lib/Sync.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport * as path from 'path'\nimport * as Log from './Log.js'\nimport { IConfig, ILocalConfig, ITargetConfig, resolveTarget } from './Config.js'\nimport { RemoteConnection } from './RemoteConnection.js'\nimport { IFilelist, getRemoteFilelist, buildLocalFilelist, remotePathForKey } from './Filelist.js'\nimport { ConfirmStreams, confirmUpload } from './Prompt.js'\n\nexport interface IChangeset {\n added: string[]\n changed: string[]\n removed: string[]\n}\n\nexport class Sync {\n private _conn: RemoteConnection\n private _config: IConfig\n private _target: ITargetConfig\n\n constructor(conn: RemoteConnection, config: IConfig, target: string | ITargetConfig) {\n this._conn = conn\n this._config = config\n this._target = resolveTarget(config, target)\n }\n\n async run(isDryRun = false, forceConfirmation = false): Promise<IChangeset | null> {\n const { localFiles, remoteFiles } = await this._getFilelists(this._config.local, this._target, this._conn)\n const changeset = this._buildChangeset(localFiles, remoteFiles)\n const filesToUpload = [...changeset.added, ...changeset.changed]\n\n if (isDryRun || (filesToUpload.length === 0 && changeset.removed.length === 0)) {\n return changeset\n }\n\n const _verifiedDirs = new Map();\n\n const verifyDirExists = async (remoteDir: string) => {\n if (!_verifiedDirs.has(remoteDir)) {\n const isVerified = await this._conn.remoteDirectoryExists(remoteDir)\n _verifiedDirs.set(remoteDir, isVerified)\n }\n return true\n }\n\n Log.info(\"\\n%s files to upload...\", filesToUpload.length)\n Log.info(\"%s files to remove...\\n\", changeset.removed.length)\n\n const confirmed = forceConfirmation === true\n ? true\n : await this._confirmUpload(this._target, filesToUpload.length)\n\n if (!confirmed) {\n Log.info(\"Canceled upload.\")\n return null\n }\n\n Log.log(\"Uploading %s files...\", filesToUpload.length)\n\n // The remote path for a cache key, or null (with a warning) when the key is outside the\n // current local.path.\n const remotePathFor = (filePath: string): string | null => {\n const remotePath = remotePathForKey(this._config.local.path, this._target.path, filePath)\n if (remotePath === null) {\n Log.urgent(` -> Skipping ${filePath}: it isn't under local.path (${this._config.local.path}), so its remote path is unknown.`)\n }\n return remotePath\n }\n\n let sequentialUploader: any = Promise.resolve(true)\n\n filesToUpload.forEach((filePath: string) => {\n sequentialUploader = sequentialUploader.then(async () => {\n const remotePath = remotePathFor(filePath)\n if (remotePath === null) return true\n const remoteDir = path.posix.dirname(remotePath)\n const localPath = path.resolve(filePath)\n\n await verifyDirExists(remoteDir)\n await this._conn.putFile(localPath, remotePath)\n return true\n })\n })\n\n if (this._config.local.deleteRemoteFiles && changeset.removed.length > 0) {\n changeset.removed.forEach((filePath: string) => {\n sequentialUploader = sequentialUploader.then(async () => {\n const remotePath = remotePathFor(filePath)\n if (remotePath === null) return true\n const didDelete = await this._conn.deleteRemoteFile(remotePath)\n if (!didDelete) Log.urgent(\" -> Error deleting file\")\n return true\n })\n })\n }\n\n await sequentialUploader\n\n const remoteCachePath = `${this._target.path}/${this._target.cache}`\n await verifyDirExists(path.dirname(remoteCachePath))\n await this._conn.putContent(\n JSON.stringify(localFiles, null, 2),\n remoteCachePath\n )\n\n return changeset\n }\n\n private async _confirmUpload(target: ITargetConfig, fileCount: number, streams?: ConfirmStreams): Promise<boolean> {\n // If no prompt is specified, auto-confirm upload!\n if (!target.prompt) return true\n\n const message = typeof target.prompt === 'string'\n ? target.prompt\n : \"Confirm upload?\"\n\n return confirmUpload(message, streams)\n }\n\n private async _getFilelists(local: ILocalConfig, target: ITargetConfig, conn: RemoteConnection) {\n // Load/build Filesets in parallel.\n const [localFiles, remoteFiles] = await Promise.all([\n buildLocalFilelist(local),\n getRemoteFilelist(target, conn),\n ])\n return { localFiles, remoteFiles }\n }\n\n private _buildChangeset(localFiles: IFilelist, remoteFiles: IFilelist) {\n const changeset: IChangeset = {\n added: [],\n removed: [],\n changed: []\n }\n\n // Build list of files that need to be uploaded...\n Object.keys(localFiles).forEach((localFilename: string) => {\n const localHash = localFiles[localFilename]\n const remoteHash = remoteFiles[localFilename]\n\n // Local hash not in remote cache\n if (!(localFilename in remoteFiles)) {\n changeset.added.push(localFilename)\n }\n // Local hash unequal to remote cache\n else if (remoteHash != localHash) {\n changeset.changed.push(localFilename)\n }\n // Local hash and remote hash are equal!\n })\n\n // Build list of files to be removed from the server...\n Object.keys(remoteFiles).forEach((remoteFilename) => {\n if (!(remoteFilename in localFiles)) {\n changeset.removed.push(remoteFilename)\n }\n })\n\n return changeset\n }\n\n static create(conn: RemoteConnection, config: IConfig, target: string | ITargetConfig) {\n return new Sync(conn, config, target);\n }\n}"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { IConfig, ITargetConfig } from './Config.js';
|
|
2
|
+
import { ConnectionFactory } from './RemoteConnection.js';
|
|
3
|
+
import { IChangeset } from './Sync.js';
|
|
4
|
+
export declare class TargetServer {
|
|
5
|
+
private _config;
|
|
6
|
+
private _targetConfig;
|
|
7
|
+
private _connect;
|
|
8
|
+
get config(): ITargetConfig;
|
|
9
|
+
get name(): string;
|
|
10
|
+
/**
|
|
11
|
+
* `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);
|
|
12
|
+
* tests pass a fake.
|
|
13
|
+
*/
|
|
14
|
+
constructor(config: IConfig, target: string | ITargetConfig | undefined, connect?: ConnectionFactory);
|
|
15
|
+
private _withConnection;
|
|
16
|
+
/**
|
|
17
|
+
* Add a cache file, if there's not one. `target.path` must already exist: a missing path is
|
|
18
|
+
* most likely a typo, so this rejects (telling the user to create the folder) and creates
|
|
19
|
+
* nothing.
|
|
20
|
+
*/
|
|
21
|
+
prepare(force?: boolean): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Verify the server has a cache file
|
|
24
|
+
*/
|
|
25
|
+
verify(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Sync local files to target server
|
|
28
|
+
*/
|
|
29
|
+
sync(isDryRun: boolean, forceConfirmation?: boolean): Promise<IChangeset | null>;
|
|
30
|
+
static create(config: IConfig, targetConfig: string | ITargetConfig | undefined, connect?: ConnectionFactory): TargetServer;
|
|
31
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import * as Log from './Log.js';
|
|
2
|
+
import { resolveTarget } from './Config.js';
|
|
3
|
+
import { Connection } from './Connection.js';
|
|
4
|
+
import { Sync } from './Sync.js';
|
|
5
|
+
export class TargetServer {
|
|
6
|
+
get config() {
|
|
7
|
+
return this._targetConfig;
|
|
8
|
+
}
|
|
9
|
+
get name() {
|
|
10
|
+
return this.config._name;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);
|
|
14
|
+
* tests pass a fake.
|
|
15
|
+
*/
|
|
16
|
+
constructor(config, target, connect = Connection.create) {
|
|
17
|
+
this._config = config;
|
|
18
|
+
this._targetConfig = resolveTarget(config, target);
|
|
19
|
+
this._connect = connect;
|
|
20
|
+
}
|
|
21
|
+
// Opens a connection, runs `task` with it, and closes it, whether `task` resolves or rejects.
|
|
22
|
+
async _withConnection(task) {
|
|
23
|
+
const conn = this._connect(this._targetConfig);
|
|
24
|
+
try {
|
|
25
|
+
return await task(conn);
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
try {
|
|
29
|
+
await conn.close();
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
Log.debug("Error closing the connection:", err);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Add a cache file, if there's not one. `target.path` must already exist: a missing path is
|
|
38
|
+
* most likely a typo, so this rejects (telling the user to create the folder) and creates
|
|
39
|
+
* nothing.
|
|
40
|
+
*/
|
|
41
|
+
async prepare(force) {
|
|
42
|
+
const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`;
|
|
43
|
+
return this._withConnection(async (conn) => {
|
|
44
|
+
const remoteIsConfigured = await conn.remoteFileExists(remoteCachePath);
|
|
45
|
+
if (remoteIsConfigured && force !== true) {
|
|
46
|
+
Log.info(`${this._targetConfig.host} already is initialized.`);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const { path, host } = this._targetConfig;
|
|
50
|
+
const remoteDirFound = await conn.remoteDirectoryFound(path);
|
|
51
|
+
if (!remoteDirFound) {
|
|
52
|
+
throw new Error(`Remote path "${path}" not found on ${host}. ` +
|
|
53
|
+
`Check the target's "path", or create the folder on the server, then run init again.`);
|
|
54
|
+
}
|
|
55
|
+
await conn.putContent("{}", remoteCachePath);
|
|
56
|
+
return true;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify the server has a cache file
|
|
61
|
+
*/
|
|
62
|
+
async verify() {
|
|
63
|
+
const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`;
|
|
64
|
+
return this._withConnection(conn => conn.remoteFileExists(remoteCachePath));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Sync local files to target server
|
|
68
|
+
*/
|
|
69
|
+
async sync(isDryRun, forceConfirmation = false) {
|
|
70
|
+
return this._withConnection(conn => Sync.create(conn, this._config, this._targetConfig).run(isDryRun, forceConfirmation));
|
|
71
|
+
}
|
|
72
|
+
static create(config, targetConfig, connect) {
|
|
73
|
+
return new TargetServer(config, targetConfig, connect);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=TargetServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TargetServer.js","sourceRoot":"./","sources":["lib/TargetServer.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Log from './Log.js'\nimport { IConfig, ITargetConfig, resolveTarget } from './Config.js'\nimport { Connection } from './Connection.js'\nimport { ConnectionFactory, RemoteConnection } from './RemoteConnection.js'\nimport { Sync, IChangeset } from './Sync.js'\n\nexport class TargetServer {\n private _config: IConfig\n private _targetConfig: ITargetConfig\n private _connect: ConnectionFactory\n\n get config() {\n return this._targetConfig\n }\n\n get name() {\n return this.config._name\n }\n\n /**\n * `connect` opens the connection for each operation. It defaults to FTP/FTPS (`Connection`);\n * tests pass a fake.\n */\n constructor(config: IConfig, target: string | ITargetConfig | undefined, connect: ConnectionFactory = Connection.create) {\n this._config = config\n this._targetConfig = resolveTarget(config, target)\n this._connect = connect\n }\n\n // Opens a connection, runs `task` with it, and closes it, whether `task` resolves or rejects.\n private async _withConnection<T>(task: (conn: RemoteConnection) => Promise<T>): Promise<T> {\n const conn = this._connect(this._targetConfig)\n try {\n return await task(conn)\n }\n finally {\n try {\n await conn.close()\n }\n catch (err) {\n Log.debug(\"Error closing the connection:\", err)\n }\n }\n }\n\n /**\n * Add a cache file, if there's not one. `target.path` must already exist: a missing path is\n * most likely a typo, so this rejects (telling the user to create the folder) and creates\n * nothing.\n */\n async prepare(force?: boolean): Promise<boolean> {\n const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`\n return this._withConnection(async (conn) => {\n const remoteIsConfigured = await conn.remoteFileExists(remoteCachePath)\n\n if (remoteIsConfigured && force !== true) {\n Log.info(`${this._targetConfig.host} already is initialized.`)\n return false\n }\n\n const { path, host } = this._targetConfig\n const remoteDirFound = await conn.remoteDirectoryFound(path)\n if (!remoteDirFound) {\n throw new Error(\n `Remote path \"${path}\" not found on ${host}. ` +\n `Check the target's \"path\", or create the folder on the server, then run init again.`)\n }\n\n await conn.putContent(\"{}\", remoteCachePath)\n\n return true\n })\n }\n\n /**\n * Verify the server has a cache file\n */\n async verify(): Promise<boolean> {\n const remoteCachePath = `${this._targetConfig.path}/${this._targetConfig.cache}`\n return this._withConnection(conn => conn.remoteFileExists(remoteCachePath))\n }\n\n /**\n * Sync local files to target server\n */\n async sync(isDryRun: boolean, forceConfirmation = false): Promise<IChangeset | null> {\n return this._withConnection(conn =>\n Sync.create(conn, this._config, this._targetConfig).run(isDryRun, forceConfirmation))\n }\n\n static create(config: IConfig, targetConfig: string | ITargetConfig | undefined, connect?: ConnectionFactory): TargetServer {\n return new TargetServer(config, targetConfig, connect)\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './Client.js';
|
|
2
|
+
export * from './Config.js';
|
|
3
|
+
export * from './ConfigSchema.js';
|
|
4
|
+
export * from './Connection.js';
|
|
5
|
+
export * from './Filelist.js';
|
|
6
|
+
export * from './RemoteConnection.js';
|
|
7
|
+
export * from './Sync.js';
|
|
8
|
+
export * from './TargetServer.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './Client.js';
|
|
2
|
+
export * from './Config.js';
|
|
3
|
+
export * from './ConfigSchema.js';
|
|
4
|
+
export * from './Connection.js';
|
|
5
|
+
export * from './Filelist.js';
|
|
6
|
+
export * from './RemoteConnection.js';
|
|
7
|
+
export * from './Sync.js';
|
|
8
|
+
export * from './TargetServer.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"./","sources":["lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,mBAAmB,CAAA;AACjC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,uBAAuB,CAAA;AACrC,cAAc,WAAW,CAAA;AACzB,cAAc,mBAAmB,CAAA","sourcesContent":["export * from './Client.js'\nexport * from './Config.js'\nexport * from './ConfigSchema.js'\nexport * from './Connection.js'\nexport * from './Filelist.js'\nexport * from './RemoteConnection.js'\nexport * from './Sync.js'\nexport * from './TargetServer.js'"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type Options = {
|
|
2
|
+
/**
|
|
3
|
+
Strip trailing commas in addition to comments.
|
|
4
|
+
|
|
5
|
+
@default false
|
|
6
|
+
*/
|
|
7
|
+
readonly trailingCommas?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
Replace comments and trailing commas with whitespace instead of stripping them entirely.
|
|
10
|
+
|
|
11
|
+
@default true
|
|
12
|
+
*/
|
|
13
|
+
readonly whitespace?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
Strip comments from JSON. Lets you use comments in your JSON files!
|
|
17
|
+
|
|
18
|
+
It will replace single-line comments `//` and multi-line comments `/**\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.
|
|
19
|
+
|
|
20
|
+
@param jsonString - Accepts a string with JSON.
|
|
21
|
+
@returns A JSON string without comments.
|
|
22
|
+
*/
|
|
23
|
+
export default function stripJsonComments(jsonString: string, { whitespace, trailingCommas }?: Options): string;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
7
|
+
|
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
11
|
+
*/
|
|
12
|
+
const singleComment = Symbol('singleComment');
|
|
13
|
+
const multiComment = Symbol('multiComment');
|
|
14
|
+
const stripWithoutWhitespace = (_string, _start, _end) => '';
|
|
15
|
+
// Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output.
|
|
16
|
+
const stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/[^ \t\r\n]/g, ' ');
|
|
17
|
+
const isEscaped = (jsonString, quotePosition) => {
|
|
18
|
+
let index = quotePosition - 1;
|
|
19
|
+
let backslashCount = 0;
|
|
20
|
+
while (jsonString[index] === '\\') {
|
|
21
|
+
index -= 1;
|
|
22
|
+
backslashCount += 1;
|
|
23
|
+
}
|
|
24
|
+
return Boolean(backslashCount % 2);
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
Strip comments from JSON. Lets you use comments in your JSON files!
|
|
28
|
+
|
|
29
|
+
It will replace single-line comments `//` and multi-line comments `/**\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.
|
|
30
|
+
|
|
31
|
+
@param jsonString - Accepts a string with JSON.
|
|
32
|
+
@returns A JSON string without comments.
|
|
33
|
+
*/
|
|
34
|
+
export default function stripJsonComments(jsonString, { whitespace = true, trailingCommas = false } = {}) {
|
|
35
|
+
if (typeof jsonString !== 'string') {
|
|
36
|
+
throw new TypeError(`Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``);
|
|
37
|
+
}
|
|
38
|
+
const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;
|
|
39
|
+
let isInsideString = false;
|
|
40
|
+
let isInsideComment = false;
|
|
41
|
+
let offset = 0;
|
|
42
|
+
let buffer = '';
|
|
43
|
+
let result = '';
|
|
44
|
+
let commaIndex = -1;
|
|
45
|
+
for (let index = 0; index < jsonString.length; index++) {
|
|
46
|
+
const currentCharacter = jsonString[index];
|
|
47
|
+
const nextCharacter = jsonString[index + 1];
|
|
48
|
+
if (!isInsideComment && currentCharacter === '"') {
|
|
49
|
+
// Enter or exit string
|
|
50
|
+
const escaped = isEscaped(jsonString, index);
|
|
51
|
+
if (!escaped) {
|
|
52
|
+
isInsideString = !isInsideString;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (isInsideString) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (!isInsideComment && currentCharacter + nextCharacter === '//') {
|
|
59
|
+
// Enter single-line comment
|
|
60
|
+
buffer += jsonString.slice(offset, index);
|
|
61
|
+
offset = index;
|
|
62
|
+
isInsideComment = singleComment;
|
|
63
|
+
index++;
|
|
64
|
+
}
|
|
65
|
+
else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') {
|
|
66
|
+
// Exit single-line comment via \r\n
|
|
67
|
+
index++;
|
|
68
|
+
isInsideComment = false;
|
|
69
|
+
buffer += strip(jsonString, offset, index);
|
|
70
|
+
offset = index;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
else if (isInsideComment === singleComment && currentCharacter === '\n') {
|
|
74
|
+
// Exit single-line comment via \n
|
|
75
|
+
isInsideComment = false;
|
|
76
|
+
buffer += strip(jsonString, offset, index);
|
|
77
|
+
offset = index;
|
|
78
|
+
}
|
|
79
|
+
else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {
|
|
80
|
+
// Enter multiline comment
|
|
81
|
+
buffer += jsonString.slice(offset, index);
|
|
82
|
+
offset = index;
|
|
83
|
+
isInsideComment = multiComment;
|
|
84
|
+
index++;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {
|
|
88
|
+
// Exit multiline comment
|
|
89
|
+
index++;
|
|
90
|
+
isInsideComment = false;
|
|
91
|
+
buffer += strip(jsonString, offset, index + 1);
|
|
92
|
+
offset = index + 1;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
else if (trailingCommas && !isInsideComment) {
|
|
96
|
+
if (commaIndex !== -1) {
|
|
97
|
+
if (currentCharacter === '}' || currentCharacter === ']') {
|
|
98
|
+
// Strip trailing comma
|
|
99
|
+
buffer += jsonString.slice(offset, index);
|
|
100
|
+
result += strip(buffer, 0, 1) + buffer.slice(1);
|
|
101
|
+
buffer = '';
|
|
102
|
+
offset = index;
|
|
103
|
+
commaIndex = -1;
|
|
104
|
+
}
|
|
105
|
+
else if (currentCharacter !== ' ' && currentCharacter !== '\t' && currentCharacter !== '\r' && currentCharacter !== '\n') {
|
|
106
|
+
// Hit non-whitespace following a comma; comma is not trailing
|
|
107
|
+
buffer += jsonString.slice(offset, index);
|
|
108
|
+
offset = index;
|
|
109
|
+
commaIndex = -1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (currentCharacter === ',') {
|
|
113
|
+
// Flush buffer prior to this point, and save new comma index
|
|
114
|
+
result += buffer + jsonString.slice(offset, index);
|
|
115
|
+
buffer = '';
|
|
116
|
+
offset = index;
|
|
117
|
+
commaIndex = index;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const remaining = (isInsideComment === singleComment)
|
|
122
|
+
? strip(jsonString, offset)
|
|
123
|
+
: jsonString.slice(offset);
|
|
124
|
+
return result + buffer + remaining;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=strip-json-comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-json-comments.js","sourceRoot":"./","sources":["vendor/strip-json-comments.ts"],"names":[],"mappings":"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","sourcesContent":["/*\nMIT License\n\nCopyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\n// Vendored from strip-json-comments 5.0.3 (https://github.com/sindresorhus/strip-json-comments),\n// index.js and index.d.ts, converted to TypeScript. The logic is unchanged; only type annotations\n// were added. Upstream is ESM-only, so it is vendored here instead of depended on.\n\nexport type Options = {\n\t/**\n\tStrip trailing commas in addition to comments.\n\n\t@default false\n\t*/\n\treadonly trailingCommas?: boolean;\n\n\t/**\n\tReplace comments and trailing commas with whitespace instead of stripping them entirely.\n\n\t@default true\n\t*/\n\treadonly whitespace?: boolean;\n};\n\nconst singleComment = Symbol('singleComment');\nconst multiComment = Symbol('multiComment');\n\ntype CommentState = false | typeof singleComment | typeof multiComment;\n\nconst stripWithoutWhitespace = (_string: string, _start?: number, _end?: number): string => '';\n\n// Replace all characters except ASCII spaces, tabs and line endings with regular spaces to ensure valid JSON output.\nconst stripWithWhitespace = (string: string, start?: number, end?: number): string => string.slice(start, end).replace(/[^ \\t\\r\\n]/g, ' ');\n\nconst isEscaped = (jsonString: string, quotePosition: number): boolean => {\n\tlet index = quotePosition - 1;\n\tlet backslashCount = 0;\n\n\twhile (jsonString[index] === '\\\\') {\n\t\tindex -= 1;\n\t\tbackslashCount += 1;\n\t}\n\n\treturn Boolean(backslashCount % 2);\n};\n\n/**\nStrip comments from JSON. Lets you use comments in your JSON files!\n\nIt will replace single-line comments `//` and multi-line comments `/**\\/` with whitespace. This allows JSON error positions to remain as close as possible to the original source.\n\n@param jsonString - Accepts a string with JSON.\n@returns A JSON string without comments.\n*/\nexport default function stripJsonComments(jsonString: string, {whitespace = true, trailingCommas = false}: Options = {}): string {\n\tif (typeof jsonString !== 'string') {\n\t\tthrow new TypeError(`Expected argument \\`jsonString\\` to be a \\`string\\`, got \\`${typeof jsonString}\\``);\n\t}\n\n\tconst strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace;\n\n\tlet isInsideString = false;\n\tlet isInsideComment: CommentState = false;\n\tlet offset = 0;\n\tlet buffer = '';\n\tlet result = '';\n\tlet commaIndex = -1;\n\n\tfor (let index = 0; index < jsonString.length; index++) {\n\t\tconst currentCharacter = jsonString[index];\n\t\tconst nextCharacter = jsonString[index + 1];\n\n\t\tif (!isInsideComment && currentCharacter === '\"') {\n\t\t\t// Enter or exit string\n\t\t\tconst escaped = isEscaped(jsonString, index);\n\t\t\tif (!escaped) {\n\t\t\t\tisInsideString = !isInsideString;\n\t\t\t}\n\t\t}\n\n\t\tif (isInsideString) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isInsideComment && currentCharacter + nextCharacter === '//') {\n\t\t\t// Enter single-line comment\n\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\toffset = index;\n\t\t\tisInsideComment = singleComment;\n\t\t\tindex++;\n\t\t} else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\\r\\n') {\n\t\t\t// Exit single-line comment via \\r\\n\n\t\t\tindex++;\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index);\n\t\t\toffset = index;\n\t\t\tcontinue;\n\t\t} else if (isInsideComment === singleComment && currentCharacter === '\\n') {\n\t\t\t// Exit single-line comment via \\n\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index);\n\t\t\toffset = index;\n\t\t} else if (!isInsideComment && currentCharacter + nextCharacter === '/*') {\n\t\t\t// Enter multiline comment\n\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\toffset = index;\n\t\t\tisInsideComment = multiComment;\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t} else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') {\n\t\t\t// Exit multiline comment\n\t\t\tindex++;\n\t\t\tisInsideComment = false;\n\t\t\tbuffer += strip(jsonString, offset, index + 1);\n\t\t\toffset = index + 1;\n\t\t\tcontinue;\n\t\t} else if (trailingCommas && !isInsideComment) {\n\t\t\tif (commaIndex !== -1) {\n\t\t\t\tif (currentCharacter === '}' || currentCharacter === ']') {\n\t\t\t\t\t// Strip trailing comma\n\t\t\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\t\t\tresult += strip(buffer, 0, 1) + buffer.slice(1);\n\t\t\t\t\tbuffer = '';\n\t\t\t\t\toffset = index;\n\t\t\t\t\tcommaIndex = -1;\n\t\t\t\t} else if (currentCharacter !== ' ' && currentCharacter !== '\\t' && currentCharacter !== '\\r' && currentCharacter !== '\\n') {\n\t\t\t\t\t// Hit non-whitespace following a comma; comma is not trailing\n\t\t\t\t\tbuffer += jsonString.slice(offset, index);\n\t\t\t\t\toffset = index;\n\t\t\t\t\tcommaIndex = -1;\n\t\t\t\t}\n\t\t\t} else if (currentCharacter === ',') {\n\t\t\t\t// Flush buffer prior to this point, and save new comma index\n\t\t\t\tresult += buffer + jsonString.slice(offset, index);\n\t\t\t\tbuffer = '';\n\t\t\t\toffset = index;\n\t\t\t\tcommaIndex = index;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst remaining = (isInsideComment === singleComment)\n\t\t? strip(jsonString, offset)\n\t\t: jsonString.slice(offset);\n\n\treturn result + buffer + remaining;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "syncodemayo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A simple FTP syncing tool",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
6
8
|
"bin": {
|
|
7
|
-
"syncodemayo": "
|
|
9
|
+
"syncodemayo": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"CHANGELOG.md"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22.17"
|
|
8
17
|
},
|
|
9
18
|
"scripts": {
|
|
10
|
-
"test": "
|
|
11
|
-
"compile": "tsc -p ./tsconfig.json && echo 'Finished compile.'",
|
|
19
|
+
"test": "bun run compile && node --test \"test/**/*.test.mjs\"",
|
|
20
|
+
"compile": "tsc -p ./tsconfig.json && chmod +x dist/cli.js && echo 'Finished compile.'",
|
|
12
21
|
"compile:watch": "tsc --watch -p ./tsconfig.json",
|
|
13
|
-
"
|
|
14
|
-
"prepublishOnly": "
|
|
22
|
+
"smoke:pack": "node scripts/smoke-pack.mjs",
|
|
23
|
+
"prepublishOnly": "bun run test"
|
|
15
24
|
},
|
|
16
25
|
"repository": {
|
|
17
26
|
"type": "git",
|
|
@@ -24,25 +33,14 @@
|
|
|
24
33
|
},
|
|
25
34
|
"homepage": "https://github.com/mattmccray/syncodemayo#readme",
|
|
26
35
|
"dependencies": {
|
|
27
|
-
"
|
|
28
|
-
"crc": "3.4.1",
|
|
29
|
-
"dotenv": "^4.0.0",
|
|
30
|
-
"glob": "^7.1.2",
|
|
31
|
-
"joi": "^10.6.0",
|
|
32
|
-
"jsftp": "1.3.1",
|
|
33
|
-
"jsftp-mkdirp": "0.1.2",
|
|
34
|
-
"minimatch": "^3.0.4",
|
|
35
|
-
"strip-json-comments": "^2.0.1",
|
|
36
|
-
"yesno": "^0.0.1"
|
|
36
|
+
"basic-ftp": "^6.2.6"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@types/
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"typescript": "^2.4.2"
|
|
47
|
-
}
|
|
39
|
+
"@types/node": "^22.20.5",
|
|
40
|
+
"ajv": "^8.20.0",
|
|
41
|
+
"ajv-draft-04": "^1.0.0",
|
|
42
|
+
"ftp-srv": "^4.6.3",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
},
|
|
45
|
+
"packageManager": "bun@1.4.4"
|
|
48
46
|
}
|
package/.npmignore
DELETED
package/cli.d.ts
DELETED
|
File without changes
|
package/cli.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node --harmony
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
const dotenv = require("dotenv");
|
|
5
|
-
const CLI = require("./lib/Cli");
|
|
6
|
-
const program = require("commander");
|
|
7
|
-
dotenv.config();
|
|
8
|
-
const defaultTarget = undefined;
|
|
9
|
-
let syncTarget = undefined;
|
|
10
|
-
let command = 'help';
|
|
11
|
-
program
|
|
12
|
-
.version('1.0.0')
|
|
13
|
-
.option('-c, --config [file]', 'Specify local config [file]');
|
|
14
|
-
// .option('-v, --verbose', 'Verbose logging')
|
|
15
|
-
program
|
|
16
|
-
.command('init [target]')
|
|
17
|
-
.description("Configure local folder and/or server to sync")
|
|
18
|
-
.action((target) => {
|
|
19
|
-
syncTarget = target || defaultTarget;
|
|
20
|
-
command = 'init';
|
|
21
|
-
});
|
|
22
|
-
program
|
|
23
|
-
.command('changes [target]')
|
|
24
|
-
.alias('diff')
|
|
25
|
-
.description("Perform sync dry run and display the changes")
|
|
26
|
-
.action((target) => {
|
|
27
|
-
syncTarget = target || defaultTarget;
|
|
28
|
-
command = 'changes';
|
|
29
|
-
});
|
|
30
|
-
program
|
|
31
|
-
.command('verify [target]')
|
|
32
|
-
.description("Verify server is configured")
|
|
33
|
-
.action((target) => {
|
|
34
|
-
syncTarget = target || defaultTarget;
|
|
35
|
-
command = 'check';
|
|
36
|
-
});
|
|
37
|
-
program
|
|
38
|
-
.command('sync [target]')
|
|
39
|
-
.description("Perform sync to server")
|
|
40
|
-
.option('-f, --force', "Don't prompt before uploading.", false)
|
|
41
|
-
.action((target, options) => {
|
|
42
|
-
syncTarget = target || defaultTarget;
|
|
43
|
-
command = options.force ? 'syncf' : 'sync';
|
|
44
|
-
});
|
|
45
|
-
program
|
|
46
|
-
.command('ls')
|
|
47
|
-
.description("List defined targets in config")
|
|
48
|
-
.action(() => {
|
|
49
|
-
command = "ls";
|
|
50
|
-
});
|
|
51
|
-
program.parse(process.argv);
|
|
52
|
-
switch (command) {
|
|
53
|
-
case "changes":
|
|
54
|
-
console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
|
|
55
|
-
CLI.changes(program.config, syncTarget).then(showDone).catch(showError);
|
|
56
|
-
break;
|
|
57
|
-
case "check":
|
|
58
|
-
console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
|
|
59
|
-
CLI.check(program.config, syncTarget).then(showDone).catch(showError);
|
|
60
|
-
break;
|
|
61
|
-
case "init":
|
|
62
|
-
console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}...\n`);
|
|
63
|
-
CLI.init(program.config, syncTarget).then(showDone).catch(showError);
|
|
64
|
-
break;
|
|
65
|
-
case "ls":
|
|
66
|
-
console.log(`Performing '${command}'...\n`);
|
|
67
|
-
CLI.ls(program.config).then(showDone).catch(showError);
|
|
68
|
-
break;
|
|
69
|
-
case "sync":
|
|
70
|
-
console.log(`Performing '${command}' on target: ${syncTarget || '(default)'}\n...`);
|
|
71
|
-
CLI.sync(program.config, syncTarget, false).then(showDone).catch(showError);
|
|
72
|
-
break;
|
|
73
|
-
case "syncf":
|
|
74
|
-
console.log(`Performing (forced) 'sync' on target: ${syncTarget || '(default)'}\n...`);
|
|
75
|
-
CLI.sync(program.config, syncTarget, true).then(showDone).catch(showError);
|
|
76
|
-
break;
|
|
77
|
-
case "help":
|
|
78
|
-
default:
|
|
79
|
-
syncTarget = "app";
|
|
80
|
-
program.help();
|
|
81
|
-
}
|
|
82
|
-
function showDone() {
|
|
83
|
-
console.log("\nDone.");
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
function showError(err) {
|
|
87
|
-
console.error("\n(!)", err.message, "\n", err);
|
|
88
|
-
// if (program.verbose) {
|
|
89
|
-
// console.error(err)
|
|
90
|
-
// }
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
//# sourceMappingURL=cli.js.map
|
package/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './lib';
|