Socket
Socket
Sign inDemoInstall

@tkskto/vue-component-analyzer

Package Overview
Dependencies
Maintainers
1
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tkskto/vue-component-analyzer - npm Package Compare versions

Comparing version 0.3.1 to 0.3.2

dist/icon/icon-ts.png

11

CHANGELOG.md
# Changelog
## 0.3.2
### enhancement
- add icon.
### Chores
- \#110 update `vue-eslint-parser` 7.7.2 to 7.9.0
- \#103 update `ws` 7.5.2 to 7.5.3
## 0.3.1

@@ -4,0 +15,0 @@

473

dist/client.js

@@ -1,472 +0,1 @@

class MyCustomEvent {
constructor(type) {
this.type = type;
this.currentTarget = null;
}
}
MyCustomEvent.COMPLETE = 'complete';
MyCustomEvent.CHANGE_PROPERTY = 'changeProperty';
class CustomEventListener {
constructor(type, handler, priority = 0) {
this.type = type;
this.handler = handler;
this.priority = priority;
}
equalCurrentListener(type, handler) {
return this.type === type && this.handler === handler;
}
}
class CustomEventDispatcher {
constructor() {
this.listeners = {};
}
dispatchEvent(event) {
let e;
let type;
if (event instanceof MyCustomEvent) {
type = event.type;
e = event;
}
else {
type = event;
e = new MyCustomEvent(type);
}
if (this.listeners[type] !== null) {
const len = this.listeners[type].length;
e.currentTarget = this;
for (let i = 0; i < len; i++) {
const listener = this.listeners[type][i];
try {
listener.handler(e);
}
catch (error) {
if (window.console) {
console.error(error.stack);
}
}
}
}
else {
console.warn('implement "addEventListener" before dispatch');
}
}
addEventListener(type, callback, priority = 0) {
if (!this.listeners[type]) {
this.listeners[type] = [];
}
this.listeners[type].push(new CustomEventListener(type, callback, priority));
this.listeners[type].sort((listener1, listener2) => listener2.priority - listener1.priority);
}
removeEventListener(type, callback) {
if (this.hasEventListener(type, callback)) {
for (let i = 0; i < this.listeners[type].length; i++) {
const listener = this.listeners[type][i];
if (listener.equalCurrentListener(type, callback)) {
this.listeners[type].splice(i, 1);
return;
}
}
}
}
clearEventListener() {
this.listeners = {};
}
containEventListener(type) {
if (!this.listeners[type]) {
return false;
}
return this.listeners[type].length > 0;
}
hasEventListener(type, callback) {
if (!this.listeners[type]) {
return false;
}
for (let i = 0; i < this.listeners[type].length; i++) {
const listener = this.listeners[type][i];
if (listener.equalCurrentListener(type, callback)) {
return true;
}
}
return false;
}
}
class Model extends CustomEventDispatcher {
constructor() {
super();
this._data = {
entries: [],
count: {},
};
this._viewType = 'GRAPH';
this._visibleSettings = false;
this._visibleProps = true;
this._visibleFileSize = true;
this._visibleLastUpdated = true;
this._visibleReferenceCount = true;
this.getHowManyDaysAgo = (date) => {
const time = date.getTime();
const diff = this._todayTime - time;
return Math.floor(diff / 86400000);
};
this._today = new Date();
this._todayTime = this._today.getTime();
}
get data() {
return this._data;
}
set data(value) {
this._data = value;
this.dispatchEvent(Model.EVENT.DATA_UPDATE);
}
get viewType() {
return this._viewType;
}
set viewType(value) {
this._viewType = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
get visibleSettings() {
return this._visibleSettings;
}
set visibleSettings(value) {
this._visibleSettings = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
get visibleProps() {
return this._visibleProps;
}
set visibleProps(value) {
this._visibleProps = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
get visibleFileSize() {
return this._visibleFileSize;
}
set visibleFileSize(value) {
this._visibleFileSize = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
get visibleLastUpdated() {
return this._visibleLastUpdated;
}
set visibleLastUpdated(value) {
this._visibleLastUpdated = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
get visibleReferenceCount() {
return this._visibleReferenceCount;
}
set visibleReferenceCount(value) {
this._visibleReferenceCount = value;
this.dispatchEvent(Model.EVENT.SETTING_CHANGED);
}
}
Model.EVENT = {
DATA_UPDATE: 'dataUpdate',
SETTING_CHANGED: 'settingChanged',
};
class Seed {
constructor(file, count, _model) {
this._model = _model;
this._children = [];
this._name = file.name;
this._props = file.props;
this._fileSize = file.size;
this._lastModifiedTime = file.lastModifiedTime;
this._count = count;
}
renderChildren() {
let html = '';
for (let i = 0, len = this._children.length; i < len; i++) {
html += this._children[i].render();
}
return `<div class="group">
${html}
</div>`;
}
renderProps() {
return this._props ? `<pre class="file__props">props: ${JSON.stringify(this._props, null, '\t')}</pre>` : '';
}
renderMetaData() {
let metaString = '';
const fileSize = (this._fileSize / 1024).toFixed(2);
const lastUpdated = this._lastModifiedTime === 0 ? 0 : this._model.getHowManyDaysAgo(new Date(this._lastModifiedTime));
if (fileSize) {
metaString += `<span class="file__meta meta__fileSize">FileSize: ${fileSize} KB</span>`;
}
metaString += `<span class="file__meta meta__lastUpdated">LastUpdated: ${lastUpdated} days ago</span>`;
return metaString;
}
renderDetails() {
return `<details class="detail">
<summary>${this._name}</summary>
${this.renderProps()}
${this.renderMetaData()}
</details>`;
}
render() {
const contents = this.renderDetails();
let childHTML = '';
let seedClassName = '';
if (this._children.length > 0) {
childHTML = this.renderChildren();
}
else {
seedClassName = ' -no-child';
}
return `<div class="seed${seedClassName}">
<div class="file">
<div class="filename">
<div class="file__text">${contents}</div>
${this.getCountText()}
</div>
</div>
${childHTML}
</div>`;
}
getCountText() {
if (this._count === 0) {
return '';
}
else if (this._count === 1) {
return '<span class="file__count">1 time referenced.</span>';
}
return `<span class="file__count">${this._count} times referenced.</span>`;
}
get children() {
return this._children;
}
set children(value) {
this._children = value;
}
}
class Renderer {
constructor(_model) {
this._model = _model;
this._tree = [];
this.ready = () => {
if ('GRAPH' === this._model.viewType) {
const { data } = this._model;
const { entries } = data;
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
if (data) {
const root = new Seed(entry, 0, this._model);
this._tree.push(this.generateSeed(entry, root));
}
}
}
this.render();
};
this._app = document.getElementById('app');
_model.addEventListener(Model.EVENT.DATA_UPDATE, this.ready);
}
generateSeed(data, seed) {
const tree = [];
const { children } = data;
const childSeeds = [];
const { count } = this._model.data;
for (let i = 0, len = children.length; i < len; i++) {
const child = children[i];
const childSeed = new Seed(child, count[child.name], this._model);
childSeeds.push(childSeed);
this.generateSeed(child, childSeed);
}
seed.children = childSeeds;
tree.push(seed);
return tree;
}
renderLine(name, level, isLast) {
let line = ' ';
for (let i = 0; i < level; i++) {
line += '│ ';
}
line += isLast ? '└─ ' : '├─ ';
line += name;
return `${line}\n`;
}
renderEntry(entry, level) {
let result = '';
for (let i = 0, len = entry.children.length; i < len; i++) {
const child = entry.children[i];
result += this.renderLine(child.name, level, i === (len - 1));
result += this.renderEntry(child, level + 1);
}
return result;
}
render() {
let html = '';
for (let i = 0, len = this._tree.length; i < len; i++) {
const [root] = this._tree[i];
html += root.render();
}
html = `<div class="root html">${html}</div>`;
const { entries } = this._model.data;
let text = '';
for (let i = 0, len = entries.length; i < len; i++) {
const entry = entries[i];
text += `${entry.name}\n`;
text += this.renderEntry(entries[i], 0);
if (i < len - 1) {
text += '\n';
}
}
text = `<div class="root text"><pre class="tree">${text}</pre></div>`;
if (this._app) {
this._app.innerHTML = html + text;
}
}
}
class VisibleSwitcher {
constructor(_elm, _model) {
this._elm = _elm;
this._model = _model;
this._type = _elm.dataset.settingName;
_elm.addEventListener('change', this.onChange.bind(this));
}
onChange() {
switch (this._type) {
case 'props':
this._model.visibleProps = this._elm.checked;
break;
case 'fileSize':
this._model.visibleFileSize = this._elm.checked;
break;
case 'lastUpdated':
this._model.visibleLastUpdated = this._elm.checked;
break;
case 'referenceCount':
this._model.visibleReferenceCount = this._elm.checked;
break;
default:
throw new Error(`not supported type: ${this._type}`);
}
}
}
class ViewSwitcher {
constructor(_elm, _type, _model) {
this._elm = _elm;
this._type = _type;
this._model = _model;
_elm.addEventListener('change', this.onChange.bind(this));
}
onChange() {
this._model.viewType = this._type;
}
}
const setSettings = function (model) {
const { body } = document;
const btnSwitchSettings = document.getElementById('btn-settings');
const nodeListOfSwitch = body.querySelectorAll('.js-settings-toggle');
const switcherElmForGraph = document.getElementById('js-view-switch-graph');
const switcherElmForText = document.getElementById('js-view-switch-text');
new ViewSwitcher(switcherElmForGraph, 'GRAPH', model);
new ViewSwitcher(switcherElmForText, 'TEXT', model);
const onSettingsChanged = function () {
body.className = `${model.viewType}`;
if (model.visibleSettings) {
body.classList.add('show-settings');
}
if (!model.visibleProps) {
body.classList.add('no-props');
}
if (!model.visibleFileSize) {
body.classList.add('no-fileSize');
}
if (!model.visibleLastUpdated) {
body.classList.add('no-lastUpdated');
}
if (!model.visibleReferenceCount) {
body.classList.add('no-referenceCount');
}
};
nodeListOfSwitch.forEach((node) => {
new VisibleSwitcher(node, model);
});
model.addEventListener(Model.EVENT.SETTING_CHANGED, onSettingsChanged);
if (btnSwitchSettings) {
btnSwitchSettings.addEventListener('click', () => {
model.visibleSettings = !model.visibleSettings;
});
}
onSettingsChanged();
};
class SeedOpenStateSwitcher {
constructor(_button, model) {
this._isOpen = false;
this._details = null;
this._textElement = document.getElementById('btn-toggle-visible-state-text');
_button.addEventListener('click', this.onClick.bind(this));
model.addEventListener(Model.EVENT.DATA_UPDATE, this.onDataUpdated.bind(this));
}
open() {
if (this._details) {
this._details.forEach((detail) => {
detail.open = true;
});
}
if (this._textElement) {
this._textElement.textContent = 'collapse all';
}
}
close() {
if (this._details) {
this._details.forEach((detail) => {
detail.open = false;
});
}
if (this._textElement) {
this._textElement.textContent = 'expand all';
}
}
onClick() {
this._isOpen = !this._isOpen;
if (this._isOpen) {
this.open();
}
else {
this.close();
}
}
onDataUpdated() {
this._details = document.querySelectorAll('.detail');
}
}
const setSeedOpenStateSwitcher = (model) => {
const btn = document.getElementById('btn-toggle-visible-state');
if (btn) {
new SeedOpenStateSwitcher(btn, model);
}
};
const model = new Model();
new Renderer(model);
let ws;
try {
if (window.enableWebSocket) {
ws = new WebSocket(`ws://${location.host}`);
}
}
catch (err) {
console.warn('Couldn\'t connect to analyzer websocket server so you\'ll have to reload page manually to see updates in the treemap');
}
window.addEventListener('load', () => {
if (ws) {
ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
setSeedOpenStateSwitcher(model);
model.data = msg;
setSettings(model);
});
}
else {
console.warn('Couldn\'t connect to analyzer websocket server so you\'ll have to reload page manually to see updates in the treemap');
}
});
class e{constructor(e){this.type=e,this.currentTarget=null}}e.COMPLETE="complete",e.CHANGE_PROPERTY="changeProperty";class t{constructor(e,t,s=0){this.type=e,this.handler=t,this.priority=s}equalCurrentListener(e,t){return this.type===e&&this.handler===t}}class s extends class{constructor(){this.listeners={}}dispatchEvent(t){let s,i;if(t instanceof e?(i=t.type,s=t):(i=t,s=new e(i)),null!==this.listeners[i]){const e=this.listeners[i].length;s.currentTarget=this;for(let t=0;t<e;t++){const e=this.listeners[i][t];try{e.handler(s)}catch(e){window.console&&console.error(e.stack)}}}else console.warn('implement "addEventListener" before dispatch')}addEventListener(e,s,i=0){this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].push(new t(e,s,i)),this.listeners[e].sort(((e,t)=>t.priority-e.priority))}removeEventListener(e,t){if(this.hasEventListener(e,t))for(let s=0;s<this.listeners[e].length;s++){if(this.listeners[e][s].equalCurrentListener(e,t))return void this.listeners[e].splice(s,1)}}clearEventListener(){this.listeners={}}containEventListener(e){return!!this.listeners[e]&&this.listeners[e].length>0}hasEventListener(e,t){if(!this.listeners[e])return!1;for(let s=0;s<this.listeners[e].length;s++){if(this.listeners[e][s].equalCurrentListener(e,t))return!0}return!1}}{constructor(){super(),this._data={entries:[],count:{}},this._viewType="GRAPH",this._visibleSettings=!1,this._visibleProps=!0,this._visibleFileSize=!0,this._visibleLastUpdated=!0,this._visibleReferenceCount=!0,this.getHowManyDaysAgo=e=>{const t=e.getTime(),s=this._todayTime-t;return Math.floor(s/864e5)},this._today=new Date,this._todayTime=this._today.getTime()}get data(){return this._data}set data(e){this._data=e,this.dispatchEvent(s.EVENT.DATA_UPDATE)}get viewType(){return this._viewType}set viewType(e){this._viewType=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}get visibleSettings(){return this._visibleSettings}set visibleSettings(e){this._visibleSettings=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}get visibleProps(){return this._visibleProps}set visibleProps(e){this._visibleProps=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}get visibleFileSize(){return this._visibleFileSize}set visibleFileSize(e){this._visibleFileSize=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}get visibleLastUpdated(){return this._visibleLastUpdated}set visibleLastUpdated(e){this._visibleLastUpdated=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}get visibleReferenceCount(){return this._visibleReferenceCount}set visibleReferenceCount(e){this._visibleReferenceCount=e,this.dispatchEvent(s.EVENT.SETTING_CHANGED)}}s.EVENT={DATA_UPDATE:"dataUpdate",SETTING_CHANGED:"settingChanged"};class i{constructor(e,t,s){this._model=s,this._children=[],this._name=e.name,this._props=e.props,this._fileSize=e.size,this._lastModifiedTime=e.lastModifiedTime,this._count=t}renderChildren(){let e="";for(let t=0,s=this._children.length;t<s;t++)e+=this._children[t].render();return`<div class="group">\n ${e}\n </div>`}renderProps(){return this._props?`<pre class="file__props">props: ${JSON.stringify(this._props,null,"\t")}</pre>`:""}renderMetaData(){let e="";const t=(this._fileSize/1024).toFixed(2);return t&&(e+=`<span class="file__meta meta__fileSize">FileSize: ${t} KB</span>`),e+=`<span class="file__meta meta__lastUpdated">LastUpdated: ${0===this._lastModifiedTime?0:this._model.getHowManyDaysAgo(new Date(this._lastModifiedTime))} days ago</span>`,e}renderSummary(){let e=this._name;return this._name.endsWith(".vue")?e=`<img class="icon" src="https://v3.vuejs.org/logo.png" alt="">${e}`:this._name.endsWith(".js")?e=`<img class="icon" src="https://raw.githubusercontent.com/voodootikigod/logo.js/master/js.png" alt="">${e}`:this._name.endsWith(".ts")&&(e=`<img class="icon" src="../dist/icon/icon-ts.png" alt="">${e}`),`<summary>${e}</summary>`}renderDetails(){return`<details class="detail">\n ${this.renderSummary()}\n ${this.renderProps()}\n ${this.renderMetaData()}\n </details>`}render(){const e=this.renderDetails();let t="",s="";return this._children.length>0?t=this.renderChildren():s=" -no-child",this.isJS()&&(s+=" js"),`<div class="seed${s}">\n <div class="file">\n <div class="filename">\n <div class="file__text">${e}</div>\n ${this.getCountText()}\n </div>\n </div>\n ${t}\n </div>`}getCountText(){return 0===this._count?"":1===this._count?'<span class="file__count">1 time referenced.</span>':`<span class="file__count">${this._count} times referenced.</span>`}get children(){return this._children}set children(e){this._children=e}isJS(){return this._name.endsWith(".js")}}class n{constructor(e,t){this._elm=e,this._model=t,this._type=e.dataset.settingName,e.addEventListener("change",this.onChange.bind(this))}onChange(){switch(this._type){case"props":this._model.visibleProps=this._elm.checked;break;case"fileSize":this._model.visibleFileSize=this._elm.checked;break;case"lastUpdated":this._model.visibleLastUpdated=this._elm.checked;break;case"referenceCount":this._model.visibleReferenceCount=this._elm.checked;break;default:throw new Error(`not supported type: ${this._type}`)}}}class r{constructor(e,t,s){this._elm=e,this._type=t,this._model=s,e.addEventListener("change",this.onChange.bind(this))}onChange(){this._model.viewType=this._type}}class l{constructor(e,t){this._isOpen=!1,this._details=null,this._textElement=document.getElementById("btn-toggle-visible-state-text"),e.addEventListener("click",this.onClick.bind(this)),t.addEventListener(s.EVENT.DATA_UPDATE,this.onDataUpdated.bind(this))}open(){this._details&&this._details.forEach((e=>{e.open=!0})),this._textElement&&(this._textElement.textContent="collapse all")}close(){this._details&&this._details.forEach((e=>{e.open=!1})),this._textElement&&(this._textElement.textContent="expand all")}onClick(){this._isOpen=!this._isOpen,this._isOpen?this.open():this.close()}onDataUpdated(){this._details=document.querySelectorAll(".detail")}}const a=new s;let h;new class{constructor(e){this._model=e,this._tree=[],this.ready=()=>{if("GRAPH"===this._model.viewType){const{data:e}=this._model,{entries:t}=e;for(let s=0,n=t.length;s<n;s++){const n=t[s];if(e){const e=new i(n,0,this._model);this._tree.push(this.generateSeed(n,e))}}}this.render()},this._app=document.getElementById("app"),e.addEventListener(s.EVENT.DATA_UPDATE,this.ready)}generateSeed(e,t){const s=[],{children:n}=e,r=[],{count:l}=this._model.data;for(let e=0,t=n.length;e<t;e++){const t=n[e],s=new i(t,l[t.name],this._model);r.push(s),this.generateSeed(t,s)}return t.children=r,s.push(t),s}renderLine(e,t,s){let i=" ";for(let e=0;e<t;e++)i+="│ ";return i+=s?"└─ ":"├─ ",i+=e,`${i}\n`}renderEntry(e,t){let s="";for(let i=0,n=e.children.length;i<n;i++){const r=e.children[i];s+=this.renderLine(r.name,t,i===n-1),s+=this.renderEntry(r,t+1)}return s}render(){let e="";for(let t=0,s=this._tree.length;t<s;t++){const[s]=this._tree[t];e+=s.render()}e=`<div class="root html">${e}</div>`;const{entries:t}=this._model.data;let s="";for(let e=0,i=t.length;e<i;e++){s+=`${t[e].name}\n`,s+=this.renderEntry(t[e],0),e<i-1&&(s+="\n")}s=`<div class="root text"><pre class="tree">${s}</pre></div>`,this._app&&(this._app.innerHTML=e+s)}}(a);try{window.enableWebSocket&&(h=new WebSocket(`ws://${location.host}`))}catch(e){console.warn("Couldn't connect to analyzer websocket server so you'll have to reload page manually to see updates in the treemap")}window.addEventListener("load",(()=>{h?h.addEventListener("message",(e=>{const t=JSON.parse(e.data);(e=>{const t=document.getElementById("btn-toggle-visible-state");t&&new l(t,e)})(a),a.data=t,function(e){const{body:t}=document,i=document.getElementById("btn-settings"),l=t.querySelectorAll(".js-settings-toggle"),a=document.getElementById("js-view-switch-graph"),h=document.getElementById("js-view-switch-text");new r(a,"GRAPH",e),new r(h,"TEXT",e);const o=function(){t.className=`${e.viewType}`,e.visibleSettings&&t.classList.add("show-settings"),e.visibleProps||t.classList.add("no-props"),e.visibleFileSize||t.classList.add("no-fileSize"),e.visibleLastUpdated||t.classList.add("no-lastUpdated"),e.visibleReferenceCount||t.classList.add("no-referenceCount")};l.forEach((t=>{new n(t,e)})),e.addEventListener(s.EVENT.SETTING_CHANGED,o),i&&i.addEventListener("click",(()=>{e.visibleSettings=!e.visibleSettings})),o()}(a)})):console.warn("Couldn't connect to analyzer websocket server so you'll have to reload page manually to see updates in the treemap")}));
/*!
@tkskto/vue-component-analyzer v0.3.1
@tkskto/vue-component-analyzer v0.3.2
https://github.com/tkskto/

@@ -12,460 +12,5 @@ Released under the MIT License.

opener -- 1.5.2
vue-eslint-parser -- 7.7.2
ws -- 7.5.2
vue-eslint-parser -- 7.9.0
ws -- 7.5.3
*/
'use strict';
var require$$1 = require('fs');
var require$$2 = require('path');
var require$$0 = require('vue-eslint-parser');
var require$$1$1 = require('http');
var require$$2$1 = require('ejs');
var require$$3 = require('express');
var require$$4 = require('ws');
var require$$5 = require('opener');
var require$$6 = require('mkdirp');
var require$$7 = require('commander');
var require$$8 = require('globby');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1);
var require$$2__default = /*#__PURE__*/_interopDefaultLegacy(require$$2);
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
var require$$1__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$1$1);
var require$$2__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$2$1);
var require$$3__default = /*#__PURE__*/_interopDefaultLegacy(require$$3);
var require$$4__default = /*#__PURE__*/_interopDefaultLegacy(require$$4);
var require$$5__default = /*#__PURE__*/_interopDefaultLegacy(require$$5);
var require$$6__default = /*#__PURE__*/_interopDefaultLegacy(require$$6);
var require$$7__default = /*#__PURE__*/_interopDefaultLegacy(require$$7);
var require$$8__default = /*#__PURE__*/_interopDefaultLegacy(require$$8);
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
var _temp = {};
var Constant = {};
Object.defineProperty(Constant, "__esModule", { value: true });
Constant.FORMAT = void 0;
Constant.FORMAT = {
BROWSER: 'browser',
JSON: 'json',
BOTH: 'both',
};
var Model$1 = {};
Object.defineProperty(Model$1, "__esModule", { value: true });
Model$1.model = void 0;
const Constant_1$1 = Constant;
class Model {
constructor() {
this._resourceRoot = 'src';
this._format = Constant_1$1.FORMAT.BROWSER;
this._outDirectory = 'out';
this._port = '8888';
this._isSilentMode = false;
}
get resourceRoot() {
return this._resourceRoot;
}
set resourceRoot(value) {
this._resourceRoot = value;
}
get format() {
return this._format;
}
set format(value) {
this._format = value;
}
get outDirectory() {
return this._outDirectory;
}
set outDirectory(value) {
this._outDirectory = value;
}
get port() {
return this._port;
}
set port(value) {
this._port = value;
}
get isSilentMode() {
return this._isSilentMode;
}
set isSilentMode(value) {
this._isSilentMode = value;
}
}
Model$1.model = new Model();
var Analyzer$2 = {};
var utils = {};
Object.defineProperty(utils, "__esModule", { value: true });
utils.resolveFile = utils.getDeclarationSyntax = utils.getImportDeclaration = void 0;
const Model_1$2 = Model$1;
const { existsSync } = require$$1__default['default'];
const { resolve, extname, dirname } = require$$2__default['default'];
const getImportDeclaration = (nodeArr) => {
return nodeArr.filter((node) => node.type === 'ImportDeclaration');
};
utils.getImportDeclaration = getImportDeclaration;
const getDeclarationSyntax = (tokens, targetKeyName) => {
let isTargetToken = false;
let result = '{';
let closedCount = 0;
const needQuotingTypes = ['Identifier', 'Boolean', 'Keyword'];
for (const token of tokens) {
const { type, value } = token;
if (isTargetToken || (!isTargetToken && type === 'Identifier' && value === targetKeyName)) {
const needQuoting = needQuotingTypes.includes(type);
isTargetToken = true;
if (type === 'Punctuator') {
if (value === '{') {
closedCount++;
}
else if (value === '}') {
closedCount--;
if (result[result.length - 1] === ',') {
result = result.slice(0, -1);
}
if (closedCount === 0) {
result += '}';
break;
}
}
}
if (needQuoting) {
result += '"';
}
result += value.replace(/'/ug, '"');
if (needQuoting) {
result += '"';
}
}
}
return `${result}}`;
};
utils.getDeclarationSyntax = getDeclarationSyntax;
const resolveFile = (_filename, _currentFileName) => {
let filename = '';
if (_filename.startsWith('../')) {
filename = resolve(dirname(_currentFileName), _filename);
}
else if (_filename.startsWith('./')) {
filename = `${dirname(_currentFileName)}/${_filename.replace(/\.\/|/ug, '')}`;
}
else if (_filename.startsWith('~') || _filename.startsWith('@')) {
filename = _filename.replace('~', Model_1$2.model.resourceRoot).replace('@', Model_1$2.model.resourceRoot);
}
if (filename) {
if (extname(filename) === '') {
if (existsSync(`${filename}.vue`)) {
return `${filename}.vue`;
}
else if (existsSync(`${filename}.js`)) {
return `${filename}.js`;
}
else if (existsSync(`${filename}.ts`)) {
return `${filename}.ts`;
}
}
}
return filename;
};
utils.resolveFile = resolveFile;
var FileCounter$1 = {};
Object.defineProperty(FileCounter$1, "__esModule", { value: true });
FileCounter$1.FileCounter = void 0;
const path_1$1 = require$$2__default['default'];
class FileCounter {
constructor() {
this._count = {};
}
add(_filename) {
const filename = path_1$1.resolve(_filename);
if (Object.prototype.hasOwnProperty.call(this._count, filename)) {
this._count[filename]++;
}
else {
this._count[filename] = 1;
}
}
get count() {
return this._count;
}
}
FileCounter$1.FileCounter = FileCounter;
var VueComponent$1 = {};
Object.defineProperty(VueComponent$1, "__esModule", { value: true });
VueComponent$1.VueComponent = void 0;
const vue_eslint_parser_1 = require$$0__default['default'];
const utils_1$1 = utils;
const parserOption = {
ecmaVersion: 2018,
sourceType: 'module',
};
class VueComponent {
constructor(filename, contents, stats) {
var _a, _b, _c;
this._filename = '';
this._lastModifiedTime = 0;
this._size = 0;
this._template = '';
this._style = '';
this._props = '';
this._children = [];
this._importDeclaration = [];
this.getProps = (tokens) => {
try {
const propsDeclaration = JSON.parse(utils_1$1.getDeclarationSyntax(tokens, 'props'));
if (propsDeclaration && propsDeclaration.props) {
return propsDeclaration.props;
}
return '';
}
catch (err) {
console.warn('failed to analyze props.');
return '';
}
};
this._filename = filename;
this._lastModifiedTime = (stats === null || stats === void 0 ? void 0 : stats.mtimeMs) || 0;
this._size = (stats === null || stats === void 0 ? void 0 : stats.size) || 0;
const templateBody = contents.match(/(?<template><template>[\s\S]*<\/template>)/u);
const scriptBody = contents.match(/(?<script><script>[\s\S]*<\/script>)/u);
const styleBody = contents.match(/(?<style><style>[\s\S]*<\/style>)/u);
this._template = ((_a = templateBody === null || templateBody === void 0 ? void 0 : templateBody.groups) === null || _a === void 0 ? void 0 : _a.template) || '';
this._style = ((_b = styleBody === null || styleBody === void 0 ? void 0 : styleBody.groups) === null || _b === void 0 ? void 0 : _b.template) || '';
const scriptString = ((_c = scriptBody === null || scriptBody === void 0 ? void 0 : scriptBody.groups) === null || _c === void 0 ? void 0 : _c.script) || '';
const esLintProgram = vue_eslint_parser_1.parse(scriptString, parserOption);
if (esLintProgram.tokens) {
this._props = this.getProps(esLintProgram.tokens);
}
this._importDeclaration = utils_1$1.getImportDeclaration(esLintProgram.body);
}
addChildReport(report) {
this._children.push(report);
}
get importDeclaration() {
return this._importDeclaration;
}
getFileReport(isTest) {
return {
name: this._filename,
props: this._props,
size: this._size,
lastModifiedTime: isTest ? 0 : Number(this._lastModifiedTime.toFixed(0)),
children: this._children,
};
}
}
VueComponent$1.VueComponent = VueComponent;
Object.defineProperty(Analyzer$2, "__esModule", { value: true });
Analyzer$2.Analyzer = void 0;
const utils_1 = utils;
const fs_1 = require$$1__default['default'];
const path_1 = require$$2__default['default'];
const FileCounter_1 = FileCounter$1;
const VueComponent_1 = VueComponent$1;
const Model_1$1 = Model$1;
const cwd = process.cwd();
class Analyzer$1 {
constructor() {
this.getImportDeclarationTree = (fileName, parents, isTest = false) => {
const filename = path_1.resolve(cwd, fileName);
const shortFilename = filename.replace(cwd, '');
const stat = fs_1.statSync(filename);
const ancestorList = parents.concat();
ancestorList.push(fileName);
if (!Model_1$1.model.isSilentMode) {
console.log(`read ${filename}`);
}
this._counter.add(shortFilename);
if (path_1.extname(filename) === '' || path_1.extname(filename) !== '.vue') {
return {
name: shortFilename,
props: '',
size: stat.size,
lastModifiedTime: isTest ? 0 : Number(stat.mtimeMs.toFixed(0)),
children: [],
};
}
const contents = fs_1.readFileSync(filename, 'utf-8');
const component = new VueComponent_1.VueComponent(shortFilename, contents, stat);
try {
for (let i = 0, len = component.importDeclaration.length; i < len; i++) {
const source = String(component.importDeclaration[i].source.value);
if (source) {
const nextFilename = utils_1.resolveFile(source, filename);
if (nextFilename) {
if (parents.includes(nextFilename)) {
console.warn(`Circular dependency detected between ${nextFilename} and ${filename}`);
}
else {
component.addChildReport(this.getImportDeclarationTree(nextFilename, ancestorList, isTest));
}
}
}
}
}
catch (err) {
console.error(`Something went wrong with reading ${filename}`);
console.error(err.message);
}
return component.getFileReport(isTest);
};
this._counter = new FileCounter_1.FileCounter();
}
get counter() {
return this._counter;
}
}
Analyzer$2.Analyzer = Analyzer$1;
var server = {};
Object.defineProperty(server, "__esModule", { value: true });
const path$1 = require$$2__default['default'];
const http = require$$1__default$1['default'];
const { renderFile } = require$$2__default$1['default'];
const express = require$$3__default['default'];
const webSocket = require$$4__default['default'];
const opener = require$$5__default['default'];
const projectRoot = path$1.resolve(__dirname, '..');
const startServer$1 = (port, json) => {
const HOST = '127.0.0.1';
const app = express();
app.engine('ejs', renderFile);
app.set('view engine', 'ejs');
app.set('views', `${projectRoot}/views`);
app.use(express.static(`${projectRoot}/`));
const server = http.createServer(app);
const wss = new webSocket.Server({
server,
});
app.use('/', (req, res) => {
res.render('viewer', {
mode: 'server',
title: 'analyze report',
enableWebSocket: true,
});
});
server.on('error', (err) => {
console.log(err);
if (server.listening) {
server.close((serverErr) => {
console.log(serverErr);
});
}
});
server.listen(port, HOST, () => {
const addressPort = server.address().port || port;
const url = `http://${HOST}:${addressPort}/`;
console.log(`Vue Component Analyzer is started at ${url}`);
console.log('Use \'Ctrl+C\' to close it');
opener(url);
wss.on('connection', (ws) => {
wss.clients.forEach((client) => {
client.send(JSON.stringify(json));
});
ws.addEventListener('error', () => {
console.error('Something went to wrong on web socket.');
});
});
});
};
server.startServer = startServer$1;
var __awaiter = (commonjsGlobal && commonjsGlobal.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(_temp, "__esModule", { value: true });
const Constant_1 = Constant;
const Model_1 = Model$1;
const { Analyzer } = Analyzer$2;
const { startServer } = server;
const { writeFileSync } = require$$1__default['default'];
const path = require$$2__default['default'];
const mkdirp = require$$6__default['default'];
const { program } = require$$7__default['default'];
const globby = require$$8__default['default'];
function writeFileExtra(filename, data) {
mkdirp(path.dirname(filename)).then(() => {
writeFileSync(filename, data);
}).catch((err) => {
if (err) {
throw new Error(err.message);
}
});
}
(() => __awaiter(void 0, void 0, void 0, function* () {
try {
program.option('--dir [dir]', 'root directory of src.', 'src');
program.option('-f, --format [type]', 'Add the specified type of report [browser, json or both]', Constant_1.FORMAT.BROWSER);
program.option('-o, --out [dir]', 'output directory (enable with setting --format option to "json" or "both")', 'out');
program.option('-p, --port [number]', 'select a port number for the local server', '8888');
program.option('--silent', 'do not show logs with silent flag');
program.parse(process.argv);
const argv = program.opts();
Model_1.model.resourceRoot = argv.dir;
Model_1.model.format = argv.format;
Model_1.model.outDirectory = argv.out;
Model_1.model.port = argv.port;
Model_1.model.isSilentMode = argv.silent || false;
if (argv.format !== Constant_1.FORMAT.BROWSER && argv.format !== Constant_1.FORMAT.JSON && argv.format !== Constant_1.FORMAT.BOTH) {
console.error(`not support ${argv.format} format.`);
}
console.log('start analyzing.');
const analyzer = new Analyzer();
const entries = yield globby([argv.dir, '!**/node_modules/**'], {
expandDirectories: {
extensions: ['vue'],
},
});
if (entries.length === 0) {
console.log('There is no entry file.');
}
const entriesData = [];
for (let i = 0, len = entries.length; i < len; i++) {
const entryFile = entries[i];
const children = analyzer.getImportDeclarationTree(entryFile, []);
entriesData.push(children);
}
const result = {
entries: entriesData,
count: analyzer.counter.count,
};
if (argv.format === Constant_1.FORMAT.BOTH) {
startServer(argv.port, result);
writeFileExtra(path.resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4));
}
else if (argv.format === Constant_1.FORMAT.BROWSER) {
startServer(argv.port, result);
}
else if (argv.format === Constant_1.FORMAT.JSON) {
writeFileExtra(path.resolve(process.cwd(), `${argv.out}/result.json`), JSON.stringify(result, null, 4));
}
console.log('finished analyzing.');
}
catch (err) {
console.error(err.message);
}
}))();
module.exports = _temp;
"use strict";var e=require("commander"),t=require("fs"),o=require("path"),r=require("vue-eslint-parser"),s=require("express"),i=require("ws"),n=require("http"),l=require("ejs"),a=require("opener"),u=require("mkdirp"),c=require("globby");function d(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var p=d(e),f=d(t),m=d(o),h=d(r),v=d(s),g=d(i),_=d(n),y=d(l),S=d(a),w=d(u),O=d(c),b="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},M={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.FORMAT=void 0,R.FORMAT={BROWSER:"browser",JSON:"json",BOTH:"both"};var D={};Object.defineProperty(D,"__esModule",{value:!0}),D.model=void 0;const T=R;D.model=new class{constructor(){this._resourceRoot="src",this._format=T.FORMAT.BROWSER,this._outDirectory="out",this._port="8888",this._isSilentMode=!1}get resourceRoot(){return this._resourceRoot}set resourceRoot(e){this._resourceRoot=e}get format(){return this._format}set format(e){this._format=e}get outDirectory(){return this._outDirectory}set outDirectory(e){this._outDirectory=e}get port(){return this._port}set port(e){this._port=e}get isSilentMode(){return this._isSilentMode}set isSilentMode(e){this._isSilentMode=e}};var F={};Object.defineProperty(F,"__esModule",{value:!0}),F.getOptions=void 0;const j=R,$=p.default;F.getOptions=e=>($.program.option("--dir [dir]","root directory of src.","src"),$.program.option("-f, --format [type]","Add the specified type of report [browser, json or both]",j.FORMAT.BROWSER),$.program.option("-o, --out [dir]",'output directory (enable with setting --format option to "json" or "both")',"out"),$.program.option("-p, --port [number]","select a port number for the local server","8888"),$.program.option("--silent","do not show logs with silent flag"),$.program.parse(e),$.program.opts());var x={},z={};Object.defineProperty(z,"__esModule",{value:!0}),z.resolveFile=z.getDeclarationSyntax=z.getImportDeclaration=void 0;const P=D,A=f.default,C=m.default;z.getImportDeclaration=e=>e.filter((e=>"ImportDeclaration"===e.type));z.getDeclarationSyntax=(e,t)=>{let o=!1,r="{",s=0;const i=["Identifier","Boolean","Keyword"];for(const n of e){const{type:e,value:l}=n;if(o||!o&&"Identifier"===e&&l===t){const t=i.includes(e);if(o=!0,"Punctuator"===e)if("{"===l)s++;else if("}"===l&&(s--,","===r[r.length-1]&&(r=r.slice(0,-1)),0===s)){r+="}";break}t&&(r+='"'),r+=l.replace(/'/gu,'"'),t&&(r+='"')}}return`${r}}`};z.resolveFile=(e,t)=>{let o="";if(e.startsWith("../")?o=C.resolve(C.dirname(t),e):e.startsWith("./")?o=`${C.dirname(t)}/${e.replace(/\.\/|/gu,"")}`:(e.startsWith("~")||e.startsWith("@"))&&(o=e.replace("~",P.model.resourceRoot).replace("@",P.model.resourceRoot)),o&&""===C.extname(o)){if(A.existsSync(`${o}.vue`))return`${o}.vue`;if(A.existsSync(`${o}.js`))return`${o}.js`;if(A.existsSync(`${o}.ts`))return`${o}.ts`}return o};var q={};Object.defineProperty(q,"__esModule",{value:!0}),q.FileCounter=void 0;const N=m.default;q.FileCounter=class{constructor(){this._count={}}add(e){const t=N.resolve(e);Object.prototype.hasOwnProperty.call(this._count,t)?this._count[t]++:this._count[t]=1}get count(){return this._count}};var W={};Object.defineProperty(W,"__esModule",{value:!0}),W.VueComponent=void 0;const B=h.default,I=z,E={ecmaVersion:2018,sourceType:"module"};W.VueComponent=class{constructor(e,t,o){var r,s,i;this._filename="",this._lastModifiedTime=0,this._size=0,this._template="",this._style="",this._props="",this._children=[],this._importDeclaration=[],this.getProps=e=>{try{const t=JSON.parse(I.getDeclarationSyntax(e,"props"));return t&&t.props?t.props:""}catch(e){return console.warn("failed to analyze props."),""}},this._filename=e,this._lastModifiedTime=(null==o?void 0:o.mtimeMs)||0,this._size=(null==o?void 0:o.size)||0;const n=t.match(/(?<template><template>[\s\S]*<\/template>)/u),l=t.match(/(?<script><script>[\s\S]*<\/script>)/u),a=t.match(/(?<style><style>[\s\S]*<\/style>)/u);this._template=(null===(r=null==n?void 0:n.groups)||void 0===r?void 0:r.template)||"",this._style=(null===(s=null==a?void 0:a.groups)||void 0===s?void 0:s.template)||"";const u=(null===(i=null==l?void 0:l.groups)||void 0===i?void 0:i.script)||"",c=B.parse(u,E);c.tokens&&(this._props=this.getProps(c.tokens)),this._importDeclaration=I.getImportDeclaration(c.body)}addChildReport(e){this._children.push(e)}get importDeclaration(){return this._importDeclaration}getFileReport(e){return{name:this._filename,props:this._props,size:this._size,lastModifiedTime:e?0:Number(this._lastModifiedTime.toFixed(0)),children:this._children}}},Object.defineProperty(x,"__esModule",{value:!0}),x.analyzer=void 0;const J=z,k=f.default,V=m.default,H=q,K=W,L=D,U=process.cwd();x.analyzer=new class{constructor(){this.getImportDeclarationTree=(e,t,o=!1)=>{const r=V.resolve(U,e),s=r.replace(U,""),i=k.statSync(r),n=t.concat();if(n.push(e),L.model.isSilentMode||console.log(`read ${r}`),this._counter.add(s),""===V.extname(r)||".vue"!==V.extname(r))return{name:s,props:"",size:i.size,lastModifiedTime:o?0:Number(i.mtimeMs.toFixed(0)),children:[]};const l=k.readFileSync(r,"utf-8"),a=new K.VueComponent(s,l,i);try{for(let e=0,s=a.importDeclaration.length;e<s;e++){const s=String(a.importDeclaration[e].source.value);if(s){const e=J.resolveFile(s,r);e&&(t.includes(e)?console.warn(`Circular dependency detected between ${e} and ${r}`):a.addChildReport(this.getImportDeclarationTree(e,n,o)))}}}catch(e){console.error(`Something went wrong with reading ${r}`),console.error(e.message)}return a.getFileReport(o)},this._counter=new H.FileCounter}get counter(){return this._counter}};var G={},Q=b&&b.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(G,"__esModule",{value:!0}),G.startServer=void 0;const X=Q(v.default),Y=Q(g.default),Z=Q(m.default),ee=Q(_.default),te=y.default,oe=Q(S.default),re=Z.default.resolve(__dirname,"..");G.startServer=(e,t)=>{const o="127.0.0.1",r=X.default();r.engine("ejs",te.renderFile),r.set("view engine","ejs"),r.set("views",`${re}/views`),r.use(X.default.static(`${re}/`));const s=ee.default.createServer(r),i=new Y.default.Server({server:s});r.use("/",((e,t)=>{t.render("viewer",{mode:"server",title:"analyze report",enableWebSocket:!0})})),s.on("error",(e=>{console.log(e),s.listening&&s.close((e=>{console.log(e)}))})),s.listen(Number(e),o,(()=>{const r=null==s?void 0:s.address();let n=e;"string"==typeof r?n=r:"object"==typeof r&&(n=String(null==r?void 0:r.port)||e);const l=`http://${o}:${n}/`;console.log(`Vue Component Analyzer is started at ${l}`),console.log("Use 'Ctrl+C' to close it"),oe.default(l),i.on("connection",(e=>{i.clients.forEach((e=>{e.send(JSON.stringify(t))})),e.addEventListener("error",(()=>{console.error("Something went to wrong on web socket.")}))}))}))};var se=b&&b.__awaiter||function(e,t,o,r){return new(o||(o=Promise))((function(s,i){function n(e){try{a(r.next(e))}catch(e){i(e)}}function l(e){try{a(r.throw(e))}catch(e){i(e)}}function a(e){var t;e.done?s(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(n,l)}a((r=r.apply(e,t||[])).next())}))},ie=b&&b.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(M,"__esModule",{value:!0});const ne=R,le=D,ae=F,ue=x,ce=G,de=f.default,pe=ie(m.default),fe=ie(w.default),me=ie(O.default);function he(e,t){fe.default(pe.default.dirname(e)).then((()=>{de.writeFileSync(e,t)})).catch((e=>{if(e)throw new Error(e.message)}))}se(void 0,void 0,void 0,(function*(){try{const e=ae.getOptions(process.argv);le.model.resourceRoot=e.dir,le.model.format=e.format,le.model.outDirectory=e.out,le.model.port=e.port,le.model.isSilentMode=e.silent||!1,e.format!==ne.FORMAT.BROWSER&&e.format!==ne.FORMAT.JSON&&e.format!==ne.FORMAT.BOTH&&console.error(`not support ${e.format} format.`),console.log("start analyzing.");const t=yield me.default([e.dir,"!**/node_modules/**"],{expandDirectories:{extensions:["vue"]}});0===t.length&&console.log("There is no entry file.");const o=[];for(let e=0,r=t.length;e<r;e++){const r=t[e],s=ue.analyzer.getImportDeclarationTree(r,[]);o.push(s)}const r={entries:o,count:ue.analyzer.counter.count};e.format===ne.FORMAT.BOTH?(ce.startServer(e.port,r),he(pe.default.resolve(process.cwd(),`${e.out}/result.json`),JSON.stringify(r,null,4))):e.format===ne.FORMAT.BROWSER?ce.startServer(e.port,r):e.format===ne.FORMAT.JSON&&he(pe.default.resolve(process.cwd(),`${e.out}/result.json`),JSON.stringify(r,null,4)),console.log("finished analyzing.")}catch(e){console.error(e.message)}})),module.exports=M;
{
"name": "@tkskto/vue-component-analyzer",
"version": "0.3.1",
"version": "0.3.2",
"description": "Analyze dependency tree for Vue.js SFC (Single File Component)",

@@ -54,12 +54,14 @@ "main": "dist/index.js",

"opener": "1.5.2",
"vue-eslint-parser": "7.7.2",
"ws": "7.5.2"
"vue-eslint-parser": "7.9.0",
"ws": "7.5.3"
},
"devDependencies": {
"@mitsue/eslint-config": "4.0.1",
"@rollup/plugin-commonjs": "19.0.0",
"@rollup/plugin-commonjs": "19.0.1",
"@rollup/plugin-json": "4.1.0",
"@rollup/plugin-node-resolve": "13.0.0",
"@rollup/plugin-typescript": "8.2.1",
"@rollup/plugin-node-resolve": "13.0.2",
"@rollup/plugin-typescript": "8.2.3",
"@types/ejs": "3.0.7",
"@types/express": "4.17.12",
"@types/mkdirp": "1.0.2",
"@types/mocha": "8.2.2",

@@ -76,4 +78,5 @@ "@types/node": "14.17.4",

"nyc": "15.1.0",
"rollup": "2.52.7",
"rollup": "2.53.2",
"rollup-plugin-license": "2.5.0",
"rollup-plugin-terser": "7.0.2",
"ts-mocha": "8.0.0",

@@ -80,0 +83,0 @@ "typescript": "4.3.5"

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc