feat: Add script to check if packages pass publint (#523)

* Add zod schemas for CI validation

* Require npm field for components.json

* Remove svelte-layout-resizable

* Stricter Zod validation

* Stricter repository field validation

* Implement requested changes

* Add back accidentally removed field

* Move SvelteStore to templates.json

* Update category and tags

* Add script to get npm data

* Add script to get publint data

* Re-run updateNpm.js

* Re-run updatePublint.js

* Implement initial feedback

* Use npm.json data

* Update npm.js

* Switch async/await to then/catch

Co-authored-by: MacFJA <MacFJA@users.noreply.github.com>

* Fix prettier

* Run script

* Re-run updateNpm

* Also read tools.json

* Add @types/node

* Restructure npm.json output

* Fix updatePublint.js

* Display last update on components page

* Add to weekly workflow

* Clarify updating npm data

* Update src/lib/utils/injectNpmData.ts

Co-authored-by: MacFJA <MacFJA@users.noreply.github.com>

* Smaller date font, add version

* Improve error text

* Fix property title

* Move json to lib/data directory

* Fix npm.json path

* Also check tools.json

* Add to automated PR

* Add injectPublintData function

* Update publint

---------

Co-authored-by: MacFJA <MacFJA@users.noreply.github.com>
This commit is contained in:
Lachlan Collins
2023-12-18 13:26:25 +11:00
committed by GitHub
parent 02dc9dfa9e
commit 156f65436c
10 changed files with 894 additions and 4 deletions

View File

@@ -25,6 +25,8 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Update npm data
run: node scripts/updateNpm.js
- name: Update publint data
run: node scripts/updatePublint.js
- name: Run format
run: pnpm run format
- name: Create Pull Request
@@ -32,8 +34,8 @@ jobs:
with:
commit-message: "(AUTO) Update data"
title: "🤖 Update data"
body: Automatically fetch latest data from NPM, GitHub and GitLab
body: Automatically fetch latest data from GitHub, GitLab, NPM and Publint.
branch: ci-update-data
add-paths: src/lib/data/npm.json,src/lib/data/stars.json
add-paths: src/lib/data/npm.json,src/lib/data/publint.json,src/lib/data/stars.json
delete-branch: true
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -30,12 +30,15 @@
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-svelte": "^2.35.1",
"get-npm-tarball-url": "^2.1.0",
"highlight.js": "^11.9.0",
"itemsjs": "^2.1.24",
"mdsvex": "^0.11.0",
"package-name-regex": "^3.1.1",
"pako": "^2.1.0",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"publint": "^0.2.6",
"rehype-slug": "^6.0.0",
"svelte": "^4.2.8",
"svelte-check": "^3.6.2",

82
pnpm-lock.yaml generated
View File

@@ -50,6 +50,9 @@ devDependencies:
eslint-plugin-svelte:
specifier: ^2.35.1
version: 2.35.1(eslint@8.56.0)(svelte@4.2.8)
get-npm-tarball-url:
specifier: ^2.1.0
version: 2.1.0
highlight.js:
specifier: ^11.9.0
version: 11.9.0
@@ -62,12 +65,18 @@ devDependencies:
package-name-regex:
specifier: ^3.1.1
version: 3.1.1
pako:
specifier: ^2.1.0
version: 2.1.0
prettier:
specifier: ^3.1.1
version: 3.1.1
prettier-plugin-svelte:
specifier: ^3.1.2
version: 3.1.2(prettier@3.1.1)(svelte@4.2.8)
publint:
specifier: ^0.2.6
version: 0.2.6
rehype-slug:
specifier: ^6.0.0
version: 6.0.0
@@ -993,6 +1002,12 @@ packages:
concat-map: 0.0.1
dev: true
/brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
dependencies:
balanced-match: 1.0.2
dev: true
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
@@ -1488,6 +1503,11 @@ packages:
resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}
dev: true
/get-npm-tarball-url@2.1.0:
resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==}
engines: {node: '>=12.17'}
dev: true
/get-stream@8.0.1:
resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
engines: {node: '>=16'}
@@ -1522,6 +1542,17 @@ packages:
path-is-absolute: 1.0.1
dev: true
/glob@8.1.0:
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
engines: {node: '>=12'}
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
inherits: 2.0.4
minimatch: 5.1.6
once: 1.4.0
dev: true
/globals@13.20.0:
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
engines: {node: '>=8'}
@@ -1590,6 +1621,13 @@ packages:
safari-14-idb-fix: 1.0.6
dev: true
/ignore-walk@5.0.1:
resolution: {integrity: sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
minimatch: 5.1.6
dev: true
/ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'}
@@ -1807,6 +1845,13 @@ packages:
brace-expansion: 1.1.11
dev: true
/minimatch@5.1.6:
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimist@1.2.8:
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
dev: true
@@ -1862,6 +1907,29 @@ packages:
engines: {node: '>=0.10.0'}
dev: true
/npm-bundled@2.0.1:
resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dependencies:
npm-normalize-package-bin: 2.0.0
dev: true
/npm-normalize-package-bin@2.0.0:
resolution: {integrity: sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
dev: true
/npm-packlist@5.1.3:
resolution: {integrity: sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
hasBin: true
dependencies:
glob: 8.1.0
ignore-walk: 5.0.1
npm-bundled: 2.0.1
npm-normalize-package-bin: 2.0.0
dev: true
/npm-run-path@5.1.0:
resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
@@ -1920,6 +1988,10 @@ packages:
engines: {node: '>=14'}
dev: true
/pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
dev: true
/parent-module@1.0.1:
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
engines: {node: '>=6'}
@@ -2094,6 +2166,16 @@ packages:
engines: {node: '>=6'}
dev: true
/publint@0.2.6:
resolution: {integrity: sha512-zMwDVwrlLnCsviDXlczhuc5nIljsjZUgbLeKNyMYqbIJLRhcW81xrKsHlEu21YUaIxpa8T66tdIqP0mZm9ym3A==}
engines: {node: '>=16'}
hasBin: true
dependencies:
npm-packlist: 5.1.3
picocolors: 1.0.0
sade: 1.8.1
dev: true
/punycode@2.3.0:
resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==}
engines: {node: '>=6'}

44
scripts/tarball.js Normal file
View File

@@ -0,0 +1,44 @@
// @ts-check
// Source: https://github.com/bluwy/publint/blob/master/site/src/utils/tarball.js
/** @typedef {{ name: string, buffer: ArrayBuffer }} TarballFile */
/**
* @param {TarballFile[]} files
* @return {import('publint').Vfs}
* */
export function createTarballVfs(files) {
return {
getDirName: (path) => path.replace(/\/[^/]*$/, ''),
getExtName: (path) => path.replace(/^.*\./, '.'),
isPathDir: async (path) => {
path = path.endsWith('/') ? path : path + '/';
return files.some((file) => file.name.startsWith(path));
},
isPathExist: async (path) => {
const pathDirVariant = path.endsWith('/') ? path : path + '/';
return files.some((file) => file.name === path || file.name.startsWith(pathDirVariant));
},
pathJoin: (...parts) =>
parts
.map((v) => (v.startsWith('./') ? v.slice(2) : v))
.join('/')
.replace('///', '/')
.replace('//', '/'), // TODO: optimize this please
pathRelative: (from, to) => to.replace(from, '').slice(1),
readDir: async (path) => {
path = path.endsWith('/') ? path : path + '/';
return files
.filter((file) => file.name.startsWith(path) && file.name !== path)
.map((file) => file.name.slice(path.length));
},
readFile: async (path) => {
const file = files.find((file) => file.name === path);
if (file) {
return new TextDecoder('utf-8').decode(file.buffer);
} else {
throw new Error(`Unable to read file at path: ${path}`);
}
}
};
}

306
scripts/untar.js Normal file
View File

@@ -0,0 +1,306 @@
// @ts-check
// Source: https://github.com/bluwy/publint/blob/master/site/src/utils/untar.js
export function untar(arrayBuffer) {
const tarFileStream = new UntarFileStream(arrayBuffer);
const files = [];
while (tarFileStream.hasNext()) {
const file = tarFileStream.next();
files.push(file);
}
return files;
}
// Source: https://gist.github.com/pascaldekloe/62546103a1576803dade9269ccf76330
// Unmarshals an Uint8Array to string.
function decodeUTF8(bytes) {
var s = '';
var i = 0;
while (i < bytes.length) {
var c = bytes[i++];
if (c > 127) {
if (c > 191 && c < 224) {
if (i >= bytes.length) throw 'UTF-8 decode: incomplete 2-byte sequence';
c = ((c & 31) << 6) | (bytes[i] & 63);
} else if (c > 223 && c < 240) {
if (i + 1 >= bytes.length) throw 'UTF-8 decode: incomplete 3-byte sequence';
c = ((c & 15) << 12) | ((bytes[i] & 63) << 6) | (bytes[++i] & 63);
} else if (c > 239 && c < 248) {
if (i + 2 >= bytes.length) throw 'UTF-8 decode: incomplete 4-byte sequence';
c =
((c & 7) << 18) | ((bytes[i] & 63) << 12) | ((bytes[++i] & 63) << 6) | (bytes[++i] & 63);
} else
throw 'UTF-8 decode: unknown multibyte start 0x' + c.toString(16) + ' at index ' + (i - 1);
++i;
}
if (c <= 0xffff) s += String.fromCharCode(c);
else if (c <= 0x10ffff) {
c -= 0x10000;
s += String.fromCharCode((c >> 10) | 0xd800);
s += String.fromCharCode((c & 0x3ff) | 0xdc00);
} else throw 'UTF-8 decode: code point 0x' + c.toString(16) + ' exceeds UTF-16 reach';
}
return s;
}
function PaxHeader(fields) {
this._fields = fields;
}
PaxHeader.parse = function (buffer) {
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm
// An extended header shall consist of one or more records, each constructed as follows:
// "%d %s=%s\n", <length>, <keyword>, <value>
// The extended header records shall be encoded according to the ISO/IEC10646-1:2000 standard (UTF-8).
// The <length> field, <blank>, equals sign, and <newline> shown shall be limited to the portable character set, as
// encoded in UTF-8. The <keyword> and <value> fields can be any UTF-8 characters. The <length> field shall be the
// decimal length of the extended header record in octets, including the trailing <newline>.
var bytes = new Uint8Array(buffer);
var fields = [];
while (bytes.length > 0) {
// Decode bytes up to the first space character; that is the total field length
var fieldLength = parseInt(decodeUTF8(bytes.subarray(0, bytes.indexOf(0x20))));
var fieldText = decodeUTF8(bytes.subarray(0, fieldLength));
var fieldMatch = fieldText.match(/^\d+ ([^=]+)=(.*)\n$/);
if (fieldMatch === null) {
throw new Error('Invalid PAX header data format.');
}
var fieldName = fieldMatch[1];
var fieldValue = fieldMatch[2];
if (fieldValue.length === 0) {
fieldValue = null;
} else if (fieldValue.match(/^\d+$/) !== null) {
// If it's a integer field, parse it as int
fieldValue = parseInt(fieldValue);
}
// Don't parse float values since precision is lost
var field = {
name: fieldName,
value: fieldValue
};
fields.push(field);
bytes = bytes.subarray(fieldLength); // Cut off the parsed field data
}
return new PaxHeader(fields);
};
PaxHeader.prototype = {
applyHeader: function (file) {
// Apply fields to the file
// If a field is of value null, it should be deleted from the file
// https://www.mkssoftware.com/docs/man4/pax.4.asp
this._fields.forEach(function (field) {
var fieldName = field.name;
var fieldValue = field.value;
if (fieldName === 'path') {
// This overrides the name and prefix fields in the following header block.
fieldName = 'name';
if (file.prefix !== undefined) {
delete file.prefix;
}
} else if (fieldName === 'linkpath') {
// This overrides the linkname field in the following header block.
fieldName = 'linkname';
}
if (fieldValue === null) {
delete file[fieldName];
} else {
file[fieldName] = fieldValue;
}
});
}
};
function UntarStream(arrayBuffer) {
this._bufferView = new DataView(arrayBuffer);
this._position = 0;
}
UntarStream.prototype = {
readString: function (charCount) {
//console.log("readString: position " + this.position() + ", " + charCount + " chars");
var charSize = 1;
var byteCount = charCount * charSize;
var charCodes = [];
for (var i = 0; i < charCount; ++i) {
var charCode = this._bufferView.getUint8(this.position() + i * charSize, true);
if (charCode !== 0) {
charCodes.push(charCode);
} else {
break;
}
}
this.seek(byteCount);
return String.fromCharCode.apply(null, charCodes);
},
readBuffer: function (byteCount) {
var buf;
if (typeof ArrayBuffer.prototype.slice === 'function') {
buf = this._bufferView.buffer.slice(this.position(), this.position() + byteCount);
} else {
buf = new ArrayBuffer(byteCount);
var target = new Uint8Array(buf);
var src = new Uint8Array(this._bufferView.buffer, this.position(), byteCount);
target.set(src);
}
this.seek(byteCount);
return buf;
},
seek: function (byteCount) {
this._position += byteCount;
},
peekUint32: function () {
return this._bufferView.getUint32(this.position(), true);
},
position: function (newpos) {
if (newpos === undefined) {
return this._position;
} else {
this._position = newpos;
}
},
size: function () {
return this._bufferView.byteLength;
}
};
function UntarFileStream(arrayBuffer) {
this._stream = new UntarStream(arrayBuffer);
this._globalPaxHeader = null;
}
UntarFileStream.prototype = {
hasNext: function () {
// A tar file ends with 4 zero bytes
return this._stream.position() + 4 < this._stream.size() && this._stream.peekUint32() !== 0;
},
next: function () {
return this._readNextFile();
},
_readNextFile: function () {
var stream = this._stream;
var file = {};
var isHeaderFile = false;
var paxHeader = null;
var headerBeginPos = stream.position();
var dataBeginPos = headerBeginPos + 512;
// Read header
file.name = stream.readString(100);
file.mode = stream.readString(8);
file.uid = parseInt(stream.readString(8));
file.gid = parseInt(stream.readString(8));
file.size = parseInt(stream.readString(12), 8);
file.mtime = parseInt(stream.readString(12), 8);
file.checksum = parseInt(stream.readString(8));
file.type = stream.readString(1);
file.linkname = stream.readString(100);
file.ustarFormat = stream.readString(6);
if (file.ustarFormat.indexOf('ustar') > -1) {
file.version = stream.readString(2);
file.uname = stream.readString(32);
file.gname = stream.readString(32);
file.devmajor = parseInt(stream.readString(8));
file.devminor = parseInt(stream.readString(8));
file.namePrefix = stream.readString(155);
if (file.namePrefix.length > 0) {
file.name = file.namePrefix + '/' + file.name;
}
}
stream.position(dataBeginPos);
// Derived from https://www.mkssoftware.com/docs/man4/pax.4.asp
// and https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/pxarchfm.htm
switch (file.type) {
case '0': // Normal file is either "0" or "\0".
case '': // In case of "\0", readString returns an empty string, that is "".
file.buffer = stream.readBuffer(file.size);
break;
case '1': // Link to another file already archived
// TODO Should we do anything with these?
break;
case '2': // Symbolic link
// TODO Should we do anything with these?
break;
case '3': // Character special device (what does this mean??)
break;
case '4': // Block special device
break;
case '5': // Directory
break;
case '6': // FIFO special file
break;
case '7': // Reserved
break;
case 'g': // Global PAX header
isHeaderFile = true;
this._globalPaxHeader = PaxHeader.parse(stream.readBuffer(file.size));
break;
case 'x': // PAX header
isHeaderFile = true;
paxHeader = PaxHeader.parse(stream.readBuffer(file.size));
break;
default: // Unknown file type
break;
}
if (file.buffer === undefined) {
file.buffer = new ArrayBuffer(0);
}
var dataEndPos = dataBeginPos + file.size;
// File data is padded to reach a 512 byte boundary; skip the padded bytes too.
if (file.size % 512 !== 0) {
dataEndPos += 512 - (file.size % 512);
}
stream.position(dataEndPos);
if (isHeaderFile) {
file = this._readNextFile();
}
if (this._globalPaxHeader !== null) {
this._globalPaxHeader.applyHeader(file);
}
if (paxHeader !== null) {
paxHeader.applyHeader(file);
}
return file;
}
};

77
scripts/updatePublint.js Normal file
View File

@@ -0,0 +1,77 @@
// @ts-check
// Source: https://github.com/bluwy/publint/blob/master/site/src/utils/worker.js
import { writeFileSync } from 'node:fs';
import { inflate } from 'pako';
import getNpmTarballUrl from 'get-npm-tarball-url';
import { componentsSchema, toolsSchema } from '../src/lib/schemas.js';
import components from '../src/routes/components/components.json' assert { type: 'json' };
import tools from '../src/routes/tools/tools.json' assert { type: 'json' };
import npm from '../src/lib/data/npm.json' assert { type: 'json' };
import { publint } from 'publint';
import { untar } from './untar.js';
import { createTarballVfs } from './tarball.js';
const dataWithoutVersions = [...componentsSchema.parse(components), ...toolsSchema.parse(tools)];
/** @param input {import('zod').infer<typeof toolsSchema>} */
const injectVersions = (input) => {
const output = [];
for (const item of input) {
/** @type {string} */
const version = npm[item.npm]?.version;
if (version) {
output.push({ ...item, version });
}
}
return output;
};
const dataWithVersions = injectVersions(dataWithoutVersions);
const output = await Promise.all(
dataWithVersions.map(async (pkg) => {
try {
return await processPackage(pkg);
} catch (error) {
console.log(error.message);
}
})
).then((values) => {
let versions = {};
for (const value of values) {
if (value) {
versions[value.name] = value.valid;
}
}
return versions;
});
writeFileSync('src/lib/data/publint.json', JSON.stringify(output));
/** @param pkg {ReturnType<typeof injectVersions>[0]} */
async function processPackage(pkg) {
const tarballUrl = getNpmTarballUrl(pkg.npm, pkg.version);
let resultBuffer;
try {
const result = await fetch(tarballUrl);
resultBuffer = await result.arrayBuffer();
} catch (e) {
postMessage({ type: 'error', data: 'Package not found' });
console.error(e);
}
let files;
try {
const tarBuffer = inflate(resultBuffer).buffer; // Handles gzip (gz)
files = untar(tarBuffer); // Handles tar (t)
} catch (e) {
postMessage({ type: 'error', data: 'Failed to unpack package' });
console.error(e);
return;
}
const vfs = createTarballVfs(files);
const pkgDir = files.length ? files[0].name.split('/')[0] : 'package';
const { messages } = await publint({ pkgDir, vfs, level: 'warning' });
return { name: pkg.npm, valid: messages.length === 0 };
}

366
src/lib/data/publint.json Normal file
View File

@@ -0,0 +1,366 @@
{
"svelte-stopwatch": true,
"curseur": true,
"svelte-zod-form": true,
"svelte-selecto": false,
"svelte-pilot": true,
"super-sitemap": true,
"svelte-scrollactive": true,
"svelte-tel-input": true,
"svault": true,
"svelte-datatables-net": true,
"stwui": false,
"@sveltejs/adapter-auto": true,
"@sveltejs/adapter-netlify": true,
"@sveltejs/adapter-cloudflare-workers": true,
"@sveltejs/adapter-cloudflare": true,
"@sveltejs/adapter-node": true,
"@sveltejs/adapter-static": true,
"@sveltejs/adapter-vercel": true,
"svelte-lazy-loader": true,
"svelte-carbonbadge": true,
"svelte-form-validation": true,
"date-picker-svelte": true,
"svelte-virtual-table": false,
"svelte-number-spinner": false,
"svelte-remixicon": true,
"svelte-fast-marquee": true,
"sswr": false,
"svelte-adapter-firebase": true,
"@architect/sveltekit-adapter": true,
"svelte-adapter-deno": true,
"svelte-client-router": false,
"felte": false,
"sveltefire": true,
"svelte-time-picker": true,
"svelte-formula": false,
"svelte-calendar": true,
"svelte-tags-input": false,
"sveltedoc-parser": true,
"aovi-svelte": false,
"svelte-chota": false,
"svelte-eventbus": false,
"tinro": false,
"fa-svelte": true,
"svelte-fullscreen": true,
"svelte-grid-responsive": false,
"svelte-infinite-scroll": true,
"svelte-formly": true,
"svelte-websocket-store": true,
"sveltestrap": false,
"@beyonk/gdpr-cookie-consent-banner": true,
"@beyonk/svelte-carousel": true,
"@beyonk/svelte-facebook-customer-chat": true,
"@beyonk/svelte-facebook-pixel": false,
"@beyonk/svelte-google-analytics": false,
"@beyonk/svelte-googlemaps": false,
"@beyonk/svelte-mapbox": true,
"@beyonk/svelte-notifications": true,
"@beyonk/svelte-scrollspy": false,
"svelte-simple-icons": false,
"@beyonk/svelte-trustpilot": true,
"@bjornlu/svelte-router": false,
"svelte-sortable-list": true,
"svelte-data-grid": false,
"query-store": true,
"svelma": false,
"svelte-forms": true,
"svelte-credit-cards": false,
"svelte-headroom": false,
"svelte-fa": false,
"svelte-table": false,
"svelte-moveable": true,
"svelte-ruler": false,
"waxwing-rating": true,
"@dopry/svelte-auth0": false,
"svelte-feather-icons": false,
"@easylogic/svelte-summernote": false,
"svelte-dev-helper": false,
"svelte-routing": false,
"svelte-rate-it": true,
"@equipmentshare/date-range-input": true,
"svelte-tree": false,
"svelteify": true,
"svelte-simple-modal": true,
"@urql/svelte": false,
"svelte-icons": false,
"svelte-native": false,
"svelte-copyright": true,
"svelte-flex": true,
"svelte-frappe-charts": false,
"svelte-material-ui": true,
"@ikun-ui/core": false,
"carbon-components-svelte": false,
"attractions": false,
"svelte-spa-router": true,
"svelte-flatpickr": false,
"svelte-navaid": false,
"@jamen/svelte-router": false,
"svelte-router": true,
"svelte-pick-a-place": false,
"svelte-tabs": false,
"svelteml": false,
"svelte-compare-image-slider": false,
"svelte-router-spa": false,
"svelte-css-vars": true,
"svelte-i18n": true,
"@nubolab-ffwd/svelte-fluent": true,
"svelte-loadable": true,
"svero": false,
"svelte-notifications": false,
"svelte-fragment-component": false,
"svelte-htm": false,
"svelte-jsx": true,
"svelte-favicon-badge": false,
"svelte-redux-connect": false,
"svelte-jester": true,
"svelte-jest": true,
"echarts-for-svelte": false,
"@lottiefiles/svelte-lottie-player": false,
"@pwa/cli": false,
"svelte-inview": true,
"smelte": false,
"@melt-ui/svelte": true,
"radix-svelte": true,
"svelte-image": true,
"svelte-waypoint": false,
"sveltejs-forms": false,
"svelte-navigator": false,
"layercake": true,
"@egjs/svelte-infinitegrid": false,
"svelte-page-progress": false,
"@okrad/svelte-progressbar": true,
"svelte-intl": false,
"svql": false,
"yrv": false,
"svelte-asyncable": false,
"svelte-content-loader": false,
"svelte-image-compare": false,
"svelte-imask": false,
"svelte-page-router": true,
"svelte-pathfinder": false,
"svelte-ticker": false,
"svelte-viewpoint": false,
"select-madu": true,
"svelte-match-media": false,
"svelte-webext-storage-adapter": true,
"svelte-writable-derived": false,
"storez": true,
"svelte-adapter": false,
"svelte-test": false,
"svelte-fusioncharts": false,
"simple-svelte-autocomplete": true,
"svelte-hash-router": false,
"svelte-color-picker": false,
"svelte-inspector": true,
"swheel": false,
"svelte-accessible-dialog": false,
"@sveltejs/pancake": true,
"svelte-select": true,
"svelte-awesome": true,
"jest-transform-svelte": false,
"svelte-image-encoder": true,
"multicarousel": false,
"svelte-heatmap": false,
"svelte-swipe": true,
"@slick-for/svelte": true,
"svelte-marquee": false,
"svelte-range-slider-pips": false,
"svelte-infinite-loading": false,
"@spaceavocado/svelte-form": false,
"@spaceavocado/svelte-router": false,
"svelte-multitoneimage": true,
"@storybook/sveltekit": false,
"@storybook/svelte": false,
"svelte-toolbox": false,
"routify": false,
"@sveltejs/gestures": true,
"@sveltejs/gl": false,
"@sveltejs/svelte-repl": false,
"@sveltejs/svelte-scroller": true,
"@sveltejs/svelte-subdivide": false,
"@sveltejs/svelte-virtual-list": true,
"svelte-virtual-list-ce": true,
"svelte-state-renderer": true,
"@testing-library/svelte": true,
"svelte-file-dropzone": true,
"svelte-apollo": true,
"svelte-observable": false,
"sveltemantic": false,
"svelte-forms-lib": false,
"svelte-grid": false,
"svelte-popover": true,
"svelte-easy-crop": true,
"svelte-mui": false,
"s-offline": true,
"minna-ui": true,
"shadcn-svelte": true,
"svelte-headlessui": true,
"svelte-media-query": true,
"svelte-mobx": false,
"svelte-input-mask": true,
"svelidation": false,
"svelte-fullcalendar": true,
"svelte-item-list": true,
"@zooplus/zoo-web-components": true,
"svelte-atoms": false,
"overmind-svelte": false,
"svelte-tiny-virtual-list": false,
"@svelte-parts/drop-file": true,
"@svelte-parts/form": true,
"@svelte-parts/icons": false,
"@svelte-parts/zoom": true,
"svelte-appwrite": false,
"svelte-pdf": true,
"svelte-micro": true,
"svantic": true,
"@macfja/svelte-undoable": true,
"renderless-svelte": false,
"svate": false,
"svelte-parallax": false,
"svelte-modals": true,
"@macfja/svelte-persistent-store": false,
"@macfja/svelte-invalidable": true,
"svelte-carousel": true,
"svelte-restate": true,
"svelte-pincode": true,
"svelte-particles": true,
"svelte-tiptap": true,
"spaper": true,
"@event-calendar/core": true,
"@kahi-ui/framework": true,
"@macfja/svelte-multi-adapter": true,
"svelte-multiselect": true,
"svelte-toc": true,
"svelte-bricks": true,
"@macfja/svelte-oauth2": false,
"svelte-boring-avatars": false,
"filedrop-svelte": true,
"focus-svelte": true,
"svelte-translate": false,
"svelecte": false,
"svelte-adapter-azure-swa": true,
"svelte-adapter-appengine": true,
"sveltekit-adapter-browser-extension": true,
"svelte-fsm": true,
"@yellowinq/svelte-pin-input": true,
"svelte-codesandbox": true,
"svelte-icons-pack": false,
"@joeinnes/svelte-image": true,
"@budgetdraw/sveltekit-cloudflare-adapter": true,
"svelte-intl-precompile": true,
"svelte-cleavejs": false,
"svelty-picker": true,
"svelte-slider": false,
"sveltekit-adapter-wordpress-shortcode": true,
"@macfja/svelte-expirable": false,
"svelte-store2": true,
"@macfja/svelte-adapter-neutralino": true,
"sveltekit-adapter-html-like": true,
"svelte-gestures": false,
"svelte-adapter-github": true,
"svelte-steps": true,
"agnostic-svelte": true,
"svelte-adapter-static-digitalocean": true,
"@kitql/all-in": true,
"svelte-brick-gallery": true,
"svelte-adapter-bun": true,
"@rgossiaux/svelte-headlessui": true,
"@brewer/beerui": false,
"@skeletonlabs/skeleton": true,
"svelte-hover-draw-svg": true,
"casual-ui-svelte": true,
"@specialdoom/proi-ui": false,
"@canutin/svelte-currency-input": true,
"flowbite-svelte": true,
"typesafe-i18n": false,
"@tolgee/svelte": true,
"sthemer": false,
"svelte-exstore": true,
"@macfja/svelte-scroll-video": true,
"@prgm/sveltekit-progress-bar": true,
"svelte-unicons": false,
"svelte-google-auth": true,
"sveltekit-adapter-firebase": true,
"sveltekit-search-params": true,
"@geoffcox/sterling-svelte": true,
"simple-ui-components-in-svelte": true,
"@tanstack/svelte-query": true,
"@pragmatic-engineering/svelte-form-builder-community": true,
"@ptkdev/sveltekit-cordova-adapter": true,
"@ptkdev/sveltekit-electron-adapter": true,
"@carlosv2/adapter-node-ws": true,
"leblog": true,
"html-svelte-parser": true,
"svelte-droplet": true,
"chat-embed": true,
"lucide-svelte": false,
"yesvelte": true,
"@bonosoft/sveltekit-qrcode": true,
"@bonosoft/sveltekit-progress": true,
"@radar-azdelta/svelte-datatable": true,
"svelte-svg-transform": true,
"@nerd-coder/svelte-zod-form": true,
"sveltekit-adapter-iis": true,
"svelte-dx-table": false,
"svelte-switch": false,
"svelte-pagination": false,
"stdf": true,
"@twicpics/components": false,
"@shipbit/svane": true,
"sveltekit-html-minifier": true,
"svelte-tex": false,
"drab": true,
"supasveltekit": true,
"@jill64/sveltekit-adapter-aws": true,
"sveltekit-adapter-versioned-worker": true,
"layerchart": true,
"@egjs/svelte-flicking": false,
"svelte-ux": true,
"@born05/sveltekit-proxy": true,
"@mismerge/core": true,
"svelte-ripple-action": true,
"sveltekit-superforms": true,
"svelte-reparent": true,
"svelte-inline-modal": true,
"@tanstack/svelte-table": false,
"@histoire/plugin-svelte": true,
"lucia": true,
"@monaco-auth/sveltekit": true,
"trpc-svelte-query": true,
"svelte-legos": false,
"@supabase/auth-helpers-sveltekit": true,
"svelte-markdown": false,
"svelte-exmarkdown": true,
"@sentry/svelte": false,
"svelte-french-toast": true,
"@zerodevx/svelte-toast": true,
"@mavthedev/svodals": true,
"rollup-plugin-svelte": true,
"svelte-loader": false,
"@sveltejs/vite-plugin-svelte": true,
"esbuild-svelte": true,
"rollup-plugin-svelte-hot": true,
"parcel-transformer-svelte3-plus": true,
"parcel-plugin-svelte": true,
"sveltify": true,
"gulp-svelte": true,
"sveltejs-brunch": true,
"svelte-preprocess": true,
"svelte-preprocess-markdown": true,
"mdsvex": false,
"svelte-preprocess-less": false,
"svelte-switch-case": true,
"@modular-css/svelte": true,
"svelte-preprocess-sass": false,
"svelte-preprocess-css-hash": true,
"svelte-preprocess-html-asset": true,
"svelte-preprocessor-fetch": false,
"prettier-plugin-svelte": true,
"svelte-check": true,
"svelte-reactive-css-preprocess": true,
"svelte-subcomponent-preprocessor": true,
"eslint-plugin-svelte": true,
"full-client-server-sveltekit": true,
"svelte-preprocess-delegate-events": true
}

View File

@@ -1,4 +1,5 @@
import npm from '$lib/data/npm.json';
import publint from '$lib/data/publint.json';
import type { z } from 'zod';
import type { componentsSchema } from '$lib/schemas';
@@ -10,3 +11,12 @@ export const injectNpmData = (input: z.infer<typeof componentsSchema>) => {
}
return output;
};
export const injectPublintData = (input: z.infer<typeof componentsSchema>) => {
const output = [];
for (const item of input) {
const extra = publint[item.npm] ?? false;
output.push({ ...item, publint: extra });
}
return output;
};

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import components from './components.json';
import SearchableJson from '$lib/SearchableJson.svelte';
import { injectNpmData } from '$utils/injectNpmData';
import { injectNpmData } from '$utils/injectData';
import { injectStars } from '$utils/stars';
</script>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import tools from '../tools/tools.json';
import SearchableJson from '$lib/SearchableJson.svelte';
import { injectNpmData } from '$utils/injectNpmData';
import { injectNpmData } from '$utils/injectData';
import { injectStars } from '$utils/stars';
</script>