Skip to content

Commit

Permalink
fix: support quoted characters, thanks @Ethan-Arrowood for the C++ port
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Apr 25, 2024
1 parent fa68423 commit e0367d4
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 2 deletions.
39 changes: 39 additions & 0 deletions packages/next/src/server/lib/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
getFormattedNodeOptionsWithoutInspect,
getParsedDebugAddress,
formatNodeOptions,
tokenizeArgs,
} from './utils'

const originalNodeOptions = process.env.NODE_OPTIONS
Expand All @@ -10,6 +11,34 @@ afterAll(() => {
process.env.NODE_OPTIONS = originalNodeOptions
})

describe('tokenizeArgs', () => {
it('splits arguments by spaces', () => {
const result = tokenizeArgs('--spaces "thing with spaces" --normal 1234')

expect(result).toEqual([
'--spaces',
'thing with spaces',
'--normal',
'1234',
])
})

it('supports quoted values', () => {
const result = tokenizeArgs(
'--spaces "thing with spaces" --spacesAndQuotes "thing with \\"spaces\\"" --normal 1234'
)

expect(result).toEqual([
'--spaces',
'thing with spaces',
'--spacesAndQuotes',
'thing with "spaces"',
'--normal',
'1234',
])
})
})

describe('formatNodeOptions', () => {
it('wraps values with spaces in quotes', () => {
const result = formatNodeOptions({
Expand Down Expand Up @@ -63,6 +92,16 @@ describe('getFormattedNodeOptionsWithoutInspect', () => {
)
})

it('handles options with quotes', () => {
process.env.NODE_OPTIONS =
'--require "./file with spaces to-require-with-node-require-option.js"'
const result = getFormattedNodeOptionsWithoutInspect()

expect(result).toBe(
'--require="./file with spaces to-require-with-node-require-option.js"'
)
})

it('removes --inspect option with parameters', () => {
process.env.NODE_OPTIONS = '--other --inspect=0.0.0.0:1234 --additional'
const result = getFormattedNodeOptionsWithoutInspect()
Expand Down
62 changes: 60 additions & 2 deletions packages/next/src/server/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,72 @@ const parseNodeArgs = (args: string[]) => {
return values
}

/**
* Tokenizes the arguments string into an array of strings, supporting quoted
* values and escaped characters.
*
* @param input The arguments string to be tokenized.
* @returns An array of strings with the tokenized arguments.
*/
export const tokenizeArgs = (input: string): string[] => {
let args: string[] = []
let isInString = false
let willStartNewArg = true

for (let i = 0; i < input.length; i++) {
let char = input[i]

// Skip any escaped characters in strings.
if (char === '\\' && isInString) {
// Ensure we don't have an escape character at the end.
if (input.length === i + 1) {
throw new Error('Invalid escape character at the end.')
}

// Skip the next character.
char = input[++i]
}
// If we find a space outside of a string, we should start a new argument.
else if (char === ' ' && !isInString) {
willStartNewArg = true
continue
}

// If we find a quote, we should toggle the string flag.
else if (char === '"') {
isInString = !isInString
continue
}

// If we're starting a new argument, we should add it to the array.
if (willStartNewArg) {
args.push(char)
willStartNewArg = false
}
// Otherwise, add it to the last argument.
else {
args[args.length - 1] += char
}
}

if (isInString) {
throw new Error('Unterminated string')
}

return args
}

/**
* Get the node options from the environment variable `NODE_OPTIONS` and returns
* them as an array of strings.
*
* @returns An array of strings with the node options.
*/
const getNodeOptionsArgs = () =>
process.env.NODE_OPTIONS?.split(' ').map((arg) => arg.trim()) ?? []
const getNodeOptionsArgs = () => {
if (!process.env.NODE_OPTIONS) return []

return tokenizeArgs(process.env.NODE_OPTIONS)
}

/**
* The debug address is in the form of `[host:]port`. The host is optional.
Expand Down

0 comments on commit e0367d4

Please sign in to comment.