*
,
*
,
*
,
*
{time},
*
*/
const contentEl = rowEl.children[1] as Element;
const linkToEl = contentEl.firstElementChild.firstElementChild as HTMLAnchorElement;
let iconPath = '';
if (iconSVGEl) {
const iconSVGClassName = iconSVGEl.className.baseVal;
if (iconSVGClassName.includes('octicon-file-text') || iconSVGClassName.includes('octicon-file ')) {
iconPath = getFileIcon(linkToEl.innerText.toLowerCase());
} else if (iconSVGClassName.includes('octicon-file-directory')) {
const name = linkToEl.innerText.toLowerCase();
iconPath = getFolderIcon(name.split('/').shift());
} else if (iconSVGClassName.includes('octicon-file-submodule')) {
iconPath = getIconForFolder('submodules');
} else if (iconSVGClassName.includes('octicon-file-symlink-file')) {
iconPath = DEFAULT_FILE;
} else if (iconSVGClassName.includes('octicon-file-symlink-directory')) {
iconPath = DEFAULT_FILE;
} else {
console.error(`Unknown filetype: "${iconSVGClassName}", please report`);
return;
}
const x = mutate(() => {
iconSVGEl.outerHTML = `
})
`;
});
}
// else {
// console.error(`Error during parsing: "td.icon > svg.octoicon" doesnt exists for ${i}. row`);
// }
}
function update(e?: any) {
showIconsForSegments();
if (isCommit()) {
// showDiffIcon();
}
}
export function initGithub() {
// Update on fragment update
observe(QUERY_FILE_TABLE_ITEMS, {
add(rowEl) {
showRepoTreeIcons(rowEl);
},
});
update();
document.addEventListener('pjax:end', update); // Update on page change
}
================================================
FILE: packages/content/pages/GitLab.ts
================================================
import { getIconForFile, getIconForFolder, getIconForOpenFolder, getIconUrl } from '../utils/Icons';
import { isGitLabRepo } from '../utils/PageDetect';
import { mutate } from 'fastdom';
import { getFolderIcon, getFileIcon } from '../utils/Dev';
export const QUERY_TREE_ITEMS = '.tree-item';
function showRepoTreeIcons() {
const treeItems = document.querySelectorAll(QUERY_TREE_ITEMS);
for (let i = 0; i < treeItems.length; i++) {
/**
* [TR:
* [TD: [[I: icon], [A: [SPAN: name]]]],
* [TD: [SPAN: [A: message]]],
* [TD: [TIME: ago]]
* ]
*/
const itemEl = treeItems[i];
const newIconEl = document.createElement('img');
const iconAndNameEls = itemEl.firstElementChild!;
const iconEl = iconAndNameEls.firstElementChild!;
const nameEl = iconAndNameEls.lastElementChild as HTMLAnchorElement;
const name = nameEl.innerText.toLowerCase();
if (i === 0 && name === '..') {
continue;
}
const iconPath = nameEl.href.indexOf('/tree/') > 0 ? getFolderIcon(name) : getFileIcon(name);
mutate(() => {
newIconEl.setAttribute('src', getIconUrl(iconPath));
newIconEl.setAttribute('class', 'vscode-icon');
iconAndNameEls.replaceChild(newIconEl, iconEl);
});
}
}
function update(e?: any) {
if (isGitLabRepo()) {
showRepoTreeIcons();
}
}
export function initGitLab() {
update();
}
================================================
FILE: packages/content/pages/PasteBin.ts
================================================
import { getIconForPBSyntax, getIconUrl, DEFAULT_FILE } from '../utils/Icons';
import { isPastebinUserList, isPasteOpen } from '../utils/PageDetect';
import { mutate } from 'fastdom';
const QUERY_PASTEBIN_ITEMS = '.maintable>tbody>tr';
const QUERY_PASTEBIN_PASTE = '#code_buttons>span:last-child';
function showIconsForFiles() {
const pastes = document.querySelectorAll(QUERY_PASTEBIN_ITEMS);
// skip first tr, which is header
for (let i = 1; i < pastes.length; i++) {
/**
* [TR:
* [TD: [[IMAGE: icon], [A: name]]],
* [TD: added],
* [TD: expires],
* [TD: hits],
* [TD: [A: syntax]],
* [TD: ?]
* ]
*/
const item = pastes[i];
const iconAndNameEl = item.firstElementChild as HTMLTableDataCellElement;
const iconEl = iconAndNameEl.firstElementChild as HTMLImageElement;
const syntaxEl = item.childNodes[9] as HTMLAnchorElement;
const syntaxName = syntaxEl.innerText;
const iconPath = getIconForPBSyntax(syntaxName);
const newIconEl = document.createElement('img');
mutate(() => {
newIconEl.setAttribute('src', getIconUrl(iconPath));
newIconEl.setAttribute('class', 'vscode-icon vsi-pb');
iconAndNameEl.replaceChild(newIconEl, iconEl);
});
}
}
function showIconForPaste() {
// TODO:
}
const domLoaded = new Promise((resolve) => {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', resolve);
} else {
resolve(null);
}
});
function update(e?: any) {
if (isPastebinUserList) {
showIconsForFiles();
} else if (isPasteOpen) {
showIconForPaste();
}
}
export function initPasteBin() {
update();
}
================================================
FILE: packages/content/pages/SourceForge.ts
================================================
import { getIconForFile, getIconForFolder, getIconUrl } from '../utils/Icons';
import { isSourceForgeFiles } from '../utils/PageDetect';
import { mutate } from 'fastdom';
import { getFolderIcon, getFileIcon } from '../utils/Dev';
export const QUERY_SOURCEFORGE_ITEMS = '#files_list>tbody>tr';
function showIconsForFiles() {
const items = document.querySelectorAll(QUERY_SOURCEFORGE_ITEMS);
for (let i = 0; i < items.length; i++) {
const item = items[i];
const newIconEl = document.createElement('img');
newIconEl.setAttribute('class', 'vscode-icon sf-icon');
const iconAndNameEl = item.firstElementChild.firstElementChild as HTMLTableHeaderCellElement;
const isFolder = item.className.includes('folder');
if (isFolder) {
/**
* [TR:
* [TH: [A: [SVG: icon], [SPAN: folderName]]],
* [TD: [ABBR: date]],
* [TD: size],
* [TD: [DIV: Populated by JS], [DIV: [A: chart]]],
* ]
*/
const iconEl = iconAndNameEl.firstElementChild as SVGAElement;
const nameEl = iconAndNameEl.lastElementChild as HTMLAnchorElement;
const name = nameEl.innerText.toLowerCase();
const iconPath = getFolderIcon(name);
mutate(() => {
newIconEl.setAttribute('src', getIconUrl(iconPath));
iconAndNameEl.replaceChild(newIconEl, iconEl);
});
} else {
/**
* [TR:
* [TH: [A: [SVG: icon]]],
* [TD: [ABBR: date]],
* [TD: size],
* [TD: [DIV: Populated by JS], [DIV: [A: chart]]],
* ]
*/
const nameEl = iconAndNameEl.firstElementChild as HTMLAnchorElement;
const name = nameEl.innerText.toLowerCase();
const iconPath = getFileIcon(name);
mutate(() => {
newIconEl.setAttribute('src', getIconUrl(iconPath));
iconAndNameEl.insertBefore(newIconEl, nameEl);
});
}
}
}
function update(e?: any) {
if (isSourceForgeFiles()) {
showIconsForFiles();
}
}
export function initSourceForge() {
update();
}
================================================
FILE: packages/content/utils/Dev.ts
================================================
import * as VSCJS from 'vscode-icons-js';
export const getFolderIcon =
process.env.NODE_ENV === 'production'
? VSCJS.getIconForFolder
: (folderName: string): string => {
const folderIcon = VSCJS.getIconForFolder(folderName);
if (folderIcon === VSCJS.DEFAULT_FOLDER) {
save('FOLDER', folderName);
}
return folderIcon;
};
export const getFileIcon =
process.env.NODE_ENV === 'production'
? VSCJS.getIconForFile
: (filename: string): string => {
const fileIcon = VSCJS.getIconForFile(filename);
if (fileIcon === VSCJS.DEFAULT_FILE) {
save('FILE', filename);
}
return fileIcon;
};
type Items = {
folders: {
[folderName: string]: number;
};
files: {
[fileName: string]: number;
};
};
/**
* Save name of file/folder that doesn't have an icon
*/
export const save = (type: 'FOLDER' | 'FILE', name: string) => {
console.log('tracking: ', type, name);
const allItemsStr = localStorage.getItem('items');
if (allItemsStr) {
const items = JSON.parse(allItemsStr) as Items;
const names = type === 'FOLDER' ? items.folders : items.files;
if (name in names) {
names[name] = names[name] + 1;
} else {
names[name] = 1;
}
localStorage.setItem('items', JSON.stringify(items));
} else {
const items = { folders: {}, files: {} } as Items;
const names = type === 'FOLDER' ? items.folders : items.files;
names[name] = 1;
localStorage.setItem('items', JSON.stringify(items));
}
};
/**
* Load name of file/folder that doesn't have an icon
*/
export const load = (): Items => {
const allItemsStr = localStorage.getItem('items');
if (allItemsStr) {
return JSON.parse(allItemsStr);
} else {
return {
files: {},
folders: {},
};
}
};
================================================
FILE: packages/content/utils/Icons.ts
================================================
type NAME_TO_ICON = { [name: string]: string };
export {
getIconForFile,
getIconForFolder,
getIconForOpenFolder,
DEFAULT_FILE,
DEFAULT_FOLDER,
DEFAULT_FOLDER_OPENED,
DEFAULT_ROOT_OPENED,
DEFAULT_ROOT,
} from 'vscode-icons-js';
import { DEFAULT_FILE } from 'vscode-icons-js';
const PBSyntaxesToIcon = require('../data/PastebinSyntaxesToIcon.json') as NAME_TO_ICON;
/**
* Retrieve url of icon within chrome
*/
export const getIconUrl = (iconFileName: string) => chrome.runtime.getURL('icons/' + iconFileName);
/**
* Get icon for a pastebin syntaxes
* @desc list of supported syntaxes https://pastebin.com/languages
* @param syntaxName name of syntax to icon for
* @return icon filename
*/
export function getIconForPBSyntax(syntaxName: string) {
const syntaxIcon = PBSyntaxesToIcon[syntaxName];
if (syntaxIcon !== undefined && syntaxIcon !== '') {
return syntaxIcon;
}
return DEFAULT_FILE;
}
================================================
FILE: packages/content/utils/PageDetect.ts
================================================
import * as select from 'select-dom';
import { isRepo, utils } from 'github-url-detection';
export const isHistoryForFile = () => isRepo() && /^\/commits\/[0-9a-f]{5,40}\/.+/.test(utils.getRepoPath());
/**
* BitBucket related detections
*/
export const isBitBucketRepo = () => location.href.indexOf('bitbucket.org/') > 0;
/**
* GitLab related detections
*/
export const isGitLabRepo = () => select.exists('.tree-content-holder');
/**
* Pastebin related detections
*/
export const isPastebinUserList = () =>
location.href.indexOf('pastebin.com/u/') > 0 && select.exists('table.maintable');
export const isPasteOpen = () => select.exists('#code_frame2');
/**
* SourceForge related detections
*/
export const isSourceForgeFiles = () => select.exists('#files_list');
================================================
FILE: packages/content/utils/showIconsForHosting.ts
================================================
import { SupportedHostings } from '../../common/SupportedHostings';
import { initBitBucket } from '../pages/BitBucket';
import { initGistGithub } from '../pages/GistGitHub';
import { initGithub } from '../pages/GitHub';
import { initGitLab } from '../pages/GitLab';
import { initPasteBin } from '../pages/PasteBin';
import { initSourceForge } from '../pages/SourceForge';
/**
* @param host - will show icons on page for host
*/
export function showIconsForHosting(host: SupportedHostings) {
const funcsToShowIcons: { [H in SupportedHostings]: () => void } = {
bitbucket: initBitBucket,
githubgist: initGistGithub,
github: initGithub,
gitlab: initGitLab,
pastebin: initPasteBin,
sourceforge: initSourceForge,
};
funcsToShowIcons[host]();
}
================================================
FILE: packages/popup/Popup.tsx
================================================
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { LocalStorage } from '../common/LocalStorage';
import { sendMessage } from '../common/Messenger';
import { getHostData } from '../common/HostData';
type State = {
storage: LocalStorage;
isSomethingChanged: boolean;
};
type Props = {
storage: LocalStorage;
};
class Popup extends React.Component
{
constructor(props: Props) {
super(props);
this.state = {
storage: props.storage,
isSomethingChanged: false,
};
}
handleToggleClick = (hosting: keyof LocalStorage['showIcons']) => {
const prevStorage = this.state.storage;
const newStorage: LocalStorage = {
...prevStorage,
showIcons: {
...prevStorage.showIcons,
[hosting]: !prevStorage.showIcons[hosting],
},
};
sendMessage({ type: 'STORAGE_SET', storage: newStorage });
this.setState({
storage: newStorage,
isSomethingChanged: true,
});
};
handleResetButton = async () => {
const defaultState = (await sendMessage({ type: 'STORAGE_RESET' })) as LocalStorage;
this.setState({
storage: defaultState,
});
};
render() {
const hostings = Object.keys(this.props.storage.showIcons) as (keyof LocalStorage['showIcons'])[];
const changedText = this.state.isSomethingChanged ? (
In order to see changes on pages, please reload them using refresh button
) : null;
return (
display icons for:
{hostings.map((hosting, index) => {
const hostData = getHostData(hosting);
return (
);
})}
{changedText}
{/*
*/}
);
}
}
(async function () {
const storage = (await sendMessage({ type: 'STORAGE_GET' })) as LocalStorage;
ReactDOM.render(, document.getElementById('app') as HTMLDivElement);
})();
================================================
FILE: scripts/create-manifest.ts
================================================
/**
* Compress build/ folder to create archive which will be consumed by browser
*/
import { script } from './utils';
import { writeFileSync } from 'fs';
import { createChromeManifest } from '../packages/ManifestChrome';
import { createEdgeManifest } from '../packages/ManifestEdge';
import { createFirefoxManifest } from '../packages/ManifesFirefox';
if (process.env.BROWSER) {
script(__filename, `Creating manifest.json`, (_, exit) => {
let manifest;
switch(process.env.BROWSER) {
case 'EDGE': {
manifest = createEdgeManifest();
break;
}
case 'FIREFOX': {
manifest = createFirefoxManifest();
break;
}
case 'CHROME': {
manifest = createChromeManifest();
break;
}
default: {
throw new Error(`unknown BROWSER env '${process.env.BROWSER}'. Please use 'EDGE', 'FIREFOX' or 'CHROME'`);
}
}
const manifestJSON = JSON.stringify(manifest, null, 2);
writeFileSync('./build/manifest.json', manifestJSON);
exit();
});
} else {
throw new Error('No browser selected, please add BROWSER env');
}
================================================
FILE: scripts/make-dist-zip.script.ts
================================================
/**
* Compress build/ folder to create archive which will be consumed by browser
*/
import { script } from './utils';
import { createWriteStream } from 'fs';
import * as Archiver from 'archiver';
const manifest = require('../build/manifest.json');
const manifestVersion = manifest.version;
const archiveName = 'github-vsci';
if (process.env.BROWSER && ['CHROME', 'FIREFOX', 'EDGE'].includes(process.env.BROWSER)) {
const name = process.env.BROWSER.toLowerCase();
script(__filename, `Creating '${name}-${archiveName}-${manifestVersion}.zip' ready to upload to stores`, ({ log, Ch }, exit) => {
const distZip = createWriteStream(`${process.cwd()}/dist/${name}-${archiveName}-${manifestVersion}.zip`);
const archive = Archiver('zip', { zlib: { level: 9 } });
// On end, print total bytes
distZip.on('close', () => {
log(archive.pointer() + ' total bytes');
log(Ch.green(`> '${name}-${archiveName}-${manifestVersion}.zip' file created`));
exit();
});
archive.pipe(distZip);
archive.directory('./build', false);
archive.finalize();
});
} else {
throw new Error('Please, set BROWSER env to CHROME, FIREFOX or EDGE');
}
================================================
FILE: scripts/utils.ts
================================================
import * as Path from 'path';
import * as Ch from 'chalk';
const log = console.log;
export async function script(filename: string, desc: string, callback: (tools: { log: Console['log'], Ch: typeof Ch }, exit: (error?: any) => void) => Promise | void) {
const baseName = Path.basename(filename);
log(Ch.bgYellow(`(${baseName}) ${desc}`));
const sTime = Date.now();
try {
callback({ log, Ch }, (error?: any) => {
const diff = Date.now() - sTime;
if (error) {
console.log(`Execution time: ${Ch.bgRed(diff.toString() + 'ms')}`);
} else {
console.log(`Execution time: ${Ch.bgGreen(diff.toString() + 'ms')}`);
}
});
} catch (error) {
const diff = Date.now() - sTime;
console.log(`Execution time: ${Ch.bgRed(diff.toString() + 'ms')}`);
}
}
================================================
FILE: tests/packages/content/pages/BitBucket.test.ts
================================================
import { fetchRenderedDocument,SITE_RETRIEVE_TIMEOUT } from '../../../utils';
import { QUERY_FILE_TABLE_ROWS, QUERY_ICONS_TO_REPLACE } from '../../../../packages/content/pages/BitBucket';
describe('Test Bitbucket queries', () => {
it('Repo root', async () => {
const renderedDocument = await fetchRenderedDocument('https://bitbucket.org/pypa/distlib/src/master/');
expect(renderedDocument.querySelectorAll(QUERY_FILE_TABLE_ROWS).length).not.toBe(0);
expect(renderedDocument.querySelectorAll(QUERY_ICONS_TO_REPLACE).length).not.toBe(0);
},SITE_RETRIEVE_TIMEOUT);
it('Subfolder with filetable', async () => {
const renderedDocument = await fetchRenderedDocument('https://bitbucket.org/pypa/distlib/src/master/tests/');
expect(renderedDocument.querySelectorAll(QUERY_FILE_TABLE_ROWS).length).not.toBe(0);
expect(renderedDocument.querySelectorAll(QUERY_ICONS_TO_REPLACE).length).not.toBe(0);
},SITE_RETRIEVE_TIMEOUT);
it('Open file', async () => {
const renderedDocument = await fetchRenderedDocument(
'https://bitbucket.org/pypa/distlib/src/master/README.rst',
null
);
expect(renderedDocument.querySelectorAll(QUERY_FILE_TABLE_ROWS).length).toBe(0);
expect(renderedDocument.querySelectorAll(QUERY_ICONS_TO_REPLACE).length).toBe(0);
},SITE_RETRIEVE_TIMEOUT);
});
================================================
FILE: tests/packages/content/pages/GistGitHub.test.ts
================================================
import { fetchDocument,SITE_RETRIEVE_TIMEOUT } from '../../../utils';
import { QUERY_FILE_INFO } from '../../../../packages/content/pages/GistGitHub';
it('Test GistGitHub query', async () => {
const fetchedDocument = await fetchDocument('https://gist.github.com/spences10/5c492e197e95158809a83650ff97fc3a');
expect(fetchedDocument.querySelectorAll(QUERY_FILE_INFO).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
================================================
FILE: tests/packages/content/pages/GitHub.test.ts
================================================
/**
* @jest-environment jsdom
*/
import { fetchDocument, SITE_RETRIEVE_TIMEOUT } from '../../../utils';
import {
QUERY_FILE_TABLE_ITEMS,
QUERY_PATH_SEGMENTS,
QUERY_PJAX_CONTAINER,
QUERY_LAST_PATH_SEGMENT
} from '../../../../packages/content/pages/GitHub';
describe('Test Github queries', () => {
it('Repo root', async () => {
const fetchedDocument = await fetchDocument('https://github.com/dderevjanik/github-vscode-icons');
expect(fetchedDocument.querySelectorAll(QUERY_FILE_TABLE_ITEMS).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PATH_SEGMENTS).length).toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PJAX_CONTAINER).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_LAST_PATH_SEGMENT).length).toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
it('Subfolder with filetable', async () => {
const fetchedDocument = await fetchDocument(
'https://github.com/dderevjanik/github-vscode-icons/tree/master/packages/content/pages'
);
expect(fetchedDocument.querySelectorAll(QUERY_FILE_TABLE_ITEMS).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PATH_SEGMENTS).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PJAX_CONTAINER).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_LAST_PATH_SEGMENT).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
it('Open file', async () => {
const fetchedDocument = await fetchDocument(
'https://github.com/dderevjanik/github-vscode-icons/blob/master/packages/content/pages/GitHub.ts'
);
expect(fetchedDocument.querySelectorAll(QUERY_FILE_TABLE_ITEMS).length).toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PATH_SEGMENTS).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_PJAX_CONTAINER).length).not.toBe(0);
expect(fetchedDocument.querySelectorAll(QUERY_LAST_PATH_SEGMENT).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
});
================================================
FILE: tests/packages/content/pages/GitLab.test.ts
================================================
import { fetchRenderedDocument, SITE_RETRIEVE_TIMEOUT} from '../../../utils';
import { QUERY_TREE_ITEMS } from '../../../../packages/content/pages/GitLab';
describe('Test Gitlab queries', () => {
it('Repo root', async () => {
const renderedDocument = await fetchRenderedDocument('https://gitlab.com/pycqa/flake8', '.tree-table');
expect(renderedDocument.querySelectorAll(QUERY_TREE_ITEMS).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
it('Subfolder with filetable', async () => {
const renderedDocument = await fetchRenderedDocument(
'https://gitlab.com/pycqa/flake8/-/tree/master/src/flake8',
'.tree-table'
);
expect(renderedDocument.querySelectorAll(QUERY_TREE_ITEMS).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
it('Open file', async () => {
const renderedDocument = await fetchRenderedDocument(
'https://gitlab.com/pycqa/flake8/-/blob/master/src/flake8/__init__.py',
null
);
expect(renderedDocument.querySelectorAll(QUERY_TREE_ITEMS).length).toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
});
================================================
FILE: tests/packages/content/pages/SourceForge.test.ts
================================================
import { fetchDocument, SITE_RETRIEVE_TIMEOUT } from '../../../utils';
import { QUERY_SOURCEFORGE_ITEMS } from '../../../../packages/content/pages/SourceForge';
describe('Test SourceForge queries', () => {
it('Repo root', async () => {
const fetchedDocument = await fetchDocument('https://sourceforge.net/projects/python-fire.mirror/files/');
expect(fetchedDocument.querySelectorAll(QUERY_SOURCEFORGE_ITEMS).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
it('Subfolder with filetable', async () => {
const fetchedDocument = await fetchDocument('https://sourceforge.net/projects/python-fire.mirror/files/v0.3.1/');
expect(fetchedDocument.querySelectorAll(QUERY_SOURCEFORGE_ITEMS).length).not.toBe(0);
}, SITE_RETRIEVE_TIMEOUT);
});
================================================
FILE: tests/tsconfig.tests.json
================================================
{
"extends": "../tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */
}
}
================================================
FILE: tests/utils.ts
================================================
import { JSDOM } from 'jsdom';
import fetch from 'node-fetch';
import { table } from 'console';
export const SITE_RETRIEVE_TIMEOUT = 30000;
export const getElementFromSource = (elementSource: string): Document => {
const dom = new JSDOM(elementSource);
return dom.window.document;
// const parser = new DOMParser();
// return parser.parseFromString(elementSource, 'text/html').documentElement;
};
export const fetchDocument = async (url: string): Promise => {
const response = await fetch(url);
const pageSource = await response.text();
return getElementFromSource(pageSource);
};
export const fetchRenderedDocument = async (url: string, waitSelector: string | null = 'table'): Promise => {
await page.goto(url, { waitUntil: 'networkidle2' });
if (waitSelector !== null) {
await page.waitForSelector(waitSelector);
}
const renderedPageSource = await page.evaluate(() => {
const nrOfFoundTables = document.querySelector('html').outerHTML;
return nrOfFoundTables;
});
return getElementFromSource(renderedPageSource);
};
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
/* Basic Options */
"target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
"lib": ["dom", "es2015", "es6", "es7"] /* Specify library files to be included in the compilation: */,
"allowJs": true /* Allow javascript files to be compiled. */,
"checkJs": true /* Report errors in .js files. */,
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
"strictNullChecks": false /* Enable strict null checks. */,
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": ["node_modules/@types", "typings"] /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
// "skipLibCheck": true
},
"include": ["./packages/**/*.d.ts", "./scripts/**/*.ts"],
"exclude": ["node_modules"]
}
================================================
FILE: typings/fastdom/index.d.ts
================================================
declare module 'fastdom' {
/**
* Clears any scheduled job.
*/
export const clear: () => void;
/**
* Schedules a job for the 'measure' queue. Returns a unique ID that can be used to clear the scheduled job.
*/
export const measure: (callback: () => void) => void;
/**
* Schedules a job for the 'mutate' queue. Returns a unique ID that can be used to clear the scheduled job.
*/
export const mutate: (callback: () => void) => void;
export const extend: () => void;
}
================================================
FILE: typings/icons.d.ts
================================================
declare module '*icons.json' {
const json: {
iconDefinitions: { [iconKey: string]: { iconPath: string } };
folderNames: { [folderName: string]: string };
fileExtensions: {
[fileExtension: string]: string;
};
fileNames: { [fileName: string]: string };
languagesIds: { [languageId: string]: string };
light: {
folderNames: { [folderName: string]: string };
fileExtensions: {
[fileExtension: string]: string;
};
fileNames: { [fileName: string]: string };
languagesIds: { [languageId: string]: string };
};
};
export default json;
}
================================================
FILE: typings/languages-vscode.d.ts
================================================
declare module '*languages-vscode.json' {
const json: {
[languageId: string]: {
extensions: string[];
filenames: string[];
};
};
export default json;
}
================================================
FILE: typings/languages-vsi.d.ts
================================================
declare module '*languages-vsi.json' {
const json: {
[languageId: string]: {
defaultExtension: string;
};
};
export default json;
}
================================================
FILE: typings/select-dom/index.d.ts
================================================
declare module 'select-dom' {
/**
* Check if any element with inserted query exists
*/
export function exists(query: string): boolean;
}
================================================
FILE: webpack.config.js
================================================
const path = require('path');
const Webpack = require('webpack');
const baseConfig = {
devtool: 'source-map',
entry: {
content: './packages/content/Content.ts',
popup: './packages/popup/Popup.tsx',
background: './packages/background/Background.ts'
},
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].js'
},
devServer: {
contentBase: __dirname + '/public'
},
resolve: {
// Add '.ts' and '.tsx' as a resolvable extension.
extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js', '.json']
},
module: {
rules: [
// all files with a '.ts' or '.tsx' extension will be handled by 'ts-loader'
{
test: /\.tsx?$/,
use: 'ts-loader'
}
]
},
plugins: []
};
module.exports = baseConfig;