mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
feat(server): bigcapital cli commands
This commit is contained in:
282
packages/server/bin/bigcapital.js
Normal file
282
packages/server/bin/bigcapital.js
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const commander = require('commander');
|
||||||
|
const color = require('colorette');
|
||||||
|
const argv = require('getopts');
|
||||||
|
const Knex = require('knex');
|
||||||
|
const { knexSnakeCaseMappers } = require('objection');
|
||||||
|
const config = require('../src/config');
|
||||||
|
|
||||||
|
function initSystemKnex() {
|
||||||
|
return Knex({
|
||||||
|
client: config.system.db_client,
|
||||||
|
connection: {
|
||||||
|
host: config.system.db_host,
|
||||||
|
user: config.system.db_user,
|
||||||
|
password: config.system.db_password,
|
||||||
|
database: config.system.db_name,
|
||||||
|
charset: 'utf8',
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: config.system.migrations_dir,
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: config.system.seeds_dir,
|
||||||
|
},
|
||||||
|
pool: { min: 0, max: 7 },
|
||||||
|
...knexSnakeCaseMappers({ upperCase: true }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTenantKnex(organizationId) {
|
||||||
|
return Knex({
|
||||||
|
client: config.tenant.db_client,
|
||||||
|
connection: {
|
||||||
|
host: config.tenant.db_host,
|
||||||
|
user: config.tenant.db_user,
|
||||||
|
password: config.tenant.db_password,
|
||||||
|
database: `${config.tenant.db_name_prefix}${organizationId}`,
|
||||||
|
charset: config.tenant.charset,
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: config.tenant.migrations_dir,
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: config.tenant.seeds_dir,
|
||||||
|
},
|
||||||
|
pool: { min: 0, max: 5 },
|
||||||
|
...knexSnakeCaseMappers({ upperCase: true }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function exit(text) {
|
||||||
|
if (text instanceof Error) {
|
||||||
|
console.error(
|
||||||
|
color.red(`${text.detail ? `${text.detail}\n` : ''}${text.stack}`)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(color.red(text));
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function success(text) {
|
||||||
|
console.log(text);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
function log(text) {
|
||||||
|
console.log(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllSystemTenants(knex) {
|
||||||
|
return knex('tenants');
|
||||||
|
}
|
||||||
|
|
||||||
|
// module.exports = {
|
||||||
|
// log,
|
||||||
|
// success,
|
||||||
|
// exit,
|
||||||
|
// initSystemKnex,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// - bigcapital system:migrate:latest
|
||||||
|
// - bigcapital system:migrate:rollback
|
||||||
|
// - bigcapital tenants:migrate:latest
|
||||||
|
// - bigcapital tenants:migrate:latest --tenant_id=XXX
|
||||||
|
// - bigcapital tenants:migrate:rollback
|
||||||
|
// - bigcapital tenants:migrate:rollback --tenant_id=XXX
|
||||||
|
// - bigcapital tenants:migrate:make
|
||||||
|
// - bigcapital system:migrate:make
|
||||||
|
// - bigcapital tenants:list
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('system:migrate:rollback')
|
||||||
|
.description('Migrate the system database of the application.')
|
||||||
|
.action(async () => {
|
||||||
|
try {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
const [batchNo, _log] = await sysKnex.migrate.rollback();
|
||||||
|
|
||||||
|
if (_log.length === 0) {
|
||||||
|
success(color.cyan('Already at the base migration'));
|
||||||
|
}
|
||||||
|
success(
|
||||||
|
color.green(`Batch ${batchNo} rolled back: ${_log.length} migrations`) +
|
||||||
|
(argv.verbose ? `\n${color.cyan(_log.join('\n'))}` : '')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('system:migrate:latest')
|
||||||
|
.description('Migrate latest mgiration of the system database.')
|
||||||
|
.action(async () => {
|
||||||
|
try {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
const [batchNo, log] = await sysKnex.migrate.latest();
|
||||||
|
|
||||||
|
if (log.length === 0) {
|
||||||
|
success(color.cyan('Already up to date'));
|
||||||
|
}
|
||||||
|
success(
|
||||||
|
color.green(`Batch ${batchNo} run: ${log.length} migrations`) +
|
||||||
|
(argv.verbose ? `\n${color.cyan(log.join('\n'))}` : '')
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('system:migrate:make <name>')
|
||||||
|
.description('Created a named migration file to the system database.')
|
||||||
|
.action(async (name) => {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
|
||||||
|
sysKnex.migrate
|
||||||
|
.make(name)
|
||||||
|
.then((name) => {
|
||||||
|
success(color.green(`Created Migration: ${name}`));
|
||||||
|
})
|
||||||
|
.catch(exit);
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('tenants:list')
|
||||||
|
.description('Retrieve a list of all system tenants databases.')
|
||||||
|
.action(async (cmd) => {
|
||||||
|
try {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
const tenants = await getAllSystemTenants(sysKnex);
|
||||||
|
|
||||||
|
tenants.forEach((tenant) => {
|
||||||
|
const dbName = `${config.tenant.db_name_prefix}${tenant.organizationId}`;
|
||||||
|
console.log(
|
||||||
|
`ID: ${tenant.id} | Organization ID: ${tenant.organizationId} | DB Name: ${dbName}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
success('---');
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('tenants:migrate:make <name>')
|
||||||
|
.description('Created a named migration file to the tenants database.')
|
||||||
|
.action(async (name) => {
|
||||||
|
const sysKnex = await initTenantKnex();
|
||||||
|
|
||||||
|
sysKnex.migrate
|
||||||
|
.make(name)
|
||||||
|
.then((name) => {
|
||||||
|
success(color.green(`Created Migration: ${name}`));
|
||||||
|
})
|
||||||
|
.catch(exit);
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('tenants:migrate:latest')
|
||||||
|
.description('Migrate all tenants or the given tenant id.')
|
||||||
|
.option('-t, --tenant_id [tenant_id]', 'Which tenant id do you migrate.')
|
||||||
|
.action(async (cmd) => {
|
||||||
|
try {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
const tenants = await getAllSystemTenants(sysKnex);
|
||||||
|
const tenantsOrgsIds = tenants.map((tenant) => tenant.organizationId);
|
||||||
|
|
||||||
|
if (cmd.tenant_id && tenantsOrgsIds.indexOf(cmd.tenant_id) === -1) {
|
||||||
|
exit(`The given tenant id ${cmd.tenant_id} is not exists.`);
|
||||||
|
}
|
||||||
|
// Validate the tenant id exist first of all.
|
||||||
|
const migrateOpers = [];
|
||||||
|
const migrateTenant = async (organizationId) => {
|
||||||
|
try {
|
||||||
|
const tenantKnex = await initTenantKnex(organizationId);
|
||||||
|
const [batchNo, _log] = await tenantKnex.migrate.latest();
|
||||||
|
|
||||||
|
const tenantDb = `${config.tenant.db_name_prefix}${organizationId}`;
|
||||||
|
|
||||||
|
if (_log.length === 0) {
|
||||||
|
log(color.cyan('Already up to date'));
|
||||||
|
}
|
||||||
|
log(
|
||||||
|
color.green(
|
||||||
|
`Tenant ${tenantDb} > Batch ${batchNo} run: ${_log.length} migrations`
|
||||||
|
) + (argv.verbose ? `\n${color.cyan(log.join('\n'))}` : '')
|
||||||
|
);
|
||||||
|
log('-------------------');
|
||||||
|
} catch (error) {
|
||||||
|
log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!cmd.tenant_id) {
|
||||||
|
tenants.forEach((tenant) => {
|
||||||
|
const oper = migrateTenant(tenant.organizationId);
|
||||||
|
migrateOpers.push(oper);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const oper = migrateTenant(cmd.tenant_id);
|
||||||
|
migrateOpers.push(oper);
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(migrateOpers).then(() => {
|
||||||
|
success('All tenants are migrated.');
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commander
|
||||||
|
.command('tenants:migrate:rollback')
|
||||||
|
.description('Rollback the last batch of tenants migrations.')
|
||||||
|
.option('-t, --tenant_id [tenant_id]', 'Which tenant id do you migrate.')
|
||||||
|
.action(async (cmd) => {
|
||||||
|
try {
|
||||||
|
const sysKnex = await initSystemKnex();
|
||||||
|
const tenants = await getAllSystemTenants(sysKnex);
|
||||||
|
const tenantsOrgsIds = tenants.map((tenant) => tenant.organizationId);
|
||||||
|
|
||||||
|
if (cmd.tenant_id && tenantsOrgsIds.indexOf(cmd.tenant_id) === -1) {
|
||||||
|
exit(`The given tenant id ${cmd.tenant_id} is not exists.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const migrateOpers = [];
|
||||||
|
const migrateTenant = async (organizationId) => {
|
||||||
|
try {
|
||||||
|
const tenantKnex = await initTenantKnex(organizationId);
|
||||||
|
const [batchNo, _log] = await tenantKnex.migrate.rollback();
|
||||||
|
const tenantDb = `${config.tenant.db_name_prefix}${organizationId}`;
|
||||||
|
|
||||||
|
if (_log.length === 0) {
|
||||||
|
log(color.cyan('Already at the base migration'));
|
||||||
|
}
|
||||||
|
log(
|
||||||
|
color.green(
|
||||||
|
`Tenant: ${tenantDb} > Batch ${batchNo} rolled back: ${_log.length} migrations`
|
||||||
|
) + (argv.verbose ? `\n${color.cyan(_log.join('\n'))}` : '')
|
||||||
|
);
|
||||||
|
log('---------------');
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!cmd.tenant_id) {
|
||||||
|
tenants.forEach((tenant) => {
|
||||||
|
const oper = migrateTenant(tenant.organizationId);
|
||||||
|
migrateOpers.push(oper);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const oper = migrateTenant(cmd.tenant_id);
|
||||||
|
migrateOpers.push(oper);
|
||||||
|
}
|
||||||
|
Promise.all(migrateOpers).then(() => {
|
||||||
|
success('All tenants are rollbacked.');
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
exit(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
commander.parse();
|
||||||
51
packages/server/bin/utils.js
Normal file
51
packages/server/bin/utils.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
const Knex = require('knex');
|
||||||
|
const { knexSnakeCaseMappers } = require('objection');
|
||||||
|
const color = require('colorette');
|
||||||
|
const config = require('./config');
|
||||||
|
|
||||||
|
function initSystemKnex() {
|
||||||
|
return Knex({
|
||||||
|
client: config.system.db_client,
|
||||||
|
connection: {
|
||||||
|
host: config.system.db_host,
|
||||||
|
user: config.system.db_user,
|
||||||
|
password: config.system.db_password,
|
||||||
|
database: config.system.db_name,
|
||||||
|
charset: 'utf8',
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
directory: config.system.migrations_dir,
|
||||||
|
},
|
||||||
|
seeds: {
|
||||||
|
directory: config.system.seeds_dir,
|
||||||
|
},
|
||||||
|
pool: { min: 0, max: 7 },
|
||||||
|
...knexSnakeCaseMappers({ upperCase: true }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function exit(text) {
|
||||||
|
if (text instanceof Error) {
|
||||||
|
console.error(
|
||||||
|
color.red(`${text.detail ? `${text.detail}\n` : ''}${text.stack}`)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(color.red(text));
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function success(text) {
|
||||||
|
console.log(text);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
function log(text) {
|
||||||
|
console.log(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
log,
|
||||||
|
success,
|
||||||
|
exit,
|
||||||
|
initSystemKnex,
|
||||||
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import dotenv from 'dotenv';
|
const dotenv = require('dotenv');
|
||||||
|
|
||||||
// Set the NODE_ENV to 'development' by default
|
// Set the NODE_ENV to 'development' by default
|
||||||
// process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
// process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
@@ -9,7 +9,7 @@ if (envFound.error) {
|
|||||||
throw new Error("⚠️ Couldn't find .env file ⚠️");
|
throw new Error("⚠️ Couldn't find .env file ⚠️");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* Your favorite port
|
* Your favorite port
|
||||||
*/
|
*/
|
||||||
42
packages/webapp/package-lock.json
generated
42
packages/webapp/package-lock.json
generated
@@ -1144,6 +1144,11 @@
|
|||||||
"@babel/plugin-transform-typescript": "^7.9.0"
|
"@babel/plugin-transform-typescript": "^7.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@babel/regjsgen": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
|
||||||
|
},
|
||||||
"@babel/runtime": {
|
"@babel/runtime": {
|
||||||
"version": "7.20.13",
|
"version": "7.20.13",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
|
||||||
@@ -3516,9 +3521,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"async-each": {
|
"async-each": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
|
||||||
"integrity": "sha512-5QzqtU3BlagehwmdoqwaS2FBQF2P5eL6vFqXwNsb5jwoEsmtfAXg1ocFvW7I6/gGLFhBMKwcMwZuy7uv/Bo9jA=="
|
"integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg=="
|
||||||
},
|
},
|
||||||
"async-foreach": {
|
"async-foreach": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
@@ -4681,9 +4686,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001450",
|
"version": "1.0.30001451",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz",
|
||||||
"integrity": "sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew=="
|
"integrity": "sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w=="
|
||||||
},
|
},
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@@ -6015,9 +6020,9 @@
|
|||||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||||
},
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.4.285",
|
"version": "1.4.289",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.285.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.289.tgz",
|
||||||
"integrity": "sha512-47o4PPgxfU1KMNejz+Dgaodf7YTcg48uOfV1oM6cs3adrl2+7R+dHkt3Jpxqo0LRCbGJEzTKMUt0RdvByb/leg=="
|
"integrity": "sha512-relLdMfPBxqGCxy7Gyfm1HcbRPcFUJdlgnCPVgQ23sr1TvUrRJz0/QPoGP0+x41wOVSTN/Wi3w6YDgHiHJGOzg=="
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "6.5.4",
|
"version": "6.5.4",
|
||||||
@@ -10984,9 +10989,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node-releases": {
|
"node-releases": {
|
||||||
"version": "2.0.9",
|
"version": "2.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
||||||
"integrity": "sha512-2xfmOrRkGogbTK9R6Leda0DGiXeY3p2NJpy4+gNCffdUvV6mdEJnaDEic1i3Ec2djAo8jWYoJMR5PB0MSMpxUA=="
|
"integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w=="
|
||||||
},
|
},
|
||||||
"node-sass": {
|
"node-sass": {
|
||||||
"version": "4.14.1",
|
"version": "4.14.1",
|
||||||
@@ -14129,23 +14134,18 @@
|
|||||||
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="
|
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="
|
||||||
},
|
},
|
||||||
"regexpu-core": {
|
"regexpu-core": {
|
||||||
"version": "5.2.2",
|
"version": "5.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.0.tgz",
|
||||||
"integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==",
|
"integrity": "sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@babel/regjsgen": "^0.8.0",
|
||||||
"regenerate": "^1.4.2",
|
"regenerate": "^1.4.2",
|
||||||
"regenerate-unicode-properties": "^10.1.0",
|
"regenerate-unicode-properties": "^10.1.0",
|
||||||
"regjsgen": "^0.7.1",
|
|
||||||
"regjsparser": "^0.9.1",
|
"regjsparser": "^0.9.1",
|
||||||
"unicode-match-property-ecmascript": "^2.0.0",
|
"unicode-match-property-ecmascript": "^2.0.0",
|
||||||
"unicode-match-property-value-ecmascript": "^2.1.0"
|
"unicode-match-property-value-ecmascript": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"regjsgen": {
|
|
||||||
"version": "0.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz",
|
|
||||||
"integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA=="
|
|
||||||
},
|
|
||||||
"regjsparser": {
|
"regjsparser": {
|
||||||
"version": "0.9.1",
|
"version": "0.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz",
|
||||||
|
|||||||
Reference in New Issue
Block a user