v-transform 1.0.5 → 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/package.json +2 -1
- package/src/dump.js +63 -0
- package/src/index.js +20 -23
- package/src/loader.js +67 -0
- package/src/{transform/parser.js → parser.js} +6 -8
- package/src/prepare.js +26 -0
- package/src/transform.js +60 -0
- package/src/io.js +0 -88
- package/src/transform/index.js +0 -69
- package/src/transform/merge.js +0 -8
package/package.json
CHANGED
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
"vt": "src/index.js"
|
|
5
5
|
},
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "2.0.0",
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"repository": "https://github.com/VickScarlet/vTransform.git",
|
|
10
10
|
"author": "Vick Scarlet <scarlet_vick@outlook.com>",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"commander": "^8.2.0",
|
|
14
|
+
"glob": "^8.0.3",
|
|
14
15
|
"js-yaml": "^4.1.0",
|
|
15
16
|
"xlsx": "^0.17.1"
|
|
16
17
|
}
|
package/src/dump.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { writeFile, stat, mkdir } from 'fs/promises';
|
|
4
|
+
|
|
5
|
+
function jsonify(data, space) {
|
|
6
|
+
return JSON.stringify(data, null, space);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function cjsify(data, space) {
|
|
10
|
+
return `module.exports = ${jsonify(data, space)}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function esmify(data, space) {
|
|
14
|
+
return `export default ${jsonify(data, space)}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function yamlify(data, space) {
|
|
18
|
+
return yaml.dump(data, {
|
|
19
|
+
indent: space || undefined,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function mkdirs(dir) {
|
|
24
|
+
try {
|
|
25
|
+
await stat(dir);
|
|
26
|
+
} catch(e) {
|
|
27
|
+
await mkdirs(path.dirname(dir));
|
|
28
|
+
mkdir(dir);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
async function write(sheet, data) {
|
|
33
|
+
console.info(`Dump ${sheet}`);
|
|
34
|
+
await mkdirs(path.dirname(sheet));
|
|
35
|
+
await writeFile(sheet, data);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function dump(sheet, data, type, space) {
|
|
39
|
+
let ext, ify;
|
|
40
|
+
switch(type) {
|
|
41
|
+
case 'cjs':
|
|
42
|
+
ext = '.js';
|
|
43
|
+
ify = cjsify;
|
|
44
|
+
break;
|
|
45
|
+
case 'js':
|
|
46
|
+
case 'mjs':
|
|
47
|
+
case 'esm':
|
|
48
|
+
ext = '.js';
|
|
49
|
+
ify = esmify;
|
|
50
|
+
break;
|
|
51
|
+
case 'yaml':
|
|
52
|
+
case 'yml':
|
|
53
|
+
ext = '.yaml';
|
|
54
|
+
ify = yamlify;
|
|
55
|
+
break;
|
|
56
|
+
case 'json':
|
|
57
|
+
default:
|
|
58
|
+
ext = '.json';
|
|
59
|
+
ify = jsonify;
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
return write(`${sheet}${ext}`, ify(data, space));
|
|
63
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,31 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
2
|
+
import { Command } from 'commander';
|
|
3
3
|
import { readFile } from 'fs/promises';
|
|
4
|
-
import {
|
|
5
|
-
import { jobs as doJobs } from './transform/index.js';
|
|
4
|
+
import { transform } from './transform.js';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
console.info(
|
|
11
|
-
JSON.parse(
|
|
12
|
-
await readFile('package.json')
|
|
13
|
-
)
|
|
14
|
-
.version
|
|
15
|
-
);
|
|
16
|
-
});
|
|
6
|
+
(async() => {
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
17
9
|
|
|
18
|
-
|
|
10
|
+
program.name('vt');
|
|
11
|
+
program.version(JSON.parse(await readFile('package.json')).version);
|
|
12
|
+
program
|
|
19
13
|
.command('transform [list...]')
|
|
20
|
-
.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
.option('-t, --type <type>', 'type of transform, available: js, esm, cjs, json', 'json')
|
|
15
|
+
.option('-s, --space <space>', 'format space number', 0)
|
|
16
|
+
.option('-c, --config <config>', 'configure file', null)
|
|
17
|
+
.option('-w, --cwd <cwd>', 'current work dir', null)
|
|
18
|
+
.option('-o, --output <output>', 'output dir', null)
|
|
19
|
+
.option('-d, --dest <dest>', 'dest dir', null)
|
|
20
|
+
.action((list, options) => {
|
|
21
|
+
if(!options.dest) options.dest = options.output;
|
|
22
|
+
options.list = list;
|
|
23
|
+
transform(options);
|
|
26
24
|
});
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
.name('vt');
|
|
26
|
+
program.parse(process.argv);
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
})();
|
package/src/loader.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import glob from 'glob';
|
|
2
|
+
import yaml from 'js-yaml';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
|
|
6
|
+
export async function load({type, space, config, dest, list, cwd}) {
|
|
7
|
+
type = type || 'json';
|
|
8
|
+
cwd = cwd || process.cwd();
|
|
9
|
+
space = Number(space) || 0;
|
|
10
|
+
dest = dest || cwd;
|
|
11
|
+
const def = {cwd, type, space, dest};
|
|
12
|
+
const m = (...l)=>globit(Object.assign({}, ...l));
|
|
13
|
+
const cfgs = [];
|
|
14
|
+
|
|
15
|
+
if(list?.length)
|
|
16
|
+
cfgs.unshift(await m(def, {glob: list}));
|
|
17
|
+
|
|
18
|
+
if(config) {
|
|
19
|
+
const c = await loadConfig(config);
|
|
20
|
+
const dir = path.dirname(config);
|
|
21
|
+
const cdef = {};
|
|
22
|
+
if(c.cwd) cdef.cwd = path.resolve(dir, c.cwd);
|
|
23
|
+
if(c.type) cdef.type = c.type;
|
|
24
|
+
if(typeof c.space == 'number') cdef.space = c.space;
|
|
25
|
+
if(c.dest) cdef.dest = path.resolve(dir, c.dest);
|
|
26
|
+
for(const cfg of c.configurations) {
|
|
27
|
+
if(cfg.cwd) cfg.cwd = path.resolve(dir, cfg.cwd);
|
|
28
|
+
if(cfg.dest) cfg.dest = path.resolve(dir, cfg.dest);
|
|
29
|
+
cfgs.push(await m(def, cdef, cfg));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return cfgs;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function loadConfig(config) {
|
|
37
|
+
switch(path.extname(config)) {
|
|
38
|
+
case '.json':
|
|
39
|
+
case '.yaml':
|
|
40
|
+
case '.yml':
|
|
41
|
+
return yaml.load(
|
|
42
|
+
await readFile(config)
|
|
43
|
+
);
|
|
44
|
+
case '.js':
|
|
45
|
+
case '.mjs':
|
|
46
|
+
return (await import(config)).default;
|
|
47
|
+
default:
|
|
48
|
+
throw new Error(`Unknown config file type: ${config}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function globit(options) {
|
|
53
|
+
const {glob: gs, cwd} = options;
|
|
54
|
+
const files = [];
|
|
55
|
+
const g = p=>new Promise(r=>glob(
|
|
56
|
+
p, {cwd}, (err, files) => r(err?[]:files)
|
|
57
|
+
))
|
|
58
|
+
if(Array.isArray(gs))
|
|
59
|
+
for(const p of gs)
|
|
60
|
+
files.push(...await g(p));
|
|
61
|
+
else if(typeof gs == 'string')
|
|
62
|
+
files.push(...await g(gs));
|
|
63
|
+
else
|
|
64
|
+
throw new Error(`Unknown glob type: ${gs}`);
|
|
65
|
+
|
|
66
|
+
return Object.assign({files}, options);
|
|
67
|
+
}
|
|
@@ -91,10 +91,10 @@ function formatRow({type, key, source, subs}, row, original, json) {
|
|
|
91
91
|
const value = row[source];
|
|
92
92
|
if(value != void 0) {
|
|
93
93
|
try {
|
|
94
|
-
return {
|
|
95
|
-
key,
|
|
96
|
-
value: json.includes(source)
|
|
97
|
-
? yaml.load(value)
|
|
94
|
+
return {
|
|
95
|
+
key,
|
|
96
|
+
value: json.includes(source)
|
|
97
|
+
? yaml.load(value)
|
|
98
98
|
: value
|
|
99
99
|
};
|
|
100
100
|
} catch(e) {
|
|
@@ -150,10 +150,8 @@ function formatSheet(struct, rawSheet, json) {
|
|
|
150
150
|
return data;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
function parser(rawSheet) {
|
|
153
|
+
export function parser(rawSheet) {
|
|
154
154
|
const struct = parseStruct(rawSheet.shift());
|
|
155
155
|
rawSheet.shift();
|
|
156
156
|
return formatSheet(struct, rawSheet, struct.json);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export { parser };
|
|
157
|
+
}
|
package/src/prepare.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { readFile } from 'fs/promises';
|
|
3
|
+
import * as XLSX from 'xlsx';
|
|
4
|
+
|
|
5
|
+
export async function prepare(xlsxPath) {
|
|
6
|
+
switch(path.extname(xlsxPath)) {
|
|
7
|
+
case '.xls':
|
|
8
|
+
case '.xlsx':
|
|
9
|
+
break;
|
|
10
|
+
default:
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
const xlsxFileBuffer = await readFile(xlsxPath);
|
|
14
|
+
const xlsx = XLSX.read(xlsxFileBuffer, {type: 'buffer'});
|
|
15
|
+
const sheets = xlsx.Sheets;
|
|
16
|
+
const data = [];
|
|
17
|
+
for(const sheetName in sheets) {
|
|
18
|
+
const sheetRawData = sheets[sheetName];
|
|
19
|
+
if(!sheetRawData['!ref']) break;
|
|
20
|
+
data.push({
|
|
21
|
+
name: sheetName,
|
|
22
|
+
data: XLSX.utils.sheet_to_json(sheetRawData, { header: 1 })
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return data;
|
|
26
|
+
}
|
package/src/transform.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { load } from './loader.js';
|
|
3
|
+
import { prepare } from './prepare.js';
|
|
4
|
+
import { parser } from './parser.js';
|
|
5
|
+
import { dump } from './dump.js';
|
|
6
|
+
|
|
7
|
+
export async function transform(options) {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
const configurations = await load(options);
|
|
10
|
+
for(const config of configurations)
|
|
11
|
+
await task(config);
|
|
12
|
+
console.info(`Transformed in ${Date.now() - now}ms`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function task({files, dest, cwd, type, space}) {
|
|
16
|
+
console.info('Transform task config:', {files, dest, cwd, type, space});
|
|
17
|
+
const m = new Map();
|
|
18
|
+
for(const file of files) {
|
|
19
|
+
const dir = path.resolve(dest, path.dirname(file));
|
|
20
|
+
for(const {name, data} of (await prepare(path.resolve(cwd, file)))) {
|
|
21
|
+
if(name[0] === "#") continue;
|
|
22
|
+
let sheet = path.resolve(dir, name.split('#')[0]);
|
|
23
|
+
if(sheet[0] === '>') sheet = sheet.substring(1)
|
|
24
|
+
sheet = sheet.replace('<arr>', '');
|
|
25
|
+
if(!m.has(sheet))
|
|
26
|
+
m.set(sheet, new JobData());
|
|
27
|
+
m.get(sheet).append(parser(data));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
for(const [sheet, data] of m)
|
|
31
|
+
await dump(sheet, data.result(), type, space);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
class JobData {
|
|
35
|
+
constructor(data) {
|
|
36
|
+
if(data) this.append(data);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#data = [];
|
|
40
|
+
|
|
41
|
+
append(data) {
|
|
42
|
+
this.#data.push(data);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
result() {
|
|
46
|
+
if(!this.#data.length) return {};
|
|
47
|
+
const data = this.#data;
|
|
48
|
+
let result;
|
|
49
|
+
if(Array.isArray(data[0])) {
|
|
50
|
+
result = [];
|
|
51
|
+
for(const subs of data)
|
|
52
|
+
result.push(...Object.values(subs));
|
|
53
|
+
} else {
|
|
54
|
+
result = {};
|
|
55
|
+
for(const subs of data)
|
|
56
|
+
Object.assign(result, subs);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/io.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, stat, readdir } from 'fs/promises';
|
|
2
|
-
import * as XLSX from 'xlsx';
|
|
3
|
-
import { join, extname, dirname, resolve } from 'path';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async function readXLSX(xlsxPath) {
|
|
7
|
-
const xlsxFileBuffer = await readFile(xlsxPath);
|
|
8
|
-
const xlsx = XLSX.read(xlsxFileBuffer, {type: 'buffer'});
|
|
9
|
-
const sheets = xlsx.Sheets;
|
|
10
|
-
const data = {};
|
|
11
|
-
for(const sheetName in sheets) {
|
|
12
|
-
const sheetRawData = sheets[sheetName];
|
|
13
|
-
if(!sheetRawData['!ref']) break;
|
|
14
|
-
data[sheetName] = XLSX.utils.sheet_to_json(sheetRawData, { header: 1 });
|
|
15
|
-
}
|
|
16
|
-
return data;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function writeJSON(data, savePath) {
|
|
20
|
-
await writeFile(
|
|
21
|
-
savePath,
|
|
22
|
-
JSON.stringify(data, null, 4),
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function walk(filePath) {
|
|
27
|
-
const xlsxPaths = [];
|
|
28
|
-
if(Array.isArray(filePath)) {
|
|
29
|
-
for(const subPath of filePath)
|
|
30
|
-
xlsxPaths.push(await walk(subPath));
|
|
31
|
-
return xlsxPaths.flat();
|
|
32
|
-
}
|
|
33
|
-
const fileStat = await stat(filePath);
|
|
34
|
-
if(!fileStat.isDirectory()) {
|
|
35
|
-
const ext = extname(filePath);
|
|
36
|
-
if( ext=='.xls' || ext=='.xlsx' ) xlsxPaths.push(filePath);
|
|
37
|
-
return xlsxPaths;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const dirData = await readdir(filePath);
|
|
41
|
-
for(const subPath of dirData)
|
|
42
|
-
xlsxPaths.push(await walk(join(filePath, subPath)));
|
|
43
|
-
return xlsxPaths.flat();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function req(config) {
|
|
47
|
-
const { configurations } = JSON.parse(await readFile(config));
|
|
48
|
-
const jobs = [];
|
|
49
|
-
const dir = dirname(config);
|
|
50
|
-
for(const { source, target } of configurations) {
|
|
51
|
-
const job = {};
|
|
52
|
-
if(Array.isArray(source)) {
|
|
53
|
-
job.source = [];
|
|
54
|
-
for(const p of source)
|
|
55
|
-
job.source.push(await walk(resolve(dir, p)))
|
|
56
|
-
job.source = job.source.flat();
|
|
57
|
-
} else {
|
|
58
|
-
job.source = await walk(resolve(dir, source));
|
|
59
|
-
}
|
|
60
|
-
job.target = resolve(dir, target);
|
|
61
|
-
jobs.push(job);
|
|
62
|
-
}
|
|
63
|
-
return jobs;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function jobs(configs) {
|
|
67
|
-
const list = [];
|
|
68
|
-
const files = [];
|
|
69
|
-
for(const config of configs) {
|
|
70
|
-
switch(extname(config)) {
|
|
71
|
-
case '.json':
|
|
72
|
-
const jobs = await req(config);
|
|
73
|
-
if(jobs) list.push(jobs);
|
|
74
|
-
break;
|
|
75
|
-
default:
|
|
76
|
-
files.push(config);
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if(files.length > 0) {
|
|
81
|
-
const source = await walk(files);
|
|
82
|
-
const target = dirname(source[0]);
|
|
83
|
-
list.push({ source, target });
|
|
84
|
-
}
|
|
85
|
-
return list.flat();
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export { readXLSX, writeJSON, jobs };
|
package/src/transform/index.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { join } from 'path';
|
|
2
|
-
import { parser } from './parser.js';
|
|
3
|
-
import { merge } from './merge.js';
|
|
4
|
-
|
|
5
|
-
async function jobs(jobs, r, w) {
|
|
6
|
-
for(const { source, target } of jobs)
|
|
7
|
-
await job(source, target, r, w);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async function job(source, target, r, w) {
|
|
11
|
-
const merges = {};
|
|
12
|
-
for(const xlsxPath of source) {
|
|
13
|
-
const rawData = await r(xlsxPath);
|
|
14
|
-
const { merge: mergeData, write } = transform(rawData);
|
|
15
|
-
if(write) {
|
|
16
|
-
for(const sheetName in write) {
|
|
17
|
-
const savePath = join(target, `${sheetName}.json`);
|
|
18
|
-
w(write[sheetName], savePath);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if(mergeData) {
|
|
23
|
-
for(const sheetName in mergeData) {
|
|
24
|
-
const [a, b] = sheetName.split('.');
|
|
25
|
-
if(b) {
|
|
26
|
-
const data = {[b]: mergeData[sheetName]}
|
|
27
|
-
if(!merges[a]) {
|
|
28
|
-
merges[a] = [ data ];
|
|
29
|
-
} else {
|
|
30
|
-
merges[a].push(data);
|
|
31
|
-
}
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
if(!merges[sheetName]) {
|
|
35
|
-
merges[sheetName] = [ mergeData[sheetName] ];
|
|
36
|
-
} else {
|
|
37
|
-
merges[sheetName].push(mergeData[sheetName]);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
for(const sheetName in merges) {
|
|
45
|
-
const savePath = join(target, `${sheetName}.json`);
|
|
46
|
-
const data = merge(merges[sheetName]);
|
|
47
|
-
w(data, savePath);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function transform(rawSheetsData) {
|
|
52
|
-
const merge = {};
|
|
53
|
-
const write = {};
|
|
54
|
-
for(const rawSheetName in rawSheetsData) {
|
|
55
|
-
const rawData = rawSheetsData[rawSheetName];
|
|
56
|
-
if(rawSheetName[0] === "#") continue;
|
|
57
|
-
let sheetName = rawSheetName;
|
|
58
|
-
const isArray = rawSheetName.substr(-5) === "<arr>";
|
|
59
|
-
if(isArray) sheetName = sheetName.substring(0, sheetName.length - 5);
|
|
60
|
-
const isMerge = rawSheetName[0] === ">";
|
|
61
|
-
if(isMerge) sheetName = sheetName.substr(1);
|
|
62
|
-
const data = parser(rawData, isArray);
|
|
63
|
-
if(isMerge) merge[sheetName] = data;
|
|
64
|
-
else write[sheetName] = data;
|
|
65
|
-
}
|
|
66
|
-
return { merge, write };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export { jobs, job, transform };
|