refactor for new host
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
# Simple workflow for deploying static content to GitHub Pages
|
||||
name: Deploy static content to Pages
|
||||
|
||||
on:
|
||||
# Runs on pushes targeting the default branch
|
||||
push:
|
||||
branches: ["master"]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
# Allow one concurrent deployment
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
# Single deploy job since we're just deploying
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
# Upload entire repository
|
||||
path: '.'
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.wrangler
|
||||
assets/*.js*
|
||||
@@ -1,13 +1,11 @@
|
||||
# OBSConnect
|
||||
This project provides a basic authentication page to make using a password on your OBS Websocket easier, by saving it once on the auth page and providing it to other pages after you allow it.
|
||||
## For Developers
|
||||
Send your users to the page `https://sugoidogo.github.io/obsconnect?redirect_uri=yourpageurlencodedhere` to prompt for acces to OBS-Websocket. If they allow the connection, this page will sort out gathering the connection info, testing that it works, and then returning the user to your page with the query string `?obsurl=ws://localhost:4455&obspassword=examplepassword&userChoice=allow` or `?userChoice=deny`, depending on what they chose. You can now save that info using something like [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and connect to obs with something like [obs-websocket-js](https://github.com/obs-websocket-community-projects/obs-websocket-js) for a fully abstracted connection method.
|
||||
Send your users to the page `https://obsconnect.sugoidogo.com/?redirect_uri=yourpageurlencodedhere` to prompt for acces to OBS-Websocket. If they allow the connection, this page will sort out gathering the connection info, testing that it works, and then returning the user to your page with the query string `?obsurl=ws://localhost:4455&obspassword=examplepassword&userChoice=allow` or `?userChoice=deny`, depending on what they chose. You can now save that info using something like [localStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) and connect to obs with something like [obs-websocket-js](https://github.com/obs-websocket-community-projects/obs-websocket-js) for a fully abstracted connection method.
|
||||
## For Users
|
||||
Send [this page](https://github.com/sugoidogo/obsconnect) (THE LINK, NOT A SCREENSHOT) to the developer of whatever web-based obs tool you want to use it.
|
||||
Send [this page](https://gitea.sugoidogo.com/sugoidogo/obsconnect) (THE LINK, NOT A SCREENSHOT) to the developer of whatever web-based obs tool you want to use it.
|
||||
|
||||
( If you user sent you this in a screenshot anyways, it's at github.com/sugoidogo/obsconnect )
|
||||
## Support
|
||||
Support information for all my software is on my [GitHub profile](https://github.com/sugoidogo)
|
||||
( If you user sent you this in a screenshot anyways, it's at gitea.sugoidogo.com/sugoidogo/obsconnect )
|
||||
## Security
|
||||
This project stores your OBS Websocket password in `localStorage` for re-use whenever a new web app wants to connect to OBS. Depending on your browser, that means your password is stored unencrypted somewhere in your browser data. That would be a possible security concern, if obs-websocket weren't already [storing your password in plain text](https://github.com/obsproject/obs-websocket/discussions/766#:~:text=via%20this%20dialog.-,%5BTechnical%20Note%3A%5D,-This%20feature%20has).
|
||||
## Licence
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<link rel="icon" href="data:image/png;base64,iVBORw0KGgo=">
|
||||
<script type="module" src="index.js"></script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
* {
|
||||
font-size: 3vmin;
|
||||
}
|
||||
|
||||
video {
|
||||
max-width: 100vw;
|
||||
max-height: 50vh;
|
||||
}
|
||||
</style>
|
||||
<div id="status">Loading...</div>
|
||||
<div id="permission">
|
||||
<button onclick="allow()">Allow</button>
|
||||
<button onclick="deny()">Deny</button>
|
||||
</div>
|
||||
<span id="connectionSettings" hidden>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Websocket URL:</td>
|
||||
<td><input id="obsurl" type="url"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Websocket Password:</td>
|
||||
<td><input id="obspassword" type="password"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<button id="connect" onclick="connect()">Connect</button>
|
||||
<button id="useQR" onclick="useQrCode()">Use QR Code</button>
|
||||
<button id="skip" onclick="skipVerification()">Skip Verification</button>
|
||||
<br>
|
||||
<span id="qr" hidden>
|
||||
<video id="qrvideo" hidden></video>
|
||||
<br>
|
||||
<span>Make sure this device and the one running OBS are on the same network.</span>
|
||||
<br>
|
||||
<button onclick="screenCapture()">Screen Capture</button>
|
||||
<span id="loadingCameras">Loading video device list...</span>
|
||||
</span>
|
||||
</span>
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
<style>
|
||||
* {
|
||||
font-size: 3vmin;
|
||||
}
|
||||
|
||||
video {
|
||||
max-width: 100vw;
|
||||
max-height: 50vh;
|
||||
}
|
||||
</style>
|
||||
<div id="status">Loading...</div>
|
||||
<div id="permission">
|
||||
<button onclick="allow()">Allow</button>
|
||||
<button onclick="deny()">Deny</button>
|
||||
</div>
|
||||
<span id="connectionSettings" hidden>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Websocket URL:</td>
|
||||
<td><input id="obsurl" type="url"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Websocket Password:</td>
|
||||
<td><input id="obspassword" type="password"></td>
|
||||
</tr>
|
||||
</table>
|
||||
<button id="connect" onclick="connect()">Connect</button>
|
||||
<button id="useQR" onclick="useQrCode()">Use QR Code</button>
|
||||
<br>
|
||||
<span id="qr" hidden>
|
||||
<video id="qrvideo" hidden></video>
|
||||
<br>
|
||||
<span>Make sure this device and the one running OBS are on the same network.</span>
|
||||
<br>
|
||||
<button onclick="screenCapture()">Screen Capture</button>
|
||||
<span id="loadingCameras">Loading video device list...</span>
|
||||
</span>
|
||||
</span>
|
||||
<script src="https://cdn.jsdelivr.net/npm/obs-websocket-js@5.0.1/dist/obs-ws.min.js"
|
||||
integrity="sha256-UbkntZrYTokZf8tldcF9nmOn5CNgcmDP+MNsUcKUpEE=" crossorigin="anonymous">
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/qr-scanner@1.4.1/qr-scanner.umd.min.js"
|
||||
integrity="sha256-jstRoCLjzwXwLN2o0DMkkwRHtqYCQMxrRkktAHwZekU=" crossorigin="anonymous">
|
||||
</script>
|
||||
<script>
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const redirect_uri = params.get('redirect_uri')
|
||||
document.querySelector('#status').innerHTML = 'The website at:<br>' + redirect_uri + '<br>wants to connect to your OBS Websocket.<br>This will give them FULL CONTROL OF OBS.'
|
||||
document.querySelector('#permission').hidden = false
|
||||
const obs = new OBSWebSocket()
|
||||
|
||||
async function connect() {
|
||||
document.querySelector('#status').innerHTML = 'Connecting to OBS...'
|
||||
const url=document.querySelector('#obsurl').value
|
||||
const password=document.querySelector('#obspassword').value
|
||||
try {
|
||||
await obs.connect(url,password)
|
||||
} catch (error) {
|
||||
const status=document.querySelector('#status')
|
||||
if(status.innerHTML='Connecting to OBS...'){
|
||||
status.innerHTML=error
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
let interval
|
||||
const video = document.querySelector('video')
|
||||
|
||||
async function useQrCode() {
|
||||
console.log('starting QR Code scanner')
|
||||
interval = setInterval(qrScan, 1000)
|
||||
const qr = document.querySelector('#qr')
|
||||
qr.hidden = false
|
||||
video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
|
||||
let mediaDevices = await navigator.mediaDevices.enumerateDevices()
|
||||
mediaDevices = mediaDevices.filter(function (device) {
|
||||
return device.kind == "videoinput"
|
||||
})
|
||||
for (const device of mediaDevices) {
|
||||
const button = document.createElement('button')
|
||||
button.innerHTML = device.label
|
||||
button.onclick = function () { switchCamera(device) }
|
||||
qr.appendChild(button)
|
||||
}
|
||||
document.querySelector('#loadingCameras').hidden = true
|
||||
}
|
||||
|
||||
async function screenCapture() {
|
||||
video.srcObject = null
|
||||
const screenCaptureDevice = await navigator.mediaDevices.getDisplayMedia()
|
||||
video.srcObject = screenCaptureDevice
|
||||
video.play()
|
||||
video.hidden = false
|
||||
}
|
||||
|
||||
async function switchCamera(device) {
|
||||
video.srcObject = null
|
||||
const camera = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: device.deviceId } } })
|
||||
video.srcObject = camera
|
||||
video.play()
|
||||
video.hidden = false
|
||||
}
|
||||
|
||||
function qrScan() {
|
||||
QrScanner.scanImage(video, { returnDetailedScanResult: true })
|
||||
.then(onQrScan).catch(function (error) { console.warn(error) })
|
||||
}
|
||||
|
||||
async function onQrScan(result) {
|
||||
var data = result.data.split('/')
|
||||
if (data.shift() != 'obsws:') {
|
||||
console.log('found non-OBS QR Code')
|
||||
return false
|
||||
}
|
||||
clearInterval(interval)
|
||||
console.log('found OBS QR Code')
|
||||
data.shift()
|
||||
const fullurl = 'ws://' + data.shift()
|
||||
const port = fullurl.split(':').pop()
|
||||
const localurl = 'ws://localhost:' + port
|
||||
const password = data.shift()
|
||||
const obsurl = document.querySelector('#obsurl')
|
||||
const obspassword = document.querySelector('#obspassword')
|
||||
obspassword.value = password
|
||||
obsurl.value = localurl
|
||||
console.log('attempting localhost connection')
|
||||
if (await connect()) {
|
||||
return true
|
||||
}
|
||||
obsurl.value = fullurl
|
||||
console.log('attempting local network connection')
|
||||
if (await connect()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
obs.addListener('ConnectionError', function onConnectionError(error){
|
||||
document.querySelector('#status').innerHTML=error
|
||||
})
|
||||
|
||||
obs.addListener('ConnectionClosed', function onConnectionClosed(error) {
|
||||
switch (error.code) {
|
||||
case 1000:
|
||||
console.log('unknown error, trying again')
|
||||
connect()
|
||||
break
|
||||
case 1006:
|
||||
document.querySelector('#status').innerHTML = 'obs websocket not found.' +
|
||||
'<br>Is <a href="https://github.com/obsproject/obs-websocket/releases/latest">obs-websocket</a> installed?' +
|
||||
'<br>Is obs open? Check your websocket settings in OBS under the Tools menu.' +
|
||||
'<br>Is the info below correct? Consider using the QR scanner.'
|
||||
break
|
||||
case 4009:
|
||||
document.querySelector('#status').innerHTML = error.message.split(' ').slice(-3).join(' ').toLowerCase().replace('authentication', 'Password') +
|
||||
'<br>You can view or change your websocket password in OBS under the Tools menu.'
|
||||
break
|
||||
default:
|
||||
document.querySelector('#status').innerHTML = 'connection closed: Error ' + error.code + ': ' + error.message
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
obs.addListener('ConnectionOpened', function onConnectedOpened() {
|
||||
console.log('connected to socket, saving url')
|
||||
document.querySelector('#status').innerHTML = 'Connected'
|
||||
localStorage.setItem('obsurl', document.querySelector('#obsurl').value)
|
||||
})
|
||||
|
||||
obs.addListener('Identified', function onIdentified() {
|
||||
console.log('authentication passed, saving password')
|
||||
document.querySelector('#status').innerHTML = 'Password Passed'
|
||||
localStorage.setItem('obspassword', document.querySelector('#obspassword').value)
|
||||
//obs.disconnect()
|
||||
redirect()
|
||||
})
|
||||
|
||||
async function redirect() {
|
||||
try {
|
||||
document.querySelector('#status').innerHTML = 'Redirecting...'
|
||||
const url = new URL(redirect_uri)
|
||||
url.searchParams.append('obsurl', document.querySelector('#obsurl').value)
|
||||
url.searchParams.append('obspassword', document.querySelector('#obspassword').value)
|
||||
url.searchParams.append('userChoice', 'allow')
|
||||
window.location.replace(url.href)
|
||||
} catch (e) {
|
||||
document.querySelector('#status').innerHTML = 'Error during redirect: '+e
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
function allow() {
|
||||
console.log('user allowed connection, attempting to use saved info')
|
||||
document.querySelector('#permission').hidden = true
|
||||
document.querySelector('#connectionSettings').hidden = false
|
||||
document.querySelector('#obsurl').value = localStorage.getItem('obsurl') || 'ws://localhost:4455'
|
||||
document.querySelector('#obspassword').value = localStorage.getItem('obspassword') || ''
|
||||
connect()
|
||||
}
|
||||
|
||||
function deny() {
|
||||
console.log('user denied connection, returning to redirect_uri')
|
||||
const url = new URL(redirect_uri)
|
||||
url.searchParams.append('userChoice', 'deny')
|
||||
window.location.replace(url.href)
|
||||
}
|
||||
|
||||
async function bypass(){
|
||||
const url=document.querySelector('#obsurl').value
|
||||
await navigator.clipboard.writeText(url)
|
||||
}
|
||||
|
||||
</script>
|
||||
Generated
+2145
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"scripts": {
|
||||
"test": "wrangler dev --ip=localhost"
|
||||
},
|
||||
"dependencies": {
|
||||
"obs-websocket-js": "^5.0.7",
|
||||
"qr-scanner": "^1.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.27.2",
|
||||
"wrangler": "^4.56.0"
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
import QrScanner from "qr-scanner"
|
||||
import OBSWebSocket from "obs-websocket-js"
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const redirect_uri = params.get('redirect_uri')
|
||||
document.querySelector('#status').innerHTML = 'The website at:<br>' + redirect_uri + '<br>wants to connect to your OBS Websocket.<br>This will give them FULL CONTROL OF OBS.'
|
||||
document.querySelector('#permission').hidden = false
|
||||
const obs = new OBSWebSocket()
|
||||
|
||||
async function connect() {
|
||||
document.querySelector('#status').innerHTML = 'Connecting to OBS...'
|
||||
const url = document.querySelector('#obsurl').value
|
||||
const password = document.querySelector('#obspassword').value
|
||||
try {
|
||||
await obs.connect(url, password)
|
||||
} catch (error) {
|
||||
const status = document.querySelector('#status')
|
||||
if (status.innerHTML = 'Connecting to OBS...') {
|
||||
status.innerHTML = error
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
let interval
|
||||
const video = document.querySelector('video')
|
||||
|
||||
async function useQrCode() {
|
||||
console.log('starting QR Code scanner')
|
||||
interval = setInterval(qrScan, 1000)
|
||||
const qr = document.querySelector('#qr')
|
||||
qr.hidden = false
|
||||
video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } })
|
||||
let mediaDevices = await navigator.mediaDevices.enumerateDevices()
|
||||
mediaDevices = mediaDevices.filter(function (device) {
|
||||
return device.kind == "videoinput"
|
||||
})
|
||||
for (const device of mediaDevices) {
|
||||
const button = document.createElement('button')
|
||||
button.innerHTML = device.label
|
||||
button.onclick = function () { switchCamera(device) }
|
||||
qr.appendChild(button)
|
||||
}
|
||||
document.querySelector('#loadingCameras').hidden = true
|
||||
}
|
||||
|
||||
async function screenCapture() {
|
||||
video.srcObject = null
|
||||
const screenCaptureDevice = await navigator.mediaDevices.getDisplayMedia()
|
||||
video.srcObject = screenCaptureDevice
|
||||
video.play()
|
||||
video.hidden = false
|
||||
}
|
||||
|
||||
async function switchCamera(device) {
|
||||
video.srcObject = null
|
||||
const camera = await navigator.mediaDevices.getUserMedia({ video: { deviceId: { exact: device.deviceId } } })
|
||||
video.srcObject = camera
|
||||
video.play()
|
||||
video.hidden = false
|
||||
}
|
||||
|
||||
function qrScan() {
|
||||
QrScanner.scanImage(video, { returnDetailedScanResult: true })
|
||||
.then(onQrScan).catch(function (error) { console.warn(error) })
|
||||
}
|
||||
|
||||
async function onQrScan(result) {
|
||||
var data = result.data.split('/')
|
||||
if (data.shift() != 'obsws:') {
|
||||
console.log('found non-OBS QR Code')
|
||||
return false
|
||||
}
|
||||
clearInterval(interval)
|
||||
console.log('found OBS QR Code')
|
||||
data.shift()
|
||||
const fullurl = 'ws://' + data.shift()
|
||||
const port = fullurl.split(':').pop()
|
||||
const localurl = 'ws://localhost:' + port
|
||||
const password = data.shift()
|
||||
const obsurl = document.querySelector('#obsurl')
|
||||
const obspassword = document.querySelector('#obspassword')
|
||||
obspassword.value = password
|
||||
obsurl.value = localurl
|
||||
console.log('attempting localhost connection')
|
||||
if (await connect()) {
|
||||
return true
|
||||
}
|
||||
obsurl.value = fullurl
|
||||
console.log('attempting local network connection')
|
||||
if (await connect()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
obs.addListener('ConnectionError', function onConnectionError(error) {
|
||||
document.querySelector('#status').innerHTML = error
|
||||
})
|
||||
|
||||
obs.addListener('ConnectionClosed', function onConnectionClosed(error) {
|
||||
switch (error.code) {
|
||||
case 1000:
|
||||
console.log('unknown error, trying again')
|
||||
connect()
|
||||
break
|
||||
case 1006:
|
||||
document.querySelector('#status').innerHTML = 'obs websocket not found.' +
|
||||
'<br>Is <a href="https://github.com/obsproject/obs-websocket/releases/latest">obs-websocket</a> installed?' +
|
||||
'<br>Is obs open? Check your websocket settings in OBS under the Tools menu.' +
|
||||
'<br>Is the info below correct? Consider using the QR scanner.'
|
||||
break
|
||||
case 4009:
|
||||
document.querySelector('#status').innerHTML = error.message.split(' ').slice(-3).join(' ').toLowerCase().replace('authentication', 'Password') +
|
||||
'<br>You can view or change your websocket password in OBS under the Tools menu.'
|
||||
break
|
||||
default:
|
||||
document.querySelector('#status').innerHTML = 'connection closed: Error ' + error.code + ': ' + error.message
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
obs.addListener('ConnectionOpened', function onConnectedOpened() {
|
||||
console.log('connected to socket, saving url')
|
||||
document.querySelector('#status').innerHTML = 'Connected'
|
||||
localStorage.setItem('obsurl', document.querySelector('#obsurl').value)
|
||||
})
|
||||
|
||||
obs.addListener('Identified', function onIdentified() {
|
||||
console.log('authentication passed, saving password')
|
||||
document.querySelector('#status').innerHTML = 'Password Passed'
|
||||
localStorage.setItem('obspassword', document.querySelector('#obspassword').value)
|
||||
//obs.disconnect()
|
||||
redirect()
|
||||
})
|
||||
|
||||
async function redirect() {
|
||||
try {
|
||||
document.querySelector('#status').innerHTML = 'Redirecting...'
|
||||
const url = new URL(redirect_uri)
|
||||
url.searchParams.append('obsurl', document.querySelector('#obsurl').value)
|
||||
url.searchParams.append('obspassword', document.querySelector('#obspassword').value)
|
||||
url.searchParams.append('userChoice', 'allow')
|
||||
window.location.replace(url.href)
|
||||
} catch (e) {
|
||||
document.querySelector('#status').innerHTML = 'Error during redirect: ' + e
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
function allow() {
|
||||
console.log('user allowed connection, attempting to use saved info')
|
||||
document.querySelector('#permission').hidden = true
|
||||
document.querySelector('#connectionSettings').hidden = false
|
||||
document.querySelector('#skip').hidden = false
|
||||
document.querySelector('#obsurl').value = localStorage.getItem('obsurl') || 'ws://localhost:4455'
|
||||
document.querySelector('#obspassword').value = localStorage.getItem('obspassword') || ''
|
||||
connect()
|
||||
}
|
||||
|
||||
function deny() {
|
||||
console.log('user denied connection, returning to redirect_uri')
|
||||
const url = new URL(redirect_uri)
|
||||
url.searchParams.append('userChoice', 'deny')
|
||||
window.location.replace(url.href)
|
||||
}
|
||||
|
||||
async function bypass() {
|
||||
const url = document.querySelector('#obsurl').value
|
||||
await navigator.clipboard.writeText(url)
|
||||
}
|
||||
|
||||
function skipVerification() {
|
||||
localStorage.setItem('obsurl', document.querySelector('#obsurl').value)
|
||||
localStorage.setItem('obspassword', document.querySelector('#obspassword').value)
|
||||
redirect()
|
||||
}
|
||||
|
||||
globalThis.allow = allow
|
||||
globalThis.deny = deny
|
||||
globalThis.connect = connect
|
||||
globalThis.useQrCode = useQrCode
|
||||
globalThis.screenCapture = screenCapture
|
||||
globalThis.skipVerification = skipVerification
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"name": "obs-connect",
|
||||
"compatibility_date": "2025-12-20",
|
||||
"assets": {"directory": "assets"},
|
||||
"workers_dev": false,
|
||||
"keep_vars": true,
|
||||
"build": {
|
||||
"watch_dir": "./src",
|
||||
"command": "esbuild --outdir=assets --format=esm --bundle --minify --sourcemap --sources-content src/*"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user