build: strip bundled comments and sanitize fvad loader

This commit is contained in:
mssoftjp 2025-12-14 12:25:41 +09:00
parent 6c81ba0491
commit 4f41ee0df4
3 changed files with 17 additions and 120 deletions

View file

@ -43,10 +43,10 @@ const context = await esbuild.context({
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
minifyWhitespace: prod,
minifyIdentifiers: prod,
minifySyntax: prod,
legalComments: 'inline',
// Production builds must be minified (Obsidian dev docs: Optimize plugin load time)
minify: prod,
// Remove all other comments; banner remains
legalComments: 'none',
outfile: path.join(outDir, "main.js"),
});

View file

@ -1,111 +1,9 @@
/**
* AudioWorklet source code as a string
* This allows us to create a blob URL for loading in restricted environments
* AudioWorklet source code as a single-line string with comments stripped
* to keep bundled output comment-free (except the esbuild banner).
*/
export const AUDIO_WORKLET_SOURCE = `
// AudioWorkletProcessor for real-time audio processing
class AudioProcessorWorklet extends AudioWorkletProcessor {
constructor() {
super();
this.bufferSize = 4096;
this.audioBuffer = [];
this.isRecording = false;
// Listen for messages from main thread
this.port.onmessage = (event) => {
this.handleMessage(event.data);
};
}
/**
* Handle messages from main thread
*/
handleMessage(data) {
switch (data.type) {
case 'start':
this.isRecording = true;
this.audioBuffer = [];
break;
case 'stop':
this.isRecording = false;
// Send any remaining buffered audio
if (this.audioBuffer.length > 0) {
this.sendAudioData();
}
break;
case 'configure':
if (data.bufferSize) {
this.bufferSize = data.bufferSize;
}
break;
}
}
/**
* Process audio - called for each render quantum (128 samples)
*/
process(inputs, outputs, parameters) {
const input = inputs[0];
if (!this.isRecording || !input || input.length === 0) {
return true; // Keep processor alive
}
// Get the first channel
const channelData = input[0];
if (!channelData || channelData.length === 0) {
return true;
}
// Buffer the audio data
this.audioBuffer.push(new Float32Array(channelData));
// Check if we've accumulated enough data
const totalSamples = this.audioBuffer.reduce((sum, buffer) => sum + buffer.length, 0);
if (totalSamples >= this.bufferSize) {
this.sendAudioData();
}
return true; // Keep processor alive
}
/**
* Send accumulated audio data to main thread
*/
sendAudioData() {
if (this.audioBuffer.length === 0) {
return;
}
// Combine all buffers into one
const totalLength = this.audioBuffer.reduce((sum, buffer) => sum + buffer.length, 0);
const combinedBuffer = new Float32Array(totalLength);
let offset = 0;
for (const buffer of this.audioBuffer) {
combinedBuffer.set(buffer, offset);
offset += buffer.length;
}
// Send to main thread
this.port.postMessage({
type: 'audio',
data: combinedBuffer,
timestamp: currentTime
});
// Clear the buffer
this.audioBuffer = [];
}
}
// Register the processor
registerProcessor('audio-processor-worklet', AudioProcessorWorklet);
`;
export const AUDIO_WORKLET_SOURCE =
`class AudioProcessorWorklet extends AudioWorkletProcessor{constructor(){super();this.bufferSize=4096;this.audioBuffer=[];this.isRecording=!1;this.port.onmessage=e=>{this.handleMessage(e.data)}}handleMessage(e){switch(e.type){case\"start\":this.isRecording=!0,this.audioBuffer=[];break;case\"stop\":this.isRecording=!1,this.audioBuffer.length>0&&this.sendAudioData();break;case\"configure\":e.bufferSize&&(this.bufferSize=e.bufferSize);break}}process(e){const t=e[0];if(!this.isRecording||!t||t.length===0)return!0;const s=t[0];if(!s||s.length===0)return!0;this.audioBuffer.push(new Float32Array(s));const r=this.audioBuffer.reduce((n,o)=>n+o.length,0);return r>=this.bufferSize&&this.sendAudioData(),!0}sendAudioData(){if(this.audioBuffer.length===0)return;const e=this.audioBuffer.reduce((t,s)=>t+s.length,0),t=new Float32Array(e);let s=0;for(const r of this.audioBuffer)t.set(r,s),s+=r.length;this.port.postMessage({type:\"audio\",data:t,timestamp:currentTime}),this.audioBuffer=[]}}registerProcessor(\"audio-processor-worklet\",AudioProcessorWorklet);`;
/**
* Create a blob URL for the AudioWorklet

View file

@ -114,22 +114,21 @@ export class VADProcessor extends Disposable {
// fvad.js の内容を読み込んで評価(プラグインルートから)
const fvadJsPath = this.getPluginAssetPath('fvad.js');
const fvadJsContent = await this.readPluginTextAsset('fvad.js');
const sanitizedFvadJsContent = fvadJsContent
// Remove block comments
.replace(/\/\*[\s\S]*?\*\//g, '')
// Remove line comments
.replace(/^\s*\/\/.*$/gm, '');
// モジュールを評価するための一時的な環境を作成
return new Promise((resolve, reject) => {
// スクリプトタグを作成
const script = document.createElement('script');
script.type = 'module';
script.textContent = `
// import.meta.url のポリフィル
const importMeta = { url: 'file:///${fvadJsPath}' };
// fvad モジュールを定義
${fvadJsContent}
// グローバルに公開
window.__fvadModule = fvad;
`;
script.textContent =
`const importMeta={url:'file:///${fvadJsPath}'};` +
`${sanitizedFvadJsContent}` +
`window.__fvadModule=fvad;`;
// エラーハンドリング
script.onerror = (error) => {