mirror of https://github.com/vuejs/core.git
fix(compiler-sfc): rewrite default export with AST analysis instead of regex (#7068)
closes #7038 closes #7041 closes #7078
This commit is contained in:
parent
7def8b15b8
commit
701b95ff3d
|
@ -2,8 +2,9 @@ import { rewriteDefault } from '../src'
|
||||||
|
|
||||||
describe('compiler sfc: rewriteDefault', () => {
|
describe('compiler sfc: rewriteDefault', () => {
|
||||||
test('without export default', () => {
|
test('without export default', () => {
|
||||||
expect(rewriteDefault(`export a = {}`, 'script')).toMatchInlineSnapshot(`
|
expect(rewriteDefault(`export const a = {}`, 'script'))
|
||||||
"export a = {}
|
.toMatchInlineSnapshot(`
|
||||||
|
"export const a = {}
|
||||||
const script = {}"
|
const script = {}"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -14,6 +15,14 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
).toMatchInlineSnapshot(`"const script = {}"`)
|
).toMatchInlineSnapshot(`"const script = {}"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('rewrite variable value default', () => {
|
||||||
|
expect(rewriteDefault(`export const foo = 'default'`, 'script'))
|
||||||
|
.toMatchInlineSnapshot(`
|
||||||
|
"export const foo = 'default'
|
||||||
|
const script = {}"
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
test('rewrite export named default', () => {
|
test('rewrite export named default', () => {
|
||||||
expect(
|
expect(
|
||||||
rewriteDefault(
|
rewriteDefault(
|
||||||
|
@ -36,6 +45,18 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
export { a as b, a as c}
|
export { a as b, a as c}
|
||||||
const script = a"
|
const script = a"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rewriteDefault(
|
||||||
|
`const a = 1 \n export { a as b } \n export { a as default, a as c }`,
|
||||||
|
'script'
|
||||||
|
)
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"const a = 1
|
||||||
|
export { a as b }
|
||||||
|
export { a as c }
|
||||||
|
const script = a"
|
||||||
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('w/ comments', async () => {
|
test('w/ comments', async () => {
|
||||||
|
@ -52,7 +73,7 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"let App = {}
|
"let App = {}
|
||||||
export {
|
export {
|
||||||
|
|
||||||
}
|
}
|
||||||
const _sfc_main = App"
|
const _sfc_main = App"
|
||||||
`)
|
`)
|
||||||
|
@ -96,25 +117,25 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
expect(
|
expect(
|
||||||
rewriteDefault(`export { default, foo } from './index.js'`, 'script')
|
rewriteDefault(`export { default, foo } from './index.js'`, 'script')
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { foo } from './index.js'
|
export { foo } from './index.js'
|
||||||
const script = __VUE_DEFAULT__"
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
rewriteDefault(`export { default , foo } from './index.js'`, 'script')
|
rewriteDefault(`export { default , foo } from './index.js'`, 'script')
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { foo } from './index.js'
|
export { foo } from './index.js'
|
||||||
const script = __VUE_DEFAULT__"
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
rewriteDefault(`export { foo, default } from './index.js'`, 'script')
|
rewriteDefault(`export { foo, default } from './index.js'`, 'script')
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { default as __VUE_DEFAULT__ } from './index.js'
|
"import { default as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { foo, } from './index.js'
|
export { foo, } from './index.js'
|
||||||
const script = __VUE_DEFAULT__"
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -123,9 +144,9 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
'script'
|
'script'
|
||||||
)
|
)
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { foo } from './index.js'
|
"import { foo as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { bar } from './index.js'
|
export { bar } from './index.js'
|
||||||
const script = foo"
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -134,9 +155,9 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
'script'
|
'script'
|
||||||
)
|
)
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { foo } from './index.js'
|
"import { foo as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { bar } from './index.js'
|
export { bar } from './index.js'
|
||||||
const script = foo"
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
|
@ -145,18 +166,42 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
'script'
|
'script'
|
||||||
)
|
)
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"import { foo } from './index.js'
|
"import { foo as __VUE_DEFAULT__ } from './index.js'
|
||||||
export { bar, } from './index.js'
|
export { bar, } from './index.js'
|
||||||
const script = foo"
|
const script = __VUE_DEFAULT__"
|
||||||
|
`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rewriteDefault(
|
||||||
|
`export { foo as default } from './index.js' \n const foo = 1`,
|
||||||
|
'script'
|
||||||
|
)
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"import { foo as __VUE_DEFAULT__ } from './index.js'
|
||||||
|
export { } from './index.js'
|
||||||
|
const foo = 1
|
||||||
|
const script = __VUE_DEFAULT__"
|
||||||
|
`)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rewriteDefault(
|
||||||
|
`const a = 1 \nexport { a as default } from 'xxx'`,
|
||||||
|
'script'
|
||||||
|
)
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"import { a as __VUE_DEFAULT__ } from 'xxx'
|
||||||
|
const a = 1
|
||||||
|
export { } from 'xxx'
|
||||||
|
const script = __VUE_DEFAULT__"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('export default class', async () => {
|
test('export default class', async () => {
|
||||||
expect(rewriteDefault(`export default class Foo {}`, 'script'))
|
expect(rewriteDefault(`export default class Foo {}`, 'script'))
|
||||||
.toMatchInlineSnapshot(`
|
.toMatchInlineSnapshot(`
|
||||||
"class Foo {}
|
" class Foo {}
|
||||||
const script = Foo"
|
const script = Foo"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('export default class w/ comments', async () => {
|
test('export default class w/ comments', async () => {
|
||||||
|
@ -164,7 +209,7 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
|
rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"// export default
|
"// export default
|
||||||
class Foo {}
|
class Foo {}
|
||||||
const script = Foo"
|
const script = Foo"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -190,16 +235,18 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"/*
|
"/*
|
||||||
export default class Foo {}*/
|
export default class Foo {}*/
|
||||||
class Bar {}
|
class Bar {}
|
||||||
const script = Bar"
|
const script = Bar"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('@Component\nexport default class', async () => {
|
test('@Component\nexport default class', async () => {
|
||||||
expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script'))
|
expect(
|
||||||
.toMatchInlineSnapshot(`
|
rewriteDefault(`@Component\nexport default class Foo {}`, 'script', [
|
||||||
"@Component
|
'decorators-legacy'
|
||||||
class Foo {}
|
])
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
"@Component class Foo {}
|
||||||
const script = Foo"
|
const script = Foo"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -208,12 +255,12 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
expect(
|
expect(
|
||||||
rewriteDefault(
|
rewriteDefault(
|
||||||
`// export default\n@Component\nexport default class Foo {}`,
|
`// export default\n@Component\nexport default class Foo {}`,
|
||||||
'script'
|
'script',
|
||||||
|
['decorators-legacy']
|
||||||
)
|
)
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
"// export default
|
"// export default
|
||||||
@Component
|
@Component class Foo {}
|
||||||
class Foo {}
|
|
||||||
const script = Foo"
|
const script = Foo"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
@ -242,7 +289,7 @@ describe('compiler sfc: rewriteDefault', () => {
|
||||||
"/*
|
"/*
|
||||||
@Component
|
@Component
|
||||||
export default class Foo {}*/
|
export default class Foo {}*/
|
||||||
class Bar {}
|
class Bar {}
|
||||||
const script = Bar"
|
const script = Bar"
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
|
@ -53,7 +53,7 @@ import {
|
||||||
} from './cssVars'
|
} from './cssVars'
|
||||||
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
|
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
|
||||||
import { warnOnce } from './warn'
|
import { warnOnce } from './warn'
|
||||||
import { rewriteDefault } from './rewriteDefault'
|
import { rewriteDefaultAST } from './rewriteDefault'
|
||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
import { shouldTransform, transformAST } from '@vue/reactivity-transform'
|
import { shouldTransform, transformAST } from '@vue/reactivity-transform'
|
||||||
|
|
||||||
|
@ -231,7 +231,9 @@ export function compileScript(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cssVars.length) {
|
if (cssVars.length) {
|
||||||
content = rewriteDefault(content, DEFAULT_VAR, plugins)
|
const s = new MagicString(content)
|
||||||
|
rewriteDefaultAST(scriptAst.body, s, DEFAULT_VAR)
|
||||||
|
content = s.toString()
|
||||||
content += genNormalScriptCssVarsCode(
|
content += genNormalScriptCssVarsCode(
|
||||||
cssVars,
|
cssVars,
|
||||||
bindings,
|
bindings,
|
||||||
|
@ -1759,6 +1761,7 @@ export function compileScript(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...scriptSetup,
|
...scriptSetup,
|
||||||
|
s,
|
||||||
bindings: bindingMetadata,
|
bindings: bindingMetadata,
|
||||||
imports: userImports,
|
imports: userImports,
|
||||||
content: s.toString(),
|
content: s.toString(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ export { parse } from './parse'
|
||||||
export { compileTemplate } from './compileTemplate'
|
export { compileTemplate } from './compileTemplate'
|
||||||
export { compileStyle, compileStyleAsync } from './compileStyle'
|
export { compileStyle, compileStyleAsync } from './compileStyle'
|
||||||
export { compileScript } from './compileScript'
|
export { compileScript } from './compileScript'
|
||||||
export { rewriteDefault } from './rewriteDefault'
|
export { rewriteDefault, rewriteDefaultAST } from './rewriteDefault'
|
||||||
export {
|
export {
|
||||||
shouldTransform as shouldTransformRef,
|
shouldTransform as shouldTransformRef,
|
||||||
transform as transformRef,
|
transform as transformRef,
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { TemplateCompiler } from './compileTemplate'
|
||||||
import { parseCssVars } from './cssVars'
|
import { parseCssVars } from './cssVars'
|
||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
import { hmrShouldReload, ImportBinding } from './compileScript'
|
import { hmrShouldReload, ImportBinding } from './compileScript'
|
||||||
|
import MagicString from 'magic-string'
|
||||||
|
|
||||||
export const DEFAULT_FILENAME = 'anonymous.vue'
|
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ export interface SFCTemplateBlock extends SFCBlock {
|
||||||
|
|
||||||
export interface SFCScriptBlock extends SFCBlock {
|
export interface SFCScriptBlock extends SFCBlock {
|
||||||
type: 'script'
|
type: 'script'
|
||||||
|
s: MagicString
|
||||||
setup?: string | boolean
|
setup?: string | boolean
|
||||||
bindings?: BindingMetadata
|
bindings?: BindingMetadata
|
||||||
imports?: Record<string, ImportBinding>
|
imports?: Record<string, ImportBinding>
|
||||||
|
|
|
@ -1,55 +1,55 @@
|
||||||
import { parse, ParserPlugin } from '@babel/parser'
|
import { parse } from '@babel/parser'
|
||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
|
import type { ParserPlugin } from '@babel/parser'
|
||||||
|
import type { Identifier, Statement } from '@babel/types'
|
||||||
|
|
||||||
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
|
|
||||||
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)(?:as)?(\s*)default/s
|
|
||||||
const exportDefaultClassRE =
|
|
||||||
/((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility for rewriting `export default` in a script block into a variable
|
|
||||||
* declaration so that we can inject things into it
|
|
||||||
*/
|
|
||||||
export function rewriteDefault(
|
export function rewriteDefault(
|
||||||
input: string,
|
input: string,
|
||||||
as: string,
|
as: string,
|
||||||
parserPlugins?: ParserPlugin[]
|
parserPlugins?: ParserPlugin[]
|
||||||
): string {
|
): string {
|
||||||
if (!hasDefaultExport(input)) {
|
|
||||||
return input + `\nconst ${as} = {}`
|
|
||||||
}
|
|
||||||
|
|
||||||
let replaced: string | undefined
|
|
||||||
|
|
||||||
const classMatch = input.match(exportDefaultClassRE)
|
|
||||||
if (classMatch) {
|
|
||||||
replaced =
|
|
||||||
input.replace(exportDefaultClassRE, '$1class $2') +
|
|
||||||
`\nconst ${as} = ${classMatch[2]}`
|
|
||||||
} else {
|
|
||||||
replaced = input.replace(defaultExportRE, `$1const ${as} =`)
|
|
||||||
}
|
|
||||||
if (!hasDefaultExport(replaced)) {
|
|
||||||
return replaced
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the script somehow still contains `default export`, it probably has
|
|
||||||
// multi-line comments or template strings. fallback to a full parse.
|
|
||||||
const s = new MagicString(input)
|
|
||||||
const ast = parse(input, {
|
const ast = parse(input, {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
plugins: parserPlugins
|
plugins: parserPlugins
|
||||||
}).program.body
|
}).program.body
|
||||||
|
const s = new MagicString(input)
|
||||||
|
|
||||||
|
rewriteDefaultAST(ast, s, as)
|
||||||
|
|
||||||
|
return s.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for rewriting `export default` in a script block into a variable
|
||||||
|
* declaration so that we can inject things into it
|
||||||
|
*/
|
||||||
|
export function rewriteDefaultAST(
|
||||||
|
ast: Statement[],
|
||||||
|
s: MagicString,
|
||||||
|
as: string
|
||||||
|
): void {
|
||||||
|
if (!hasDefaultExport(ast)) {
|
||||||
|
s.append(`\nconst ${as} = {}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the script somehow still contains `default export`, it probably has
|
||||||
|
// multi-line comments or template strings. fallback to a full parse.
|
||||||
ast.forEach(node => {
|
ast.forEach(node => {
|
||||||
if (node.type === 'ExportDefaultDeclaration') {
|
if (node.type === 'ExportDefaultDeclaration') {
|
||||||
if (node.declaration.type === 'ClassDeclaration') {
|
if (node.declaration.type === 'ClassDeclaration') {
|
||||||
s.overwrite(node.start!, node.declaration.id.start!, `class `)
|
let start: number =
|
||||||
|
node.declaration.decorators && node.declaration.decorators.length > 0
|
||||||
|
? node.declaration.decorators[
|
||||||
|
node.declaration.decorators.length - 1
|
||||||
|
].end!
|
||||||
|
: node.start!
|
||||||
|
s.overwrite(start, node.declaration.id.start!, ` class `)
|
||||||
s.append(`\nconst ${as} = ${node.declaration.id.name}`)
|
s.append(`\nconst ${as} = ${node.declaration.id.name}`)
|
||||||
} else {
|
} else {
|
||||||
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
|
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
|
||||||
}
|
}
|
||||||
}
|
} else if (node.type === 'ExportNamedDeclaration') {
|
||||||
if (node.type === 'ExportNamedDeclaration') {
|
|
||||||
for (const specifier of node.specifiers) {
|
for (const specifier of node.specifiers) {
|
||||||
if (
|
if (
|
||||||
specifier.type === 'ExportSpecifier' &&
|
specifier.type === 'ExportSpecifier' &&
|
||||||
|
@ -58,56 +58,64 @@ export function rewriteDefault(
|
||||||
) {
|
) {
|
||||||
if (node.source) {
|
if (node.source) {
|
||||||
if (specifier.local.name === 'default') {
|
if (specifier.local.name === 'default') {
|
||||||
const end = specifierEnd(input, specifier.local.end!, node.end!)
|
|
||||||
s.prepend(
|
s.prepend(
|
||||||
`import { default as __VUE_DEFAULT__ } from '${node.source.value}'\n`
|
`import { default as __VUE_DEFAULT__ } from '${node.source.value}'\n`
|
||||||
)
|
)
|
||||||
s.overwrite(specifier.start!, end, ``)
|
const end = specifierEnd(s, specifier.local.end!, node.end!)
|
||||||
|
s.remove(specifier.start!, end)
|
||||||
s.append(`\nconst ${as} = __VUE_DEFAULT__`)
|
s.append(`\nconst ${as} = __VUE_DEFAULT__`)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
const end = specifierEnd(
|
|
||||||
input,
|
|
||||||
specifier.exported.end!,
|
|
||||||
node.end!
|
|
||||||
)
|
|
||||||
s.prepend(
|
s.prepend(
|
||||||
`import { ${input.slice(
|
`import { ${s.slice(
|
||||||
specifier.local.start!,
|
specifier.local.start!,
|
||||||
specifier.local.end!
|
specifier.local.end!
|
||||||
)} } from '${node.source.value}'\n`
|
)} as __VUE_DEFAULT__ } from '${node.source.value}'\n`
|
||||||
)
|
)
|
||||||
s.overwrite(specifier.start!, end, ``)
|
const end = specifierEnd(s, specifier.exported.end!, node.end!)
|
||||||
s.append(`\nconst ${as} = ${specifier.local.name}`)
|
s.remove(specifier.start!, end)
|
||||||
|
s.append(`\nconst ${as} = __VUE_DEFAULT__`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const end = specifierEnd(input, specifier.end!, node.end!)
|
|
||||||
s.overwrite(specifier.start!, end, ``)
|
const end = specifierEnd(s, specifier.end!, node.end!)
|
||||||
|
s.remove(specifier.start!, end)
|
||||||
s.append(`\nconst ${as} = ${specifier.local.name}`)
|
s.append(`\nconst ${as} = ${specifier.local.name}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return s.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasDefaultExport(input: string): boolean {
|
export function hasDefaultExport(ast: Statement[]): boolean {
|
||||||
return defaultExportRE.test(input) || namedDefaultExportRE.test(input)
|
for (const stmt of ast) {
|
||||||
|
if (stmt.type === 'ExportDefaultDeclaration') {
|
||||||
|
return true
|
||||||
|
} else if (
|
||||||
|
stmt.type === 'ExportNamedDeclaration' &&
|
||||||
|
stmt.specifiers.some(
|
||||||
|
spec => (spec.exported as Identifier).name === 'default'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function specifierEnd(input: string, end: number, nodeEnd: number | null) {
|
function specifierEnd(s: MagicString, end: number, nodeEnd: number | null) {
|
||||||
// export { default , foo } ...
|
// export { default , foo } ...
|
||||||
let hasCommas = false
|
let hasCommas = false
|
||||||
let oldEnd = end
|
let oldEnd = end
|
||||||
while (end < nodeEnd!) {
|
while (end < nodeEnd!) {
|
||||||
if (/\s/.test(input.charAt(end))) {
|
if (/\s/.test(s.slice(end, end + 1))) {
|
||||||
end++
|
end++
|
||||||
} else if (input.charAt(end) === ',') {
|
} else if (s.slice(end, end + 1) === ',') {
|
||||||
end++
|
end++
|
||||||
hasCommas = true
|
hasCommas = true
|
||||||
break
|
break
|
||||||
} else if (input.charAt(end) === '}') {
|
} else if (s.slice(end, end + 1) === '}') {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue