Compare commits
1 Commits
2026.04.29
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e03f1bd503 |
878
modules/shp.js
Normal file
878
modules/shp.js
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
Сервис интеграции ПП Парус 8 с WEB API
|
||||
Дополнительный модуль: Sheet Parser (SHP)
|
||||
*/
|
||||
|
||||
//------------------------------
|
||||
// Подключение внешних библиотек
|
||||
//------------------------------
|
||||
|
||||
const xml2js = require("xml2js"); //Конвертация XML в JSON и JSON в XML
|
||||
const XLSX = require("./shp_utils/node_modules/xlsx"); //Парсер табличных данных
|
||||
|
||||
//---------------------
|
||||
// Глобальные константы
|
||||
//---------------------
|
||||
|
||||
//Количество строк, загружаемых за одно обращение к серверу
|
||||
const NIMPORT_BUFF_LEN = 500;
|
||||
|
||||
//Форматы файлов данных */
|
||||
const NSHPFL_FORMAT_XLS = 0; //MS Excel (xls, xlsx)
|
||||
const NSHPFL_FORMAT_ODS = 1; //OpenDocument Spreadsheet (ods)
|
||||
const NSHPFL_FORMAT_CSV = 2; //Comma-Separated Values (csv)
|
||||
const NSHPFL_FORMAT_DBF = 3; //Database File (dbf)
|
||||
|
||||
//Кодировки файлов данных */
|
||||
const NSHPFL_ENC_DEFAULT = 0; //По умолчанию
|
||||
const NSHPFL_ENC_WIN1251 = 1; //Windows-1251
|
||||
const NSHPFL_ENC_UTF8 = 2; //UTF-8
|
||||
|
||||
//------------------------
|
||||
// Вспомогательные функции
|
||||
//------------------------
|
||||
|
||||
//Разбор XML (обёртка для async/await)
|
||||
const parseXML = ({ sXML, options }) => {
|
||||
//Возвращаем промис для использования async/await
|
||||
return new Promise((resolve, reject) => {
|
||||
//Разбираем XML
|
||||
xml2js.parseString(sXML, options, (err, result) => {
|
||||
//Если при разборе возникла ошибка
|
||||
if (err) reject(err);
|
||||
//Если всё успешно - возвращаем результат
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//Конвертация в XML
|
||||
const toXML = ({ rootName, data, headless = false }) => {
|
||||
//Формируем XML
|
||||
const builder = new xml2js.Builder({ rootName, headless });
|
||||
return builder.buildObject(data);
|
||||
};
|
||||
|
||||
//Получение текстового представления ячейки
|
||||
const getCellText = cl => {
|
||||
//Если ячейки нет - возвращаем пустую строку
|
||||
if (!cl) return "";
|
||||
//Если есть значение v - берём его
|
||||
if (cl.v !== null && cl.v !== undefined) return String(cl.v);
|
||||
//Если есть форматированное значение w - берём его
|
||||
if (cl.w !== null && cl.w !== undefined) return String(cl.w);
|
||||
//Если значений нет - возвращаем пустую строку
|
||||
return "";
|
||||
};
|
||||
|
||||
//Построение индекса колонок по заголовкам (имя заголовка -> номер колонки)
|
||||
const buildHeadersIndex = ({ sheet, sheetData, nHeaderLineNo }) => {
|
||||
//Если листа нет - индекса заголовков нет
|
||||
if (!sheet) return null;
|
||||
//Если данных листа нет - индекса заголовков нет
|
||||
if (!Array.isArray(sheetData) || sheetData.length === 0) return null;
|
||||
//Номер строки заголовков должен быть валидным (1..)
|
||||
const nHdrLine = Number.isFinite(nHeaderLineNo) && nHeaderLineNo > 0 ? nHeaderLineNo : null;
|
||||
//Если номер строки заголовков невалиден - индекса заголовков нет
|
||||
if (!nHdrLine) return null;
|
||||
//Строка заголовков
|
||||
const headerRow = sheetData[nHdrLine - 1] || [];
|
||||
//Если строки заголовков нет - индекса заголовков нет
|
||||
if (!Array.isArray(headerRow) || headerRow.length === 0) return null;
|
||||
//Создаём резолвер объединённых ячеек
|
||||
const { resolveCell } = createMergedCellResolver(sheet);
|
||||
//Результат индекса
|
||||
const idx = new Map();
|
||||
//Обходим все колонки строки заголовков
|
||||
for (let c = 0; c < headerRow.length; c++) {
|
||||
//Номер колонки
|
||||
const colNo = c + 1;
|
||||
//Резолвим ячейку заголовка
|
||||
const cl = resolveCell(nHdrLine - 1, c, (headerRow || [])[c]);
|
||||
//Текст заголовка
|
||||
const sName = getCellText(cl).trim();
|
||||
//Пустые заголовки не индексируем
|
||||
if (!sName) continue;
|
||||
//Ключ заголовка
|
||||
const key = sName.toUpperCase();
|
||||
//Если такой заголовок уже есть - не перезаписываем
|
||||
if (idx.has(key)) continue;
|
||||
//Сохраняем номер колонки
|
||||
idx.set(key, colNo);
|
||||
}
|
||||
//Если индекс пустой - возвращаем null
|
||||
if (idx.size === 0) return null;
|
||||
//Возвращаем индекс
|
||||
return idx;
|
||||
};
|
||||
|
||||
//Формирование XML заголовков таблицы
|
||||
const buildTableHeadersXML = ({ workbook, sSheetName, aColumns, nColumnsCount, nHeaderLineNo }) => {
|
||||
//Если книги нет - заголовки построить нельзя
|
||||
if (!workbook) return null;
|
||||
//Если имя листа не задано - заголовки построить нельзя
|
||||
if (!sSheetName) return null;
|
||||
//Получаем лист
|
||||
const sheet = workbook.Sheets?.[sSheetName] || null;
|
||||
//Если листа нет - заголовки построить нельзя
|
||||
if (!sheet) return null;
|
||||
//Dense-данные листа (строки/ячейки)
|
||||
const sheetData = sheet?.["!data"] || [];
|
||||
//Если данных нет - заголовки построить нельзя
|
||||
if (!Array.isArray(sheetData) || sheetData.length === 0) return null;
|
||||
//Номер строки заголовков должен быть валидным (1..)
|
||||
const nHdrLine = Number.isFinite(nHeaderLineNo) && nHeaderLineNo > 0 ? nHeaderLineNo : 1;
|
||||
//Получаем строку заголовков
|
||||
const headerRow = sheetData[nHdrLine - 1] || [];
|
||||
//Создаём резолвер объединённых ячеек
|
||||
const { hasMerges, resolveCell } = createMergedCellResolver(sheet);
|
||||
//Нормализуем количество колонок таблицы
|
||||
const nCols = Number.isFinite(nColumnsCount) && nColumnsCount > 0 ? nColumnsCount : 0;
|
||||
//Если колонок нет - формируем пустой RHEADERS
|
||||
if (!nCols) return toXML({ rootName: "RHEADERS", data: {}, headless: true });
|
||||
//Список заголовков (RHEADER)
|
||||
const rHeaders = [];
|
||||
//Если задан список колонок
|
||||
if (Array.isArray(aColumns) && aColumns.length > 0) {
|
||||
//Обходим все выбранные колонки в заданном порядке
|
||||
for (let i = 0; i < aColumns.length; i++) {
|
||||
//Номер исходной колонки
|
||||
const colNo = aColumns[i];
|
||||
//Если номер колонки невалиден - пропускаем
|
||||
if (!Number.isFinite(colNo) || colNo < 1) continue;
|
||||
//Резолвим ячейку заголовка
|
||||
const cl = resolveCell(nHdrLine - 1, colNo - 1, (headerRow || [])[colNo - 1]);
|
||||
//Текст заголовка
|
||||
const sName = getCellText(cl).trim();
|
||||
//Добавляем RHEADER
|
||||
rHeaders.push({ NCOLUMN: colNo, SCOLUMN: sName });
|
||||
}
|
||||
}
|
||||
//Иначе - заголовки строим по фактическому набору колонок (1..nCols)
|
||||
else {
|
||||
//Определяем количество колонок в строке заголовков
|
||||
const nHeaderRowCols = hasMerges ? nCols : (headerRow || []).length;
|
||||
const nIter = Math.max(nCols, nHeaderRowCols, 0);
|
||||
//Обходим колонки 1..nIter
|
||||
for (let c = 0; c < nIter; c++) {
|
||||
//Номер колонки
|
||||
const colNo = c + 1;
|
||||
//Резолвим ячейку заголовка
|
||||
const cl = resolveCell(nHdrLine - 1, c, (headerRow || [])[c]);
|
||||
//Текст заголовка
|
||||
const sName = getCellText(cl).trim();
|
||||
//Добавляем RHEADER
|
||||
rHeaders.push({ NCOLUMN: colNo, SCOLUMN: sName });
|
||||
}
|
||||
}
|
||||
//Собираем XML заголовков
|
||||
return toXML({
|
||||
rootName: "RHEADERS",
|
||||
data: {
|
||||
...(rHeaders.length > 0 ? { RHEADER: rHeaders } : {})
|
||||
},
|
||||
headless: true
|
||||
});
|
||||
};
|
||||
|
||||
//Преобразование "одиночное значение | массив | undefined" -> массив
|
||||
const toArray = value => {
|
||||
//Если значения нет - возвращаем пустой массив
|
||||
if (!value) return [];
|
||||
//Если уже массив - возвращаем как есть, иначе оборачиваем
|
||||
return Array.isArray(value) ? value : [value];
|
||||
};
|
||||
|
||||
//Разбор параметров выполнения SHP (clOptions: XML -> JSON)
|
||||
const parseShpOptionsXML = async sOptionsXML => {
|
||||
//Если параметр отсутствует или не строка - параметров выполнения нет
|
||||
if (!sOptionsXML || !(sOptionsXML instanceof String || typeof sOptionsXML === "string")) return null;
|
||||
//Если строка пустая - параметров выполнения нет
|
||||
if (!String(sOptionsXML).trim()) return null;
|
||||
//Результат парсинга XML
|
||||
let parseRes = null;
|
||||
try {
|
||||
//Парсим XML
|
||||
parseRes = await parseXML({
|
||||
sXML: sOptionsXML,
|
||||
options: {
|
||||
explicitArray: false,
|
||||
mergeAttrs: true,
|
||||
valueProcessors: [xml2js.processors.parseNumbers, xml2js.processors.parseBooleans]
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
//Ошибка разбора параметров выполнения
|
||||
throw new Error(`Ошибка разбора XML параметров выполнения: ${e.message || e}`);
|
||||
}
|
||||
//Корневой элемент параметров выполнения
|
||||
const root = parseRes?.OPTIONS || null;
|
||||
//Если корня нет - параметров выполнения нет
|
||||
if (!root) return null;
|
||||
//OPTION может быть одиночным объектом или массивом
|
||||
const options = toArray(root.OPTION).filter(Boolean);
|
||||
//Возвращаем нормализованный объект
|
||||
return { options };
|
||||
};
|
||||
|
||||
//Преобразование номера/имени колонки в число (A->1, B->2, AA->27, ...)
|
||||
const normalizeColumn = value => {
|
||||
//Если значение отсутствует - возвращаем null
|
||||
if (value === null || value === undefined) return null;
|
||||
//Приводим к строке и убираем пробелы
|
||||
const sVal = String(value).trim();
|
||||
//Пустое значение - нечего нормализовать
|
||||
if (!sVal) return null;
|
||||
//Если это число в строковом представлении
|
||||
if (/^\d+$/.test(sVal)) {
|
||||
//Преобразуем в число
|
||||
const n = parseInt(sVal, 10);
|
||||
//Колонки начинаются с 1
|
||||
return Number.isFinite(n) && n > 0 ? n : null;
|
||||
}
|
||||
//Приводим буквы к верхнему регистру
|
||||
const s = sVal.toUpperCase();
|
||||
//Ожидаем только латинские буквы (A..Z)
|
||||
if (!/^[A-Z]+$/.test(s)) return null;
|
||||
//Результат в десятичном виде
|
||||
let n = 0;
|
||||
//Переводим base-26 представление в десятичное
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
//A=1 ... Z=26
|
||||
n = n * 26 + (s.charCodeAt(i) - 64);
|
||||
}
|
||||
//Возвращаем результат, если он валиден
|
||||
return n > 0 ? n : null;
|
||||
};
|
||||
|
||||
//Преобразование имени/номера колонки в число с учётом заголовков
|
||||
const normalizeColumnWithHeaders = ({ value, headersIndex }) => {
|
||||
//Если значение отсутствует - возвращаем null
|
||||
if (value === null || value === undefined) return null;
|
||||
//Приводим к строке и убираем пробелы
|
||||
const sVal = String(value).trim();
|
||||
//Пустое значение - нечего нормализовать
|
||||
if (!sVal) return null;
|
||||
//Если задан индекс заголовков - сначала ищем по нему
|
||||
if (headersIndex && headersIndex instanceof Map) {
|
||||
//Ключ поиска
|
||||
const key = sVal.toUpperCase();
|
||||
//Пробуем найти колонку по заголовку
|
||||
const nByHdr = headersIndex.get(key);
|
||||
//Если нашли валидный номер - возвращаем
|
||||
if (Number.isFinite(nByHdr) && nByHdr > 0) return nByHdr;
|
||||
}
|
||||
//Если по заголовку не нашли - преобразуем имя колонки в число
|
||||
return normalizeColumn(sVal);
|
||||
};
|
||||
|
||||
//Формирование списка индексов (1..max) по описанию (RANGE/ENUM/COUNT)
|
||||
const buildIndexList = ({ selector, nMax, normalizeItem, getDefaultFrom }) => {
|
||||
//Нормализуем максимум
|
||||
const nMaxSafe = Number.isFinite(nMax) && nMax > 0 ? nMax : 0;
|
||||
//Если селектора нет или максимум не задан - считаем что фильтра нет
|
||||
if (!selector || !nMaxSafe) return null;
|
||||
//Тип выборки (RANGE/ENUM/COUNT)
|
||||
const sType = String(selector.SADDRESS_TYPE || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
//Если тип не задан - считаем что фильтра нет
|
||||
if (!sType) return null;
|
||||
//Добавление уникальных значений в множество
|
||||
const pushUnique = (set, value) => {
|
||||
//Должно быть число
|
||||
if (!Number.isFinite(value)) return;
|
||||
//Должно попадать в диапазон 1..nMaxSafe
|
||||
if (value < 1 || value > nMaxSafe) return;
|
||||
//Добавляем уникально
|
||||
set.add(value);
|
||||
};
|
||||
//Множество для уникальных значений
|
||||
const res = new Set();
|
||||
//Адрес-диапазон
|
||||
if (sType === "RANGE") {
|
||||
//Узел диапазона
|
||||
const range = selector.RADDRESS_RANGE || null;
|
||||
//Начало диапазона обязательно
|
||||
const nFrom = normalizeItem?.(range?.SVALUE_FROM);
|
||||
//Если начала нет - фильтр считаем некорректным
|
||||
if (!Number.isFinite(nFrom) || nFrom < 1) return null;
|
||||
//Конец диапазона опционален
|
||||
let nTo = normalizeItem?.(range?.SVALUE_TO);
|
||||
//Если конец не указан - берём до конца
|
||||
if (!Number.isFinite(nTo) || nTo < nFrom) nTo = nMaxSafe;
|
||||
//Заполняем диапазон
|
||||
for (let i = nFrom; i <= Math.min(nTo, nMaxSafe); i++) pushUnique(res, i);
|
||||
//Возвращаем отсортированный список
|
||||
return Array.from(res).sort((a, b) => a - b);
|
||||
}
|
||||
//Адрес-перечисление
|
||||
if (sType === "ENUM") {
|
||||
//Узел перечисления
|
||||
const en = selector.RADDRESS_ENUM || null;
|
||||
//Значение может быть одиночным значением или массивом
|
||||
for (const v of toArray(en?.SVALUE)) pushUnique(res, normalizeItem?.(v));
|
||||
//Возвращаем отсортированный список
|
||||
return Array.from(res).sort((a, b) => a - b);
|
||||
}
|
||||
//Адрес-количество
|
||||
if (sType === "COUNT") {
|
||||
//Узел количества
|
||||
const cnt = selector.RADDRESS_COUNT || null;
|
||||
//Количество берём из SVALUE
|
||||
const nCount = parseInt(String(cnt?.SVALUE ?? "").trim(), 10);
|
||||
//Количество должно быть > 0
|
||||
if (!Number.isFinite(nCount) || nCount <= 0) return null;
|
||||
//Признак, что старт задан явно
|
||||
const bHasFrom = cnt?.SVALUE_FROM !== null && cnt?.SVALUE_FROM !== undefined && String(cnt?.SVALUE_FROM).trim() !== "";
|
||||
//Старт опционален (если не задан - берём вычисляемое значение, иначе 1)
|
||||
const nFromRaw = bHasFrom ? normalizeItem?.(cnt?.SVALUE_FROM) : (getDefaultFrom?.() ?? 1);
|
||||
//Если старт некорректен - начинаем с 1
|
||||
const nFromSafe = Number.isFinite(nFromRaw) && nFromRaw > 0 ? nFromRaw : 1;
|
||||
//Добавляем нужное количество индексов
|
||||
for (let i = nFromSafe; i < nFromSafe + nCount; i++) pushUnique(res, i);
|
||||
//Возвращаем отсортированный список
|
||||
return Array.from(res).sort((a, b) => a - b);
|
||||
}
|
||||
//Неизвестный тип
|
||||
return null;
|
||||
};
|
||||
|
||||
//Формирование списка всех индексов (1..nMax)
|
||||
const buildAllIndexList = ({ nMax }) => {
|
||||
//Нормализуем максимум
|
||||
const nMaxSafe = Number.isFinite(nMax) && nMax > 0 ? nMax : 0;
|
||||
//Если максимум некорректен - возвращаем пустой список
|
||||
if (!nMaxSafe) return [];
|
||||
//Создаём массив нужной длины
|
||||
const res = new Array(nMaxSafe);
|
||||
//Заполняем индексами
|
||||
for (let i = 0; i < nMaxSafe; i++) res[i] = i + 1;
|
||||
//Возвращаем результат
|
||||
return res;
|
||||
};
|
||||
|
||||
//Проверка наличия данных в листе
|
||||
const isSheetNotEmpty = ({ workbook, sSheetName }) =>
|
||||
//Лист не пустой, если есть dense-данные и они содержат хотя бы одну строку
|
||||
Array.isArray(workbook?.Sheets[sSheetName]?.["!data"]) && workbook?.Sheets[sSheetName]?.["!data"].length > 0;
|
||||
|
||||
//Подсчёт предельного количества столбцов в листе
|
||||
const getSheetColumnsCount = ({ workbook, sSheetName }) => {
|
||||
//Получаем dense-данные листа
|
||||
const rows = workbook?.Sheets?.[sSheetName]?.["!data"];
|
||||
//Если данных нет - возвращаем 0
|
||||
if (!Array.isArray(rows) || rows.length === 0) return 0;
|
||||
//Возвращаем максимальную длину строки (количество колонок)
|
||||
return rows.reduce((cnt, curRow) => Math.max(cnt, (curRow || []).length), 0);
|
||||
};
|
||||
|
||||
//Проверка, что ячейка содержит данные (v/w)
|
||||
const isCellHasData = cl => {
|
||||
//Если ячейки нет - данных нет
|
||||
if (!cl) return false;
|
||||
//Если есть значение v - проверяем его на пустоту
|
||||
if (cl.v !== null && cl.v !== undefined && String(cl.v) !== "") return true;
|
||||
//Если есть текстовое значение w - проверяем его на пустоту
|
||||
if (cl.w !== null && cl.w !== undefined && String(cl.w) !== "") return true;
|
||||
//Данных нет
|
||||
return false;
|
||||
};
|
||||
|
||||
//Поиск первой строки, где есть данные
|
||||
const getFirstDataLineNo = ({ sheetData }) => {
|
||||
//Если данных листа нет - возвращаем 1
|
||||
if (!Array.isArray(sheetData) || sheetData.length === 0) return 1;
|
||||
//Обходим строки сверху вниз
|
||||
for (let r = 0; r < sheetData.length; r++) {
|
||||
//Строка листа (массив ячеек)
|
||||
const row = sheetData[r] || [];
|
||||
//Обходим ячейки строки
|
||||
for (let c = 0; c < row.length; c++) {
|
||||
//Если нашли данные - возвращаем номер строки
|
||||
if (isCellHasData(row[c])) return r + 1;
|
||||
}
|
||||
}
|
||||
//Если данных не нашли - возвращаем 1
|
||||
return 1;
|
||||
};
|
||||
|
||||
//Поиск первой колонки, где есть данные
|
||||
const getFirstDataColumnNo = ({ sheetData }) => {
|
||||
//Если данных листа нет - возвращаем 1
|
||||
if (!Array.isArray(sheetData) || sheetData.length === 0) return 1;
|
||||
//Минимальный индекс колонки с данными
|
||||
let minCol = null;
|
||||
//Обходим строки
|
||||
for (let r = 0; r < sheetData.length; r++) {
|
||||
//Строка листа (массив ячеек)
|
||||
const row = sheetData[r] || [];
|
||||
//Обходим ячейки строки
|
||||
for (let c = 0; c < row.length; c++) {
|
||||
//Если данных нет - пропускаем
|
||||
if (!isCellHasData(row[c])) continue;
|
||||
//Фиксируем минимальную колонку с данными
|
||||
minCol = minCol === null ? c : Math.min(minCol, c);
|
||||
}
|
||||
}
|
||||
//Если данных не нашли - возвращаем 1
|
||||
if (minCol === null) return 1;
|
||||
//Возвращаем номер колонки
|
||||
return minCol + 1;
|
||||
};
|
||||
|
||||
//Построение таблиц для импорта по параметрам выполнения
|
||||
const buildDataTablesByOptions = ({ workbook, nShpFileRn, options, sDataSheetName, bIgnoreSheetName, bHasHeaders }) => {
|
||||
//Результирующий список таблиц
|
||||
const res = [];
|
||||
//Обходим все OPTION
|
||||
for (const opt of toArray(options)) {
|
||||
//Имя листа, к которому относится параметр
|
||||
const sSheetName = String(opt?.SSHEET || "").trim();
|
||||
//Если имя листа не задано - пропускаем
|
||||
if (!sSheetName) continue;
|
||||
//Если лист пустой - пропускаем
|
||||
if (!isSheetNotEmpty({ workbook, sSheetName: bIgnoreSheetName ? sDataSheetName : sSheetName })) continue;
|
||||
//Список таблиц для данного параметра
|
||||
const rTables = toArray(opt?.RTABLES?.RTABLE);
|
||||
//Если таблицы не указаны - пропускаем
|
||||
if (rTables.length === 0) continue;
|
||||
//Фактическое количество колонок листа
|
||||
const nColumnsCountRaw = getSheetColumnsCount({ workbook, sSheetName: bIgnoreSheetName ? sDataSheetName : sSheetName });
|
||||
//Фактическое количество строк листа
|
||||
const nLinesCountRaw = (workbook.Sheets[bIgnoreSheetName ? sDataSheetName : sSheetName]["!data"] || []).length;
|
||||
//Обходим описанные таблицы
|
||||
for (const rt of rTables) {
|
||||
//Имя таблицы
|
||||
const sTableName = rt?.SNAME ? String(rt.SNAME).trim() : null;
|
||||
//Лист, откуда берём данные
|
||||
const sheet = workbook?.Sheets?.[bIgnoreSheetName ? sDataSheetName : sSheetName] || null;
|
||||
//Dense-данные листа
|
||||
const sheetData = sheet?.["!data"] || [];
|
||||
//Номер строки заголовков
|
||||
const nHeaderLineNo = bHasHeaders ? getFirstDataLineNo({ sheetData }) : null;
|
||||
//Индекс заголовков
|
||||
const headersIndex = bHasHeaders ? buildHeadersIndex({ sheet, sheetData, nHeaderLineNo }) : null;
|
||||
//Формируем список колонок по описателю
|
||||
const aCols = buildIndexList({
|
||||
selector: rt?.RCOLUMNS || null,
|
||||
nMax: nColumnsCountRaw,
|
||||
normalizeItem: v => normalizeColumnWithHeaders({ value: v, headersIndex }),
|
||||
getDefaultFrom: () => getFirstDataColumnNo({ sheetData })
|
||||
});
|
||||
//Формируем список строк по описателю
|
||||
const aLines = buildIndexList({
|
||||
selector: rt?.RLINES || null,
|
||||
nMax: nLinesCountRaw,
|
||||
normalizeItem: v => {
|
||||
//Если значения нет - не нормализуем
|
||||
if (v === null || v === undefined) return null;
|
||||
//Парсим строку как число
|
||||
const n = parseInt(String(v).trim(), 10);
|
||||
//Индекс строки должен быть положительным
|
||||
return Number.isFinite(n) && n > 0 ? n : null;
|
||||
},
|
||||
getDefaultFrom: () => getFirstDataLineNo({ sheetData })
|
||||
});
|
||||
//Добавляем описание таблицы в результат
|
||||
res.push({
|
||||
sSheetName,
|
||||
sDataSheetName: bIgnoreSheetName ? sDataSheetName : sSheetName,
|
||||
sTableName,
|
||||
nShpFileRn,
|
||||
aColumns: aCols,
|
||||
aLines
|
||||
});
|
||||
}
|
||||
}
|
||||
//Возвращаем список таблиц
|
||||
return res;
|
||||
};
|
||||
|
||||
//Преобразование значения ячейки в формат импорта
|
||||
const makeImportCell = ({ cl, n }) => {
|
||||
//Нормализуем номер колонки (1..)
|
||||
const nCol = Number.isFinite(n) && n > 0 ? n : null;
|
||||
//Если ячейки нет - пишем пустую ячейку
|
||||
if (!cl) return { $: { t: "z", n: nCol }, v: null };
|
||||
//Тип ячейки
|
||||
const t = cl.t || "z";
|
||||
//Значение ячейки (даты приводим к ISO-строке)
|
||||
const v = (t === "d" ? cl.v?.toISOString?.() : cl.v) || cl.w || null;
|
||||
//Возвращаем формат ячейки для импорта
|
||||
return { $: { t, n: nCol }, v };
|
||||
};
|
||||
|
||||
//Резолвер значений для объединённых ячеек
|
||||
const createMergedCellResolver = sheet => {
|
||||
//Считываем описания объединений ячеек (если они есть)
|
||||
const merges = Array.isArray(sheet?.["!merges"]) ? sheet["!merges"] : [];
|
||||
//Если объединений нет - возвращаем быстрый резолвер без дополнительной логики
|
||||
if (merges.length === 0) {
|
||||
return {
|
||||
hasMerges: false,
|
||||
resolveCell: (_r, _c, cell) => cell || null
|
||||
};
|
||||
}
|
||||
//Создаём индекс объединений по строкам
|
||||
const byRow = new Map();
|
||||
//Обходим все описания объединений
|
||||
for (const m of merges) {
|
||||
//Начальная координата диапазона объединения
|
||||
const s = m?.s;
|
||||
//Конечная координата диапазона объединения
|
||||
const e = m?.e;
|
||||
//Если координат нет - пропускаем элемент
|
||||
if (!s || !e) continue;
|
||||
//Нормализуем начало по строкам
|
||||
const rFrom = Math.min(s.r ?? 0, e.r ?? 0);
|
||||
//Нормализуем конец по строкам
|
||||
const rTo = Math.max(s.r ?? 0, e.r ?? 0);
|
||||
//Нормализуем начало по колонкам
|
||||
const cFrom = Math.min(s.c ?? 0, e.c ?? 0);
|
||||
//Нормализуем конец по колонкам
|
||||
const cTo = Math.max(s.c ?? 0, e.c ?? 0);
|
||||
//Проверяем валидность нормализованных координат
|
||||
const bOk = Number.isFinite(rFrom) && Number.isFinite(rTo) && Number.isFinite(cFrom) && Number.isFinite(cTo);
|
||||
//Если координаты невалидны - пропускаем объединение
|
||||
if (!bOk) continue;
|
||||
//Индексируем объединение по всем строкам диапазона
|
||||
for (let r = rFrom; r <= rTo; r++) {
|
||||
//Текущий список объединений по строке (если уже есть)
|
||||
const arr = byRow.get(r);
|
||||
//Сохраняем диапазон и “ведущую” ячейку
|
||||
const item = { rFrom, rTo, cFrom, cTo, rLead: rFrom, cLead: cFrom };
|
||||
//Если массив по строке уже существует - добавляем
|
||||
if (arr) arr.push(item);
|
||||
//Иначе создаём новый список по строке
|
||||
else byRow.set(r, [item]);
|
||||
}
|
||||
}
|
||||
//Получаем dense-данные листа (для доступа к “ведущей” ячейке)
|
||||
const data = sheet?.["!data"] || [];
|
||||
//Возвращаем резолвер с поддержкой объединений
|
||||
return {
|
||||
hasMerges: true,
|
||||
resolveCell: (r, c, cell) => {
|
||||
//Если ячейка уже есть - возвращаем её без изменений
|
||||
if (cell) return cell;
|
||||
//Получаем список объединений для текущей строки
|
||||
const rowMerges = byRow.get(r);
|
||||
//Если объединений в этой строке нет - возвращаем null
|
||||
if (!rowMerges || rowMerges.length === 0) return null;
|
||||
//Проверяем попадание колонки в один из диапазонов объединений
|
||||
for (const m of rowMerges) {
|
||||
//Если колонка вне диапазона - проверяем следующее объединение
|
||||
if (c < m.cFrom || c > m.cTo) continue;
|
||||
//Возвращаем “ведущую” ячейку диапазона
|
||||
return (data[m.rLead] || [])[m.cLead] || null;
|
||||
}
|
||||
//Если не попали ни в одно объединение - возвращаем null
|
||||
return null;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//------------
|
||||
// Тело модуля
|
||||
//------------
|
||||
|
||||
//Обработчик "До"
|
||||
const importBefore = async ({ queue, dbConn }) => {
|
||||
//Если задано тело сообщения
|
||||
if (queue.blMsg) {
|
||||
//В теле должен быть рег. номер файла данных (EXSEXTSHPFL)
|
||||
const nShpFileRn = parseInt(queue.blMsg.toString(), 10);
|
||||
//Если там рег. номер (число)
|
||||
if (!isNaN(nShpFileRn)) {
|
||||
//Читаем по нему запись файла данных
|
||||
const shpFileData = await dbConn.executeStored({
|
||||
connection: dbConn.connection,
|
||||
sName: "PKG_EXS_EXT_SHP.FILE_GET",
|
||||
inPrms: { NEXSEXTSHPFL: nShpFileRn },
|
||||
outPrms: { RFILE: dbConn.connector.DT_CURSOR }
|
||||
});
|
||||
//Если запись найдена
|
||||
if (shpFileData.RFILE.length === 1) {
|
||||
//Разбираем её
|
||||
try {
|
||||
//Признак наличия заголовков
|
||||
const bHasHeaders = String(shpFileData.RFILE[0]?.nHasHeaders ?? "0").trim() === "1";
|
||||
//Формат файла данных
|
||||
const nFileFormat = shpFileData.RFILE[0]?.nFormat;
|
||||
//Заголовки формируем только для CSV/DBF и только если они есть
|
||||
const bNeedSendHeaders = bHasHeaders && [NSHPFL_FORMAT_CSV, NSHPFL_FORMAT_DBF].includes(nFileFormat);
|
||||
//Объект рабочей книги
|
||||
let workbook = null;
|
||||
try {
|
||||
//Кодировка файла данных
|
||||
const nEnc = shpFileData.RFILE[0].nEnc;
|
||||
//Кодовая страница (параметры чтения XLSX)
|
||||
const cp = {};
|
||||
//Байты файла данных
|
||||
let blData = shpFileData.RFILE[0].blFileData;
|
||||
//Если это CSV
|
||||
if (nFileFormat === NSHPFL_FORMAT_CSV) {
|
||||
//Если указана Win-1251 - перекодируем CSV в UTF-8
|
||||
if (nEnc === NSHPFL_ENC_WIN1251) {
|
||||
//Декодируем исходные байты как windows-1251
|
||||
const s = new TextDecoder("windows-1251").decode(blData);
|
||||
//Преобразуем строку обратно в байты UTF-8
|
||||
blData = Buffer.from(s);
|
||||
}
|
||||
//Задаём codepage UTF-8
|
||||
cp.codepage = 65001;
|
||||
}
|
||||
//Если это DBF
|
||||
else if (nFileFormat === NSHPFL_FORMAT_DBF) {
|
||||
//Если указана Win-1251
|
||||
if (nEnc === NSHPFL_ENC_WIN1251)
|
||||
//Задаём codepage 1251
|
||||
cp.codepage = 1251;
|
||||
else
|
||||
//Задаём codepage UTF-8
|
||||
cp.codepage = 65001;
|
||||
}
|
||||
//Выполняем парсинг
|
||||
workbook = XLSX.read(blData, { dense: true, cellDates: true, ...cp });
|
||||
} catch (e) {
|
||||
//Ошибка чтения/парсинга файла данных
|
||||
throw new Error(`Ошибка разбора файла данных: ${e.message}`);
|
||||
}
|
||||
//Если есть сведения о листахх
|
||||
if (workbook && workbook?.SheetNames?.length > 0) {
|
||||
//Признак формата, где листов фактически нет (csv/dbf) или их не нужно учитывать
|
||||
const bIgnoreSheetName = ![NSHPFL_FORMAT_XLS, NSHPFL_FORMAT_ODS].includes(nFileFormat);
|
||||
//Имя листа с данными (для csv/dbf используем первый лист книги)
|
||||
const sDataSheetName = workbook.SheetNames[0];
|
||||
//Разбор параметров выполнения (если есть)
|
||||
const shpOptions = await parseShpOptionsXML(shpFileData.RFILE[0]?.clOptions);
|
||||
//Определим буфер для хранения таблиц файла данных
|
||||
const dataTables = [];
|
||||
//Формирование списка таблиц для импорта
|
||||
const tablesSpec = shpOptions
|
||||
? buildDataTablesByOptions({
|
||||
workbook,
|
||||
nShpFileRn,
|
||||
options: shpOptions.options,
|
||||
sDataSheetName,
|
||||
bIgnoreSheetName,
|
||||
bHasHeaders
|
||||
})
|
||||
: (bIgnoreSheetName
|
||||
? [sDataSheetName]
|
||||
: workbook.SheetNames.filter(sSheetName => isSheetNotEmpty({ workbook, sSheetName }))
|
||||
).map(sSheetName => ({
|
||||
sSheetName,
|
||||
sDataSheetName: bIgnoreSheetName ? sDataSheetName : sSheetName,
|
||||
sTableName: null,
|
||||
nShpFileRn,
|
||||
aColumns: null,
|
||||
aLines: null
|
||||
}));
|
||||
//Регистрируем таблицы на сервере
|
||||
for (const tbl of tablesSpec) {
|
||||
//Считаем фактическое количество колонок листа
|
||||
const nColumnsCountRaw = getSheetColumnsCount({ workbook, sSheetName: tbl.sDataSheetName || tbl.sSheetName });
|
||||
//Считаем фактическое количество строк листа
|
||||
const nLinesCountRaw = (workbook.Sheets[tbl.sDataSheetName || tbl.sSheetName]["!data"] || []).length;
|
||||
//Список колонок (или null - значит все)
|
||||
const aColumns = Array.isArray(tbl.aColumns) ? tbl.aColumns : null;
|
||||
//Список строк (или null - значит все)
|
||||
const aLines = Array.isArray(tbl.aLines) ? tbl.aLines : null;
|
||||
//Количество колонок для описания таблицы
|
||||
const nColumnsCount = aColumns ? aColumns.length : nColumnsCountRaw;
|
||||
//Определяем лист, с которого читаем данные
|
||||
const sDataSheetName = tbl.sDataSheetName || tbl.sSheetName;
|
||||
//Dense-данные листа
|
||||
const sheetData = workbook?.Sheets?.[sDataSheetName]?.["!data"] || [];
|
||||
//Номер строки заголовков (если заголовки нужны - берём первую строку с данными, иначе null)
|
||||
const nHeaderLineNo = bNeedSendHeaders ? getFirstDataLineNo({ sheetData }) : null;
|
||||
//Количество строк для описания таблицы
|
||||
const nLinesCount = (() => {
|
||||
//Если заголовков нет - количество строк
|
||||
const nRaw = aLines ? aLines.length : nLinesCountRaw;
|
||||
//Если заголовки не требуются - возвращаем как есть
|
||||
if (!bNeedSendHeaders) return nRaw;
|
||||
//Если строк нет - нечего исключать
|
||||
if (!Number.isFinite(nRaw) || nRaw <= 0) return nRaw;
|
||||
//Если номер заголовка невалиден
|
||||
if (!Number.isFinite(nHeaderLineNo) || nHeaderLineNo <= 0) return nRaw;
|
||||
//Если выбран конкретный список строк - исключаем заголовок только если он туда попал
|
||||
if (aLines) return aLines.includes(nHeaderLineNo) ? Math.max(nRaw - 1, 0) : nRaw;
|
||||
//Если строки не фильтруются - заголовок считаем частью данных листа и исключаем его
|
||||
return nHeaderLineNo <= nLinesCountRaw ? Math.max(nRaw - 1, 0) : nRaw;
|
||||
})();
|
||||
//Формируем XML заголовков (только если требуется отправка заголовков)
|
||||
const sHeadersXML = bNeedSendHeaders
|
||||
? buildTableHeadersXML({
|
||||
workbook,
|
||||
sSheetName: sDataSheetName,
|
||||
aColumns,
|
||||
nColumnsCount,
|
||||
nHeaderLineNo
|
||||
})
|
||||
: null;
|
||||
try {
|
||||
//Создаём таблицу в БД
|
||||
const shpFileDataTable = await dbConn.executeStored({
|
||||
connection: dbConn.connection,
|
||||
sName: "PKG_EXS_EXT_SHP.FILE_TABLE_ADD",
|
||||
inPrms: {
|
||||
NEXSEXTSHPFL: nShpFileRn,
|
||||
SSHEET: tbl.sSheetName,
|
||||
STBL: tbl.sTableName,
|
||||
NCOLS: nColumnsCount,
|
||||
...(sHeadersXML ? { CHEADERS: sHeadersXML } : {}),
|
||||
NLINES: nLinesCount
|
||||
},
|
||||
outPrms: { NEXSEXTSHPFLTB: dbConn.connector.DT_NUMBER }
|
||||
});
|
||||
//Сохраняем данные таблицы для дальнейшей загрузки строк
|
||||
dataTables.push({
|
||||
sSheetName: tbl.sSheetName,
|
||||
sDataSheetName: tbl.sDataSheetName || tbl.sSheetName,
|
||||
sTableName: tbl.sTableName,
|
||||
nTableRn: shpFileDataTable.NEXSEXTSHPFLTB,
|
||||
nColumnsCount,
|
||||
nLinesCount,
|
||||
aColumns,
|
||||
aLines,
|
||||
nHeaderLineNo
|
||||
});
|
||||
} catch (e) {
|
||||
//Ошибка регистрации таблицы
|
||||
throw new Error(
|
||||
`Ошибка сохранения описания таблицы "${tbl.sTableName || "<НЕ УКАЗАНА>"}" листа "${tbl.sSheetName || "<НЕ УКАЗАН>"}": ${e.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
//Теперь загружаем таблицы - обходим их последовательно
|
||||
for (const dataTable of dataTables) {
|
||||
//Буфер строк для порционной загрузки
|
||||
let impBuff = [];
|
||||
//Сброс буфера
|
||||
const flushBuff = async () => {
|
||||
//Если буфер пуст - нечего сохранять
|
||||
if (impBuff.length === 0) return;
|
||||
//Преобразуем буфер в XML
|
||||
let sXML = null;
|
||||
try {
|
||||
//Сборка XML для порции строк
|
||||
sXML = toXML({ rootName: "lines", data: impBuff });
|
||||
} catch (e) {
|
||||
//Ошибка сборки XML
|
||||
throw new Error(`Ошибка преобразования буфера в XML: ${e.message}.`);
|
||||
}
|
||||
//Сохраняем на сервере
|
||||
const nLineFrom = impBuff[0].line.$.n;
|
||||
const nLineTo = impBuff[impBuff.length - 1].line.$.n;
|
||||
try {
|
||||
//Сохраняем порцию строк в БД
|
||||
await dbConn.executeStored({
|
||||
connection: dbConn.connection,
|
||||
sName: "PKG_EXS_EXT_SHP.FILE_TABLE_LINE_ADD",
|
||||
inPrms: {
|
||||
NEXSEXTSHPFLTB: dataTable.nTableRn,
|
||||
NLN_FROM: nLineFrom,
|
||||
NLN_TO: nLineTo,
|
||||
BDT: Buffer.from(sXML)
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
//Ошибка сохранения порции строк
|
||||
throw new Error(
|
||||
`Ошибка сохранения строк (c ${nLineFrom} - по ${nLineTo}) таблицы "${dataTable.sTableName || "<НЕ УКАЗАНА>"}" листа "${dataTable.sSheetName || "<НЕ УКАЗАН>"}": ${e.message}`
|
||||
);
|
||||
}
|
||||
//Очищаем буфер после успешной отправки
|
||||
impBuff = [];
|
||||
};
|
||||
//Получаем объект листа
|
||||
const sheet = workbook.Sheets[dataTable.sDataSheetName || dataTable.sSheetName];
|
||||
//Получаем dense-данные листа (строки/ячейки)
|
||||
const sheetData = sheet?.["!data"] || [];
|
||||
//Создаём резолвер объединённых ячеек
|
||||
const { hasMerges, resolveCell } = createMergedCellResolver(sheet);
|
||||
//Формируем список строк (или берём все)
|
||||
const aLines = dataTable.aLines ? dataTable.aLines : buildAllIndexList({ nMax: sheetData.length });
|
||||
//Список колонок (или null - значит все)
|
||||
const aColumns = dataTable.aColumns;
|
||||
//Предыдущая строка для контроля непрерывности (NLN_FROM/NLN_TO)
|
||||
let prevLineNo = null;
|
||||
for (const lineNo of aLines) {
|
||||
//Защита от невалидных индексов
|
||||
if (!Number.isFinite(lineNo) || lineNo < 1 || lineNo > sheetData.length) continue;
|
||||
//Если это строка заголовков - не отправляем её как данные
|
||||
if (Number.isFinite(dataTable.nHeaderLineNo) && lineNo === dataTable.nHeaderLineNo) continue;
|
||||
//Если есть разрыв диапазона строк - сбрасываем буфер (важно для NLN_FROM/NLN_TO)
|
||||
if (prevLineNo !== null && lineNo !== prevLineNo + 1) await flushBuff();
|
||||
//Получаем строку листа
|
||||
const sheetLine = sheetData[lineNo - 1] || [];
|
||||
//Формируем ячейки строки с учётом фильтра колонок и объединений
|
||||
const cells = aColumns
|
||||
? aColumns.map(colNo =>
|
||||
//Для выбранной колонки получаем ячейку (с учётом объединения) и приводим к формату импорта
|
||||
makeImportCell({
|
||||
//Резолвим ячейку по исходной колонке
|
||||
cl: resolveCell(lineNo - 1, colNo - 1, (sheetLine || [])[colNo - 1]),
|
||||
//Пишем номер исходной колонки
|
||||
n: colNo
|
||||
})
|
||||
)
|
||||
: (() => {
|
||||
//Определяем количество колонок для строки (в режиме объединений берём из описания таблицы)
|
||||
const nCols = hasMerges ? dataTable.nColumnsCount : (sheetLine || []).length;
|
||||
//Готовим массив ячеек нужной длины
|
||||
const res = new Array(nCols);
|
||||
//Обходим все колонки строки
|
||||
for (let c = 0; c < nCols; c++) {
|
||||
//Резолвим ячейку (с учётом объединения) и приводим к формату импорта
|
||||
res[c] = makeImportCell({
|
||||
//Резолвим ячейку по исходной колонке
|
||||
cl: resolveCell(lineNo - 1, c, (sheetLine || [])[c]),
|
||||
//Пишем номер исходной колонки
|
||||
n: c + 1
|
||||
});
|
||||
}
|
||||
//Возвращаем массив ячеек строки
|
||||
return res;
|
||||
})();
|
||||
//Добавляем строку в буфер импорта
|
||||
impBuff.push({
|
||||
line: {
|
||||
$: { n: lineNo },
|
||||
cell: cells
|
||||
}
|
||||
});
|
||||
//Запоминаем предыдущую строку
|
||||
prevLineNo = lineNo;
|
||||
//Если набрали порцию - отправляем
|
||||
if (impBuff.length === NIMPORT_BUFF_LEN) await flushBuff();
|
||||
}
|
||||
//Сохраняем остаток буфера
|
||||
await flushBuff();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
//Любая ошибка импорта
|
||||
throw new Error(e.message || "Неожиданная ошибка.");
|
||||
}
|
||||
}
|
||||
//Не нашли запись файла данных
|
||||
else throw new Error("Файл данных не определен.");
|
||||
}
|
||||
//Не смогли считать рег. номер из тела сообщения
|
||||
else throw new Error("Некорректный идентификатор файла данных для разбора.");
|
||||
}
|
||||
//Тело сообщения не задано
|
||||
else throw new Error("Нет данных для разбора.");
|
||||
//Обработку закончили - продолжать не надо, мы всё сделали в обработчике
|
||||
return { bStopPropagation: true };
|
||||
};
|
||||
|
||||
//-----------------
|
||||
// Интерфейс модуля
|
||||
//-----------------
|
||||
|
||||
exports.importBefore = importBefore;
|
||||
16
modules/shp_utils/node_modules/.bin/xlsx
generated
vendored
Normal file
16
modules/shp_utils/node_modules/.bin/xlsx
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||
|
||||
case `uname` in
|
||||
*CYGWIN*|*MINGW*|*MSYS*)
|
||||
if command -v cygpath > /dev/null 2>&1; then
|
||||
basedir=`cygpath -w "$basedir"`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -x "$basedir/node" ]; then
|
||||
exec "$basedir/node" "$basedir/../xlsx/bin/xlsx.njs" "$@"
|
||||
else
|
||||
exec node "$basedir/../xlsx/bin/xlsx.njs" "$@"
|
||||
fi
|
||||
17
modules/shp_utils/node_modules/.bin/xlsx.cmd
generated
vendored
Normal file
17
modules/shp_utils/node_modules/.bin/xlsx.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
@ECHO off
|
||||
GOTO start
|
||||
:find_dp0
|
||||
SET dp0=%~dp0
|
||||
EXIT /b
|
||||
:start
|
||||
SETLOCAL
|
||||
CALL :find_dp0
|
||||
|
||||
IF EXIST "%dp0%\node.exe" (
|
||||
SET "_prog=%dp0%\node.exe"
|
||||
) ELSE (
|
||||
SET "_prog=node"
|
||||
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||
)
|
||||
|
||||
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\xlsx\bin\xlsx.njs" %*
|
||||
28
modules/shp_utils/node_modules/.bin/xlsx.ps1
generated
vendored
Normal file
28
modules/shp_utils/node_modules/.bin/xlsx.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env pwsh
|
||||
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||
|
||||
$exe=""
|
||||
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||
# Fix case when both the Windows and Linux builds of Node
|
||||
# are installed in the same directory
|
||||
$exe=".exe"
|
||||
}
|
||||
$ret=0
|
||||
if (Test-Path "$basedir/node$exe") {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "$basedir/node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
|
||||
} else {
|
||||
& "$basedir/node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
} else {
|
||||
# Support pipeline input
|
||||
if ($MyInvocation.ExpectingInput) {
|
||||
$input | & "node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
|
||||
} else {
|
||||
& "node$exe" "$basedir/../xlsx/bin/xlsx.njs" $args
|
||||
}
|
||||
$ret=$LASTEXITCODE
|
||||
}
|
||||
exit $ret
|
||||
19
modules/shp_utils/node_modules/.package-lock.json
generated
vendored
Normal file
19
modules/shp_utils/node_modules/.package-lock.json
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "shp_utils",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.20.3",
|
||||
"resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
361
modules/shp_utils/node_modules/xlsx/CHANGELOG.md
generated
vendored
Normal file
361
modules/shp_utils/node_modules/xlsx/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,361 @@
|
||||
# CHANGELOG
|
||||
|
||||
This log is intended to keep track of backwards-incompatible changes, including
|
||||
but not limited to API changes and file location changes. Minor behavioral
|
||||
changes may not be included if they are not expected to break existing code.
|
||||
|
||||
## v0.20.3
|
||||
|
||||
* Correct parsing of NUMBERS and ODS merge cells (h/t @s-ashwin)
|
||||
* More precise treatment of infinite and NaN values
|
||||
* XLML Streaming Write
|
||||
* Parse `Int8Array` objects (for compatibility with JS engines in Java)
|
||||
* CSV Export only quote leading ID (h/t @lako12)
|
||||
|
||||
## v0.20.2
|
||||
|
||||
* Reworked parsing methods to avoid slow regexes (CVE-2024-22363)
|
||||
* HTML properly encode data-v attribute
|
||||
* SYLK read and write error cells
|
||||
|
||||
## v0.20.1
|
||||
|
||||
* `init` use packaged test files to work around GitHub breaking changes
|
||||
* SSF date code rounding to 15 decimal digits (h/t @davidtamaki)
|
||||
* `sheet_to_json` force UTC interpretation for formatted strings (h/t @Blanay)
|
||||
* QPW extract result of string formula
|
||||
* XLSX parse non-compliant merge cell expressions
|
||||
* NUMBERS correctly handle rows omitted from official exports
|
||||
* DBF parse empty logical field (h/t @Roman91)
|
||||
* `dense` option added to types
|
||||
* package.json add mini and core scripts to export map (h/t @stof)
|
||||
|
||||
## v0.20.0
|
||||
|
||||
* Use UTC interpretation of Date objects for date cells (potentially breaking)
|
||||
* API functions support UTC and local time value interpretations
|
||||
* Export `NaN` values to `#NUM!` and infinite values to `#DIV/0!`
|
||||
|
||||
## v0.19.3
|
||||
|
||||
* XLSX Ensure comment address is valid (h/t @slonser)
|
||||
* Enforce Excel worksheet name restrictions
|
||||
* Fixed "Prototype Pollution" vulnerability (CVE-2023-30533)
|
||||
|
||||
## v0.19.2
|
||||
|
||||
* XLSX proper decoding of hyperlinks (h/t @tw-yaxu)
|
||||
* XLSX ignore unexpected attributes in rich text (h/t @colin4)
|
||||
* `sheet_to_json` type fix (h/t @chsdwn)
|
||||
|
||||
## v0.19.1
|
||||
|
||||
* Fixed types issue in strict mode (h/t @younes-io)
|
||||
* Numbers 12.2 parsing skip ActivityStream.iwa
|
||||
|
||||
## v0.19.0
|
||||
|
||||
* XLSX export hyperlinks compatible with google sheets (h/t Evan Bovie)
|
||||
* NUMBERS export multiple sheets, full worksheet range
|
||||
* formalized `dense` mode
|
||||
|
||||
## v0.18.12
|
||||
|
||||
* `package.json` added types in `exports` structure
|
||||
* uncapped NUMBERS single-sheet single-table export
|
||||
* DBF export records using supported codepages
|
||||
|
||||
## v0.18.11
|
||||
|
||||
* Base64 input ignore data URI wrapper
|
||||
* Parse ZIP files that use ZIP64 extended information field
|
||||
* More precise handling of time-only values
|
||||
* Threaded Comment fallback text for older Excel
|
||||
|
||||
## v0.18.10
|
||||
|
||||
* `exports` field in package.json to satiate ViteJS and newer tooling
|
||||
* JSC (Safari / Bun) perf, see <https://bugs.webkit.org/show_bug.cgi?id=243148>
|
||||
* workbook `bookType` property to denote the origin format when parsed from file
|
||||
* XLSX force export of stub cells with number formats when `sheetStubs` is set
|
||||
|
||||
## v0.18.9
|
||||
|
||||
* XLSX / ODS write defined names
|
||||
* sync defined names to AutoFilter setting on export
|
||||
* 1904 date system setting properly roundtripped
|
||||
* ODS read/write number formats
|
||||
|
||||
## v0.18.8
|
||||
|
||||
* Plaintext parsing of dateless meridien time values (`1:23:45 PM`)
|
||||
* Legacy format (SYLK / WK# / Multiplan) minutiae
|
||||
|
||||
## v0.18.7
|
||||
|
||||
* Normalized handling of `\r` and `\n` newline characters
|
||||
|
||||
## v0.18.6
|
||||
|
||||
* Removed all npm dependencies
|
||||
* Auto-correct bad Google Sheets format `d.m`
|
||||
* NUMBERS write merge cells, cells up to column "ALL"
|
||||
|
||||
## v0.18.5
|
||||
|
||||
* Enabled `sideEffects: false` in package.json
|
||||
* Basic NUMBERS write support
|
||||
|
||||
## v0.18.4
|
||||
|
||||
* CSV output omits trailing record separator
|
||||
* Properly terminate NodeJS Streams
|
||||
* DBF preserve column types on import and use when applicable on export
|
||||
|
||||
## v0.18.3
|
||||
|
||||
* Removed references to `require` and `process` in browser builds
|
||||
|
||||
## v0.18.2
|
||||
|
||||
* Hotfix for unicode processing of XLSX exports
|
||||
|
||||
## v0.18.1
|
||||
|
||||
* Removed Node ESM build script and folded into standard ESM build
|
||||
* Removed undocumented aliases including `make_formulae` and `get_formulae`
|
||||
|
||||
## v0.18.0
|
||||
|
||||
* Browser scripts only expose `XLSX` variable
|
||||
* Module no longer ships with `dist/jszip.js` browser script
|
||||
|
||||
## v0.17.4
|
||||
|
||||
* CLI script moved to `xlsx-cli` package
|
||||
|
||||
## v0.17.3
|
||||
|
||||
* `window.XLSX` explicit assignment to satiate LWC
|
||||
* CSV Proper formatting of errors
|
||||
* HTML emit data-\* attributes
|
||||
|
||||
## v0.17.2
|
||||
|
||||
* Browser and Node optional ESM support
|
||||
* DSV correct handling of bare quotes (h/t @bgamrat)
|
||||
|
||||
## v0.17.1
|
||||
|
||||
* `XLSB` writer uses short cell form when viable
|
||||
|
||||
## 0.17.0:
|
||||
|
||||
* mini build includes ODS parse/write support
|
||||
* DBF explicitly cap worksheet to 1<<20 rows
|
||||
* XLS throw errors on truncated records
|
||||
|
||||
## v0.16.2
|
||||
|
||||
* Disabled `PRN` parsing by default (better support for CSV without delimeters)
|
||||
|
||||
## v0.16.1
|
||||
|
||||
* skip empty custom property tags if data is absent (fixes DocSecurity issue)
|
||||
* HTML output add raw value, type, number format
|
||||
* DOM parse look for `v` / `t` / `z` attributes when determining value
|
||||
* double quotes in properties escaped using `_x0022_`
|
||||
* changed AMD structure for NetSuite and other RequireJS implementations
|
||||
- `encode_cell` and `decode_cell` do not rely on `encode_col` / `decode_col`
|
||||
|
||||
## v0.16.0
|
||||
|
||||
* Date handling changed
|
||||
* XLML certain tag tests are now case insensitive
|
||||
* Fixed potentially vulnerable regular expressions
|
||||
|
||||
## v0.15.6
|
||||
|
||||
* CFB prevent infinite loop
|
||||
* ODS empty cells marked as stub (type "z")
|
||||
* `cellStyles` option implies `sheetStubs`
|
||||
|
||||
## v0.15.5
|
||||
|
||||
* `sheets` parse option to specify which sheets to parse
|
||||
|
||||
## v0.15.4
|
||||
|
||||
* AOA utilities properly preserve number formats
|
||||
* Number formats captured in stub cells
|
||||
|
||||
## v0.15.3
|
||||
|
||||
* Properties and Custom Properties properly XML-encoded
|
||||
|
||||
## v0.15.2
|
||||
|
||||
- `sheet_get_cell` utility function
|
||||
- `sheet_to_json` explicitly support `null` as alias for default behavior
|
||||
- `encode_col` throw on negative column index
|
||||
- HTML properly handle whitespace around tags in a run
|
||||
- HTML use `id` option on write
|
||||
- Files starting with `0x09` followed by a display character are now TSV files
|
||||
- XLS parse references col/row indices mod by the correct number for BIFF ver
|
||||
- XLSX comments moved to avoid overlapping cell
|
||||
- XLSB outline level
|
||||
- AutoFilter update `_FilterDatabase` defined name on write
|
||||
- XLML skip CDATA blocks
|
||||
|
||||
## v0.15.1 (2019-08-14)
|
||||
|
||||
* XLSX ignore XML artifacts
|
||||
* HTML capture and persist merges
|
||||
|
||||
## v0.15.0
|
||||
|
||||
* `dist/xlsx.mini.min.js` mini build with XLSX read/write and some utilities
|
||||
* Removed legacy conversion utility functions
|
||||
|
||||
## v0.14.5
|
||||
|
||||
* XLS PtgNameX lookup
|
||||
* XLS always create stub cells for blank cells with comments
|
||||
|
||||
|
||||
## v0.14.4
|
||||
|
||||
* Better treatment of `skipHidden` in CSV output
|
||||
* Ignore CLSID in XLS
|
||||
* SYLK 7-bit character encoding
|
||||
* SYLK and DBF codepage support
|
||||
|
||||
## v0.14.3
|
||||
|
||||
* Proper shifting of addresses in Shared Formulae
|
||||
|
||||
## v0.14.2
|
||||
|
||||
* Proper XML encoding of comments
|
||||
|
||||
## v0.14.1
|
||||
|
||||
* raw cell objects can be passed to `sheet_add_aoa`
|
||||
* `_FilterDatabase` fix for AutoFilter-related crashes
|
||||
* `stream.to_json` doesn't end up accidentally scanning to max row
|
||||
|
||||
## 0.14.0 (2018-09-06)
|
||||
|
||||
* `sheet_to_json` default flipped to `raw: true`
|
||||
|
||||
## 0.13.5 (2018-08-25)
|
||||
|
||||
* HTML output generates `<br/>` instead of encoded newline character
|
||||
|
||||
## 0.13.2 (2018-07-08)
|
||||
|
||||
* Buffer.from shim replaced, will not be defined in node `<=0.12`
|
||||
|
||||
## 0.13.0 (2018-06-01)
|
||||
|
||||
* Library reshaped to support AMD out of the box
|
||||
|
||||
## 0.12.11 (2018-04-27)
|
||||
|
||||
* XLS/XLSX/XLSB range truncation (errors in `WTF` mode)
|
||||
|
||||
## 0.12.4 (2018-03-04)
|
||||
|
||||
* `JSZip` renamed to `JSZipSync`
|
||||
|
||||
## 0.12.0 (2018-02-08)
|
||||
|
||||
* Extendscript target script in NPM package
|
||||
|
||||
## 0.11.19 (2018-02-03)
|
||||
|
||||
* Error on empty workbook
|
||||
|
||||
## 0.11.16 (2017-12-30)
|
||||
|
||||
* XLS ANSI/CP separation
|
||||
* 'array' write type and ArrayBuffer processing
|
||||
|
||||
## 0.11.6 (2017-10-16)
|
||||
|
||||
* Semicolon-delimited files are detected
|
||||
|
||||
## 0.11.5 (2017-09-30)
|
||||
|
||||
* Bower main script shifted to full version
|
||||
* 'binary' / 'string' encoding
|
||||
|
||||
## 0.11.3 (2017-08-19)
|
||||
|
||||
* XLS cell ixfe/XF removed
|
||||
|
||||
## 0.11.0 (2017-07-31)
|
||||
|
||||
* Strip `require` statements from minified version
|
||||
* minifier mangler enabled
|
||||
|
||||
## 0.10.9 (2017-07-28)
|
||||
|
||||
* XLML/HTML resolution logic looks further into the data stream to decide type
|
||||
* Errors thrown on suspected RTF files
|
||||
|
||||
## 0.10.5 (2017-06-09)
|
||||
|
||||
* HTML Table output header/footer should not include `<table>` tag
|
||||
|
||||
## 0.10.2 (2017-05-16)
|
||||
|
||||
* Dates are converted to numbers by default (set `cellDates:true` to emit Dates)
|
||||
* Module does not export CFB
|
||||
|
||||
## 0.9.10 (2017-04-08)
|
||||
|
||||
* `--perf` renamed to `--read-only`
|
||||
|
||||
## 0.9.9 (2017-04-03)
|
||||
|
||||
* default output format changed to XLSB
|
||||
* comment text line endings are now normalized
|
||||
* errors thrown on write when worksheets have invalid names
|
||||
|
||||
## 0.9.7 (2017-03-28)
|
||||
|
||||
* XLS legacy `!range` field removed
|
||||
* Hyperlink tooltip is stored in the `Tooltip` field
|
||||
|
||||
## 0.9.6 (2017-03-25)
|
||||
|
||||
* `sheet_to_json` now passes `null` values when `raw` is set to `true`
|
||||
* `sheet_to_json` treats `null` stub cells as values in conjunction with `raw`
|
||||
|
||||
## 0.9.5 (2017-03-22)
|
||||
|
||||
* `cellDates` affects parsing in non-XLSX formats
|
||||
|
||||
## 0.9.3 (2017-03-15)
|
||||
|
||||
* XLML property names are more closely mapped to the XLSX equivalent
|
||||
* Stub cells are now cell type `z`
|
||||
|
||||
## 0.9.2 (2017-03-13)
|
||||
|
||||
* Removed stale TypeScript definition files. Flowtype comments are used in the
|
||||
`xlsx.flow.js` source and stripped to produce `xlsx.js`.
|
||||
* sed usage reworked to support GNU sed in-place form. BSD sed seems to work,
|
||||
but the build script has not been tested on other sed variants:
|
||||
|
||||
```bash
|
||||
$ sed -i.ext [...] # GNU
|
||||
$ sed -i .ext [...] # bsd
|
||||
```
|
||||
|
||||
## 0.9.0 (2017-03-09)
|
||||
|
||||
* Removed ods.js source. The xlsx.js source absorbed the ODS logic and exposes
|
||||
the ODS variable, so projects should remove references to ods.js
|
||||
|
||||
201
modules/shp_utils/node_modules/xlsx/LICENSE
generated
vendored
Normal file
201
modules/shp_utils/node_modules/xlsx/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2012-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
41
modules/shp_utils/node_modules/xlsx/README.md
generated
vendored
Normal file
41
modules/shp_utils/node_modules/xlsx/README.md
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# [SheetJS](https://sheetjs.com)
|
||||
|
||||
The SheetJS Community Edition offers battle-tested open-source solutions for
|
||||
extracting useful data from almost any complex spreadsheet and generating new
|
||||
spreadsheets that will work with legacy and modern software alike.
|
||||
|
||||
[SheetJS Pro](https://sheetjs.com/pro) offers solutions beyond data processing:
|
||||
Edit complex templates with ease; let out your inner Picasso with styling; make
|
||||
custom sheets with images/graphs/PivotTables; evaluate formula expressions and
|
||||
port calculations to web apps; automate common spreadsheet tasks, and much more!
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API and Usage Documentation](https://docs.sheetjs.com)
|
||||
|
||||
- [Downloadable Scripts and Modules](https://cdn.sheetjs.com)
|
||||
|
||||
## Constellation
|
||||
|
||||
- <https://oss.sheetjs.com/notes/>: File Format Notes
|
||||
|
||||
- [`ssf`](packages/ssf): Format data using ECMA-376 spreadsheet format codes
|
||||
|
||||
- [`xlsx-cli`](packages/xlsx-cli): NodeJS command-line tool for processing files
|
||||
|
||||
- [`cfb`](https://git.sheetjs.com/SheetJS/js-cfb): Container (OLE/ZIP) file
|
||||
processing library
|
||||
|
||||
- [`codepage`](https://git.sheetjs.com/SheetJS/js-codepage): Legacy text
|
||||
encodings for XLS and other legacy spreadsheet formats
|
||||
|
||||
- [`dta`](packages/dta): Stata DTA file processor
|
||||
|
||||
- [`test_files`](https://github.com/sheetjs/test_files): Test files and various
|
||||
plaintext baselines.
|
||||
|
||||
## License
|
||||
|
||||
Please consult the attached LICENSE file for details. All rights not explicitly
|
||||
granted by the Apache 2.0 License are reserved by the Original Author.
|
||||
|
||||
310
modules/shp_utils/node_modules/xlsx/bin/xlsx.njs
generated
vendored
Normal file
310
modules/shp_utils/node_modules/xlsx/bin/xlsx.njs
generated
vendored
Normal file
@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env node
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
/* eslint-env node */
|
||||
/* vim: set ts=2 ft=javascript: */
|
||||
var n = "xlsx";
|
||||
var X = require('../');
|
||||
try { X = require('../xlsx.flow'); } catch(e) {}
|
||||
try { require('exit-on-epipe'); } catch(e) {}
|
||||
var fs = require('fs'), program;
|
||||
try { program = require('commander'); } catch(e) {
|
||||
[
|
||||
"The `xlsx` command line tool is deprecated in favor of `xlsx-cli`.",
|
||||
"",
|
||||
"For new versions of node, we recommend using `npx`:",
|
||||
" $ npx xlsx-cli --help",
|
||||
"",
|
||||
"For older versions of node, explicitly install `xlsx-cli` globally:",
|
||||
" $ npm i -g xlsx-cli",
|
||||
" $ xlsx-cli --help"
|
||||
].forEach(function(m) { console.error(m); });
|
||||
process.exit(1);
|
||||
}
|
||||
program
|
||||
.version(X.version)
|
||||
.usage('[options] <file> [sheetname]')
|
||||
.option('-f, --file <file>', 'use specified workbook')
|
||||
.option('-s, --sheet <sheet>', 'print specified sheet (default first sheet)')
|
||||
.option('-N, --sheet-index <idx>', 'use specified sheet index (0-based)')
|
||||
.option('-p, --password <pw>', 'if file is encrypted, try with specified pw')
|
||||
.option('-l, --list-sheets', 'list sheet names and exit')
|
||||
.option('-o, --output <file>', 'output to specified file')
|
||||
|
||||
.option('-B, --xlsb', 'emit XLSB to <sheetname> or <file>.xlsb')
|
||||
.option('-M, --xlsm', 'emit XLSM to <sheetname> or <file>.xlsm')
|
||||
.option('-X, --xlsx', 'emit XLSX to <sheetname> or <file>.xlsx')
|
||||
.option('-I, --xlam', 'emit XLAM to <sheetname> or <file>.xlam')
|
||||
.option('-Y, --ods', 'emit ODS to <sheetname> or <file>.ods')
|
||||
.option('-8, --xls', 'emit XLS to <sheetname> or <file>.xls (BIFF8)')
|
||||
.option('-5, --biff5','emit XLS to <sheetname> or <file>.xls (BIFF5)')
|
||||
.option('-4, --biff4','emit XLS to <sheetname> or <file>.xls (BIFF4)')
|
||||
.option('-3, --biff3','emit XLS to <sheetname> or <file>.xls (BIFF3)')
|
||||
.option('-2, --biff2','emit XLS to <sheetname> or <file>.xls (BIFF2)')
|
||||
.option('-i, --xla', 'emit XLA to <sheetname> or <file>.xla')
|
||||
.option('-6, --xlml', 'emit SSML to <sheetname> or <file>.xls (2003 XML)')
|
||||
.option('-T, --fods', 'emit FODS to <sheetname> or <file>.fods (Flat ODS)')
|
||||
.option('--wk3', 'emit WK3 to <sheetname> or <file>.txt (Lotus WK3)')
|
||||
.option('--numbers', 'emit NUMBERS to <sheetname> or <file>.numbers')
|
||||
|
||||
.option('-S, --formulae', 'emit list of values and formulae')
|
||||
.option('-j, --json', 'emit formatted JSON (all fields text)')
|
||||
.option('-J, --raw-js', 'emit raw JS object (raw numbers)')
|
||||
.option('-A, --arrays', 'emit rows as JS objects (raw numbers)')
|
||||
.option('-H, --html', 'emit HTML to <sheetname> or <file>.html')
|
||||
.option('-D, --dif', 'emit DIF to <sheetname> or <file>.dif (Lotus DIF)')
|
||||
.option('-U, --dbf', 'emit DBF to <sheetname> or <file>.dbf (MSVFP DBF)')
|
||||
.option('-K, --sylk', 'emit SYLK to <sheetname> or <file>.slk (Excel SYLK)')
|
||||
.option('-P, --prn', 'emit PRN to <sheetname> or <file>.prn (Lotus PRN)')
|
||||
.option('-E, --eth', 'emit ETH to <sheetname> or <file>.eth (Ethercalc)')
|
||||
.option('-t, --txt', 'emit TXT to <sheetname> or <file>.txt (UTF-8 TSV)')
|
||||
.option('-r, --rtf', 'emit RTF to <sheetname> or <file>.txt (Table RTF)')
|
||||
.option('--wk1', 'emit WK1 to <sheetname> or <file>.txt (Lotus WK1)')
|
||||
.option('-z, --dump', 'dump internal representation as JSON')
|
||||
.option('--props', 'dump workbook properties as CSV')
|
||||
|
||||
.option('-F, --field-sep <sep>', 'CSV field separator', ",")
|
||||
.option('-R, --row-sep <sep>', 'CSV row separator', "\n")
|
||||
.option('-n, --sheet-rows <num>', 'Number of rows to process (0=all rows)')
|
||||
.option('--codepage <cp>', 'default to specified codepage when ambiguous')
|
||||
.option('--req <module>', 'require module before processing')
|
||||
.option('--sst', 'generate shared string table for XLS* formats')
|
||||
.option('--compress', 'use compression when writing XLSX/M/B and ODS')
|
||||
.option('--read', 'read but do not generate output')
|
||||
.option('--book', 'for single-sheet formats, emit a file per worksheet')
|
||||
.option('--all', 'parse everything; write as much as possible')
|
||||
.option('--dev', 'development mode')
|
||||
.option('--sparse', 'sparse mode')
|
||||
.option('-q, --quiet', 'quiet mode');
|
||||
|
||||
program.on('--help', function() {
|
||||
console.log(' Default output format is CSV');
|
||||
console.log(' Support email: dev@sheetjs.com');
|
||||
console.log(' Web Demo: http://oss.sheetjs.com/js-'+n+'/');
|
||||
});
|
||||
|
||||
/* flag, bookType, default ext */
|
||||
var workbook_formats = [
|
||||
['xlsx', 'xlsx', 'xlsx'],
|
||||
['xlsm', 'xlsm', 'xlsm'],
|
||||
['xlam', 'xlam', 'xlam'],
|
||||
['xlsb', 'xlsb', 'xlsb'],
|
||||
['xls', 'xls', 'xls'],
|
||||
['xla', 'xla', 'xla'],
|
||||
['biff5', 'biff5', 'xls'],
|
||||
['numbers', 'numbers', 'numbers'],
|
||||
['ods', 'ods', 'ods'],
|
||||
['fods', 'fods', 'fods'],
|
||||
['wk3', 'wk3', 'wk3']
|
||||
];
|
||||
var wb_formats_2 = [
|
||||
['xlml', 'xlml', 'xls']
|
||||
];
|
||||
program.parse(process.argv);
|
||||
|
||||
var filename = '', sheetname = '';
|
||||
if(program.args[0]) {
|
||||
filename = program.args[0];
|
||||
if(program.args[1]) sheetname = program.args[1];
|
||||
}
|
||||
if(program.sheet) sheetname = program.sheet;
|
||||
if(program.file) filename = program.file;
|
||||
|
||||
if(!filename) {
|
||||
console.error(n + ": must specify a filename");
|
||||
process.exit(1);
|
||||
}
|
||||
if(!fs.existsSync(filename)) {
|
||||
console.error(n + ": " + filename + ": No such file or directory");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if(program.req) program.req.split(",").forEach(function(r) {
|
||||
require((fs.existsSync(r) || fs.existsSync(r + '.js')) ? require('path').resolve(r) : r);
|
||||
});
|
||||
|
||||
var opts = {}, wb/*:?Workbook*/;
|
||||
if(program.listSheets) opts.bookSheets = true;
|
||||
if(program.sheetRows) opts.sheetRows = program.sheetRows;
|
||||
if(program.password) opts.password = program.password;
|
||||
var seen = false;
|
||||
function wb_fmt() {
|
||||
seen = true;
|
||||
opts.cellFormula = true;
|
||||
opts.cellNF = true;
|
||||
opts.xlfn = true;
|
||||
if(program.output) sheetname = program.output;
|
||||
}
|
||||
function isfmt(m/*:string*/)/*:boolean*/ {
|
||||
if(!program.output) return false;
|
||||
var t = m.charAt(0) === "." ? m : "." + m;
|
||||
return program.output.slice(-t.length) === t;
|
||||
}
|
||||
workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
|
||||
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) { wb_fmt(); } });
|
||||
if(seen) {
|
||||
} else if(program.formulae) opts.cellFormula = true;
|
||||
else opts.cellFormula = false;
|
||||
|
||||
var wopts = ({WTF:opts.WTF, bookSST:program.sst}/*:any*/);
|
||||
if(program.compress) wopts.compression = true;
|
||||
|
||||
if(program.all) {
|
||||
opts.cellFormula = true;
|
||||
opts.bookVBA = true;
|
||||
opts.cellNF = true;
|
||||
opts.cellHTML = true;
|
||||
opts.cellStyles = true;
|
||||
opts.sheetStubs = true;
|
||||
opts.cellDates = true;
|
||||
wopts.cellFormula = true;
|
||||
wopts.cellStyles = true;
|
||||
wopts.sheetStubs = true;
|
||||
wopts.bookVBA = true;
|
||||
}
|
||||
if(program.sparse) opts.dense = false; else opts.dense = true;
|
||||
if(program.codepage) opts.codepage = +program.codepage;
|
||||
|
||||
if(program.dev) {
|
||||
opts.WTF = true;
|
||||
wb = X.readFile(filename, opts);
|
||||
} else try {
|
||||
wb = X.readFile(filename, opts);
|
||||
} catch(e) {
|
||||
var msg = (program.quiet) ? "" : n + ": error parsing ";
|
||||
msg += filename + ": " + e;
|
||||
console.error(msg);
|
||||
process.exit(3);
|
||||
}
|
||||
if(program.read) process.exit(0);
|
||||
if(!wb) { console.error(n + ": error parsing " + filename + ": empty workbook"); process.exit(0); }
|
||||
/*:: if(!wb) throw new Error("unreachable"); */
|
||||
if(program.listSheets) {
|
||||
console.log((wb.SheetNames||[]).join("\n"));
|
||||
process.exit(0);
|
||||
}
|
||||
if(program.dump) {
|
||||
console.log(JSON.stringify(wb));
|
||||
process.exit(0);
|
||||
}
|
||||
if(program.props) {
|
||||
if(wb) dump_props(wb);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
/* full workbook formats */
|
||||
workbook_formats.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
|
||||
wopts.bookType = m[1];
|
||||
if(wopts.bookType == "numbers") try {
|
||||
var XLSX_ZAHL = require("../dist/xlsx.zahl");
|
||||
wopts.numbers = XLSX_ZAHL;
|
||||
} catch(e) {}
|
||||
if(wb) X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts);
|
||||
process.exit(0);
|
||||
} });
|
||||
|
||||
wb_formats_2.forEach(function(m) { if(program[m[0]] || isfmt(m[0])) {
|
||||
wopts.bookType = m[1];
|
||||
if(wb) X.writeFile(wb, program.output || sheetname || ((filename || "") + "." + m[2]), wopts);
|
||||
process.exit(0);
|
||||
} });
|
||||
|
||||
var target_sheet = sheetname || '';
|
||||
if(target_sheet === '') {
|
||||
if(+program.sheetIndex < (wb.SheetNames||[]).length) target_sheet = wb.SheetNames[+program.sheetIndex];
|
||||
else target_sheet = (wb.SheetNames||[""])[0];
|
||||
}
|
||||
|
||||
var ws;
|
||||
try {
|
||||
ws = wb.Sheets[target_sheet];
|
||||
if(!ws) {
|
||||
console.error("Sheet " + target_sheet + " cannot be found");
|
||||
process.exit(3);
|
||||
}
|
||||
} catch(e) {
|
||||
console.error(n + ": error parsing "+filename+" "+target_sheet+": " + e);
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
if(!program.quiet && !program.book) console.error(target_sheet);
|
||||
|
||||
/* single worksheet file formats */
|
||||
[
|
||||
['biff2', '.xls'],
|
||||
['biff3', '.xls'],
|
||||
['biff4', '.xls'],
|
||||
['sylk', '.slk'],
|
||||
['html', '.html'],
|
||||
['prn', '.prn'],
|
||||
['eth', '.eth'],
|
||||
['rtf', '.rtf'],
|
||||
['txt', '.txt'],
|
||||
['dbf', '.dbf'],
|
||||
['wk1', '.wk1'],
|
||||
['dif', '.dif']
|
||||
].forEach(function(m) { if(program[m[0]] || isfmt(m[1])) {
|
||||
wopts.bookType = m[0];
|
||||
if(program.book) {
|
||||
/*:: if(wb == null) throw new Error("Unreachable"); */
|
||||
wb.SheetNames.forEach(function(n, i) {
|
||||
wopts.sheet = n;
|
||||
X.writeFile(wb, (program.output || sheetname || filename || "") + m[1] + "." + i, wopts);
|
||||
});
|
||||
} else X.writeFile(wb, program.output || sheetname || ((filename || "") + m[1]), wopts);
|
||||
process.exit(0);
|
||||
} });
|
||||
|
||||
function outit(o, fn) { if(fn) fs.writeFileSync(fn, o); else console.log(o); }
|
||||
|
||||
function doit(cb) {
|
||||
/*:: if(!wb) throw new Error("unreachable"); */
|
||||
if(program.book) wb.SheetNames.forEach(function(n, i) {
|
||||
/*:: if(!wb) throw new Error("unreachable"); */
|
||||
outit(cb(wb.Sheets[n]), (program.output || sheetname || filename) + "." + i);
|
||||
});
|
||||
else outit(cb(ws), program.output);
|
||||
}
|
||||
|
||||
var jso = {};
|
||||
switch(true) {
|
||||
case program.formulae:
|
||||
doit(function(ws) { return X.utils.sheet_to_formulae(ws).join("\n"); });
|
||||
break;
|
||||
|
||||
case program.arrays: jso.header = 1;
|
||||
/* falls through */
|
||||
case program.rawJs: jso.raw = true;
|
||||
/* falls through */
|
||||
case program.json:
|
||||
doit(function(ws) { return JSON.stringify(X.utils.sheet_to_json(ws,jso)); });
|
||||
break;
|
||||
|
||||
default:
|
||||
if(!program.book) {
|
||||
var stream = X.stream.to_csv(ws, {FS:program.fieldSep||",", RS:program.rowSep||"\n"});
|
||||
if(program.output) stream.pipe(fs.createWriteStream(program.output));
|
||||
else stream.pipe(process.stdout);
|
||||
} else doit(function(ws) { return X.utils.sheet_to_csv(ws,{FS:program.fieldSep, RS:program.rowSep}); });
|
||||
break;
|
||||
}
|
||||
|
||||
function dump_props(wb/*:Workbook*/) {
|
||||
var propaoa = [];
|
||||
if(Object.assign && Object.entries) propaoa = Object.entries(Object.assign({}, wb.Props, wb.Custprops));
|
||||
else {
|
||||
var Keys/*:: :Array<string> = []*/, pi;
|
||||
if(wb.Props) {
|
||||
Keys = Object.keys(wb.Props);
|
||||
for(pi = 0; pi < Keys.length; ++pi) {
|
||||
if(Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
|
||||
}
|
||||
}
|
||||
if(wb.Custprops) {
|
||||
Keys = Object.keys(wb.Custprops);
|
||||
for(pi = 0; pi < Keys.length; ++pi) {
|
||||
if(Object.prototype.hasOwnProperty.call(Keys, Keys[pi])) propaoa.push([Keys[pi], Keys[/*::+*/Keys[pi]]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(X.utils.sheet_to_csv(X.utils.aoa_to_sheet(propaoa)));
|
||||
}
|
||||
22
modules/shp_utils/node_modules/xlsx/bower.json
generated
vendored
Normal file
22
modules/shp_utils/node_modules/xlsx/bower.json
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "js-xlsx",
|
||||
"homepage": "https://github.com/SheetJS/js-xlsx",
|
||||
"main": ["xlsx.js"],
|
||||
"ignore": [
|
||||
"bin",
|
||||
"bits",
|
||||
"misc",
|
||||
"**/.*"
|
||||
],
|
||||
"keywords": [
|
||||
"excel",
|
||||
"xls",
|
||||
"xml",
|
||||
"xlsx",
|
||||
"xlsm",
|
||||
"xlsb",
|
||||
"ods",
|
||||
"js-xls",
|
||||
"js-xlsx"
|
||||
]
|
||||
}
|
||||
201
modules/shp_utils/node_modules/xlsx/dist/LICENSE
generated
vendored
Normal file
201
modules/shp_utils/node_modules/xlsx/dist/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright (C) 2012-present SheetJS LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
39
modules/shp_utils/node_modules/xlsx/dist/cpexcel.d.ts
generated
vendored
Normal file
39
modules/shp_utils/node_modules/xlsx/dist/cpexcel.d.ts
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/* codepage.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
// TypeScript Version: 2.2
|
||||
|
||||
/** Codepage index type (integer or string representation) */
|
||||
export type CP$Index = number | string;
|
||||
|
||||
/* Individual codepage converter */
|
||||
export interface CP$Conv {
|
||||
enc: {[n: string]: number; };
|
||||
dec: {[n: number]: string; };
|
||||
}
|
||||
|
||||
/** Encode input type (string, array of characters, Buffer) */
|
||||
export type CP$String = string | string[] | Uint8Array;
|
||||
|
||||
/** Encode output / decode input type */
|
||||
export type CP$Data = string | number[] | Uint8Array;
|
||||
|
||||
/** General utilities */
|
||||
export interface CP$Utils {
|
||||
decode(cp: CP$Index, data: CP$Data): string;
|
||||
encode(cp: CP$Index, data: CP$String, opts?: any): CP$Data;
|
||||
hascp(n: number): boolean;
|
||||
magic: {[cp: string]: string};
|
||||
}
|
||||
|
||||
/* note: TS cannot export top-level indexer, hence default workaround */
|
||||
export interface CP$Module {
|
||||
/** Version string */
|
||||
version: string;
|
||||
|
||||
/** Utility Functions */
|
||||
utils: CP$Utils;
|
||||
|
||||
/** Codepage Converters */
|
||||
[cp: number]: CP$Conv;
|
||||
}
|
||||
export const cptable: CP$Module;
|
||||
export default cptable;
|
||||
1502
modules/shp_utils/node_modules/xlsx/dist/cpexcel.full.mjs
generated
vendored
Normal file
1502
modules/shp_utils/node_modules/xlsx/dist/cpexcel.full.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1506
modules/shp_utils/node_modules/xlsx/dist/cpexcel.js
generated
vendored
Normal file
1506
modules/shp_utils/node_modules/xlsx/dist/cpexcel.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
modules/shp_utils/node_modules/xlsx/dist/shim.min.js
generated
vendored
Normal file
2
modules/shp_utils/node_modules/xlsx/dist/shim.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
18
modules/shp_utils/node_modules/xlsx/dist/xlsx.core.min.js
generated
vendored
Normal file
18
modules/shp_utils/node_modules/xlsx/dist/xlsx.core.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.core.min.map
generated
vendored
Normal file
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.core.min.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
28259
modules/shp_utils/node_modules/xlsx/dist/xlsx.extendscript.js
generated
vendored
Normal file
28259
modules/shp_utils/node_modules/xlsx/dist/xlsx.extendscript.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24
modules/shp_utils/node_modules/xlsx/dist/xlsx.full.min.js
generated
vendored
Normal file
24
modules/shp_utils/node_modules/xlsx/dist/xlsx.full.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.full.min.map
generated
vendored
Normal file
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.full.min.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
10
modules/shp_utils/node_modules/xlsx/dist/xlsx.mini.min.js
generated
vendored
Normal file
10
modules/shp_utils/node_modules/xlsx/dist/xlsx.mini.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.mini.min.map
generated
vendored
Normal file
1
modules/shp_utils/node_modules/xlsx/dist/xlsx.mini.min.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
4
modules/shp_utils/node_modules/xlsx/dist/xlsx.zahl.js
generated
vendored
Normal file
4
modules/shp_utils/node_modules/xlsx/dist/xlsx.zahl.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
2
modules/shp_utils/node_modules/xlsx/dist/xlsx.zahl.mjs
generated
vendored
Normal file
2
modules/shp_utils/node_modules/xlsx/dist/xlsx.zahl.mjs
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
4
modules/shp_utils/node_modules/xlsx/dist/zahl.d.ts
generated
vendored
Normal file
4
modules/shp_utils/node_modules/xlsx/dist/zahl.d.ts
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/* zahl.d.ts (C) 2022-present SheetJS */
|
||||
// TypeScript Version: 2.2
|
||||
declare const XLSX_ZAHL_PAYLOAD: string;
|
||||
export default XLSX_ZAHL_PAYLOAD;
|
||||
183
modules/shp_utils/node_modules/xlsx/package.json
generated
vendored
Normal file
183
modules/shp_utils/node_modules/xlsx/package.json
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
{
|
||||
"name": "xlsx",
|
||||
"version": "0.20.3",
|
||||
"author": "sheetjs",
|
||||
"description": "SheetJS Spreadsheet data parser and writer",
|
||||
"keywords": [
|
||||
"excel",
|
||||
"xls",
|
||||
"xlsx",
|
||||
"xlsb",
|
||||
"xlsm",
|
||||
"ods",
|
||||
"csv",
|
||||
"dbf",
|
||||
"dif",
|
||||
"sylk",
|
||||
"office",
|
||||
"spreadsheet"
|
||||
],
|
||||
"bin": {
|
||||
"xlsx": "./bin/xlsx.njs"
|
||||
},
|
||||
"main": "xlsx.js",
|
||||
"module": "xlsx.mjs",
|
||||
"unpkg": "dist/xlsx.full.min.js",
|
||||
"jsdelivr": "dist/xlsx.full.min.js",
|
||||
"types": "types/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./xlsx.mjs",
|
||||
"require": "./xlsx.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./xlsx.mjs": {
|
||||
"import": "./xlsx.mjs",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./xlsx.js": {
|
||||
"require": "./xlsx.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.core.min": {
|
||||
"import": "./dist/xlsx.core.min.js",
|
||||
"require": "./dist/xlsx.core.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.core.min.js": {
|
||||
"import": "./dist/xlsx.core.min.js",
|
||||
"require": "./dist/xlsx.core.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.full.min": {
|
||||
"import": "./dist/xlsx.full.min.js",
|
||||
"require": "./dist/xlsx.full.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.full.min.js": {
|
||||
"import": "./dist/xlsx.full.min.js",
|
||||
"require": "./dist/xlsx.full.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.mini.min": {
|
||||
"import": "./dist/xlsx.mini.min.js",
|
||||
"require": "./dist/xlsx.mini.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.mini.min.js": {
|
||||
"import": "./dist/xlsx.mini.min.js",
|
||||
"require": "./dist/xlsx.mini.min.js",
|
||||
"types": "./types/index.d.ts"
|
||||
},
|
||||
"./dist/xlsx.zahl": {
|
||||
"import": "./dist/xlsx.zahl.mjs",
|
||||
"require": "./dist/xlsx.zahl.js",
|
||||
"types": "./dist/zahl.d.ts"
|
||||
},
|
||||
"./dist/xlsx.zahl.mjs": {
|
||||
"import": "./dist/xlsx.zahl.mjs",
|
||||
"types": "./dist/zahl.d.ts"
|
||||
},
|
||||
"./dist/xlsx.zahl.js": {
|
||||
"require": "./dist/xlsx.zahl.js",
|
||||
"types": "./dist/zahl.d.ts"
|
||||
},
|
||||
"./dist/cpexcel": {
|
||||
"import": "./dist/cpexcel.full.mjs",
|
||||
"require": "./dist/cpexcel.js",
|
||||
"types": "./dist/cpexcel.d.ts"
|
||||
},
|
||||
"./dist/cpexcel.js": {
|
||||
"require": "./dist/cpexcel.js",
|
||||
"types": "./dist/cpexcel.d.ts"
|
||||
},
|
||||
"./dist/cpexcel.full": {
|
||||
"import": "./dist/cpexcel.full.mjs",
|
||||
"require": "./dist/cpexcel.js",
|
||||
"types": "./dist/cpexcel.d.ts"
|
||||
},
|
||||
"./dist/cpexcel.full.mjs": {
|
||||
"import": "./dist/cpexcel.full.mjs",
|
||||
"types": "./dist/cpexcel.d.ts"
|
||||
}
|
||||
},
|
||||
"browser": {
|
||||
"buffer": false,
|
||||
"crypto": false,
|
||||
"stream": false,
|
||||
"process": false,
|
||||
"fs": false
|
||||
},
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sheetjs/uglify-js": "~2.7.3",
|
||||
"@types/node": "^8.5.9",
|
||||
"acorn": "7.4.1",
|
||||
"adler-32": "~1.3.1",
|
||||
"alex": "8.1.1",
|
||||
"blanket": "~1.2.3",
|
||||
"cfb": "~1.2.2",
|
||||
"codepage": "~1.15.0",
|
||||
"commander": "~2.17.1",
|
||||
"crc-32": "~1.2.2",
|
||||
"dtslint": "^0.1.2",
|
||||
"eslint": "7.23.0",
|
||||
"eslint-plugin-html": "^6.1.2",
|
||||
"eslint-plugin-json": "^2.1.2",
|
||||
"exit-on-epipe": "~1.0.1",
|
||||
"fflate": "^0.7.1",
|
||||
"jsdom": "~11.1.0",
|
||||
"markdown-spellcheck": "^1.3.1",
|
||||
"mocha": "~2.5.3",
|
||||
"sinon": "^1.17.7",
|
||||
"ssf": "~0.11.2",
|
||||
"typescript": "2.2.0",
|
||||
"wmf": "~1.0.1",
|
||||
"word": "~0.3.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.sheetjs.com/SheetJS/sheetjs"
|
||||
},
|
||||
"scripts": {
|
||||
"pretest": "npm run lint",
|
||||
"test": "npm run tests-only",
|
||||
"pretest-only": "git submodule init && git submodule update",
|
||||
"tests-only": "make travis",
|
||||
"build": "make",
|
||||
"lint": "make fullint",
|
||||
"dtslint": "dtslint types"
|
||||
},
|
||||
"config": {
|
||||
"blanket": {
|
||||
"pattern": "xlsx.js"
|
||||
}
|
||||
},
|
||||
"alex": {
|
||||
"allow": [
|
||||
"chinese",
|
||||
"special",
|
||||
"simple",
|
||||
"just",
|
||||
"crash",
|
||||
"wtf",
|
||||
"holes"
|
||||
]
|
||||
},
|
||||
"homepage": "https://sheetjs.com/",
|
||||
"files": [
|
||||
"CHANGELOG.md", "LICENSE", "README.md", "bower.json", "package.json", "xlsx.js", "xlsx.mjs", "xlsxworker.js",
|
||||
"bin/xlsx.njs",
|
||||
"dist/LICENSE", "dist/*.mjs", "dist/*.js", "dist/*.map", "dist/*.d.ts",
|
||||
"types/index.d.ts", "types/tsconfig.json"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://git.sheetjs.com/SheetJS/sheetjs/issues"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
}
|
||||
1065
modules/shp_utils/node_modules/xlsx/types/index.d.ts
generated
vendored
Normal file
1065
modules/shp_utils/node_modules/xlsx/types/index.d.ts
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
15
modules/shp_utils/node_modules/xlsx/types/tsconfig.json
generated
vendored
Normal file
15
modules/shp_utils/node_modules/xlsx/types/tsconfig.json
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"lib": [ "es5" ],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"strictNullChecks": false,
|
||||
"baseUrl": ".",
|
||||
"paths": { "xlsx": ["."] },
|
||||
"types": [],
|
||||
"noEmit": true,
|
||||
"strictFunctionTypes": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
28103
modules/shp_utils/node_modules/xlsx/xlsx.js
generated
vendored
Normal file
28103
modules/shp_utils/node_modules/xlsx/xlsx.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28228
modules/shp_utils/node_modules/xlsx/xlsx.mjs
generated
vendored
Normal file
28228
modules/shp_utils/node_modules/xlsx/xlsx.mjs
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14
modules/shp_utils/node_modules/xlsx/xlsxworker.js
generated
vendored
Normal file
14
modules/shp_utils/node_modules/xlsx/xlsxworker.js
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/* xlsx.js (C) 2013-present SheetJS -- http://sheetjs.com */
|
||||
importScripts('dist/shim.min.js');
|
||||
/* uncomment the next line for encoding support */
|
||||
importScripts('dist/cpexcel.js');
|
||||
importScripts('xlsx.js');
|
||||
postMessage({t:"ready"});
|
||||
|
||||
onmessage = function (evt) {
|
||||
var v;
|
||||
try {
|
||||
v = XLSX.read(evt.data.d, {type: evt.data.b, codepage: evt.data.c});
|
||||
postMessage({t:"xlsx", d:JSON.stringify(v)});
|
||||
} catch(e) { postMessage({t:"e",d:e.stack||e}); }
|
||||
};
|
||||
24
modules/shp_utils/package-lock.json
generated
Normal file
24
modules/shp_utils/package-lock.json
generated
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "shp_utils",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.20.3",
|
||||
"resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
modules/shp_utils/package.json
Normal file
5
modules/shp_utils/package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user