Improvement on core API:

- improvement performance of some VFS utility API
- Allow compress/extract zip file in Files
- CodePad/Files/MarketPlace use some common system APIs instead of defining in-package functions
This commit is contained in:
lxsang 2021-04-17 17:33:04 +02:00
parent e657c98688
commit fd4474d598
8 changed files with 543 additions and 743 deletions

Binary file not shown.

View File

@ -1770,9 +1770,7 @@ namespace OS {
* - array: the result will be an Array of bytes (numbers between 0 and 255). * - array: the result will be an Array of bytes (numbers between 0 and 255).
* - uint8array : the result will be a Uint8Array. This requires a compatible browser. * - uint8array : the result will be a Uint8Array. This requires a compatible browser.
* - arraybuffer : the result will be a ArrayBuffer. This requires a compatible browser. * - arraybuffer : the result will be a ArrayBuffer. This requires a compatible browser.
* - blob : the result will be a Blob. This requires a compatible browser. * - blob : the result will be a Blob. This requires a compatible browser. *
* - nodebuffer : the result will be a nodejs Buffer. This requires nodejs.
*
* If file_name is not specified, the first file_name in the zip archive will be read * If file_name is not specified, the first file_name in the zip archive will be read
* @export * @export
* @param {string} file zip file * @param {string} file zip file
@ -1784,91 +1782,65 @@ namespace OS {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
await API.requires("os://scripts/jszip.min.js"); await API.requires("os://scripts/jszip.min.js");
try { const data = await file.asFileHandle().read("binary");
const data = await file.asFileHandle().read("binary"); const zip = await JSZip.loadAsync(data);
try { if (!file_name) {
const zip = await JSZip.loadAsync(data); for (let name in zip.files) {
if (!file_name) { file_name = name;
for (let name in zip.files) { break;
file_name = name;
break;
}
}
try {
const udata = await zip.file(file_name).async(type);
resolve(udata);
} catch (e_2) {
return reject(__e(e_2));
}
} catch (e_1) {
return reject(__e(e_1));
} }
} catch (e) {
return reject(__e(e));
} }
} catch (e_3) { const udata = await zip.file(file_name).async(type);
return reject(__e(e_3)); resolve(udata);
} catch (e) {
return reject(__e(e));
} }
}); });
} }
/** /**
* Cat al file to a single out-put * Cat all files to a single out-put
* *
* @export * @export
* @param {string[]} list list of VFS files * @param {string[]} list list of VFS files
* @param {string} data input data string that will be cat to the files content * @param {string} data input data string that will be cat to the files content
* @param {string} join_by join on files content by this string
* @return {*} {Promise<string>} * @return {*} {Promise<string>}
*/ */
export function cat(list: string[], data: string): Promise<string> { export function cat(list: string[], data: string, join_by: string = "\n"): Promise<string> {
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
if (list.length === 0) { if (list.length === 0) {
return resolve(data); return resolve(data);
} }
const file = list.splice(0, 1)[0].asFileHandle(); const promises = [];
return file for (const file of list) {
.read() promises.push(file.asFileHandle().read("text"));
.then((text: string) => { }
data = data + "\n" + text; try {
return cat(list, data) const results = await Promise.all(promises);
.then((d) => resolve(d)) resolve(`${data}${join_by}${results.join(join_by)}`);
.catch((e) => reject(__e(e))); } catch (error) {
}) reject(__e(error));
.catch((e: Error) => reject(__e(e))); }
}); });
} }
/** /**
* Read all files content to on the list * Read all files content on the list
* *
* @export * @export
* @param {string[]} list list of VFS files * @param {string[]} list list of VFS files
* @param {GenericObject<string>[]} contents content array * @param {GenericObject<string>[]} contents content array
* @return {*} {Promise<GenericObject<string>[]>} * @return {void}
*/ */
export function read_files(list: string[], contents: GenericObject<string>[]): Promise<GenericObject<string>[]> { export function read_files(list: string[]): Promise<GenericObject<string>[]> {
return new Promise((resolve, reject) => { const promises = [];
if (list.length === 0) { for (const file of list) {
return resolve(contents); promises.push(file.asFileHandle().read("text"));
} }
const file = list.splice(0, 1)[0].asFileHandle(); return Promise.all(promises);
return file
.read()
.then((text: string) => {
contents.push({
path: file.path,
content: text
});
return read_files(list, contents)
.then((d) => resolve(d))
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
} }
@ -1878,71 +1850,45 @@ namespace OS {
* @export * @export
* @param {string[]} files list of files * @param {string[]} files list of files
* @param {string} to destination folder * @param {string} to destination folder
* @return {*} {Promise<void>} * @return {*} {Promise<any[]>}
*/ */
export function copy(files: string[], to: string): Promise<void> { export function copy(files: string[], to: string): Promise<any[]> {
return new Promise((resolve, reject) => { const promises = [];
if (files.length === 0) { for (const path of files) {
return resolve(); promises.push(new Promise(async (resolve, reject) => {
} try {
const file = files.splice(0, 1)[0].asFileHandle(); const file = path.asFileHandle();
const tof = `${to}/${file.basename}`.asFileHandle(); const tof = `${to}/${file.basename}`.asFileHandle();
return file const meta = await file.onready();
.onready()
.then((meta: { type: string }) => {
if (meta.type === "dir") { if (meta.type === "dir") {
// copy directory
const desdir = to.asFileHandle(); const desdir = to.asFileHandle();
return desdir await desdir.mk(file.basename);
.mk(file.basename) const ret = await file.read();
.then(() => { const files = ret.result.map((v: API.FileInfoType) => v.path);
// read the dir content if (files.length > 0) {
return file await copy(files, `${desdir.path}/${file.basename}`);
.read() resolve(undefined);
.then((data: API.RequestResult) => { }
const list = (data.result as API.FileInfoType[]).map( else {
(v) => v.path resolve(undefined);
); }
return copy(
list,
`${desdir.path}/${file.basename}`
)
.then(() => {
return copy(files, to)
.then(() => resolve())
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
} else {
// copy file
return file
.read("binary")
.then(async (data: ArrayBuffer) => {
const d = await tof
.setCache(
new Blob([data], {
type: file.info.mime,
})
)
.write(file.info.mime);
try {
await copy(files, to);
return resolve();
} catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
} }
}) else {
.catch((e: Error) => reject(__e(e))); const content = await file.read("binary");
}); await tof
.setCache(
new Blob([content], {
type: file.info.mime,
}))
.write(file.info.mime);
resolve(undefined);
}
} catch (error) {
reject(__e(error));
}
}));
}
return Promise.all(promises);
} }
/** /**
@ -1954,69 +1900,38 @@ namespace OS {
* @return {*} {Promise<any>} * @return {*} {Promise<any>}
*/ */
function aradd(list: string[], zip: any, base: string): Promise<any> { function aradd(list: string[], zip: any, base: string): Promise<any> {
return new Promise((resolve, reject) => { const promises = [];
if (list.length === 0) { for (const path of list) {
return resolve(zip); promises.push(new Promise(async (resolve, reject) => {
} const file = path.asFileHandle();
const path = list.splice(0, 1)[0]; try {
const file = path.asFileHandle(); const meta = await file.asFileHandle().onready();
return file if (meta.type == "dir") {
.onready() const ret = await file.read();
.then((meta: { type: string }) => { const dirs: string[] = ret.result.map((v: API.FileInfoType) => v.path);
if (meta.type === "dir") { if (dirs.length > 0) {
return file await aradd(dirs, zip, `${base}${file.basename}/`);
.read() resolve(undefined);
.then( }
(d: { else {
result: resolve(undefined);
| Iterable<unknown> }
| ArrayLike<unknown>;
}) => {
const l = (d.result as API.FileInfoType[]).map(
(v) => v.path
);
return aradd(
l,
zip,
`${base}${file.basename}/`
)
.then(() => {
return aradd(
list,
zip,
base
)
.then(() => resolve(zip))
.catch((e) =>
reject(__e(e))
);
})
.catch((e) => reject(__e(e)));
}
)
.catch((e: Error) => reject(__e(e)));
} else {
return file
.read("binary")
.then(async (d: any) => {
const zpath = `${base}${file.basename}`.replace(
/^\/+|\/+$/g,
""
);
zip.file(zpath, d, { binary: true });
try {
await aradd(list, zip, base);
return resolve(zip);
}
catch (e) {
return reject(__e(e));
}
})
.catch((e: Error) => reject(__e(e)));
} }
}) else {
.catch((e: Error) => reject(__e(e))); const u_data = await file.read("binary");
}); const z_path = `${base}${file.basename}`.replace(
/^\/+|\/+$/g,
""
);
zip.file(z_path, u_data, { binary: true });
resolve(undefined);
}
} catch (error) {
reject(__e(error));
}
}));
}
return Promise.all(promises);
} }
@ -2024,57 +1939,36 @@ namespace OS {
* Create a zip archive from a folder * Create a zip archive from a folder
* *
* @export * @export
* @param {string} src source folder * @param {string} src source file/folder
* @param {string} dest destination archive * @param {string} dest destination archive
* @return {*} {Promise<void>} * @return {*} {Promise<void>}
*/ */
export function mkar(src: string, dest: string): Promise<void> { export function mkar(src: string, dest: string): Promise<void> {
return new Promise((resolve, reject) => { console.log(src, dest);
return new Promise(async (r, e) => { return new Promise(async (resolve, reject) => {
try { try {
await API.requires("os://scripts/jszip.min.js"); await API.requires("os://scripts/jszip.min.js");
try { const zip = new JSZip();
const d = await src.asFileHandle().read(); const fhd = src.asFileHandle();
return r(d.result); const meta = await fhd.onready();
} catch (ex) { if(meta.type === "file")
return e(__e(ex)); {
} await aradd([src], zip, "/");
} catch (ex_1) {
return e(__e(ex_1));
} }
}) else
.then((files: API.FileInfoType[]) => { {
return new Promise(async (r, e) => { const ret = await fhd.read();
const zip = new JSZip(); await aradd(
try { ret.result.map((v: API.FileInfoType) => v.path), zip, "/");
const z = await aradd( }
files.map((v: { path: any }) => v.path), const z_data = await zip.generateAsync({ type: "base64" });
zip, await dest.asFileHandle()
"/" .setCache("data:application/zip;base64," + z_data)
); .write("base64");
return r(z); resolve();
} catch (ex) { } catch (error) {
return e(__e(ex)); reject(__e(error));
} }
});
})
.then((zip: any) => {
return zip
.generateAsync({ type: "base64" })
.then((data: string) => {
return dest
.asFileHandle()
.setCache(
"data:application/zip;base64," + data
)
.write("base64")
.then((r: any) => {
resolve();
})
.catch((e: Error) => reject(__e(e)));
});
})
.catch((e) => reject(__e(e)));
}); });
} }
@ -2083,35 +1977,105 @@ namespace OS {
* *
* @export * @export
* @param {string[]} list of directories to be created * @param {string[]} list of directories to be created
* @return {*} {Promise<void>} * @param {boolen} sync sync/async of directory creation
* @return {*} {Promise<any>}
*/ */
export function mkdirAll(list: string[]): Promise<void> { export function mkdirAll(list: string[], sync?: boolean): Promise<any> {
return new Promise((resolve, reject) => { if (!sync) {
if (list.length === 0) { const promises = [];
return resolve(); for (const dir of list) {
const path = dir.asFileHandle()
promises.push(path.parent().mk(path.basename));
} }
const path = list.splice(0, 1)[0].asFileHandle(); return Promise.all(promises);
return path }
.parent() else {
.mk(path.basename) return new Promise(async (resolve, reject) => {
.then((d: any) => { try {
return mkdirAll(list) if (list.length === 0) {
.then(() => resolve()) return resolve(true);
.catch((e) => reject(__e(e))); }
}) const dir = list.splice(0, 1)[0].asFileHandle();
.catch((e: Error) => reject(__e(e))); const path = dir.parent();
}); const dname = dir.basename;
const r = await path.asFileHandle().mk(dname);
if (r.error) {
return reject(
this._api.throwe(
__(
"Cannot create {0}",
`${path}/${dir}`
)
)
);
}
await mkdirAll(list, sync);
resolve(true);
}
catch (e) {
reject(__e(e));
}
});
}
} }
/** /**
*
* *
* @export *
* @param {Array<string[]>} list of templates mapping files * @export Extract a zip fle
* @param {string} path path stored create files * @param {string} zfile zip file to extract
* @param {string} name * @param {(zip:any) => Promise<string>} [dest_callback] a callback to get extraction destination
* @return {*} {Promise<void>} * @return {*} {Promise<void>}
*/ */
export function extractZip(zfile: string | API.VFS.BaseFileHandle, dest_callback: (zip: any) => Promise<string>): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
await API.requires("os://scripts/jszip.min.js");
const data = await zfile.asFileHandle().read("binary");
const zip = await JSZip.loadAsync(data);
const to = await dest_callback(zip);
const dirs = [];
const files = [];
for (const name in zip.files) {
const file = zip.files[name];
if (file.dir) {
dirs.push(to + "/" + name);
} else {
files.push(name);
}
}
await mkdirAll(dirs, true);
const promises = [];
for (const file of files) {
promises.push(new Promise(async (res, rej) => {
try {
const data = await zip.file(file).async("uint8array");
const path = `${to}/${file}`;
const fp = path.asFileHandle();
fp.cache = new Blob([data], { type: "octet/stream" });
const r = await fp.write("text/plain");
if (r.error) {
return rej(
API.throwe(
__("Cannot extract file to {0}", path)
)
);
}
return resolve(res(path));
} catch (error) {
rej(__e(error));
}
}));
}
await Promise.all(promises);
resolve();
} catch (e) {
return reject(__e(e));
}
});
}
/** /**
* Make files from a set of template files * Make files from a set of template files
@ -2120,35 +2084,28 @@ namespace OS {
* @param {Array<string[]>} list mapping paths between templates files and created files * @param {Array<string[]>} list mapping paths between templates files and created files
* @param {string} path files destination * @param {string} path files destination
* @param {(data: string) => string} callback: pre-processing files content before writing to destination files * @param {(data: string) => string} callback: pre-processing files content before writing to destination files
* @return {*} {Promise<void>} * @return {*} {Promise<any[]>}
*/ */
export function mktpl( export function mktpl(
list: Array<string[]>, list: Array<string[]>,
path: string, path: string,
callback: (data: string) => string callback: (data: string) => string
): Promise<void> { ): Promise<any[]> {
return new Promise((resolve, reject) => { const promises = [];
if (list.length === 0) { for (const tpl of list) {
return resolve(); promises.push(new Promise(async (resolve, reject) => {
} try {
const item = list.splice(0, 1)[0]; const data = await `${path}/${tpl[0]}`.asFileHandle().read();
return `${path}/${item[0]}` const file = tpl[1].asFileHandle();
.asFileHandle() file.setCache(callback(data));
.read() await file.write("text/plain");
.then((data) => { resolve(undefined);
const file = item[1].asFileHandle(); } catch (error) {
return file reject(__e(error));
.setCache(callback(data)) }
.write("text/plain") }));
.then(() => { }
return mktpl(list, path, callback) return Promise.all(promises);
.then(() => resolve())
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
});
} }
} }
} }

View File

@ -89,13 +89,9 @@ namespace OS {
.then(async (meta) => { .then(async (meta) => {
try { try {
await this.build(meta, true); await this.build(meta, true);
try { await this.run(meta);
return this.run(meta);
} catch (e) {
return this.logger().error(__("Unable to run project: {0}", e.stack));
}
} catch (e_1) { } catch (e_1) {
return this.logger().error(__("Unable to build project: {0}", e_1.stack)); return this.logger().error(__("Unable to build and run project: {0}", e_1.stack));
} }
}) })
.catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack))); .catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack)));
@ -112,19 +108,13 @@ namespace OS {
.then(async (meta) => { .then(async (meta) => {
try { try {
await this.build(meta, false); await this.build(meta, false);
try { await API.VFS.mkar(
return API.VFS.mkar( `${meta.root}/build/debug`,
`${meta.root}/build/debug`, `${meta.root}/build/release/${meta.name}.zip`
`${meta.root}/build/release/${meta.name}.zip` );
); this.logger().info(__("Archive generate at: {0}", `${meta.root}/build/release/${meta.name}.zip`));
} catch (e) {
return this.logger().error(
__("Unable to create package archive: {0}",
e.stack)
);
}
} catch (e_1) { } catch (e_1) {
return this.logger().error(__("Unable to build project: {0}", e_1.stack)); return this.logger().error(__("Unable to release project: {0}", e_1.stack));
} }
}) })
.catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack))); .catch((e) => this.logger().error(__("Unable to read meta-data: {0}", e.stack)));
@ -253,54 +243,54 @@ namespace OS {
const core_lib = "os://packages/CodePad/libs/corelib.d.ts"; const core_lib = "os://packages/CodePad/libs/corelib.d.ts";
try { try {
await this.load_corelib(core_lib); await this.load_corelib(core_lib);
const arr = []; const arr = await API.VFS.read_files(files);
API.VFS.read_files(files, arr).then((_result) => { const libs: string[] = files.map((e) => e)
const libs: string[] = arr.map((e) => e.path) libs.unshift(core_lib);
libs.unshift(core_lib); const src_files: GenericObject<any> = {};
const src_files: GenericObject<any> = {}; src_files[core_lib] = AntOSDK.corelib["ts"];
src_files[core_lib] = AntOSDK.corelib["ts"]; for (const i in arr) {
for (const el of arr) { src_files[files[i]] = ts.createSourceFile(files[i], arr[i], ts.ScriptTarget.Latest);
src_files[el.path] = ts.createSourceFile(el.path, el.content, ts.ScriptTarget.Latest); }
} let js_code = "";
let js_code = ""; const host = {
const host = { fileExists: (path: string) => {
fileExists: (path: string) => { return src_files[path] != undefined;
return src_files[path] != undefined; },
}, directoryExists: (path: string) => {
directoryExists: (path: string) => { return true;
return true; },
}, getCurrentDirectory: () => "/",
getCurrentDirectory: () => "/", getDirectories: () => [],
getDirectories: () => [], getCanonicalFileName: (path: string) => path,
getCanonicalFileName: (path: string) => path, getNewLine: () => "\n",
getNewLine: () => "\n", getDefaultLibFileName: () => "",
getDefaultLibFileName: () => "", getSourceFile: (path: string) => src_files[path],
getSourceFile: (path: string) => src_files[path], readFile: (path: string) => undefined,
readFile: (path: string) => undefined, useCaseSensitiveFileNames: () => true,
useCaseSensitiveFileNames: () => true, writeFile: (path: string, data: string) => js_code = `${js_code}\n${data}`,
writeFile: (path: string, data: string) => js_code = `${js_code}\n${data}`, };
}; const program = ts.createProgram(libs, {
const program = ts.createProgram(libs, { "target": "es6",
"target": "es6", "skipLibCheck": true,
"skipLibCheck": true, }, host);
}, host); const result = program.emit();
const result = program.emit(); const diagnostics = result.diagnostics.concat((ts.getPreEmitDiagnostics(program)));
const diagnostics = result.diagnostics.concat((ts.getPreEmitDiagnostics(program))); if (diagnostics.length > 0) {
if (diagnostics.length > 0) { diagnostics.forEach(diagnostic => {
diagnostics.forEach(diagnostic => { if (diagnostic.file) {
if (diagnostic.file) { let { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
let { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!); let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"); this.logger().error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
this.logger().error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`); } else {
} else { this.logger().error(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n"));
this.logger().error(ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")); }
} });
}); return reject(API.throwe(__("Typescript compile error")));
return reject(API.throwe(__("Typescript compile error"))); }
} for (const file of files) {
resolve(js_code); this.logger().info(__("Compiled: {0}", file));
}) }
.catch((e) => { reject(__e(e)) }); resolve(js_code);
} catch (e) { } catch (e) {
return reject(__e(e)); return reject(__e(e));
} }
@ -331,12 +321,12 @@ namespace OS {
const ts_list = meta.ts.map( const ts_list = meta.ts.map(
(v: string) => `${meta.root.trimBy("/")}/${v}` (v: string) => `${meta.root.trimBy("/")}/${v}`
); );
Promise.all([ const results = await Promise.all([
this.compile_ts(ts_list), this.compile_ts(ts_list),
this.compile_coffee(coffee_list) this.compile_coffee(coffee_list)
]).then((js_codes: string[]) => { ]);
resolve(js_codes.join("\n"));
}).catch((e_1) => reject(__e(e_1))); resolve(results.join("\n"));
} catch (e_2) { } catch (e_2) {
return reject(__e(e_2)); return reject(__e(e_2));
} }
@ -358,14 +348,12 @@ namespace OS {
} }
try { try {
await this.verify_coffee(list.map((x: string) => x)); await this.verify_coffee(list.map((x: string) => x));
try { const code = await API.VFS.cat(list, "");
const code = await API.VFS.cat(list, ""); const jsrc = CoffeeScript.compile(code);
const jsrc = CoffeeScript.compile(code); for (const file of list) {
this.logger().info(__("Compiled successful")); this.logger().info(__("Compiled: {0}", file));
return resolve(jsrc);
} catch (e) {
return reject(__e(e));
} }
return resolve(jsrc);
} catch (e_1) { } catch (e_1) {
return reject(__e(e_1)); return reject(__e(e_1));
} }
@ -382,109 +370,54 @@ namespace OS {
* @memberof AntOSDK * @memberof AntOSDK
*/ */
private build(meta: GenericObject<any>, debug: boolean): Promise<void> { private build(meta: GenericObject<any>, debug: boolean): Promise<void> {
const dirs = [
`${meta.root}/build`,
`${meta.root}/build/debug`,
`${meta.root}/build/release`,
];
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
await API.VFS.mkdirAll(dirs); this.logger().info(__("Building the package", meta.name));
try { await API.VFS.mkdirAll([`${meta.root}/build`,]);
const src = await this.compile(meta); await API.VFS.mkdirAll([`${meta.root}/build/debug`, `${meta.root}/build/release`]);
let v: string; const src = await this.compile(meta);
try { let code = await API.VFS.cat(meta.javascripts.map(v => `${meta.root}/${v}`), src);
let jsrc = await API.VFS.cat( if (!debug) {
(() => { const options = {
const result = []; toplevel: true,
for (v of meta.javascripts) { compress: {
result.push(`${meta.root}/${v}`); passes: 3,
} },
return result; mangle: true,
})(), output: {
src //beautify: true,
},
};
const result = Terser.minify(code, options);
if (result.error) {
this.logger().error(
__(
"Unable to minify code: {0}",
result.error
)
); );
await new Promise<void>(async function (r, e) { } else {
let code = jsrc; code = result.code;
if (!debug) {
const options = {
toplevel: true,
compress: {
passes: 3,
},
mangle: true,
output: {
//beautify: true,
},
};
const result_1 = Terser.minify(
jsrc,
options
);
if (result_1.error) {
this.logger().error(
__(
"Unable to minify code: {0}",
result_1.error
)
);
} else {
({ code } = result_1);
}
}
try {
const d = await `${meta.root}/build/debug/main.js`
.asFileHandle()
.setCache(code)
.write("text/plain");
return r();
} catch (ex) {
return e(__e(ex));
}
});
await new Promise<void>(async (r, e) => {
const txt = await API.VFS.cat(
(() => {
const result1 = [];
for (v of meta.css) {
result1.push(`${meta.root}/${v}`);
}
return result1;
})(),
""
);
if (txt === "") {
return r();
}
try {
const d_1 = await `${meta.root}/build/debug/main.css`
.asFileHandle()
.setCache(txt)
.write("text/plain");
return r();
} catch (ex_1) {
return e(__e(ex_1));
}
});
await API.VFS.copy(
(() => {
const result1_1 = [];
for (v of meta.copies) {
result1_1.push(`${meta.root}/${v}`);
}
return result1_1;
})(),
`${meta.root}/build/debug`
);
return resolve();
} catch (e) {
return reject(__e(e));
} }
} catch (e_1) {
return reject(__e(e_1));
} }
} catch (e_2) { if (code != "")
return reject(__e(e_2)); await `${meta.root}/build/debug/main.js`
.asFileHandle()
.setCache(code)
.write("text/plain");
const txt = await API.VFS.cat(meta.css.map(v => `${meta.root}/${v}`), "");
if (txt != "")
await `${meta.root}/build/debug/main.css`
.asFileHandle()
.setCache(txt)
.write("text/plain");
await API.VFS.copy(
meta.copies.map(v => `${meta.root}/${v}`),
`${meta.root}/build/debug`
);
resolve();
} catch (e) {
return reject(__e(e));
} }
}); });
} }

View File

@ -87,8 +87,7 @@ namespace OS {
iconclass: v.iconclass, iconclass: v.iconclass,
}); });
} }
let ctx_menu = [
m.items = [
{ {
text: "__(Open with)", text: "__(Open with)",
nodes: apps, nodes: apps,
@ -103,6 +102,75 @@ namespace OS {
this.mnFile(), this.mnFile(),
this.mnEdit(), this.mnEdit(),
]; ];
if(file.mime === "application/zip")
{
ctx_menu = ctx_menu.concat([
{
text: "__(Extract Here)",
onmenuselect: (e: GUI.TagEventType<GUI.tag.MenuEventData>) => {
if (!e) {
return;
}
API.VFS.extractZip(file.path,
(z) => new Promise((r,e) => r(file.path.asFileHandle().parent().path)))
.catch((err) => this.error(__("Unable to extract file"), err));
},
},
{
text: "__(Extract to)",
onmenuselect: async (e: GUI.TagEventType<GUI.tag.MenuEventData>) => {
if (!e) {
return;
}
try {
OS.GUI.dialogs.FileDialog.last_opened = this.currdir.path;
const d = await this.openDialog("FileDialog", {
title: __("Select extract destination"),
type: "dir",
file: file.path.replace(".zip","").asFileHandle()
});
const path = `${d.file.path}/${d.name}`;
await API.VFS.mkdirAll([path]);
await API.VFS.extractZip(file.path,
(z) => new Promise((r,e) => r(path)));
} catch (error) {
this.error(__("Unable to extract file"), error);
}
},
},
]);
}
else
{
ctx_menu.push(
{
text: "__(Compress)",
onmenuselect: async (e: GUI.TagEventType<GUI.tag.MenuEventData>) => {
if (!e) {
return;
}
try {
OS.GUI.dialogs.FileDialog.last_opened = this.currdir.path;
const d = await this.openDialog("FileDialog", {
title: __("Save compressed file to"),
type: "dir",
file: `${this.currdir.path}/${file.name}.zip`.asFileHandle()
});
if(d.name.trim() === "")
{
return this.error(__("Invalid file name"));
}
const path = `${d.file.path}/${d.name}`;
await API.VFS.mkar(file.path, path);
this.notify(__("Archive file created: {0}",path ));
} catch (error) {
this.error(__("Unable to compress file, folder"), error);
}
}
}
);
}
m.items = ctx_menu;
m.show(e); m.show(e);
}; };
@ -546,7 +614,7 @@ namespace OS {
return this.notify(__("File {0} cut", file.filename)); return this.notify(__("File {0} cut", file.filename));
case `${this.name}-copy`: case `${this.name}-copy`:
if (!file && file.type !== "dir") { if (!file) {
return; return;
} }
this.clipboard = { this.clipboard = {
@ -579,30 +647,12 @@ namespace OS {
); );
}); });
} else { } else {
this.clipboard.file API.VFS.copy([this.clipboard.file.path],this.currdir.path)
.read("binary") .then(() => {
.then(async (d) => { return (this.clipboard = undefined);
const blob = new Blob([d], {
type: this.clipboard.file.info.mime,
});
const fp = `${this.currdir.path}/${this.clipboard.file.basename}`.asFileHandle();
fp.cache = blob;
try {
const r = await fp.write(this.clipboard.file.info.mime);
return (this.clipboard = undefined);
}
catch (e) {
return this.error(__("Fail to paste: {0}", this.clipboard.file.path), e);
}
}) })
.catch((e) => { .catch((e) => {
return this.error( return this.error(__("Fail to paste: {0}", this.clipboard.file.path), e);
__(
"Fail to read: {0}",
this.clipboard.file.path
),
e
);
}); });
} }
break; break;

View File

@ -6,7 +6,7 @@
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "xsang.le@gmail.com" "email": "xsang.le@gmail.com"
}, },
"version":"0.1.2-a", "version":"0.1.3-a",
"category":"System", "category":"System",
"iconclass":"fa fa-hdd-o", "iconclass":"fa fa-hdd-o",
"mimes":["dir"], "mimes":["dir"],

View File

@ -68,19 +68,15 @@ namespace OS {
}; };
this.btinstall.onbtclick = async () => { this.btinstall.onbtclick = async () => {
if (this.btinstall.data.dirty) { try {
try { if (this.btinstall.data.dirty) {
await this.updatePackage(); await this.updatePackage();
return this.notify(__("Package updated")); return this.notify(__("Package updated"));
} catch (e) {
return this.error(e.toString(), e);
} }
}
try {
const n = await this.remoteInstall(); const n = await this.remoteInstall();
return this.notify(__("Package installed: {0}", n)); return this.notify(__("Package installed: {0}", n));
} catch (e_1) { } catch (error) {
return this.error(e_1.toString(), e_1); return this.error(error.toString(), error);
} }
}; };
@ -466,40 +462,19 @@ namespace OS {
return reject(this._api.throwe(__("Unable to find package: {0}", pkgname))); return reject(this._api.throwe(__("Unable to find package: {0}", pkgname)));
} }
try { try {
const data = await this._api.blob( const n = await this.install(meta.download + "?_=" + new Date().getTime(), meta);
meta.download + "?_=" + new Date().getTime() return resolve(meta);
);
try {
const n = await this.install(data, meta);
return resolve(meta);
} catch (e) {
return reject(__e(e));
}
} catch (e_1) { } catch (e_1) {
return reject(__e(e_1)); return reject(__e(e_1));
} }
}); });
} }
private bulkInstall(list: string[]): Promise<any> { private bulkInstall(list: string[]): Promise<any> {
return new Promise((resolve, reject) => { const promises = [];
if (list.length == 0) { for (let pkgname of list) {
return resolve(true); promises.push(this.installPkg(pkgname));
} }
const pkgname = list.splice(0, 1)[0]; return Promise.all(promises);
this.installPkg(pkgname)
.then((_meta) => {
this.bulkInstall(list)
.then((b) => {
resolve(b);
})
.catch((e) => {
reject(e);
})
})
.catch((err) => {
reject(err);
})
});
} }
private remoteInstall(): Promise<string> { private remoteInstall(): Promise<string> {
const el = this.applist.selectedItem; const el = this.applist.selectedItem;
@ -512,175 +487,130 @@ namespace OS {
} }
// get blob file // get blob file
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
let pkgname = `${el.data.pkgname}@${el.data.version}`; try {
const dep = this.checkDependencies(pkgname); let pkgname = `${el.data.pkgname}@${el.data.version}`;
if (dep.notfound.size != 0) { const dep = this.checkDependencies(pkgname);
return this.openDialog("TextDialog", { if (dep.notfound.size != 0) {
disable: true, this.openDialog("TextDialog", {
title: __("Unresolved dependencies"), disable: true,
value: __( title: __("Unresolved dependencies"),
"Unable to install: The package `{0}` depends on these packages, but they are not found:\n{1}", value: __(
pkgname, "Unable to install: The package `{0}` depends on these packages, but they are not found:\n{1}",
[...dep.notfound].join("\n") pkgname,
) [...dep.notfound].join("\n")
}) )
.then((_v) => {
reject(__("Unresolved dependencies on: {0}", pkgname))
}); });
return reject(__("Unresolved dependencies on: {0}", pkgname));
}
const t = await this.openDialog("TextDialog", {
title: __("Confirm install"),
disable: true,
value: __(
"Please confirm the following operation:\n\n{0} packages will be removed:\n\n{1}\n\n{2} packages will be installed:\n\n{3}",
dep.uninstall.size.toString(),
[...dep.uninstall].join("\n"),
dep.install.size.toString(),
[...dep.install].join("\n")
)
});
if (!t) return;
await this.bulkUninstall([...dep.uninstall]);
const metas = await this.bulkInstall([...dep.install]);
this.appDetail(metas.pop());
resolve(pkgname);
} catch (error) {
reject(__e(error));
} }
this.openDialog("TextDialog", {
title: __("Confirm install"),
disable: true,
value: __(
"Please confirm the following operation:\n\n{0} packages will be removed:\n\n{1}\n\n{2} packages will be installed:\n\n{3}",
dep.uninstall.size.toString(),
[...dep.uninstall].join("\n"),
dep.install.size.toString(),
[...dep.install].join("\n")
)
}).then((_v) => {
this.bulkUninstall([...dep.uninstall])
.then((_b) => {
this.bulkInstall([...dep.install])
.then((_b1) => {
resolve(pkgname);
})
.catch((e1) => {
reject(e1);
})
})
.catch((e2) => {
reject(e2);
})
})
}); });
} }
private localInstall(): Promise<string> { private localInstall(): Promise<string> {
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
return this.openDialog("FileDialog", { try {
title: "__(Select package archive)", const d = await this.openDialog("FileDialog", {
mimes: [".*/zip"], title: "__(Select package archive)",
}).then((d) => { mimes: [".*/zip"],
return d.file.path });
.asFileHandle() const n = await this.install(d.file.path);
.read("binary") const apps = this.applist.data.map(
.then((data: Uint8Array) => { (v) => v.pkgname
return this.install(data) );
.then((n) => { const idx = apps.indexOf(n);
const apps = this.applist.data.map( if (idx >= 0) {
(v) => v.pkgname this.applist.selected = idx;
); }
const idx = apps.indexOf(n); return resolve(n.name);
if (idx >= 0) { } catch (error) {
this.applist.selected = idx; reject(__e(error));
} }
return resolve(n);
})
.catch((e: Error) => reject(__e(e)))
.catch((e: Error) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}); });
} }
private install( private install(
data: ArrayBuffer, zfile: string,
meta?: GenericObject<any> meta?: GenericObject<any>
): Promise<string> { ): Promise<GenericObject<any>> {
return new Promise((resolve, reject) => { return new Promise(async (resolve, reject) => {
return JSZip.loadAsync(data) try {
.then((zip: any) => { let v: API.PackageMetaType;
return zip let pth: string = "";
.file("package.json") await API.VFS.extractZip(zfile, (zip) => {
.async("string") return new Promise(async (res, rej) => {
.then((d: string) => { try {
let name: string; const d = await zip.file("package.json").async("string");
const v = JSON.parse(d); v = JSON.parse(d);
const pth = `${this.installdir}/${v.pkgname ? v.pkgname : v.app}`; pth = `${this.installdir}/${v.pkgname ? v.pkgname : v.app}`;
const dir = [pth]; await API.VFS.mkdirAll([pth]);
const files = []; res(pth);
for (name in zip.files) { } catch (error) {
const file = zip.files[name]; rej(__e(error))
if (file.dir) { }
dir.push(pth + "/" + name); });
} else {
files.push(name); });
} const app_meta = {
} pkgname: v.pkgname ? v.pkgname : v.app,
// create all directory name: v.name,
return this.mkdirs(dir) text: v.name,
.then(() => { icon: v.icon,
return this.installFile( iconclass: v.iconclass,
v.pkgname ? v.pkgname : v.app, category: v.category,
zip, author: v.info.author,
files version: v.version,
) description: meta
.then(() => { ? meta.description
const app_meta = { : undefined,
pkgname: v.pkgname ? v.pkgname : v.app, download: meta
name: v.name, ? meta.download
text: v.name, : undefined,
icon: v.icon, };
iconclass: v.iconclass, v.text = v.name;
category: v.category, v.filename = v.pkgname ? v.pkgname : v.app;
author: v.info.author, v.type = "app";
version: v.version, v.mime = "antos/app";
description: meta if (
? meta.description !v.iconclass &&
: undefined, !v.icon
download: meta ) {
? meta.download v.iconclass =
: undefined, "fa fa-adn";
}; }
v.text = v.name; v.path = pth;
v.filename = v.pkgname ? v.pkgname : v.app; this.systemsetting.system.packages[
v.type = "app"; v.pkgname ? v.pkgname : v.app
v.mime = "antos/app"; ] = v;
if ( return resolve(app_meta);
!v.iconclass && } catch (error) {
!v.icon reject(__e(error));
) { }
v.iconclass =
"fa fa-adn";
}
v.path = pth;
this.systemsetting.system.packages[
v.pkgname ? v.pkgname : v.app
] = v;
this.appDetail(app_meta);
return resolve(v.name);
})
.catch((e) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
})
.catch((err: Error) => reject(__e(err)));
})
.catch((e: Error) => reject(__e(e)));
}); });
} }
private bulkUninstall(list: string[]): Promise<any> { private bulkUninstall(list: string[]): Promise<any> {
return new Promise(async (resolve, reject) => { const promises = [];
if (list.length == 0) { for (let pkgname of list) {
return resolve(true); promises.push(this.uninstallPkg(pkgname));
} }
const pkgname = list.splice(0, 1)[0]; return Promise.all(promises);
this.uninstallPkg(pkgname)
.then((_meta) => {
this.bulkUninstall(list)
.then((b) => {
resolve(b);
})
.catch((e) => {
reject(e);
})
})
.catch((err) => {
reject(err);
})
});
} }
private uninstallPkg(pkgname: string): Promise<any> { private uninstallPkg(pkgname: string): Promise<any> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
@ -782,90 +712,20 @@ namespace OS {
} }
const meta = this.apps_meta[`${sel.pkgname}@${app.version}`]; const meta = this.apps_meta[`${sel.pkgname}@${app.version}`];
await this.remoteInstall(); await this.remoteInstall();
try { if (meta) {
if (meta) { if (meta.domel)
if (meta.domel) this.applist.delete(meta.domel);
this.applist.delete(meta.domel);
}
return resolve(true);
} }
catch (e) { return resolve(true);
return reject(__e(e));
}
} }
catch (e_1) { catch (e_1) {
return reject(__e(e_1)); return reject(__e(e_1));
} }
}); });
} }
private mkdirs(list: string[]): Promise<any> {
return new Promise((resolve, reject) => {
if (list.length === 0) {
return resolve(true);
}
const dir = list.splice(0, 1)[0].asFileHandle();
const path = dir.parent();
const dname = dir.basename;
return path
.asFileHandle()
.mk(dname)
.then((r) => {
if (r.error) {
return reject(
this._api.throwe(
__(
"Cannot create {0}",
`${path}/${dir}`
)
)
);
}
return this.mkdirs(list)
.then(() => resolve(true))
.catch((e) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
});
}
private installFile(n: string, zip: any, files: string[]): Promise<any> {
return new Promise((resolve, reject) => {
if (files.length === 0) {
return resolve(true);
}
const file = files.splice(0, 1)[0];
const path = `${this.installdir}/${n}/${file}`;
return zip
.file(file)
.async("uint8array")
.then((d: Uint8Array) => {
const fp = path.asFileHandle();
fp.cache = new Blob([d], { type: "octet/stream" });
return fp
.write("text/plain")
.then((r) => {
if (r.error) {
return reject(
this._api.throwe(
__("Cannot install {0}", path)
)
);
}
return this.installFile(n, zip, files)
.then(() => resolve(true))
.catch((e) => reject(__e(e)));
})
.catch((e) => reject(__e(e)));
})
.catch((e: Error) => reject(__e(e)));
});
}
} }
MarketPlace.dependencies = [ MarketPlace.dependencies = [
"os://scripts/jszip.min.js",
"os://scripts/showdown.min.js", "os://scripts/showdown.min.js",
]; ];
MarketPlace.singleton = true; MarketPlace.singleton = true;

View File

@ -1,12 +1,12 @@
{ {
"app":"MarketPlace", "app":"MarketPlace",
"name":"Application store", "name":"AntOS Application store",
"description":"Application store", "description":"Application store",
"info":{ "info":{
"author": "Xuan Sang LE", "author": "Xuan Sang LE",
"email": "xsang.le@gmail.com" "email": "xsang.le@gmail.com"
}, },
"version":"0.2.2-a", "version":"0.2.3-a",
"category":"System", "category":"System",
"iconclass":"fa fa-shopping-bag", "iconclass":"fa fa-shopping-bag",
"mimes":["none"], "mimes":["none"],