Skip to content

Commit

Permalink
try: some simple hacks to make inline plugins work
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 15, 2024
1 parent 92cb6b7 commit e8aefb0
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 9 deletions.
17 changes: 15 additions & 2 deletions src/try/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ async function reloadWorker(version: Version): Promise<Worker> {
}

export function sendIPC(message: IPCRequest): Promise<IPCResponse> {
function activateTask(worker: Worker, task: Task): void {
const activateTask = (worker: Worker, task: Task): void => {
if (activeTask) {
if (pendingTask) pendingTask.abort_()
pendingTask = task
Expand All @@ -183,9 +183,22 @@ export function sendIPC(message: IPCRequest): Promise<IPCResponse> {

return new Promise((resolve, reject) => {
workerPromise.then(worker => activateTask(worker, {
message_: message,
message_: serializeFunctions(message),
resolve_: resolve,
abort_: () => reject(new Error('Task aborted')),
}), reject)
})
}

// Hack: Serialize "Function" objects as "EvalError" objects instead
const serializeFunctions = (value: any): any => {
if (typeof value === 'function') {
const text = value + ''
return new EvalError('function ' + value.name + text.slice(text.indexOf('(')))
}
if (typeof value === 'object' && value) {
if (Array.isArray(value)) return value.map(serializeFunctions)
else return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, serializeFunctions(v)]))
}
return value
}
40 changes: 35 additions & 5 deletions src/try/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import { Mode } from './mode'
import { RichError } from './output'

const enum Kind {
// These must be in the order "-,:[]{}+"
// These must be in the order "-,:[]{}()+"
Minus,
Comma,
Colon,
OpenBracket,
CloseBracket,
OpenBrace,
CloseBrace,
OpenParen,
CloseParen,
Plus,

// The order doesn't matter for these
Identifier,
Function,
Literal,
String,
}
Expand Down Expand Up @@ -483,7 +486,7 @@ function parseOptionsAsLooseJSON(input: string): Record<string, any> {
}

// Punctuation
const index = '-,:[]{}+'.indexOf(c)
const index = '-,:[]{}()+'.indexOf(c)
if (index >= 0) {
i++
token = { line_: tokenLine, column_: tokenColumn, kind_: index as Kind, text_: c, value_: c }
Expand Down Expand Up @@ -523,6 +526,7 @@ function parseOptionsAsLooseJSON(input: string): Record<string, any> {
else if (text === 'undefined') value = undefined
else if (text === 'Infinity') value = Infinity
else if (text === 'NaN') value = NaN
else if (text === 'function') kind = Kind.Function
else kind = Kind.Identifier
token = { line_: tokenLine, column_: tokenColumn, kind_: kind, text_: text, value_: value }
return
Expand Down Expand Up @@ -561,6 +565,25 @@ function parseOptionsAsLooseJSON(input: string): Record<string, any> {
}
}

// Hack: Parse function literals by repeatedly using the browser's parser after each '}' character
const scanForFunctionLiteral = (name: string, from: number): Function => {
let closeBrace = /\}/g
let error = ''
closeBrace.lastIndex = from

for (let match: RegExpExecArray | null; match = closeBrace.exec(input);) {
try {
const code = new Function('return {' + name + input.slice(from, match.index + 1) + '}.' + name)
i = match.index + 1
return code()
} catch (err) {
error = ': ' + err.message
}
}

throwRichError(input, 'Invalid function literal' + error, token.line_, token.column_, token.text_.length)
}

const parseExpression = (): any => {
if (token.kind_ as number === Kind.OpenBrace) {
const object: Record<string, any> = Object.create(null)
Expand All @@ -575,10 +598,17 @@ function parseOptionsAsLooseJSON(input: string): Record<string, any> {
`The original key ${JSON.stringify(token.value_)} is here:`, original.line_, original.column_, original.text_.length)
}
const key = token
const endOfKey = i
let value: any
nextToken()
if (token.kind_ !== Kind.Colon) throwExpectedAfter(key, ':', 'property ' + JSON.stringify(key.value_))
nextToken()
object[key.value_] = parseExpression()
if (token.kind_ === Kind.OpenParen) value = scanForFunctionLiteral(key.value_, endOfKey)
else {
if (token.kind_ !== Kind.Colon) throwExpectedAfter(key, ':', 'property ' + JSON.stringify(key.value_))
nextToken()
if (token.kind_ === Kind.Function) value = scanForFunctionLiteral(key.value_, endOfKey)
else value = parseExpression()
}
object[key.value_] = value
originals[key.value_] = key
const beforeComma = token
nextToken()
Expand Down
14 changes: 12 additions & 2 deletions src/try/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ const formatMessages = (api: API, messages: Message[], options: FormatMessagesOp
}))
}

// Hack: Deserialize "EvalError" objects as "Function" objects instead
const deserializeFunctions = (value: any): any => {
if (typeof value === 'object' && value) {
if (value instanceof EvalError) return new Function('return ' + value.message)()
else if (Array.isArray(value)) return value.map(deserializeFunctions)
else return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deserializeFunctions(v)]))
}
return value
}

onmessage = e => {
setup(e.data).then(api => {
onmessage = e => {
Expand Down Expand Up @@ -167,7 +177,7 @@ onmessage = e => {
}
}

const request: IPCRequest = e.data
const request: IPCRequest = deserializeFunctions(e.data)
const respond: (response: IPCResponse) => void = postMessage
let color = true

Expand Down Expand Up @@ -197,7 +207,7 @@ onmessage = e => {
api.build(request.options_).then(
({ warnings, outputFiles, metafile, mangleCache }) =>
finish(warnings, (stderr: string) => respond({
outputFiles_: outputFiles.map(({ path, text }) => ({ path, text })),
outputFiles_: outputFiles,
metafile_: metafile,
mangleCache_: mangleCache,
stderr_: stderr,
Expand Down

0 comments on commit e8aefb0

Please sign in to comment.