Fix ESLint config (#38)

* Add support for Azure DevOps and BitBucket

* Remove `console.log`

* Fix ESLint config

- Fix the ESLint config
- Fix all lint errors that cropped up as a result
- Add scripts to lint
- Add prettier format script
- Add `husky` / `lint-staged` to lint/format files when they're pushed

* Move provider configs to new file as requested

* Fixes to meet maintainers specs

* Fix remaining lint warns/errs

* eslint fix

* Update eslint/prettier packges

Remove unused `@types/node-fetch` package

* Add `eslint-plugin-jsdoc`

* Add eslint rule for allowing providers to reassign params in their `replaceIcon` functions

* Add sensible defaults for prettier to override any local editor settings

* Some final cleanup

* Loosen the required engines to allow for easier installing

Co-authored-by: Claudio Santos <Claudiohbsantos@users.noreply.github.com>
This commit is contained in:
Chris Sandvik 2022-06-30 22:25:48 -04:00 committed by GitHub
parent b626e990aa
commit 05f9f8c057
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 2365 additions and 3812 deletions

2
.eslintignore Normal file
View File

@ -0,0 +1,2 @@
dist
node_modules

View File

@ -1,11 +1,38 @@
{ {
"extends": ["airbnb", "prettier"], "env": {
"plugins": ["prettier"], "browser": true,
"rules": { "es2021": true,
"prettier/prettier": ["error"] "node": true
}, },
"globals": { "globals": {
"window": true, "chrome": "readonly"
"document": true },
"extends": ["airbnb-base", "plugin:jsdoc/recommended", "prettier"],
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"no-use-before-define": ["error", { "functions": false }],
"jsdoc/require-jsdoc": "off"
},
"overrides": [
{
"files": ["scripts/*.js", "svgo.config.js"],
"parserOptions": {
"sourceType": "script"
},
"rules": {
"import/no-extraneous-dependencies": "off",
"import/no-dynamic-require": "off",
"no-console": "off"
} }
},
{
"files": ["src/providers/*.js"],
"rules": {
"no-param-reassign": "off"
}
}
]
} }

View File

@ -13,7 +13,7 @@ jobs:
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: "16.13.0" node-version: '16.13.0'
- name: Fetch release version - name: Fetch release version
id: upstream id: upstream
@ -64,4 +64,3 @@ jobs:
if: steps.upstream.outputs.release_tag != steps.upstream.outputs.current_tag if: steps.upstream.outputs.release_tag != steps.upstream.outputs.current_tag
continue-on-error: true continue-on-error: true
run: npx web-ext sign -s ./dist/firefox/ --channel=listed --api-key=${{ secrets.FIREFOX_API_JWT_ISSUER }} --api-secret=${{ secrets.FIREFOX_API_JWT_SECRET }} run: npx web-ext sign -s ./dist/firefox/ --channel=listed --api-key=${{ secrets.FIREFOX_API_JWT_ISSUER }} --api-secret=${{ secrets.FIREFOX_API_JWT_SECRET }}

1
.gitignore vendored
View File

@ -17,4 +17,5 @@ node_modules
.cache/ .cache/
.vscode/ .vscode/
.DS_Store .DS_Store
.eslintcache
Thumbs.db Thumbs.db

1
.husky/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
.cache
dist
src/*.json
!src/manifest.json

View File

@ -1,4 +1,13 @@
{ {
"printWidth": 100, "printWidth": 100,
"singleQuote": true "tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always",
"proseWrap": "preserve",
"endOfLine": "lf"
} }

5838
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,27 +14,29 @@
"url": "https://github.com/Claudiohbsantos/github-material-icons-extension/issues" "url": "https://github.com/Claudiohbsantos/github-material-icons-extension/issues"
}, },
"engines": { "engines": {
"node": "16.13.0", "node": "^16.0.0",
"npm": "8.1.0" "npm": "^8.0.0"
}, },
"dependencies": { "dependencies": {
"selector-observer": "2.1.6" "selector-observer": "2.1.6"
}, },
"devDependencies": { "devDependencies": {
"@octokit/core": "3.5.1", "@octokit/core": "3.5.1",
"@types/node-fetch": "2.5.11",
"compare-versions": "3.6.0", "compare-versions": "3.6.0",
"eslint": "7.29.0", "eslint": "8.18.0",
"eslint-config": "0.3.0", "eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb": "18.2.1", "eslint-config-prettier": "8.5.0",
"eslint-config-prettier": "8.3.0", "eslint-plugin-import": "2.26.0",
"eslint-plugin-prettier": "3.4.0", "eslint-plugin-jsdoc": "39.3.3",
"follow-redirects": "1.14.1", "follow-redirects": "1.14.1",
"fs-extra": "10.0.0", "fs-extra": "10.0.0",
"husky": "8.0.1",
"json-stable-stringify": "1.0.1",
"lint-staged": "13.0.3",
"node-fetch": "2.6.1", "node-fetch": "2.6.1",
"npm-run-all": "4.1.5", "npm-run-all": "4.1.5",
"parcel-bundler": "1.12.5", "parcel-bundler": "1.12.5",
"prettier": "2.3.1", "prettier": "2.7.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"sharp": "0.28.3", "sharp": "0.28.3",
"simple-git": "2.40.0", "simple-git": "2.40.0",
@ -59,6 +61,14 @@
"update-upstream-version": "node ./scripts/update-upstream-version", "update-upstream-version": "node ./scripts/update-upstream-version",
"update-package-version": "npm version --no-git-tag-version", "update-package-version": "npm version --no-git-tag-version",
"update": "run-s update-upstream-version \"update-package-version patch\" update-manifest-version build", "update": "run-s update-upstream-version \"update-package-version patch\" update-manifest-version build",
"release": "run-s \"update-package-version {1}\" update-manifest-version build --" "release": "run-s \"update-package-version {1}\" update-manifest-version build --",
"lint": "eslint .",
"lint-fix": "eslint --fix .",
"format": "prettier --write --ignore-unknown .",
"prepare": "husky install"
},
"lint-staged": {
"*.js": "eslint --fix",
"*": "prettier --write --ignore-unknown"
} }
} }

View File

@ -1,24 +1,16 @@
{ {
"extends": [ "extends": ["config:base"],
"config:base"
],
"lockFileMaintenance": { "lockFileMaintenance": {
"enabled": false "enabled": false
}, },
"packageRules": [ "packageRules": [
{ {
"packageNames": [ "packageNames": ["npm"],
"npm"
],
"enabled": false "enabled": false
}, },
{ {
"matchManagers": [ "matchManagers": ["npm"],
"npm" "matchDepTypes": ["devDependencies"],
],
"matchDepTypes": [
"devDependencies"
],
"enabled": false "enabled": false
} }
] ]

View File

@ -5,7 +5,7 @@ const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const rimraf = require('rimraf'); const rimraf = require('rimraf');
const simpleGit = require('simple-git'); const simpleGit = require('simple-git');
const child_process = require('child_process'); const { execSync } = require('child_process');
/** /**
* Internal depedencies * Internal depedencies
@ -13,7 +13,7 @@ const child_process = require('child_process');
const srcPath = path.resolve(__dirname, '..', 'src'); const srcPath = path.resolve(__dirname, '..', 'src');
const vsExtPath = path.resolve(__dirname, '..', 'temp'); const vsExtPath = path.resolve(__dirname, '..', 'temp');
const destSVGPath = path.resolve(__dirname, '..', 'svg'); const destSVGPath = path.resolve(__dirname, '..', 'svg');
const commitLockPath = path.resolve(__dirname, '..', 'upstream.commit') const commitLockPath = path.resolve(__dirname, '..', 'upstream.commit');
const vsExtExecOptions = { const vsExtExecOptions = {
cwd: vsExtPath, cwd: vsExtPath,
@ -27,7 +27,7 @@ const distIconsExecOptions = {
async function main() { async function main() {
rimraf.sync(vsExtPath); rimraf.sync(vsExtPath);
rimraf.sync(destSVGPath); rimraf.sync(destSVGPath);
await fs.ensureDir(destSVGPath) await fs.ensureDir(destSVGPath);
console.log('[1/7] Cloning PKief/vscode-material-icon-theme into temporary cache.'); console.log('[1/7] Cloning PKief/vscode-material-icon-theme into temporary cache.');
const git = simpleGit(); const git = simpleGit();
@ -36,25 +36,25 @@ async function main() {
'100', // fetch only last 100 commits. Guesswork, could be too shallow if upstream doesnt release often '100', // fetch only last 100 commits. Guesswork, could be too shallow if upstream doesnt release often
]); ]);
const commit = fs.readFileSync(commitLockPath, {encoding: 'utf8'})?.trim() const commit = fs.readFileSync(commitLockPath, { encoding: 'utf8' })?.trim();
console.log('Checking out to upstream commit:', commit) console.log('Checking out to upstream commit:', commit);
const upstreamGit = simpleGit(vsExtPath) const upstreamGit = simpleGit(vsExtPath);
await upstreamGit.checkout(commit, ['--force']) await upstreamGit.checkout(commit, ['--force']);
console.log('[2/7] Terminate Git repository in temporary cache.'); console.log('[2/7] Terminate Git repository in temporary cache.');
rimraf.sync(path.resolve(vsExtPath, '.git')); rimraf.sync(path.resolve(vsExtPath, '.git'));
console.log('[3/7] Install NPM dependencies for VSC extension.'); console.log('[3/7] Install NPM dependencies for VSC extension.');
child_process.execSync(`npm install --ignore-scripts`, vsExtExecOptions); execSync(`npm install --ignore-scripts`, vsExtExecOptions);
console.log('[4/7] Terminate Git tracking in temporary cache.'); console.log('[4/7] Terminate Git tracking in temporary cache.');
await fs.copy(path.resolve(vsExtPath, 'icons'), path.resolve(destSVGPath)); await fs.copy(path.resolve(vsExtPath, 'icons'), path.resolve(destSVGPath));
console.log('[5/7] Optimise extension icons using SVGO.'); console.log('[5/7] Optimise extension icons using SVGO.');
child_process.execSync(`npx svgo -r .`, distIconsExecOptions); execSync(`npx svgo -r .`, distIconsExecOptions);
console.log('[6/7] Run build tasks for VSC extension.'); console.log('[6/7] Run build tasks for VSC extension.');
child_process.execSync(`npm run build`, vsExtExecOptions); execSync(`npm run build`, vsExtExecOptions);
console.log('[7/7] Copy file icon configuration to source code directory.'); console.log('[7/7] Copy file icon configuration to source code directory.');
await fs.copy( await fs.copy(
@ -65,4 +65,4 @@ async function main() {
rimraf.sync(vsExtPath); rimraf.sync(vsExtPath);
} }
main() main();

View File

@ -21,13 +21,11 @@ fs.ensureDir(iconsPath).then(generateIcons);
* @since 1.4.0 * @since 1.4.0
*/ */
function generateIcons() { function generateIcons() {
targetSizes.map((size) => { targetSizes.forEach((size) => {
sharp(svgPath) sharp(svgPath)
.png() .png()
.resize({ width: size, height: size }) .resize({ width: size, height: size })
.toFile(`${iconsPath}/icon-${size}.png`) .toFile(`${iconsPath}/icon-${size}.png`)
.catch(function (err) { .catch(console.error);
console.log(err);
});
}); });
} }

View File

@ -4,9 +4,9 @@ const api = require('@octokit/core');
const fs = require('fs-extra'); const fs = require('fs-extra');
const fr = require('follow-redirects'); const fr = require('follow-redirects');
const glob = require('glob'); const glob = require('glob');
const stringify = require('json-stable-stringify');
const remap = require('./remap.json'); const remap = require('./remap.json');
const iconMap = require('../src/icon-map.json'); const iconMap = require('../src/icon-map.json');
const stringify = require('json-stable-stringify');
const vsDataPath = path.resolve(__dirname, '..', 'data'); const vsDataPath = path.resolve(__dirname, '..', 'data');
const srcPath = path.resolve(__dirname, '..', 'src'); const srcPath = path.resolve(__dirname, '..', 'src');
@ -20,7 +20,7 @@ const resultsPerPage = 100; // max 100
let index = 0; let index = 0;
let total; let total;
let items = []; let items = [];
let languages = []; const languages = [];
console.log('[1/7] Querying Github API for official VSC language contributions.'); console.log('[1/7] Querying Github API for official VSC language contributions.');
@ -42,7 +42,8 @@ const GITHUB_RATELIMIT = 6000;
octokit.request('GET /search/code', query).then( octokit.request('GET /search/code', query).then(
(res) => { (res) => {
if (!res.data) throw new Error(); if (!res.data) throw new Error();
query.page = index++; query.page = index;
index += 1;
total = total || res.data.total_count; total = total || res.data.total_count;
items = items.concat(res.data.items); items = items.concat(res.data.items);
if (resultsPerPage * index >= total) { if (resultsPerPage * index >= total) {
@ -69,7 +70,7 @@ function fetchLanguageContribution(item) {
const extPath = path.join(item.repository.name, resPath); const extPath = path.join(item.repository.name, resPath);
const extDir = path.dirname(extPath); const extDir = path.dirname(extPath);
if (/sample|template/.test(extDir)) { if (/sample|template/.test(extDir)) {
total--; total -= 1;
return; return;
} }
try { try {
@ -79,7 +80,7 @@ function fetchLanguageContribution(item) {
.get(urlPath, (res) => { .get(urlPath, (res) => {
res.pipe(extFile); res.pipe(extFile);
res.on('end', () => { res.on('end', () => {
index++; index += 1;
if (index === total) { if (index === total) {
console.log('[3/7] Loading VSC language contributions into Node.'); console.log('[3/7] Loading VSC language contributions into Node.');
glob(path.join(vsDataPath, '**', 'extension.json'), (err, matches) => { glob(path.join(vsDataPath, '**', 'extension.json'), (err, matches) => {
@ -92,7 +93,7 @@ function fetchLanguageContribution(item) {
}) })
.on('error', (err) => { .on('error', (err) => {
fs.unlink(extPath); fs.unlink(extPath);
throw new Error(err); throw err;
}); });
}); });
} catch (reason) { } catch (reason) {
@ -100,13 +101,13 @@ function fetchLanguageContribution(item) {
} }
} }
function loadLanguageContribution(path) { function loadLanguageContribution(filePath) {
try { try {
const data = JSON.parse(fs.readFileSync(path)); const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
data.contributes = data.contributes || {}; data.contributes = data.contributes || {};
data.contributes.languages = data.contributes.languages || []; data.contributes.languages = data.contributes.languages || [];
languages.push(...data.contributes.languages); languages.push(...data.contributes.languages);
index++; index += 1;
if (index === total) { if (index === total) {
console.log('[4/7] Processing language contributions for VSC File Icon API compatibility.'); console.log('[4/7] Processing language contributions for VSC File Icon API compatibility.');
index = 0; index = 0;
@ -114,22 +115,21 @@ function loadLanguageContribution(path) {
languages.forEach(processLanguageContribution); languages.forEach(processLanguageContribution);
} }
} catch (error) { } catch (error) {
throw new Error(`${error} (${path})`); throw new Error(`${error} (${filePath})`);
} }
} }
function processLanguageContribution(lang) { function processLanguageContribution(language) {
delete lang.aliases; const { aliases, configuration, firstLine, ...lang } = language;
delete lang.configuration;
delete lang.firstLine;
lang.extensions = lang.extensions || []; lang.extensions = lang.extensions || [];
lang.filenames = lang.filenames || []; lang.filenames = lang.filenames || [];
if (lang.filenamePatterns) { if (lang.filenamePatterns) {
lang.filenamePatterns.forEach((ptn) => { lang.filenamePatterns.forEach((ptn) => {
if (/^\*\.[^*\/\?]+$/.test(ptn)) { if (/^\*\.[^*/?]+$/.test(ptn)) {
lang.extensions.push(ptn.substring(1)); lang.extensions.push(ptn.substring(1));
} }
if (/^[^*\/\?]+$/.test(ptn)) { if (/^[^*/?]+$/.test(ptn)) {
lang.filenames.push(ptn); lang.filenames.push(ptn);
} }
}); });
@ -146,7 +146,7 @@ function processLanguageContribution(lang) {
.map((ext) => ext.substring(1)) .map((ext) => ext.substring(1))
.filter((ext) => !/\*|\/|\?/.test(ext)); .filter((ext) => !/\*|\/|\?/.test(ext));
lang.filenames = lang.filenames.filter((name) => !/\*|\/|\?/.test(name)); lang.filenames = lang.filenames.filter((name) => !/\*|\/|\?/.test(name));
index++; index += 1;
if (index === total) { if (index === total) {
console.log('[5/7] Mapping language contributions into file icon configuration.'); console.log('[5/7] Mapping language contributions into file icon configuration.');
index = 0; index = 0;
@ -162,55 +162,55 @@ const languageMap = {
function mapLanguageContribution(lang) { function mapLanguageContribution(lang) {
lang.extensions.forEach((ext) => { lang.extensions.forEach((ext) => {
let extIconName = lang.id; let extIconName = lang.id;
if (remap.extensions.hasOwnProperty(extIconName)) { if (remap.extensions[extIconName]) {
let overrideIcon = remap.extensions[extIconName]; const overrideIcon = remap.extensions[extIconName];
if (typeof overrideIcon === 'object') { if (typeof overrideIcon === 'object') {
for (const [ptn, override] in Object.entries(overrideIcon)) { Object.entries(overrideIcon).forEach(([ptn, override]) => {
if (ptn.startsWith('^') && ext.startsWith(ptn.substring(1))) { if (ptn.startsWith('^') && ext.startsWith(ptn.substring(1))) {
extIconName = override; extIconName = override;
} }
if (ptn.length && ext === ptn) { if (ptn.length && ext === ptn) {
extIconName = override; extIconName = override;
} }
} });
} }
} else { } else {
extIconName = iconMap.languageIds[extIconName] || extIconName; extIconName = iconMap.languageIds[extIconName] || extIconName;
} }
if ( if (
!remap.deletions[`extensions:${extIconName}`] && !remap.deletions[`extensions:${extIconName}`] &&
!iconMap.fileExtensions.hasOwnProperty(extIconName) && !iconMap.fileExtensions[extIconName] &&
iconMap.iconDefinitions.hasOwnProperty(extIconName) iconMap.iconDefinitions[extIconName]
) { ) {
languageMap.fileExtensions[ext] = extIconName; languageMap.fileExtensions[ext] = extIconName;
} }
}); });
lang.filenames.forEach((name) => { lang.filenames.forEach((name) => {
let fileIconName = lang.id; let fileIconName = lang.id;
if (remap.filenames.hasOwnProperty(fileIconName)) { if (remap.filenames[fileIconName]) {
let overrideIcon = remap.filenames[fileIconName]; const overrideIcon = remap.filenames[fileIconName];
if (typeof overrideIcon === 'object') { if (typeof overrideIcon === 'object') {
for (const [ptn, override] in Object.entries(overrideIcon)) { Object.entries(overrideIcon).forEach(([ptn, override]) => {
if (ptn.startsWith('^') && name.startsWith(ptn.substring(1))) { if (ptn.startsWith('^') && name.startsWith(ptn.substring(1))) {
fileIconName = override; fileIconName = override;
} }
if (ptn.length && name === ptn) { if (ptn.length && name === ptn) {
fileIconName = override; fileIconName = override;
} }
} });
} }
} else { } else {
fileIconName = iconMap.languageIds[fileIconName] || fileIconName; fileIconName = iconMap.languageIds[fileIconName] || fileIconName;
} }
if ( if (
!remap.deletions[`filenames:${fileIconName}`] && !remap.deletions[`filenames:${fileIconName}`] &&
!iconMap.fileNames.hasOwnProperty(fileIconName) && !iconMap.fileNames[fileIconName] &&
iconMap.iconDefinitions.hasOwnProperty(fileIconName) iconMap.iconDefinitions[fileIconName]
) { ) {
languageMap.fileNames[name] = fileIconName; languageMap.fileNames[name] = fileIconName;
} }
}); });
index++; index += 1;
if (index === total) { if (index === total) {
index = 0; index = 0;
generateLanguageMap(); generateLanguageMap();

View File

@ -1,14 +1,15 @@
const path = require('path'); const path = require('path');
const fs = require('fs-extra'); const fs = require('fs-extra');
const Parcel = require('parcel-bundler'); const Parcel = require('parcel-bundler');
const destSVGPath = path.resolve(__dirname, '..', 'svg'); const destSVGPath = path.resolve(__dirname, '..', 'svg');
const distBasePath = path.resolve(__dirname, '..', 'dist'); const distBasePath = path.resolve(__dirname, '..', 'dist');
const srcPath = path.resolve(__dirname, '..', 'src'); const srcPath = path.resolve(__dirname, '..', 'src');
/** Create icons cache. */ /** Create icons cache. */
function consolidateSVGFiles() { async function consolidateSVGFiles() {
console.log('[1/2] Generate icon cache for extension.'); console.log('[1/2] Generate icon cache for extension.');
return fs await fs
.copy(path.resolve(srcPath, 'custom'), destSVGPath) .copy(path.resolve(srcPath, 'custom'), destSVGPath)
.then(() => fs.readdir(destSVGPath)) .then(() => fs.readdir(destSVGPath))
.then((files) => Object.fromEntries(files.map((filename) => [filename, filename]))) .then((files) => Object.fromEntries(files.map((filename) => [filename, filename])))
@ -49,7 +50,8 @@ function buildManifest(distPath, manifestName) {
function buildDist(name, manifestName) { function buildDist(name, manifestName) {
const distPath = path.resolve(distBasePath, name); const distPath = path.resolve(distBasePath, name);
return fs.ensureDir(distPath) return fs
.ensureDir(distPath)
.then(consolidateSVGFiles) .then(consolidateSVGFiles)
.then(() => src(distPath)) .then(() => src(distPath))
.then(() => buildManifest(distPath, manifestName)) .then(() => buildManifest(distPath, manifestName))

View File

@ -11,10 +11,10 @@ const manifestPath = path.resolve(__dirname, '..', 'src', 'manifests', 'base.jso
const manifest = require(manifestPath); const manifest = require(manifestPath);
const updatedManifest = { ...manifest, version: package.version }; const updatedManifest = { ...manifest, version: package.version };
const updatedManifestStr = JSON.stringify(updatedManifest, null, 2) + '\n'; const updatedManifestStr = `${JSON.stringify(updatedManifest, null, 2)}\n`;
fs.writeFile(manifestPath, updatedManifestStr) fs.writeFile(manifestPath, updatedManifestStr)
.then(() => { .then(() => {
console.log(`Updated manifest.json version to ${package.version}`); console.log(`Updated manifest.json version to ${package.version}`);
}) })
.catch((err) => console.error(err)); .catch(console.error);

View File

@ -2,7 +2,7 @@ const fetch = require('node-fetch');
const path = require('path'); const path = require('path');
const api = require('@octokit/core'); const api = require('@octokit/core');
const compareVersions = require('compare-versions'); const compareVersions = require('compare-versions');
const fs = require('fs').promises; const fs = require('fs/promises');
const upstreamVersionFilePath = path.resolve(__dirname, '..', 'upstream.version'); const upstreamVersionFilePath = path.resolve(__dirname, '..', 'upstream.version');
const upstreamCommitFilePath = path.resolve(__dirname, '..', 'upstream.commit'); const upstreamCommitFilePath = path.resolve(__dirname, '..', 'upstream.commit');
@ -11,6 +11,8 @@ const upstreamCommitFilePath = path.resolve(__dirname, '..', 'upstream.commit');
* Gets latest VSCode Extension release version by parsing it's most recent 100 commit msgs * Gets latest VSCode Extension release version by parsing it's most recent 100 commit msgs
* *
* returns version string or undefined * returns version string or undefined
*
* @returns {Promise<string>} The current version of the upstream repository.
*/ */
const getUpstreamVersion = () => const getUpstreamVersion = () =>
fetch('https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/package.json') fetch('https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/main/package.json')
@ -19,9 +21,12 @@ const getUpstreamVersion = () =>
const octokit = new api.Octokit(); const octokit = new api.Octokit();
const getUpstreamCommit = () => const getUpstreamCommit = () =>
octokit.request('GET /repos/PKief/vscode-material-icon-theme/commits', {per_page: 1}).then(res => res.data?.[0].sha) octokit
.request('GET /repos/PKief/vscode-material-icon-theme/commits', { per_page: 1 })
.then((res) => res.data?.[0].sha);
const getLastUpstreamVersion = () => fs.readFile(upstreamVersionFilePath, { encoding: 'utf8' }).then(data => data.trim()); const getLastUpstreamVersion = () =>
fs.readFile(upstreamVersionFilePath, { encoding: 'utf8' }).then((data) => data.trim());
const updateReadmeBadge = async (version) => { const updateReadmeBadge = async (version) => {
const readmeFilePath = path.resolve(__dirname, '..', 'README.md'); const readmeFilePath = path.resolve(__dirname, '..', 'README.md');

View File

@ -6,19 +6,16 @@ import { observe } from 'selector-observer';
/** /**
* Internal depedencies. * Internal depedencies.
*/ */
import iconsList from './icon-list'; import iconsList from './icon-list.json';
import iconMap from './icon-map'; import iconMap from './icon-map.json';
import languageMap from './language-map'; import languageMap from './language-map.json';
import providerConfig from './providers'; import providerConfig from './providers';
// Expected configuration. // Expected configuration.
iconMap.options = { iconMap.options = {
...iconMap.options, ...iconMap.options,
...{
activeIconPack: 'react_redux', activeIconPack: 'react_redux',
// activeIconPack: 'nest', // TODO: implement interface to choose pack // activeIconPack: 'nest', // TODO: implement interface to choose pack
},
}; };
// replacing all icons synchronously prevents visual "blinks" but can // replacing all icons synchronously prevents visual "blinks" but can
@ -32,7 +29,7 @@ const rushFirst = (rushBatch, callback) => {
if (executions <= rushBatch) { if (executions <= rushBatch) {
callback(); // immediately run to prevent visual "blink" callback(); // immediately run to prevent visual "blink"
setTimeout(callback, 20); // run again later to catch any icons that are missed in large repositories setTimeout(callback, 20); // run again later to catch any icons that are missed in large repositories
executions++; executions += 1;
} else { } else {
setTimeout(callback, 0); // run without blocking to prevent delayed rendering of large folders too much setTimeout(callback, 0); // run without blocking to prevent delayed rendering of large folders too much
clearTimeout(timerID); clearTimeout(timerID);
@ -74,7 +71,7 @@ const gitProvider = getGitProvider();
if (gitProvider) { if (gitProvider) {
observe(gitProvider.selectors.row, { observe(gitProvider.selectors.row, {
add(row) { add(row) {
const callback = () => replaceIcon(row, iconMap, languageMap, gitProvider); const callback = () => replaceIcon(row, gitProvider);
rushFirst(90, callback); rushFirst(90, callback);
@ -86,13 +83,11 @@ if (gitProvider) {
/** /**
* Replace file/folder icons. * Replace file/folder icons.
* *
* @param {String} itemRow Item Row. * @param {HTMLElement} itemRow Item Row.
* @param {Object} iconMap Icon Map. * @param {object} provider Git Provider specs.
* @param {Object} languageMap Language Map. * @returns {undefined}
* @param {Object} provider Git Provider specs.
* @return {undefined}
*/ */
function replaceIcon(itemRow, iconMap, languageMap, provider) { function replaceIcon(itemRow, provider) {
const isLightTheme = provider.getIsLightTheme(); const isLightTheme = provider.getIsLightTheme();
// Get file/folder name. // Get file/folder name.
@ -119,18 +114,16 @@ function replaceIcon(itemRow, iconMap, languageMap, provider) {
fileExtension, fileExtension,
isDir, isDir,
isSubmodule, isSubmodule,
isSymlink, isSymlink
iconMap,
languageMap
); // returns icon name if found or undefined. ); // returns icon name if found or undefined.
if (isLightTheme) { if (isLightTheme) {
iconName = lookForLightMatch(iconName, fileName, fileExtension, isDir, iconMap); // returns icon name if found for light mode or undefined. iconName = lookForLightMatch(iconName, fileName, fileExtension, isDir); // returns icon name if found for light mode or undefined.
} }
// Get folder icon from active icon pack. // Get folder icon from active icon pack.
if (iconMap.options.activeIconPack) { if (iconMap.options.activeIconPack) {
iconName = lookForIconPackMatch(lowerFileName, iconMap) ?? iconName; iconName = lookForIconPackMatch(lowerFileName) ?? iconName;
} }
if (!iconName) return; if (!iconName) return;
@ -145,26 +138,15 @@ function replaceIcon(itemRow, iconMap, languageMap, provider) {
* Lookup for matched file/folder icon name. * Lookup for matched file/folder icon name.
* *
* @since 1.0.0 * @since 1.0.0
* * @param {string} fileName File name.
* @param {String} fileName File name. * @param {string} lowerFileName Lowercase file name.
* @param {String} lowerFileName Lowercase file name. * @param {string} fileExtension File extension.
* @param {String} fileExtension File extension. * @param {boolean} isDir Check if directory type.
* @param {Boolean} isDir Check if directory type. * @param {boolean} isSubmodule Check if submodule type.
* @param {Boolean} isSubmodule Check if submodule type. * @param {boolean} isSymlink Check if symlink
* @param {Boolean} isSymlink Check if symlink * @returns {string} The matched icon name.
* @param {Object} iconMap Icon map.
* @returns {String} The matched icon name.
*/ */
function lookForMatch( function lookForMatch(fileName, lowerFileName, fileExtension, isDir, isSubmodule, isSymlink) {
fileName,
lowerFileName,
fileExtension,
isDir,
isSubmodule,
isSymlink,
iconMap,
languageMap
) {
if (isSubmodule) return 'folder-git'; if (isSubmodule) return 'folder-git';
if (isSymlink) return 'folder-symlink'; if (isSymlink) return 'folder-symlink';
@ -201,15 +183,13 @@ function lookForMatch(
* Lookup for matched light file/folder icon name. * Lookup for matched light file/folder icon name.
* *
* @since 1.4.0 * @since 1.4.0
* * @param {string} iconName Icon name.
* @param {String} iconName Icon name. * @param {string} fileName File name.
* @param {String} fileName File name. * @param {string} fileExtension File extension.
* @param {String} fileExtension File extension. * @param {boolean} isDir Check if directory or file type.
* @param {Boolean} isDir Check if directory or file type. * @returns {string} The matched icon name.
* @param {Object} iconMap Icon map.
* @returns {String} The matched icon name.
*/ */
function lookForLightMatch(iconName, fileName, fileExtension, isDir, iconMap) { function lookForLightMatch(iconName, fileName, fileExtension, isDir) {
// First look in fileNames and folderNames. // First look in fileNames and folderNames.
if (iconMap.light.fileNames[fileName] && !isDir) return iconMap.light.fileNames[fileName]; if (iconMap.light.fileNames[fileName] && !isDir) return iconMap.light.fileNames[fileName];
if (iconMap.light.folderNames[fileName] && isDir) return iconMap.light.folderNames[fileName]; if (iconMap.light.folderNames[fileName] && isDir) return iconMap.light.folderNames[fileName];
@ -225,12 +205,10 @@ function lookForLightMatch(iconName, fileName, fileExtension, isDir, iconMap) {
* Lookup for matched icon from active icon pack. * Lookup for matched icon from active icon pack.
* *
* @since 1.4.0 * @since 1.4.0
* * @param {string} lowerFileName Lowercase file name.
* @param {String} lowerFileName Lowercase file name. * @returns {string} The matched icon name.
* @param {Object} iconMap Icon map.
* @returns {String} The matched icon name.
*/ */
function lookForIconPackMatch(lowerFileName, iconMap) { function lookForIconPackMatch(lowerFileName) {
if (iconMap.options.activeIconPack) { if (iconMap.options.activeIconPack) {
switch (iconMap.options.activeIconPack) { switch (iconMap.options.activeIconPack) {
case 'angular': case 'angular':
@ -241,7 +219,8 @@ function lookForIconPackMatch(lowerFileName, iconMap) {
case 'react_redux': case 'react_redux':
if (iconsList[`folder-react-${lowerFileName}.svg`]) { if (iconsList[`folder-react-${lowerFileName}.svg`]) {
return `folder-react-${lowerFileName}`; return `folder-react-${lowerFileName}`;
} else if (iconsList[`folder-redux-${lowerFileName}.svg`]) { }
if (iconsList[`folder-redux-${lowerFileName}.svg`]) {
return `folder-redux-${lowerFileName}`; return `folder-redux-${lowerFileName}`;
} }
break; break;
@ -249,9 +228,11 @@ function lookForIconPackMatch(lowerFileName, iconMap) {
case 'vue_vuex': case 'vue_vuex':
if (iconsList[`folder-vuex-${lowerFileName}.svg`]) { if (iconsList[`folder-vuex-${lowerFileName}.svg`]) {
return `folder-vuex-${lowerFileName}`; return `folder-vuex-${lowerFileName}`;
} else if (iconsList[`folder-vue-${lowerFileName}.svg`]) { }
if (iconsList[`folder-vue-${lowerFileName}.svg`]) {
return `folder-vue-${lowerFileName}`; return `folder-vue-${lowerFileName}`;
} else if ('nuxt' === lowerFileName) { }
if (lowerFileName === 'nuxt') {
return `folder-nuxt`; return `folder-nuxt`;
} }
break; break;
@ -277,7 +258,12 @@ function lookForIconPackMatch(lowerFileName, iconMap) {
return 'nest-guard'; return 'nest-guard';
case /\.resolver\.(t|j)s$/.test(lowerFileName): case /\.resolver\.(t|j)s$/.test(lowerFileName):
return 'nest-resolver'; return 'nest-resolver';
default:
return null;
}
default:
return null;
} }
} }
} return null;
} }

View File

@ -13,7 +13,7 @@ const azureConfig = {
document.defaultView.getComputedStyle(document.body).getPropertyValue('color') === document.defaultView.getComputedStyle(document.body).getPropertyValue('color') ===
'rgba(0, 0, 0, 0.9)', // TODO: There is probably a better way to determine whether Azure is in light mode 'rgba(0, 0, 0, 0.9)', // TODO: There is probably a better way to determine whether Azure is in light mode
getIsDirectory: (svgEl) => svgEl.classList.contains('repos-folder-icon'), getIsDirectory: (svgEl) => svgEl.classList.contains('repos-folder-icon'),
getIsSubmodule: (svgEl) => false, // There appears to be no way to tell if a folder is a submodule getIsSubmodule: () => false, // There appears to be no way to tell if a folder is a submodule
getIsSymlink: (svgEl) => svgEl.classList.contains('ms-Icon--PageArrowRight'), getIsSymlink: (svgEl) => svgEl.classList.contains('ms-Icon--PageArrowRight'),
replaceIcon: (svgEl, newSVG) => { replaceIcon: (svgEl, newSVG) => {
newSVG.style.display = 'inline-flex'; newSVG.style.display = 'inline-flex';

View File

@ -9,7 +9,7 @@ const bitbucketConfig = {
getIsLightTheme: () => true, // No dark mode available for bitbucket currently getIsLightTheme: () => true, // No dark mode available for bitbucket currently
getIsDirectory: (svgEl) => svgEl.parentNode?.getAttribute('aria-label') === 'Directory,', getIsDirectory: (svgEl) => svgEl.parentNode?.getAttribute('aria-label') === 'Directory,',
getIsSubmodule: (svgEl) => svgEl.parentNode?.getAttribute('aria-label') === 'Submodule,', getIsSubmodule: (svgEl) => svgEl.parentNode?.getAttribute('aria-label') === 'Submodule,',
getIsSymlink: (svgEl) => false, // There appears to be no way to determine this for bitbucket getIsSymlink: () => false, // There appears to be no way to determine this for bitbucket
replaceIcon: (svgEl, newSVG) => { replaceIcon: (svgEl, newSVG) => {
newSVG.style.overflow = 'hidden'; newSVG.style.overflow = 'hidden';
newSVG.style.pointerEvents = 'none'; newSVG.style.pointerEvents = 'none';

View File

@ -2,7 +2,8 @@ const githubConfig = {
name: 'github', name: 'github',
selectors: { selectors: {
row: '.js-navigation-container[role=grid] > .js-navigation-item, file-tree .ActionList-content, a.tree-browser-result', row: '.js-navigation-container[role=grid] > .js-navigation-item, file-tree .ActionList-content, a.tree-browser-result',
filename: 'div[role="rowheader"] > span, .ActionList-item-label, a.tree-browser-result > marked-text', filename:
'div[role="rowheader"] > span, .ActionList-item-label, a.tree-browser-result > marked-text',
icon: '.octicon-file, .octicon-file-directory-fill, a.tree-browser-result > svg.octicon.octicon-file', icon: '.octicon-file, .octicon-file-directory-fill, a.tree-browser-result > svg.octicon.octicon-file',
}, },
getIsLightTheme: () => document.querySelector('html').getAttribute('data-color-mode') === 'light', getIsLightTheme: () => document.querySelector('html').getAttribute('data-color-mode') === 'light',