excellentexport 3.9.11 → 3.9.15
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/.editorconfig +9 -0
- package/.github/workflows/build.yml +50 -0
- package/README.md +401 -395
- package/dist/excellentexport.d.ts +37 -45
- package/dist/excellentexport.js +132 -2
- package/dist/excellentexport.js.LICENSE.txt +206 -2
- package/dist/utils.d.ts +27 -0
- package/index.html +28 -0
- package/package.json +42 -46
- package/src/excellentexport.ts +264 -254
- package/src/utils.ts +208 -144
- package/test/checkversion.test.ts +17 -16
- package/test/convert-filters.test.ts +70 -70
- package/test/convert-table.test.ts +163 -71
- package/test/convert.format.ts +56 -56
- package/test/convert.test.ts +60 -59
- package/test/fixdata.test.ts +74 -74
- package/test/negative.test.ts +130 -129
- package/test/simple.test.ts +11 -10
- package/test/tsconfig.json +19 -0
- package/test/utils.test.ts +101 -29
- package/test/utils_fixdata.test.ts +25 -24
- package/test/utils_removeColumns.test.ts +98 -98
- package/tsconfig.json +35 -17
- package/vite.config.ts +37 -0
- package/vitest.config.ts +26 -0
- package/.babelrc +0 -8
- package/.github/workflows/webpack.yml +0 -36
- package/bower.json +0 -32
- package/jest.config.ts +0 -203
package/test/negative.test.ts
CHANGED
|
@@ -1,129 +1,130 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
element
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
});
|
|
129
|
-
|
|
1
|
+
|
|
2
|
+
import { describe, expect, test, beforeEach, it, assert } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import ExcellentExport, { ConvertOptions, SheetOptions } from '../src/excellentexport';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('convert() API', function() {
|
|
8
|
+
describe('Negative tests', function() {
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
window.URL.createObjectURL = () => "blob:fake_URL";
|
|
12
|
+
|
|
13
|
+
const element = document.createElement("div");
|
|
14
|
+
element.innerHTML = '<a id="anchor">Link</a>';
|
|
15
|
+
|
|
16
|
+
document.body.appendChild(element);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should fail if CSV has more than one sheet', function() {
|
|
20
|
+
const options = {
|
|
21
|
+
anchor: 'anchor',
|
|
22
|
+
filename: 'data_from_array',
|
|
23
|
+
format: 'csv'
|
|
24
|
+
} as ConvertOptions;
|
|
25
|
+
|
|
26
|
+
const sheets = [{
|
|
27
|
+
name: 'Sheet Name Here 1',
|
|
28
|
+
from: {}
|
|
29
|
+
}, {
|
|
30
|
+
name: 'Sheet Number 2',
|
|
31
|
+
from: {}
|
|
32
|
+
}];
|
|
33
|
+
|
|
34
|
+
assert.throws(() => {
|
|
35
|
+
ExcellentExport.convert(options, sheets)
|
|
36
|
+
}, Error);
|
|
37
|
+
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should fail if sheet does not have name', function() {
|
|
41
|
+
const options = {
|
|
42
|
+
anchor: 'anchor',
|
|
43
|
+
filename: 'data_from_array',
|
|
44
|
+
format: 'csv'
|
|
45
|
+
} as ConvertOptions;
|
|
46
|
+
|
|
47
|
+
const sheets = [{
|
|
48
|
+
// name: 'Sheet Name Here 1',
|
|
49
|
+
from: {}
|
|
50
|
+
}] as SheetOptions[];
|
|
51
|
+
|
|
52
|
+
assert.throws(() => {
|
|
53
|
+
ExcellentExport.convert(options, sheets)
|
|
54
|
+
}, Error);
|
|
55
|
+
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should fail if sheet does not have data', function() {
|
|
59
|
+
const options = {
|
|
60
|
+
anchor: 'anchor',
|
|
61
|
+
filename: 'data_from_array',
|
|
62
|
+
format: 'csv'
|
|
63
|
+
} as ConvertOptions;
|
|
64
|
+
|
|
65
|
+
const sheets = [{
|
|
66
|
+
name: 'Sheet Name Here 1',
|
|
67
|
+
// from: {}
|
|
68
|
+
}] as SheetOptions[];
|
|
69
|
+
|
|
70
|
+
assert.throws(() => {
|
|
71
|
+
ExcellentExport.convert(options, sheets)
|
|
72
|
+
}, Error);
|
|
73
|
+
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should fail if there is not format defined', function() {
|
|
77
|
+
const options = {
|
|
78
|
+
anchor: 'anchor',
|
|
79
|
+
filename: 'data_from_array',
|
|
80
|
+
//format: 'csv'
|
|
81
|
+
} as ConvertOptions;
|
|
82
|
+
|
|
83
|
+
const sheets = [{
|
|
84
|
+
name: 'Sheet Name Here 1',
|
|
85
|
+
from: {}
|
|
86
|
+
}];
|
|
87
|
+
|
|
88
|
+
assert.throws(() => {
|
|
89
|
+
ExcellentExport.convert(options, sheets)
|
|
90
|
+
}, Error);
|
|
91
|
+
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should fail if anchor is not defined/valid', function() {
|
|
95
|
+
const options = {
|
|
96
|
+
anchor: 'anchor1235d5d5d5d_invalid',
|
|
97
|
+
filename: 'data_from_array',
|
|
98
|
+
format: 'csv'
|
|
99
|
+
} as ConvertOptions;
|
|
100
|
+
|
|
101
|
+
const sheets = [{
|
|
102
|
+
name: 'Sheet Name Here 1',
|
|
103
|
+
from: {}
|
|
104
|
+
}];
|
|
105
|
+
|
|
106
|
+
assert.throws(() => {
|
|
107
|
+
ExcellentExport.convert(options, sheets)
|
|
108
|
+
}, Error);
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should fail if no anchor and not openAsDownload', function() {
|
|
113
|
+
const options = {
|
|
114
|
+
filename: 'data_from_array',
|
|
115
|
+
format: 'csv'
|
|
116
|
+
} as ConvertOptions;
|
|
117
|
+
|
|
118
|
+
const sheets = [{
|
|
119
|
+
name: 'Sheet Name Here 1',
|
|
120
|
+
from: {}
|
|
121
|
+
}];
|
|
122
|
+
|
|
123
|
+
assert.throws(() => {
|
|
124
|
+
ExcellentExport.convert(options, sheets)
|
|
125
|
+
}, Error);
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
package/test/simple.test.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
|
|
1
|
+
import { describe, expect, test, beforeEach, it, assert } from 'vitest'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
describe('Array', function() {
|
|
5
|
+
describe('#indexOf()', function() {
|
|
6
|
+
it('should return -1 when the value is not present', function() {
|
|
7
|
+
assert.equal(-1, [1,2,3].indexOf(4));
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "..",
|
|
5
|
+
"noEmit": true,
|
|
6
|
+
"types": ["node"],
|
|
7
|
+
"module": "commonjs",
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"verbatimModuleSyntax": false,
|
|
10
|
+
"lib": ["ESNext", "DOM"],
|
|
11
|
+
"paths": {
|
|
12
|
+
"@/*": ["../src/*"]
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"../src/**/*.ts",
|
|
17
|
+
"./**/*.ts"
|
|
18
|
+
]
|
|
19
|
+
}
|
package/test/utils.test.ts
CHANGED
|
@@ -1,29 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import * as utils from '../src/utils';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
describe('Test utility functions', () => {
|
|
7
|
-
|
|
8
|
-
describe('base64', () => {
|
|
9
|
-
it('should encode a string to base64', () => {
|
|
10
|
-
assert.equal(utils.base64('test'), 'dGVzdA==');
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('should encode a unicode string to base64', () => {
|
|
14
|
-
assert.equal(utils.base64('test\u00A9'), "dGVzdMKp");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
describe('test format function', () => {
|
|
21
|
-
it('should format a string', () => {
|
|
22
|
-
assert.equal(utils.format('aaaa {a} bbbb {b} cccc', {a:'1', b:'2', c:'3', d:'4'}), 'aaaa 1 bbbb 2 cccc');
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should not replace if no data is provided', () => {
|
|
26
|
-
assert.equal(utils.format('aaaa {a} bbbb {b} cccc', {}), 'aaaa {a} bbbb {b} cccc');
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
1
|
+
import { describe, expect, test, beforeEach, it, assert } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import * as utils from '../src/utils';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
describe('Test utility functions', () => {
|
|
7
|
+
|
|
8
|
+
describe('base64', () => {
|
|
9
|
+
it('should encode a string to base64', () => {
|
|
10
|
+
assert.equal(utils.base64('test'), 'dGVzdA==');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should encode a unicode string to base64', () => {
|
|
14
|
+
assert.equal(utils.base64('test\u00A9'), "dGVzdMKp");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
describe('test format function', () => {
|
|
21
|
+
it('should format a string', () => {
|
|
22
|
+
assert.equal(utils.format('aaaa {a} bbbb {b} cccc', {a:'1', b:'2', c:'3', d:'4'}), 'aaaa 1 bbbb 2 cccc');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should not replace if no data is provided', () => {
|
|
26
|
+
assert.equal(utils.format('aaaa {a} bbbb {b} cccc', {}), 'aaaa {a} bbbb {b} cccc');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('parseTable', () => {
|
|
31
|
+
function makeTable(html: string): HTMLTableElement {
|
|
32
|
+
const div = document.createElement('div');
|
|
33
|
+
div.innerHTML = html;
|
|
34
|
+
return div.querySelector('table') as HTMLTableElement;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it('should parse a simple table with no spans', () => {
|
|
38
|
+
const table = makeTable('<table><tr><td>A</td><td>B</td></tr><tr><td>C</td><td>D</td></tr></table>');
|
|
39
|
+
const { data, merges } = utils.parseTable(table);
|
|
40
|
+
expect(data).toEqual([['A', 'B'], ['C', 'D']]);
|
|
41
|
+
expect(merges).toHaveLength(0);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should handle a cell with colspan=2', () => {
|
|
45
|
+
// Row 0: [Header (colspan=2)], Row 1: [A, B]
|
|
46
|
+
const table = makeTable(
|
|
47
|
+
'<table>' +
|
|
48
|
+
'<tr><td colspan="2">Header</td></tr>' +
|
|
49
|
+
'<tr><td>A</td><td>B</td></tr>' +
|
|
50
|
+
'</table>'
|
|
51
|
+
);
|
|
52
|
+
const { data, merges } = utils.parseTable(table);
|
|
53
|
+
// Header spans columns 0 and 1; second slot should be empty
|
|
54
|
+
expect(data[0][0]).toBe('Header');
|
|
55
|
+
expect(data[0][1]).toBe('');
|
|
56
|
+
expect(data[1]).toEqual(['A', 'B']);
|
|
57
|
+
expect(merges).toHaveLength(1);
|
|
58
|
+
expect(merges[0]).toEqual({ s: { r: 0, c: 0 }, e: { r: 0, c: 1 } });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should handle a cell with rowspan=2', () => {
|
|
62
|
+
// Row 0: [Side (rowspan=2), Top], Row 1: [Bottom]
|
|
63
|
+
const table = makeTable(
|
|
64
|
+
'<table>' +
|
|
65
|
+
'<tr><td rowspan="2">Side</td><td>Top</td></tr>' +
|
|
66
|
+
'<tr><td>Bottom</td></tr>' +
|
|
67
|
+
'</table>'
|
|
68
|
+
);
|
|
69
|
+
const { data, merges } = utils.parseTable(table);
|
|
70
|
+
expect(data[0][0]).toBe('Side');
|
|
71
|
+
expect(data[0][1]).toBe('Top');
|
|
72
|
+
// Column 0 of row 1 is occupied by rowspan; Bottom goes to column 1
|
|
73
|
+
expect(data[1][0]).toBe('');
|
|
74
|
+
expect(data[1][1]).toBe('Bottom');
|
|
75
|
+
expect(merges).toHaveLength(1);
|
|
76
|
+
expect(merges[0]).toEqual({ s: { r: 0, c: 0 }, e: { r: 1, c: 0 } });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle a cell with both colspan=2 and rowspan=2', () => {
|
|
80
|
+
// 3x3 table, top-left cell spans 2 cols and 2 rows
|
|
81
|
+
const table = makeTable(
|
|
82
|
+
'<table>' +
|
|
83
|
+
'<tr><td colspan="2" rowspan="2">Big</td><td>R0C2</td></tr>' +
|
|
84
|
+
'<tr><td>R1C2</td></tr>' +
|
|
85
|
+
'<tr><td>R2C0</td><td>R2C1</td><td>R2C2</td></tr>' +
|
|
86
|
+
'</table>'
|
|
87
|
+
);
|
|
88
|
+
const { data, merges } = utils.parseTable(table);
|
|
89
|
+
expect(data[0][0]).toBe('Big');
|
|
90
|
+
expect(data[0][1]).toBe('');
|
|
91
|
+
expect(data[0][2]).toBe('R0C2');
|
|
92
|
+
expect(data[1][0]).toBe('');
|
|
93
|
+
expect(data[1][1]).toBe('');
|
|
94
|
+
expect(data[1][2]).toBe('R1C2');
|
|
95
|
+
expect(data[2]).toEqual(['R2C0', 'R2C1', 'R2C2']);
|
|
96
|
+
expect(merges).toHaveLength(1);
|
|
97
|
+
expect(merges[0]).toEqual({ s: { r: 0, c: 0 }, e: { r: 1, c: 1 } });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { fixCSVField } from '../src/utils';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { describe, expect, test, beforeEach, it, assert } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { fixCSVField } from '../src/utils';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe('Test utility functions: csv functions', () => {
|
|
8
|
+
|
|
9
|
+
it('should keep the value if not delimiter found', () => {
|
|
10
|
+
assert.equal(fixCSVField('test', ','), 'test');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should fix a string with double quotes', () => {
|
|
14
|
+
const str = 'aaa"bbb';
|
|
15
|
+
const result = fixCSVField(str, "\"");
|
|
16
|
+
assert.equal(result, '\"aaa\"\"bbb\"');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should fix a field with space delimiter', () => {
|
|
20
|
+
const str = 'aaa bbb';
|
|
21
|
+
const result = fixCSVField(str, " ");
|
|
22
|
+
assert.equal(result, '\"aaa bbb\"');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
});
|