google-spreadsheet 3.2.0 → 4.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/README.md +74 -35
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +948 -0
- package/dist/index.mjs +1 -0
- package/package.json +79 -40
- package/src/index.ts +5 -0
- package/src/lib/GoogleSpreadsheet.ts +637 -0
- package/src/lib/GoogleSpreadsheetCell.ts +307 -0
- package/src/lib/GoogleSpreadsheetCellErrorValue.ts +25 -0
- package/src/lib/GoogleSpreadsheetRow.ts +117 -0
- package/{lib/GoogleSpreadsheetWorksheet.js → src/lib/GoogleSpreadsheetWorksheet.ts} +289 -171
- package/src/lib/types/auth-types.ts +18 -0
- package/src/lib/types/drive-types.ts +40 -0
- package/src/lib/types/sheets-types.ts +528 -0
- package/src/lib/types/util-types.ts +8 -0
- package/src/lib/utils.ts +57 -0
- package/.eslintrc.js +0 -61
- package/.nvmrc +0 -1
- package/Changelog.md +0 -10
- package/TODO +0 -73
- package/UNLICENSE +0 -24
- package/index.js +0 -13
- package/lib/GoogleSpreadsheet.js +0 -387
- package/lib/GoogleSpreadsheetCell.js +0 -239
- package/lib/GoogleSpreadsheetRow.js +0 -72
- package/lib/errors.js +0 -10
- package/lib/utils.js +0 -32
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
|
|
3
|
-
const { columnToLetter } = require('./utils');
|
|
4
|
-
|
|
5
|
-
const { GoogleSpreadsheetFormulaError } = require('./errors');
|
|
6
|
-
|
|
7
|
-
class GoogleSpreadsheetCell {
|
|
8
|
-
constructor(parentSheet, rowIndex, columnIndex, cellData) {
|
|
9
|
-
this._sheet = parentSheet; // the parent GoogleSpreadsheetWorksheet instance
|
|
10
|
-
this._row = rowIndex;
|
|
11
|
-
this._column = columnIndex;
|
|
12
|
-
|
|
13
|
-
this._updateRawData(cellData);
|
|
14
|
-
return this;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// newData can be undefined/null if the cell is totally empty and unformatted
|
|
18
|
-
_updateRawData(newData = {}) {
|
|
19
|
-
this._rawData = newData;
|
|
20
|
-
this._draftData = {}; // stuff to save
|
|
21
|
-
this._error = null;
|
|
22
|
-
if (_.get(this._rawData, 'effectiveValue.errorValue')) {
|
|
23
|
-
this._error = new GoogleSpreadsheetFormulaError(this._rawData.effectiveValue.errorValue);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// CELL LOCATION/ADDRESS /////////////////////////////////////////////////////////////////////////
|
|
28
|
-
get rowIndex() { return this._row; }
|
|
29
|
-
get columnIndex() { return this._column; }
|
|
30
|
-
get a1Column() { return columnToLetter(this._column + 1); }
|
|
31
|
-
get a1Row() { return this._row + 1; } // a1 row numbers start at 1 instead of 0
|
|
32
|
-
get a1Address() { return `${this.a1Column}${this.a1Row}`; }
|
|
33
|
-
|
|
34
|
-
// CELL CONTENTS - VALUE/FORMULA/NOTES ///////////////////////////////////////////////////////////
|
|
35
|
-
get value() {
|
|
36
|
-
// const typeKey = _.keys(this._rawData.effectiveValue)[0];
|
|
37
|
-
if (this._draftData.value !== undefined) throw new Error('Value has been changed');
|
|
38
|
-
if (this._error) return this._error;
|
|
39
|
-
if (!this._rawData.effectiveValue) return null;
|
|
40
|
-
return _.values(this._rawData.effectiveValue)[0];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
set value(newValue) {
|
|
44
|
-
if (_.isBoolean(newValue)) {
|
|
45
|
-
this._draftData.valueType = 'boolValue';
|
|
46
|
-
} else if (_.isString(newValue)) {
|
|
47
|
-
if (newValue.substr(0, 1) === '=') this._draftData.valueType = 'formulaValue';
|
|
48
|
-
else this._draftData.valueType = 'stringValue';
|
|
49
|
-
} else if (_.isFinite(newValue)) {
|
|
50
|
-
this._draftData.valueType = 'numberValue';
|
|
51
|
-
} else if (_.isNil(newValue)) {
|
|
52
|
-
// null or undefined
|
|
53
|
-
this._draftData.valueType = 'stringValue';
|
|
54
|
-
newValue = '';
|
|
55
|
-
} else {
|
|
56
|
-
throw new Error('Set value to boolean, string, or number');
|
|
57
|
-
}
|
|
58
|
-
this._draftData.value = newValue;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get valueType() {
|
|
62
|
-
// an error only happens with a formula
|
|
63
|
-
if (this._error) return 'errorValue';
|
|
64
|
-
if (!this._rawData.effectiveValue) return null;
|
|
65
|
-
return _.keys(this._rawData.effectiveValue)[0];
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get formattedValue() { return this._rawData.formattedValue || null; }
|
|
69
|
-
set formattedValue(newVal) {
|
|
70
|
-
throw new Error('You cannot modify the formatted value directly');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
get formula() { return _.get(this._rawData, 'userEnteredValue.formulaValue', null); }
|
|
74
|
-
set formula(newValue) {
|
|
75
|
-
if (newValue.substr(0, 1) !== '=') throw new Error('formula must begin with "="');
|
|
76
|
-
this.value = newValue; // use existing value setter
|
|
77
|
-
}
|
|
78
|
-
get formulaError() { return this._error; }
|
|
79
|
-
|
|
80
|
-
get hyperlink() {
|
|
81
|
-
if (this._draftData.value) throw new Error('Save cell to be able to read hyperlink');
|
|
82
|
-
return this._rawData.hyperlink;
|
|
83
|
-
}
|
|
84
|
-
set hyperlink(val) {
|
|
85
|
-
throw new Error('Do not set hyperlink directly. Instead set cell.formula, for example `cell.formula = \'=HYPERLINK("http://google.com", "Google")\'`');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get note() {
|
|
89
|
-
return this._draftData.note !== undefined ? this._draftData.note : this._rawData.note;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
set note(newVal) {
|
|
93
|
-
if (newVal === null || newVal === undefined) newVal = '';
|
|
94
|
-
if (!_.isString(newVal)) throw new Error('Note must be a string');
|
|
95
|
-
if (newVal === this._rawData.note) delete this._draftData.note;
|
|
96
|
-
else this._draftData.note = newVal;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// CELL FORMATTING ///////////////////////////////////////////////////////////////////////////////
|
|
100
|
-
get userEnteredFormat() { return this._rawData.userEnteredFormat; }
|
|
101
|
-
get effectiveFormat() { return this._rawData.effectiveFormat; }
|
|
102
|
-
set userEnteredFormat(newVal) { throw new Error('Do not modify directly, instead use format properties'); }
|
|
103
|
-
set effectiveFormat(newVal) { throw new Error('Read-only'); }
|
|
104
|
-
|
|
105
|
-
_getFormatParam(param) {
|
|
106
|
-
// we freeze the object so users don't change nested props accidentally
|
|
107
|
-
// TODO: figure out something that would throw an error if you try to update it?
|
|
108
|
-
if (_.get(this._draftData, `userEnteredFormat.${param}`)) {
|
|
109
|
-
throw new Error('User format is unsaved - save the cell to be able to read it again');
|
|
110
|
-
}
|
|
111
|
-
return Object.freeze(this._rawData.userEnteredFormat[param]);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
_setFormatParam(param, newVal) {
|
|
115
|
-
if (_.isEqual(newVal, _.get(this._rawData, `userEnteredFormat.${param}`))) {
|
|
116
|
-
_.unset(this._draftData, `userEnteredFormat.${param}`);
|
|
117
|
-
} else {
|
|
118
|
-
_.set(this._draftData, `userEnteredFormat.${param}`, newVal);
|
|
119
|
-
this._draftData.clearFormat = false;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// format getters
|
|
124
|
-
get numberFormat() { return this._getFormatParam('numberFormat'); }
|
|
125
|
-
get backgroundColor() { return this._getFormatParam('backgroundColor'); }
|
|
126
|
-
get borders() { return this._getFormatParam('borders'); }
|
|
127
|
-
get padding() { return this._getFormatParam('padding'); }
|
|
128
|
-
get horizontalAlignment() { return this._getFormatParam('horizontalAlignment'); }
|
|
129
|
-
get verticalAlignment() { return this._getFormatParam('verticalAlignment'); }
|
|
130
|
-
get wrapStrategy() { return this._getFormatParam('wrapStrategy'); }
|
|
131
|
-
get textDirection() { return this._getFormatParam('textDirection'); }
|
|
132
|
-
get textFormat() { return this._getFormatParam('textFormat'); }
|
|
133
|
-
get hyperlinkDisplayType() { return this._getFormatParam('hyperlinkDisplayType'); }
|
|
134
|
-
get textRotation() { return this._getFormatParam('textRotation'); }
|
|
135
|
-
|
|
136
|
-
// format setters
|
|
137
|
-
set numberFormat(newVal) { return this._setFormatParam('numberFormat', newVal); }
|
|
138
|
-
set backgroundColor(newVal) { return this._setFormatParam('backgroundColor', newVal); }
|
|
139
|
-
set borders(newVal) { return this._setFormatParam('borders', newVal); }
|
|
140
|
-
set padding(newVal) { return this._setFormatParam('padding', newVal); }
|
|
141
|
-
set horizontalAlignment(newVal) { return this._setFormatParam('horizontalAlignment', newVal); }
|
|
142
|
-
set verticalAlignment(newVal) { return this._setFormatParam('verticalAlignment', newVal); }
|
|
143
|
-
set wrapStrategy(newVal) { return this._setFormatParam('wrapStrategy', newVal); }
|
|
144
|
-
set textDirection(newVal) { return this._setFormatParam('textDirection', newVal); }
|
|
145
|
-
set textFormat(newVal) { return this._setFormatParam('textFormat', newVal); }
|
|
146
|
-
set hyperlinkDisplayType(newVal) { return this._setFormatParam('hyperlinkDisplayType', newVal); }
|
|
147
|
-
set textRotation(newVal) { return this._setFormatParam('textRotation', newVal); }
|
|
148
|
-
|
|
149
|
-
clearAllFormatting() {
|
|
150
|
-
// need to track this separately since by setting/unsetting things, we may end up with
|
|
151
|
-
// this._draftData.userEnteredFormat as an empty object, but not an intent to clear it
|
|
152
|
-
this._draftData.clearFormat = true;
|
|
153
|
-
delete this._draftData.userEnteredFormat;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// SAVING + UTILS ////////////////////////////////////////////////////////////////////////////////
|
|
157
|
-
|
|
158
|
-
// returns true if there are any updates that have not been saved yet
|
|
159
|
-
get _isDirty() {
|
|
160
|
-
// have to be careful about checking undefined rather than falsy
|
|
161
|
-
// in case a new value is empty string or 0 or false
|
|
162
|
-
if (this._draftData.note !== undefined) return true;
|
|
163
|
-
if (_.keys(this._draftData.userEnteredFormat).length) return true;
|
|
164
|
-
if (this._draftData.clearFormat) return true;
|
|
165
|
-
if (this._draftData.value !== undefined) return true;
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
discardUnsavedChanges() {
|
|
170
|
-
this._draftData = {};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async save() {
|
|
174
|
-
await this._sheet.saveUpdatedCells([this]);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// used by worksheet when saving cells
|
|
178
|
-
// returns an individual batchUpdate request to update the cell
|
|
179
|
-
_getUpdateRequest() {
|
|
180
|
-
// this logic should match the _isDirty logic above
|
|
181
|
-
// but we need it broken up to build the request below
|
|
182
|
-
const isValueUpdated = this._draftData.value !== undefined;
|
|
183
|
-
const isNoteUpdated = this._draftData.note !== undefined;
|
|
184
|
-
const isFormatUpdated = !!_.keys(this._draftData.userEnteredFormat || {}).length;
|
|
185
|
-
const isFormatCleared = this._draftData.clearFormat;
|
|
186
|
-
|
|
187
|
-
// if no updates, we return null, which we can filter out later before sending requests
|
|
188
|
-
if (!_.some([isValueUpdated, isNoteUpdated, isFormatUpdated, isFormatCleared])) {
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// build up the formatting object, which has some quirks...
|
|
193
|
-
const format = {
|
|
194
|
-
// have to pass the whole object or it will clear existing properties
|
|
195
|
-
...this._rawData.userEnteredFormat,
|
|
196
|
-
...this._draftData.userEnteredFormat,
|
|
197
|
-
};
|
|
198
|
-
// if background color already set, cell has backgroundColor and backgroundColorStyle
|
|
199
|
-
// but backgroundColorStyle takes precendence so we must remove to set the color
|
|
200
|
-
// see https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/cells#CellFormat
|
|
201
|
-
if (_.get(this._draftData, 'userEnteredFormat.backgroundColor')) {
|
|
202
|
-
delete (format.backgroundColorStyle);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return {
|
|
206
|
-
updateCells: {
|
|
207
|
-
rows: [{
|
|
208
|
-
values: [{
|
|
209
|
-
...isValueUpdated && {
|
|
210
|
-
userEnteredValue: { [this._draftData.valueType]: this._draftData.value },
|
|
211
|
-
},
|
|
212
|
-
...isNoteUpdated && {
|
|
213
|
-
note: this._draftData.note,
|
|
214
|
-
},
|
|
215
|
-
...isFormatUpdated && {
|
|
216
|
-
userEnteredFormat: format,
|
|
217
|
-
},
|
|
218
|
-
...isFormatCleared && {
|
|
219
|
-
userEnteredFormat: {},
|
|
220
|
-
},
|
|
221
|
-
}],
|
|
222
|
-
}],
|
|
223
|
-
// turns into a string of which fields to update ex "note,userEnteredFormat"
|
|
224
|
-
fields: _.keys(_.pickBy({
|
|
225
|
-
userEnteredValue: isValueUpdated,
|
|
226
|
-
note: isNoteUpdated,
|
|
227
|
-
userEnteredFormat: isFormatUpdated || isFormatCleared,
|
|
228
|
-
})).join(','),
|
|
229
|
-
start: {
|
|
230
|
-
sheetId: this._sheet.sheetId,
|
|
231
|
-
rowIndex: this.rowIndex,
|
|
232
|
-
columnIndex: this.columnIndex,
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
module.exports = GoogleSpreadsheetCell;
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
const { columnToLetter } = require('./utils');
|
|
2
|
-
|
|
3
|
-
class GoogleSpreadsheetRow {
|
|
4
|
-
constructor(parentSheet, rowNumber, data) {
|
|
5
|
-
this._sheet = parentSheet; // the parent GoogleSpreadsheetWorksheet instance
|
|
6
|
-
this._rowNumber = rowNumber; // the A1 row (1-indexed)
|
|
7
|
-
this._rawData = data;
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < this._sheet.headerValues.length; i++) {
|
|
10
|
-
const propName = this._sheet.headerValues[i];
|
|
11
|
-
if (!propName) continue; // skip empty header
|
|
12
|
-
Object.defineProperty(this, propName, {
|
|
13
|
-
get: () => this._rawData[i],
|
|
14
|
-
set: (newVal) => { this._rawData[i] = newVal; },
|
|
15
|
-
enumerable: true,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return this;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get rowNumber() { return this._rowNumber; }
|
|
23
|
-
// TODO: deprecate rowIndex - the name implies it should be zero indexed :(
|
|
24
|
-
get rowIndex() { return this._rowNumber; }
|
|
25
|
-
get a1Range() {
|
|
26
|
-
return [
|
|
27
|
-
this._sheet.a1SheetName,
|
|
28
|
-
'!',
|
|
29
|
-
`A${this._rowNumber}`,
|
|
30
|
-
':',
|
|
31
|
-
`${columnToLetter(this._sheet.headerValues.length)}${this._rowNumber}`,
|
|
32
|
-
].join('');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async save(options = {}) {
|
|
36
|
-
if (this._deleted) throw new Error('This row has been deleted - call getRows again before making updates.');
|
|
37
|
-
|
|
38
|
-
const response = await this._sheet._spreadsheet.axios.request({
|
|
39
|
-
method: 'put',
|
|
40
|
-
url: `/values/${encodeURIComponent(this.a1Range)}`,
|
|
41
|
-
params: {
|
|
42
|
-
valueInputOption: options.raw ? 'RAW' : 'USER_ENTERED',
|
|
43
|
-
includeValuesInResponse: true,
|
|
44
|
-
},
|
|
45
|
-
data: {
|
|
46
|
-
range: this.a1Range,
|
|
47
|
-
majorDimension: 'ROWS',
|
|
48
|
-
values: [this._rawData],
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
this._rawData = response.data.updatedData.values[0];
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// delete this row
|
|
55
|
-
async delete() {
|
|
56
|
-
if (this._deleted) throw new Error('This row has been deleted - call getRows again before making updates.');
|
|
57
|
-
|
|
58
|
-
const result = await this._sheet._makeSingleUpdateRequest('deleteRange', {
|
|
59
|
-
range: {
|
|
60
|
-
sheetId: this._sheet.sheetId,
|
|
61
|
-
startRowIndex: this._rowNumber - 1, // this format is zero indexed, because of course...
|
|
62
|
-
endRowIndex: this._rowNumber,
|
|
63
|
-
},
|
|
64
|
-
shiftDimension: 'ROWS',
|
|
65
|
-
});
|
|
66
|
-
this._deleted = true;
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
async del() { return this.delete(); } // alias to mimic old version of this module
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = GoogleSpreadsheetRow;
|
package/lib/errors.js
DELETED
package/lib/utils.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
|
|
3
|
-
function getFieldMask(obj) {
|
|
4
|
-
return _.keys(obj).join(',');
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function columnToLetter(column) {
|
|
8
|
-
let temp;
|
|
9
|
-
let letter = '';
|
|
10
|
-
let col = column;
|
|
11
|
-
while (col > 0) {
|
|
12
|
-
temp = (col - 1) % 26;
|
|
13
|
-
letter = String.fromCharCode(temp + 65) + letter;
|
|
14
|
-
col = (col - temp - 1) / 26;
|
|
15
|
-
}
|
|
16
|
-
return letter;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function letterToColumn(letter) {
|
|
20
|
-
let column = 0;
|
|
21
|
-
const { length } = letter;
|
|
22
|
-
for (let i = 0; i < length; i++) {
|
|
23
|
-
column += (letter.charCodeAt(i) - 64) * 26 ** (length - i - 1);
|
|
24
|
-
}
|
|
25
|
-
return column;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
module.exports = {
|
|
29
|
-
getFieldMask,
|
|
30
|
-
columnToLetter,
|
|
31
|
-
letterToColumn,
|
|
32
|
-
};
|