mirror of
https://github.com/koush/scrypted.git
synced 2026-04-05 08:20:19 +01:00
sdk: make bin files typescript
This commit is contained in:
184
sdk/src/bin/index.ts
Normal file
184
sdk/src/bin/index.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import https from 'https';
|
||||
import axios from 'axios';
|
||||
import process from 'process';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
function getUserHome(): string {
|
||||
const ret = process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'];
|
||||
if (!ret)
|
||||
throw new Error('Neither USERPROFILE or HOME are defined.');
|
||||
return ret;
|
||||
}
|
||||
|
||||
const scryptedHome = path.join(getUserHome(), '.scrypted');
|
||||
const loginPath = path.join(scryptedHome, 'login.json');
|
||||
|
||||
interface Login {
|
||||
username?: string;
|
||||
token?: string;
|
||||
}
|
||||
|
||||
interface LoginFile {
|
||||
[ip: string]: Login;
|
||||
}
|
||||
|
||||
function getLogin(ip: string): { username: string; password: string } {
|
||||
let login: LoginFile;
|
||||
try {
|
||||
login = JSON.parse(fs.readFileSync(loginPath).toString());
|
||||
}
|
||||
catch {
|
||||
login = {};
|
||||
}
|
||||
|
||||
const ipLogin = login[ip];
|
||||
|
||||
return {
|
||||
username: ipLogin?.username || '',
|
||||
password: ipLogin?.token || '',
|
||||
};
|
||||
}
|
||||
|
||||
function showLoginError(): void {
|
||||
console.error('Authorization required. Please log in with the following:');
|
||||
console.error(' npx scrypted login [ip]');
|
||||
}
|
||||
|
||||
function toIpAndPort(ip: string): string {
|
||||
if (ip.indexOf(':') === -1)
|
||||
ip += ':10443';
|
||||
console.log(ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
export function deploy(debugHost: string, noRebind?: boolean): Promise<void> {
|
||||
debugHost = toIpAndPort(debugHost);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let out: string;
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
out = path.resolve(process.cwd(), 'dist');
|
||||
else
|
||||
out = path.resolve(process.cwd(), 'out');
|
||||
|
||||
const outFilename = 'plugin.zip';
|
||||
const main = path.resolve(out, outFilename);
|
||||
if (!fs.existsSync(main)) {
|
||||
console.error('npm run scrypted-webpack to build a webpack bundle for Scrypted.');
|
||||
reject(new Error(`Missing webpack bundle: ${main}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
|
||||
const npmPackage = packageJson.name || '';
|
||||
|
||||
const rebindQuery = noRebind ? 'no-rebind' : '';
|
||||
|
||||
const deployUrl = `https://${debugHost}/web/component/script/deploy?${rebindQuery}&npmPackage=${npmPackage}`;
|
||||
const setupUrl = `https://${debugHost}/web/component/script/setup?${rebindQuery}&npmPackage=${npmPackage}`;
|
||||
|
||||
const fileContents = fs.readFileSync(main);
|
||||
console.log(`deploying to ${debugHost}`);
|
||||
|
||||
let auth: { username: string; password: string };
|
||||
try {
|
||||
auth = getLogin(debugHost);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
showLoginError();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
});
|
||||
|
||||
axios.post(setupUrl, packageJson,
|
||||
{
|
||||
auth,
|
||||
timeout: 10000,
|
||||
maxRedirects: 0,
|
||||
httpsAgent,
|
||||
validateStatus: function (status: number) {
|
||||
if (status === 401) {
|
||||
showLoginError();
|
||||
}
|
||||
return status >= 200 && status < 300;
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`configured ${debugHost}`);
|
||||
|
||||
return axios.post(deployUrl, fileContents,
|
||||
{
|
||||
auth: getLogin(debugHost),
|
||||
timeout: 10000,
|
||||
maxRedirects: 0,
|
||||
httpsAgent,
|
||||
validateStatus: function (status: number) {
|
||||
return status >= 200 && status < 300;
|
||||
},
|
||||
headers: {
|
||||
"Content-Type": "application/zip "
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`deployed to ${debugHost}`);
|
||||
resolve();
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
if (axios.isAxiosError(err) && err.response?.data) {
|
||||
console.log('\x1b[31m%s\x1b[0m', err.response.data);
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function debug(debugHost: string, entryPoint?: string): Promise<void> {
|
||||
debugHost = toIpAndPort(debugHost);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const packageJsonPath = path.resolve(process.cwd(), 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString());
|
||||
const npmPackage = packageJson.name || '';
|
||||
|
||||
const debugUrl = `https://${debugHost}/web/component/script/debug?npmPackage=${npmPackage}`;
|
||||
console.log(`initiating debugger on ${debugHost}`);
|
||||
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
});
|
||||
|
||||
axios.post(debugUrl, undefined, {
|
||||
auth: getLogin(debugHost),
|
||||
timeout: 10000,
|
||||
maxRedirects: 0,
|
||||
httpsAgent,
|
||||
validateStatus: function (status: number) {
|
||||
return status >= 200 && status < 300;
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
console.log(`debugger ready on ${debugHost}`);
|
||||
resolve();
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
if (axios.isAxiosError(err) && err.response?.data) {
|
||||
console.log('\x1b[31m%s\x1b[0m', err.response.data);
|
||||
}
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getDefaultWebpackConfig(name: string): unknown {
|
||||
return require(path.resolve(__dirname, `../../${name}`));
|
||||
}
|
||||
61
sdk/src/bin/scrypted-changelog.ts
Normal file
61
sdk/src/bin/scrypted-changelog.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
#! /usr/bin/env node
|
||||
import child_process from 'child_process';
|
||||
import util from 'util';
|
||||
import fs from 'fs';
|
||||
|
||||
const shas = child_process.execSync('git log --format=format:%H .');
|
||||
const exec = util.promisify(child_process.exec);
|
||||
|
||||
const versions = new Map<string, string>();
|
||||
|
||||
interface VersionInfo {
|
||||
packageJson?: { version?: string };
|
||||
commit?: string;
|
||||
}
|
||||
|
||||
const promises = shas.toString().split('\n').map(sha => sha.trim()).map(async sha => {
|
||||
try {
|
||||
const result = await exec(`git show ${sha}:./package.json`);
|
||||
const packageJson = JSON.parse(result.stdout);
|
||||
const commit = await exec(`git rev-list --format=%B --max-count=1 ${sha}`);
|
||||
return {
|
||||
packageJson,
|
||||
commit: commit.stdout,
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all(promises).then(pairs => {
|
||||
const validPairs = (pairs as (VersionInfo | undefined)[]).filter((pair): pair is VersionInfo => !!pair?.packageJson?.version);
|
||||
for (const valid of validPairs) {
|
||||
const { packageJson, commit } = valid;
|
||||
const version = packageJson!.version!;
|
||||
if (!version) continue;
|
||||
|
||||
let log = versions.get(version) || '';
|
||||
const firstLine = commit?.split('\n')[1] || '';
|
||||
|
||||
if ([version, 'wip', 'logging', 'wiop', 'publish'].includes(firstLine))
|
||||
continue;
|
||||
|
||||
if (!log) {
|
||||
log = '';
|
||||
versions.set(version, log);
|
||||
}
|
||||
|
||||
log += `${firstLine}\n`;
|
||||
versions.set(version, log);
|
||||
}
|
||||
|
||||
let changeLog = '<details>\n<summary>Changelog</summary>\n\n';
|
||||
for (const [version, log] of versions.entries()) {
|
||||
changeLog += `### ${version}\n\n${log}\n\n`;
|
||||
}
|
||||
|
||||
changeLog += '</details>\n';
|
||||
|
||||
fs.writeFileSync('CHANGELOG.md', changeLog);
|
||||
});
|
||||
19
sdk/src/bin/scrypted-debug.ts
Normal file
19
sdk/src/bin/scrypted-debug.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
#! /usr/bin/env node
|
||||
import * as scrypted from './index.js';
|
||||
|
||||
function report(err: string): void {
|
||||
process.nextTick(() => {
|
||||
throw new Error(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.length != 3) {
|
||||
report('Usage: npm run scrypted-debug <ip_address> [main.js]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
scrypted.debug(process.argv[2], process.argv[3])
|
||||
.catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
report('debug failed');
|
||||
});
|
||||
22
sdk/src/bin/scrypted-deploy-debug.ts
Normal file
22
sdk/src/bin/scrypted-deploy-debug.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
#! /usr/bin/env node
|
||||
import * as scrypted from './index.js';
|
||||
|
||||
function report(err: string): void {
|
||||
process.nextTick(() => {
|
||||
throw new Error(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
report('Usage: npm run scrypted-deploy-debug <ip_address> [main.js]');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
scrypted.deploy(process.argv[2], true)
|
||||
.then(() => {
|
||||
return scrypted.debug(process.argv[2], process.argv[3]);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
report('deploy + debug failed');
|
||||
});
|
||||
19
sdk/src/bin/scrypted-deploy.ts
Normal file
19
sdk/src/bin/scrypted-deploy.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
#! /usr/bin/env node
|
||||
import * as scrypted from './index.js';
|
||||
|
||||
function report(err: string): void {
|
||||
process.nextTick(() => {
|
||||
throw new Error(err);
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.length != 3) {
|
||||
report('Usage: npm run scrypted-deploy <ip_address>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
scrypted.deploy(process.argv[2])
|
||||
.catch((err: Error) => {
|
||||
console.error(err.message);
|
||||
report('deploy failed');
|
||||
});
|
||||
24
sdk/src/bin/scrypted-package-json.ts
Normal file
24
sdk/src/bin/scrypted-package-json.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
#! /usr/bin/env node
|
||||
import fs from 'fs';
|
||||
|
||||
interface PackageJson {
|
||||
scripts?: Record<string, string>;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const pkg: PackageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
||||
pkg.scripts = Object.assign({
|
||||
"scrypted-setup-project": "scrypted-setup-project",
|
||||
"prescrypted-setup-project": "scrypted-package-json",
|
||||
"build": "scrypted-webpack",
|
||||
"preprepublishOnly": "scrypted-changelog",
|
||||
"prepublishOnly": "NODE_ENV=production scrypted-webpack",
|
||||
"prescrypted-vscode-launch": "scrypted-webpack",
|
||||
"scrypted-vscode-launch": "scrypted-deploy-debug",
|
||||
"scrypted-deploy-debug": "scrypted-deploy-debug",
|
||||
"scrypted-debug": "scrypted-debug",
|
||||
"scrypted-deploy": "scrypted-deploy",
|
||||
"scrypted-changelog": "scrypted-changelog",
|
||||
"scrypted-package-json": "scrypted-package-json",
|
||||
}, pkg.scripts);
|
||||
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 3) + '\n');
|
||||
7
sdk/src/bin/scrypted-setup-project.ts
Normal file
7
sdk/src/bin/scrypted-setup-project.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
#! /usr/bin/env node
|
||||
import ncp from 'ncp';
|
||||
import path from 'path';
|
||||
|
||||
ncp(path.join(__dirname, '../../tsconfig.plugin.json'), 'tsconfig.json', (err) => {
|
||||
if (err) console.error(err);
|
||||
});
|
||||
269
sdk/src/bin/scrypted-webpack.ts
Normal file
269
sdk/src/bin/scrypted-webpack.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
#! /usr/bin/env node
|
||||
try {
|
||||
require('adm-zip');
|
||||
}
|
||||
catch {
|
||||
throw new Error('Please "npm install" in the "sdk" directory.');
|
||||
}
|
||||
|
||||
import path from 'path';
|
||||
import process from 'process';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import AdmZip from 'adm-zip';
|
||||
import rimraf from 'rimraf';
|
||||
import webpack from 'webpack';
|
||||
import tmp from 'tmp';
|
||||
import child_process from 'child_process';
|
||||
import { once } from 'events';
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
let out: string;
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
out = path.resolve(cwd, 'dist');
|
||||
else
|
||||
out = path.resolve(cwd, 'out');
|
||||
|
||||
if (fs.existsSync(path.resolve(cwd, 'src/main.py'))) {
|
||||
const resolved = path.resolve(cwd, 'src');
|
||||
|
||||
const zip = new AdmZip();
|
||||
const readme = path.join(cwd, 'README.md');
|
||||
if (fs.existsSync(readme)) {
|
||||
zip.addLocalFile(readme);
|
||||
}
|
||||
|
||||
zip.addLocalFolder(resolved);
|
||||
|
||||
const sdk = path.join(__dirname, '../../types/scrypted_python/scrypted_sdk');
|
||||
zip.addLocalFolder(sdk, 'scrypted_sdk', filename => !filename.endsWith('.pyc'));
|
||||
|
||||
const zipfs = path.join(cwd, 'fs');
|
||||
if (fs.existsSync(zipfs))
|
||||
zip.addLocalFolder(zipfs, 'fs');
|
||||
zip.writeZip(path.join(out, 'plugin.zip'));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
interface PackageJson {
|
||||
name?: string;
|
||||
exports?: Record<string, string>;
|
||||
optionalDependencies?: Record<string, string>;
|
||||
scrypted?: {
|
||||
babel?: boolean;
|
||||
rollup?: boolean;
|
||||
interfaceDescriptors?: unknown;
|
||||
};
|
||||
type?: string;
|
||||
}
|
||||
|
||||
const packageJson: PackageJson = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
||||
const interfaceDescriptors = packageJson.scrypted?.interfaceDescriptors;
|
||||
|
||||
const optionalDependencies = Object.keys(packageJson.optionalDependencies || {});
|
||||
|
||||
if (packageJson.scrypted?.babel) {
|
||||
process.env.SCRYPTED_WEBPACK_BABEL = 'true';
|
||||
}
|
||||
|
||||
const defaultMainNodeJs = 'main.nodejs.js';
|
||||
interface Entry {
|
||||
filename: string;
|
||||
output: string;
|
||||
}
|
||||
const entries: (Entry | undefined)[] = [];
|
||||
|
||||
if (packageJson.exports) {
|
||||
for (const [key, value] of Object.entries(packageJson.exports)) {
|
||||
entries.push({
|
||||
filename: key,
|
||||
output: value,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (const search of ['src/main.js', 'src/main.ts']) {
|
||||
const resolved = path.resolve(cwd, search);
|
||||
if (fs.existsSync(resolved)) {
|
||||
entries.push({
|
||||
filename: search,
|
||||
output: defaultMainNodeJs,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nodeWebpackConfig = 'webpack.nodejs.config.js';
|
||||
|
||||
if (!entries?.length) {
|
||||
console.warn('unable to locate src/main.js or src/main.ts');
|
||||
console.warn('if a custom webpack config is used, will fall back to an entry configured there');
|
||||
entries.push(undefined);
|
||||
}
|
||||
|
||||
const zip = new AdmZip();
|
||||
|
||||
const readme = path.join(cwd, 'README.md');
|
||||
if (fs.existsSync(readme)) {
|
||||
let readmeText = fs.readFileSync(readme).toString();
|
||||
const changelog = path.join(cwd, 'CHANGELOG.md');
|
||||
if (fs.existsSync(changelog)) {
|
||||
readmeText += '\n\n\n<br/><br/>' + fs.readFileSync(changelog).toString();
|
||||
}
|
||||
zip.addFile('README.md', Buffer.from(readmeText));
|
||||
}
|
||||
|
||||
const NODE_PATH = path.resolve(__dirname, '..', '..', 'node_modules');
|
||||
|
||||
process.env.NODE_PATH = NODE_PATH;
|
||||
require('module').Module._initPaths();
|
||||
|
||||
interface WebpackConfig {
|
||||
entry?: Record<string, string> | string;
|
||||
output?: {
|
||||
filename?: string;
|
||||
path?: string;
|
||||
};
|
||||
resolve?: {
|
||||
alias?: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
async function rollup(): Promise<void> {
|
||||
if (out)
|
||||
rimraf.sync(out);
|
||||
|
||||
let rollupCmd = path.resolve(cwd, 'node_modules/.bin/rollup');
|
||||
|
||||
if (!fs.existsSync(rollupCmd)) {
|
||||
rollupCmd = path.resolve(cwd, 'node_modules/@scrypted/sdk/node_modules/.bin/rollup');
|
||||
}
|
||||
if (os.platform().startsWith('win')) {
|
||||
rollupCmd += '.cmd';
|
||||
}
|
||||
|
||||
const cp = child_process.spawn(rollupCmd, [
|
||||
'--config', path.resolve(__dirname, '../../rollup.nodejs.config.mjs'),
|
||||
], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
await once(cp, 'exit');
|
||||
if (cp.exitCode)
|
||||
throw new Error('rollup failed');
|
||||
|
||||
finishZip();
|
||||
}
|
||||
|
||||
function finishZip(): void {
|
||||
const jsFiles = fs.readdirSync(out, {
|
||||
withFileTypes: true
|
||||
}).filter(ft => ft.isFile() && ft.name.endsWith('.js')).map(ft => ft.name);
|
||||
for (const js of jsFiles) {
|
||||
zip.addLocalFile(path.join(out, js));
|
||||
const sourcemap = path.join(out, js + '.map');
|
||||
if (fs.existsSync(sourcemap))
|
||||
zip.addLocalFile(sourcemap);
|
||||
console.log(js);
|
||||
}
|
||||
|
||||
const sdkPackageJson = require(path.join(__dirname, '../../package.json'));
|
||||
zip.addFile('sdk.json', Buffer.from(JSON.stringify({
|
||||
version: (sdkPackageJson as { version: string }).version,
|
||||
interfaceDescriptors,
|
||||
})));
|
||||
|
||||
if (packageJson.type === 'module') {
|
||||
zip.addFile('package.json', Buffer.from(JSON.stringify({
|
||||
type: 'module'
|
||||
})));
|
||||
}
|
||||
|
||||
const zipfs = path.join(cwd, 'fs');
|
||||
if (fs.existsSync(zipfs))
|
||||
zip.addLocalFolder(zipfs, 'fs');
|
||||
zip.writeZip(path.join(out, 'plugin.zip'));
|
||||
}
|
||||
|
||||
async function pack(): Promise<void> {
|
||||
if (out)
|
||||
rimraf.sync(out);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
let webpackConfig: string;
|
||||
const customWebpackConfig = path.resolve(cwd, nodeWebpackConfig);
|
||||
const defaultWebpackConfig = path.resolve(__dirname, '..', '..', nodeWebpackConfig);
|
||||
if (fs.existsSync(customWebpackConfig)) {
|
||||
webpackConfig = customWebpackConfig;
|
||||
}
|
||||
else {
|
||||
webpackConfig = defaultWebpackConfig;
|
||||
}
|
||||
|
||||
process.env.SCRYPTED_DEFAULT_WEBPACK_CONFIG = defaultWebpackConfig;
|
||||
|
||||
const webpackEntries: Record<string, string> = {};
|
||||
const config: WebpackConfig = require(webpackConfig);
|
||||
for (const entry of entries) {
|
||||
const normalizedEntry = entry || {
|
||||
filename: (typeof config?.entry === 'object' ? config.entry?.main : config.entry) || '',
|
||||
output: defaultMainNodeJs,
|
||||
};
|
||||
|
||||
if (!normalizedEntry?.filename) {
|
||||
console.error("no main.ts or main.js was found, and webpack config does not supply an entry file.");
|
||||
console.error(normalizedEntry?.filename);
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const main = path.resolve(cwd, normalizedEntry.filename);
|
||||
if (!fs.existsSync(main)) {
|
||||
console.error("entry file specified in webpack config does not exist");
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
webpackEntries[normalizedEntry.output] = main;
|
||||
}
|
||||
|
||||
config.entry = webpackEntries;
|
||||
config.output = config.output || {};
|
||||
config.output.filename = '[name]';
|
||||
config.output.path = out;
|
||||
|
||||
config.resolve = config.resolve || {};
|
||||
config.resolve.alias = config.resolve.alias || {};
|
||||
|
||||
for (const opt of optionalDependencies) {
|
||||
const t = tmp.tmpNameSync({
|
||||
postfix: '.js',
|
||||
});
|
||||
fs.writeFileSync(t, `
|
||||
const e = __non_webpack_require__('${opt}');
|
||||
module.exports = e;
|
||||
`);
|
||||
config.resolve.alias![opt] = t;
|
||||
}
|
||||
|
||||
webpack(config as webpack.Configuration, (err, stats) => {
|
||||
if (err)
|
||||
return reject(err);
|
||||
|
||||
if (stats?.hasErrors()) {
|
||||
console.error(stats.toJson()?.errors);
|
||||
return reject(new Error('webpack failed'));
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
finishZip();
|
||||
}
|
||||
|
||||
(packageJson.scrypted?.rollup ? rollup : pack)()
|
||||
.catch(e => process.nextTick(() => {
|
||||
console.error(e);
|
||||
throw new Error(e);
|
||||
}));
|
||||
Reference in New Issue
Block a user