132 lines
4.3 KiB
JavaScript
132 lines
4.3 KiB
JavaScript
/**
|
|
* @typedef {function(string): boolean} GlyphCheck
|
|
*/
|
|
|
|
/**
|
|
* @type {GlyphCheck}
|
|
*/
|
|
const isAlphaLike = (inGlyph) => {
|
|
const inCode = inGlyph.charCodeAt(0);
|
|
|
|
if (inCode >= 97 && inCode <= 122) {
|
|
return true;
|
|
}
|
|
|
|
if (inCode >= 65 && inCode <= 90) {
|
|
return true;
|
|
}
|
|
|
|
return `$_.`.includes(inGlyph);
|
|
};
|
|
|
|
/**
|
|
* @type {GlyphCheck}
|
|
*/
|
|
const isWhiteSpace = (inGlyph) => `\n\r\t `.includes(inGlyph);
|
|
|
|
/**
|
|
* @type {GlyphCheck}
|
|
*/
|
|
const isQuote = (inGlyph) => `"'\``.includes(inGlyph);
|
|
|
|
/**
|
|
* @param {GlyphCheck} inCheck
|
|
* @returns {GlyphCheck}
|
|
*/
|
|
const isNot = (inCheck) => (inGlyph) => !inCheck(inGlyph);
|
|
|
|
/**
|
|
* @param {string} inText
|
|
* @param {number} inStart
|
|
* @param {GlyphCheck} inTest
|
|
* @returns {number}
|
|
*/
|
|
const contiguous = (inText, inStart, inTest) => {
|
|
let ok = true;
|
|
let index = inStart;
|
|
let count = 0;
|
|
while (ok && count < inText.length) {
|
|
count++;
|
|
ok = inTest(inText.charAt(index++));
|
|
}
|
|
return index - 1;
|
|
};
|
|
|
|
/**
|
|
* @param {string} inFile
|
|
* @param {number} [inIndex=0]
|
|
* @param {Array<{internal: string, external: string}>} inLocal
|
|
* @param {Array<string>} inForeign
|
|
* @returns {number|boolean}
|
|
*/
|
|
const findNextExport = (inFile, inIndex = 0, inLocal, inForeign) => {
|
|
const pos = inFile.indexOf("export", inIndex);
|
|
if (pos !== -1) {
|
|
if (!isAlphaLike(inFile.charAt(pos - 1)) || !isAlphaLike(inFile.charAt(pos + 6))) {
|
|
const nextCharInd = contiguous(inFile, pos + 6, isWhiteSpace);
|
|
const nextChar = inFile[nextCharInd];
|
|
|
|
if (nextChar === "*") {
|
|
const firstQuoteInd = contiguous(inFile, nextCharInd + 1, isNot(isQuote));
|
|
const secondQuoteInd = contiguous(inFile, firstQuoteInd + 1, isNot(isQuote));
|
|
inForeign.push(inFile.substring(nextCharInd, secondQuoteInd + 1));
|
|
} else if (nextChar == "{") {
|
|
const endBracketInd = contiguous(inFile, nextCharInd, (inGlyph) => inGlyph !== "}");
|
|
const nextLetterInd = contiguous(inFile, endBracketInd + 1, isWhiteSpace);
|
|
if (inFile.substring(nextLetterInd, nextLetterInd + 4) == "from") {
|
|
const firstQuoteInd = contiguous(inFile, nextLetterInd + 4, isNot(isQuote));
|
|
const secondQuoteInd = contiguous(inFile, firstQuoteInd + 1, isNot(isQuote));
|
|
inForeign.push(inFile.substring(nextCharInd, secondQuoteInd + 1));
|
|
} else {
|
|
const members = inFile.substring(nextCharInd + 1, endBracketInd).replaceAll(" as ", "|||").replace(/\s/g, '');
|
|
members.split(",").forEach(part => {
|
|
const renamed = part.split("|||");
|
|
inLocal.push({ internal: renamed, external: renamed || renamed });
|
|
});
|
|
}
|
|
} else if (isAlphaLike(nextChar)) {
|
|
const keywordEndInd = contiguous(inFile, nextCharInd, isAlphaLike);
|
|
const keyword = inFile.substring(nextCharInd, keywordEndInd);
|
|
if (keyword === "default") {
|
|
inLocal.push({ internal: keyword, external: keyword });
|
|
} else if (["const", "let", "var", "function", "class"].includes(keyword)) {
|
|
const varStartInd = contiguous(inFile, keywordEndInd + 1, isWhiteSpace);
|
|
const varEndInd = contiguous(inFile, varStartInd + 1, isAlphaLike);
|
|
const keyword = inFile.substring(varStartInd, varEndInd);
|
|
inLocal.push({ internal: keyword, external: keyword });
|
|
}
|
|
}
|
|
}
|
|
|
|
return pos + 7;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @param {string} inFile
|
|
* @returns {[local: string[], foreign: string[]]}
|
|
*/
|
|
export const Exports = (inFile) => {
|
|
let match = /** @type {number|boolean} */ (0);
|
|
let count = 0;
|
|
const local = /** @type {string[]} */ ([]);
|
|
const foreign = /** @type {string[]} */ ([]);
|
|
while (match !== false && count < 200) {
|
|
count++;
|
|
match = findNextExport(inFile, match, local, foreign);
|
|
}
|
|
return [local, foreign];
|
|
};
|
|
|
|
/**
|
|
* @param {string|URL} inURL
|
|
* @returns {Promise<[local: string[], foreign: string[]]>}
|
|
*/
|
|
export const FileExports = async (inURL) => {
|
|
const resp = await fetch(inURL);
|
|
const text = await resp.text();
|
|
return Exports(text);
|
|
};
|