瀏覽代碼

Structure tests, document methods

The tests do not depend on separate files for loading templates. This is
for the layouts to be defined alongside the tests for easier updates.

The methods have been refactored and documented for ease of use of the
consumers.
master
TheoryOfNekomata 3 年之前
父節點
當前提交
67bd13e7ae
共有 31 個檔案被更改,包括 1385 行新增1025 行删除
  1. +7
    -5
      README.md
  2. +0
    -12
      cypress/fixtures/templates/blank.html
  3. +0
    -98
      cypress/fixtures/templates/everything.html
  4. +0
    -19
      cypress/fixtures/templates/single-disabled-input.html
  5. +0
    -16
      cypress/fixtures/templates/single-file-input.html
  6. +0
    -17
      cypress/fixtures/templates/single-input-with-double-button-submitters.html
  7. +0
    -17
      cypress/fixtures/templates/single-input-with-double-input-submitters.html
  8. +0
    -16
      cypress/fixtures/templates/single-input.html
  9. +0
    -16
      cypress/fixtures/templates/single-multiple-file-input.html
  10. +0
    -21
      cypress/fixtures/templates/single-multiple-select.html
  11. +0
    -15
      cypress/fixtures/templates/single-outside-input-and-submitter.html
  12. +0
    -18
      cypress/fixtures/templates/single-outside-input.html
  13. +0
    -19
      cypress/fixtures/templates/single-readonly-input.html
  14. +0
    -20
      cypress/fixtures/templates/single-select.html
  15. +3
    -0
      cypress/fixtures/uploads/data2.json
  16. +0
    -18
      cypress/integration/blank.test.ts
  17. +82
    -0
      cypress/integration/checkbox.test.ts
  18. +0
    -38
      cypress/integration/everything.test.ts
  19. +142
    -0
      cypress/integration/file.test.ts
  20. +168
    -0
      cypress/integration/misc.test.ts
  21. +81
    -0
      cypress/integration/select.test.ts
  22. +0
    -200
      cypress/integration/single.test.ts
  23. +112
    -0
      cypress/integration/submitter.test.ts
  24. +148
    -0
      cypress/integration/text.test.ts
  25. +95
    -0
      cypress/utils/index.ts
  26. +74
    -0
      cypress/utils/jsdom-compat.ts
  27. +3
    -3
      package.json
  28. +248
    -100
      src/index.ts
  29. +0
    -145
      test/utils/index.ts
  30. +1
    -1
      tsconfig.json
  31. +221
    -211
      yarn.lock

+ 7
- 5
README.md 查看文件

@@ -1,4 +1,6 @@
# formxtr
# formxtra

_(read "form extra")_

Extract form values through the DOM.

@@ -20,7 +22,7 @@ Upon retrieving the field values somehow, some libraries attempt to duplicate th
for instance by attaching event listeners and storing the new values into some internal object or map, which can be
retrieved by some other exposed function.

With `formxtr`, there is no need to traverse the DOM for individual fields to get their values. Provided the fields are
With `formxtra`, there is no need to traverse the DOM for individual fields to get their values. Provided the fields are
associated to the form (either as a descendant of the `<form>` element or [associated through the `form=""`
attribute](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fae-form)) and has a valid
`name`, the values of these fields can be easily extracted, using the `form.elements` attribute built-in to the DOM and
@@ -32,8 +34,8 @@ of the field elements are already stored in the DOM, waiting to be accessed.
The package can be found on:

- [Modal Pack](https://js.pack.modal.sh)
- [npm](https://npmjs.com/package/formxtr)
- [GitHub Package Registry](https://github.com/TheoryOfNekomata/formxtr/packages/784699)
- [npm](https://npmjs.com/package/formxtra)
- [GitHub Package Registry](https://github.com/TheoryOfNekomata/formxtra/packages/784699)

## Usage

@@ -60,7 +62,7 @@ For an example form:
Use the library as follows (code is in TypeScript, but can work with JavaScript as well):

```typescript
import getFormValues from '@theoryofnekomata/formxtr';
import getFormValues from '@theoryofnekomata/formxtra';

const form: HTMLFormElement = document.getElementById('form');



+ 0
- 12
cypress/fixtures/templates/blank.html 查看文件

@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 98
cypress/fixtures/templates/everything.html 查看文件

@@ -1,98 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<article>
<h2></h2>
<form>
<div>
<input type="text" placeholder="First Name" name="first_name" />
</div>
<div>
<input type="text" placeholder="Middle Name" name="middle_name" />
</div>
<div>
<input type="text" placeholder="Last Name" name="last_name" />
</div>
<fieldset>
<legend>Gender</legend>
<div>
<label>
<input type="radio" name="gender" value="m" />
Male
</label>
<label>
<input type="radio" name="gender" value="f" />
Female
</label>
</div>
</fieldset>
<div>
<select name="civil_status">
<option value="">Select Civil Status</option>
<option value="single">Single</option>
<option value="married">Married</option>
<option value="divorced">Divorced</option>
<option value="separated">Separated</option>
</select>
</div>
<div>
<label>
<input type="checkbox" name="new_registration" />
New Registration
</label>
</div>
<div>
<label>
<input type="checkbox" value="filipino" name="nationality" />
Filipino
</label>
</div>
<fieldset>
<legend>
Default Dependents
</legend>
<div>
<label>
<input type="radio" value="James" name="dependent" />
James
</label>
<label>
<input type="radio" value="Jun" name="dependent" />
Jun
</label>
</div>
</fieldset>
<div>
<button type="button" class="dependents">
Add Dependents
</button>
</div>
<div>
<textarea name="notes" placeholder="Notes"></textarea>
</div>
<div>
<button name="submit" value="Hello" type="submit">Hello</button>
<button name="submit" value="Hi" type="submit">Hi</button>
</div>
</form>
</article>
<script>
Array.from(document.getElementsByClassName('dependents')).forEach(d => {
d.addEventListener('click', e => {
const container = document.createElement('div')
const input = document.createElement('input')
input.name = 'dependent'
input.type = 'text'
input.placeholder = 'Dependent'
container.classList.add('additional-dependent')
container.appendChild(input)
e.target.parentElement.parentElement.insertBefore(container, e.target.parentElement)
})
})
</script>
</body>
</html>

+ 0
- 19
cypress/fixtures/templates/single-disabled-input.html 查看文件

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input
type="text" name="hello" value="Hi"
disabled
/>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 16
cypress/fixtures/templates/single-file-input.html 查看文件

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 17
cypress/fixtures/templates/single-input-with-double-button-submitters.html 查看文件

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<button name="action" value="Foo" type="submit">Foo</button>
<button name="action" value="Bar" type="submit">Bar</button>
</form>
</body>
</html>

+ 0
- 17
cypress/fixtures/templates/single-input-with-double-input-submitters.html 查看文件

@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<input name="action" value="Foo" type="submit" />
<input name="action" value="Bar" type="submit" />
</form>
</body>
</html>

+ 0
- 16
cypress/fixtures/templates/single-input.html 查看文件

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 16
cypress/fixtures/templates/single-multiple-file-input.html 查看文件

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" multiple />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 21
cypress/fixtures/templates/single-multiple-select.html 查看文件

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello" multiple>
<option>Foo</option>
<option selected>Bar</option>
<option>Baz</option>
<option selected>Quux</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 15
cypress/fixtures/templates/single-outside-input-and-submitter.html 查看文件

@@ -1,15 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="form"></form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
<button type="submit" form="form">Submit</button>
</body>
</html>

+ 0
- 18
cypress/fixtures/templates/single-outside-input.html 查看文件

@@ -1,18 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form
id="form"
>
<button type="submit">Submit</button>
</form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
</body>
</html>

+ 0
- 19
cypress/fixtures/templates/single-readonly-input.html 查看文件

@@ -1,19 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input
type="text" name="hello" value="Hi"
readonly
/>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 0
- 20
cypress/fixtures/templates/single-select.html 查看文件

@@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello">
<option>Foo</option>
<option>Bar</option>
<option selected>Baz</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>

+ 3
- 0
cypress/fixtures/uploads/data2.json 查看文件

@@ -0,0 +1,3 @@
{
"hello": "hi"
}

+ 0
- 18
cypress/integration/blank.test.ts 查看文件

@@ -1,18 +0,0 @@
import getFormValues from '../../src'
import * as utils from '../../test/utils'

describe('blank template', () => {
beforeEach(utils.setup('blank'))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

+ 82
- 0
cypress/integration/checkbox.test.ts 查看文件

@@ -0,0 +1,82 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('checkbox', () => {
describe('basic', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Checkbox/Basic</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="checkbox" name="enabled" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have no form values', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const values = getFormValues(form, {submitter})
const before = utils.makeSearchParams(values).toString();
const after = utils.makeSearchParams(search).toString();
expect(values['enabled']).toBeUndefined();
expect(before).toEqual(after);
},
''
);
});

it('should have false checked value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const values = getFormValues(form, {submitter, booleanValuelessCheckbox: true })
expect(values['enabled']).toBe(false);
}
);
});
})

describe('checked', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Checkbox/Checked</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="checkbox" name="enabled" checked />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have single form value on a single field', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
'enabled=on'
);
});
})
})

+ 0
- 38
cypress/integration/everything.test.ts 查看文件

@@ -1,38 +0,0 @@
import getFormValues from '../../src'
import * as utils from '../../test/utils';

describe('everything template', () => {
beforeEach(utils.setup('everything'))

it('should have correct form values', () => {
utils.test(
(cy) => {
cy.get('[name="first_name"]').type('John')
cy.get('[name="middle_name"]').type('Marcelo')
cy.get('[name="last_name"]').type('Dela Cruz')
cy.get('[name="gender"][value="m"]').check()
cy.get('[name="civil_status"]').select('Married')
cy.get('[name="new_registration"]').check()
cy.get('[name="nationality"][value="filipino"]').check()
cy.get('[name="dependent"][value="Jun"]').check()

// Note: JSDOM is static for now
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Juana')
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Jane')
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Josh')

cy.get('[name="notes"]').type('Test content\n\nNew line\n\nAnother line')
return cy.get('[name="submit"][value="Hi"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
'first_name=John&middle_name=Marcelo&last_name=Dela+Cruz&gender=m&civil_status=married&new_registration=on&nationality=filipino&dependent=Jun&notes=Test+content%0D%0A%0D%0ANew+line%0D%0A%0D%0AAnother+line&submit=Hi',
);
});
})

+ 142
- 0
cypress/integration/file.test.ts 查看文件

@@ -0,0 +1,142 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('file', () => {
describe('single', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>File/Single</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have no form values when no file is selected', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
})

it('should have single form value when a file is selected', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile('uploads/data.json')

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'data.json',
}
);
})

it('should retrieve the file list upon setting appropriate option', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile('uploads/data.json')

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any) => {
const formValues = getFormValues(form, {submitter, getFileObjects: true})
expect(formValues.hello[0].name).toBe('data.json')
//expect(before).toEqual(after);
},
);
})
})

describe('multiple', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>File/Multiple</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="file" name="hello" multiple />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have no form values when no file is selected', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
})

it('should have single form value when a file is selected', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile(['uploads/data.json', 'uploads/data2.json'])

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
'hello=data.json&hello=data2.json',
);
})

it('should retrieve the file list upon setting appropriate option', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile(['uploads/data.json', 'uploads/data2.json'])

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any) => {
const formValues = getFormValues(form, {submitter, getFileObjects: true})
expect(formValues.hello[0].name).toBe('data.json')
expect(formValues.hello[1].name).toBe('data2.json')
},
);
})
})
})

+ 168
- 0
cypress/integration/misc.test.ts 查看文件

@@ -0,0 +1,168 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('misc', () => {
describe('blank', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Misc/Blank</title>
</head>
<body>
<form>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

describe('everything', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Misc/Everything</title>
</head>
<body>
<article>
<h2></h2>
<form>
<div>
<input type="text" placeholder="First Name" name="first_name" />
</div>
<div>
<input type="text" placeholder="Middle Name" name="middle_name" />
</div>
<div>
<input type="text" placeholder="Last Name" name="last_name" />
</div>
<fieldset>
<legend>Gender</legend>
<div>
<label>
<input type="radio" name="gender" value="m" />
Male
</label>
<label>
<input type="radio" name="gender" value="f" />
Female
</label>
</div>
</fieldset>
<div>
<select name="civil_status">
<option value="">Select Civil Status</option>
<option value="single">Single</option>
<option value="married">Married</option>
<option value="divorced">Divorced</option>
<option value="separated">Separated</option>
</select>
</div>
<div>
<label>
<input type="checkbox" name="new_registration" />
New Registration
</label>
</div>
<div>
<label>
<input type="checkbox" value="filipino" name="nationality" />
Filipino
</label>
</div>
<fieldset>
<legend>
Default Dependents
</legend>
<div>
<label>
<input type="radio" value="James" name="dependent" />
James
</label>
<label>
<input type="radio" value="Jun" name="dependent" />
Jun
</label>
</div>
</fieldset>
<div>
<button type="button" class="dependents">
Add Dependents
</button>
</div>
<div>
<textarea name="notes" placeholder="Notes"></textarea>
</div>
<div>
<button name="submit" value="Hello" type="submit">Hello</button>
<button name="submit" value="Hi" type="submit">Hi</button>
</div>
</form>
</article>
<script>
Array.from(document.getElementsByClassName('dependents')).forEach(d => {
d.addEventListener('click', e => {
const container = document.createElement('div')
const input = document.createElement('input')
input.name = 'dependent'
input.type = 'text'
input.placeholder = 'Dependent'
container.classList.add('additional-dependent')
container.appendChild(input)
e.target.parentElement.parentElement.insertBefore(container, e.target.parentElement)
})
})
</script>
</body>
</html>
`))

it('should have correct form values', () => {
utils.test(
(cy) => {
cy.get('[name="first_name"]').type('John')
cy.get('[name="middle_name"]').type('Marcelo')
cy.get('[name="last_name"]').type('Dela Cruz')
cy.get('[name="gender"][value="m"]').check()
cy.get('[name="civil_status"]').select('Married')
cy.get('[name="new_registration"]').check()
cy.get('[name="nationality"][value="filipino"]').check()
cy.get('[name="dependent"][value="Jun"]').check()

// Note: JSDOM is static for now
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Juana')
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Jane')
cy.get('button.dependents').click()
cy.get('.additional-dependent [name="dependent"][type="text"]').last().type('Josh')

cy.get('[name="notes"]').type('Test content\n\nNew line\n\nAnother line')
return cy.get('[name="submit"][value="Hi"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
'first_name=John&middle_name=Marcelo&last_name=Dela+Cruz&gender=m&civil_status=married&new_registration=on&nationality=filipino&dependent=Jun&notes=Test+content%0D%0A%0D%0ANew+line%0D%0A%0D%0AAnother+line&submit=Hi',
);
});
})
})

+ 81
- 0
cypress/integration/select.test.ts 查看文件

@@ -0,0 +1,81 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('select', () => {
describe('multiple', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Select/Multiple</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello" multiple>
<option>Foo</option>
<option selected>Bar</option>
<option>Baz</option>
<option selected>Quux</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have multiple form values on a single field', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
'hello=Bar&hello=Quux'
);
});
})

describe('single', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Select/Single</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<select name="hello">
<option>Foo</option>
<option>Bar</option>
<option selected>Baz</option>
</select>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have single form value on a single field', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Baz',
}
);
});
})
})

+ 0
- 200
cypress/integration/single.test.ts 查看文件

@@ -1,200 +0,0 @@
import getFormValues from '../../src'
import * as utils from '../../test/utils';

describe('single input template', () => {
beforeEach(utils.setup('single-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single outside input template', () => {
beforeEach(utils.setup('single-outside-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single outside input and submitter template', () => {
beforeEach(utils.setup('single-outside-input-and-submitter'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single readonly template', () => {
beforeEach(utils.setup('single-readonly-input'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('single disabled template', () => {
beforeEach(utils.setup('single-disabled-input'))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

describe('single input with double button submitters template', () => {
beforeEach(utils.setup('single-input-with-double-button-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Foo"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Foo',
}
);
});
})

describe('single input with double input submitters template', () => {
beforeEach(utils.setup('single-input-with-double-input-submitters'))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Bar"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Bar',
}
);
});
})

describe('single select template', () => {
beforeEach(utils.setup('single-select'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Baz',
}
);
});
})

describe('single multiple select template', () => {
beforeEach(utils.setup('single-multiple-select'))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Bar,Quux',
}
);
});
})

describe('single file input template', () => {
beforeEach(utils.setup('single-file-input'))

it('should have no form values when no file is selected', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
})

it('should have single form value when a file is selected', () => {
utils.test(
(cy: any) => {
cy
.get('[name="hello"]')
.attachFile('uploads/data.json')

return cy.get('[type="submit"]')
},
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'data.json',
}
);
})
})

+ 112
- 0
cypress/integration/submitter.test.ts 查看文件

@@ -0,0 +1,112 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('submitter', () => {
describe('button', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Submitter/Button</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<button name="action" value="Foo" type="submit">Foo</button>
<button name="action" value="Bar" type="submit">Bar</button>
</form>
</body>
</html>
`))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Foo"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Foo',
}
);
});
})

describe('input', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Submitter/Input</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<input name="action" value="Foo" type="submit" />
<input name="action" value="Bar" type="submit" />
</form>
</body>
</html>
`))

it('should have double form values', () => {
utils.test(
(cy: any) => cy.get('[name="action"][value="Bar"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
action: 'Bar',
}
);
});
})

describe('outside', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Submitter/Outside</title>
</head>
<body>
<form id="form"></form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
<button type="submit" form="form">Submit</button>
</body>
</html>
`))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})
})

+ 148
- 0
cypress/integration/text.test.ts 查看文件

@@ -0,0 +1,148 @@
import getFormValues from '../../src'
import * as utils from '../utils'

describe('text', () => {
describe('basic', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Text/Basic</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" />
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('disabled', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Text/Disabled</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input
type="text" name="hello" value="Hi"
disabled
/>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have blank form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{}
);
});
})

describe('outside', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Text/Outside</title>
</head>
<body>
<form id="form">
<button type="submit">Submit</button>
</form>
<label>
<span>Hello</span>
<input type="text" name="hello" value="Hi" form="form" />
</label>
</body>
</html>
`))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})

describe('readonly', () => {
beforeEach(utils.setup(`
<!DOCTYPE html>
<html lang="en-PH">
<head>
<meta charset="UTF-8">
<title>Text/Readonly</title>
</head>
<body>
<form>
<label>
<span>Hello</span>
<input
type="text" name="hello" value="Hi"
readonly
/>
</label>
<button type="submit">Submit</button>
</form>
</body>
</html>
`))

it('should have single form value', () => {
utils.test(
(cy: any) => cy.get('[type="submit"]'),
(form: HTMLFormElement, submitter: any, search: any) => {
const before = utils.makeSearchParams(getFormValues(form, {submitter})).toString();
const after = utils.makeSearchParams(search).toString();
expect(before).toEqual(after);
},
{
hello: 'Hi',
}
);
});
})
})

+ 95
- 0
cypress/utils/index.ts 查看文件

@@ -0,0 +1,95 @@
/// <reference types="cypress" />

import JSDOMDummyCypress from './jsdom-compat'

type ExpectedSearchValue = Record<string, string> | string

type RetrieveSubmitterFn = (wrapper: any) => any

type HTMLSubmitterElement = HTMLButtonElement | HTMLInputElement

type TestFn = (form: HTMLFormElement, submitter: HTMLSubmitterElement, after: ExpectedSearchValue) => unknown

export const setup = (template: string) => {
if (typeof cy !== 'undefined') {
return () => {
cy.intercept({ url: '/' }, { body: template });
cy.intercept({ url: '/?*' }, { body: template }).as('submitted');
}
}
return () => {
window.document.open(undefined, undefined, undefined, true)
window.document.write(template)
window.document.close()
}
}

export const test = (retrieveSubmitterFn: RetrieveSubmitterFn, testFn: TestFn, expectedValue?: ExpectedSearchValue) => {
let form: HTMLFormElement
let submitter: HTMLButtonElement | HTMLInputElement
let r: any
if (typeof cy !== 'undefined') {
cy
.visit('/')
.get('form')
.then((formResult: any) => {
[form] = Array.from(formResult);
})

r = retrieveSubmitterFn(cy)
.then((submitterQueryEl: any) => {
[submitter] = Array.from(submitterQueryEl as any[])
})

if (typeof expectedValue !== 'undefined') {
r.click()
cy
.wait('@submitted')
.location('search')
.then((search: any) => {
testFn(form, submitter, search)
})
} else {
cy
.location('search')
.then((search: any) => {
testFn(form, submitter, search)
})
}
} else {
r = retrieveSubmitterFn(new JSDOMDummyCypress())
.then((submitterQueryEl: any) => {
[submitter] = Array.from(submitterQueryEl as any[]);
[form] = Array.from(window.document.getElementsByTagName('form'))
testFn(form, submitter, expectedValue)
})

if (typeof expectedValue !== 'undefined') {
r.click()
}
}
}

export const makeSearchParams = (beforeValues: Record<string, unknown> | string) => {
switch (typeof (beforeValues as unknown)) {
case 'string':
return new URLSearchParams(beforeValues as string)
case 'object':
return Object
.entries(beforeValues)
.filter(([k]) => k.trim().length > 0)
.reduce(
(beforeSearchParams, [key, value]) => {
const theValue = !Array.isArray(value) ? [value] : value
theValue.forEach(v => {
beforeSearchParams.append(key, v)
})
return beforeSearchParams
},
new URLSearchParams()
)
default:
break
}
throw new TypeError('Invalid parameter.')
}

+ 74
- 0
cypress/utils/jsdom-compat.ts 查看文件

@@ -0,0 +1,74 @@
/// <reference types="node" />

import { readFileSync } from 'fs'
import { join } from 'path'

class JSDOMJQuery {
private selectedElements: Node[]
constructor(elements: NodeList) {
this.selectedElements = Array.from(elements)
}

type(s: string) {
this.selectedElements.forEach((el: any) => {
if (el.tagName === 'TEXTAREA') {
el.innerText = s
el.value = s
return
}
el.setAttribute('value', s)
el.value = s
})
return this
}

check() {
this.selectedElements.forEach((el: any) => {
el.setAttribute('checked', '')
el.checked = true
})
return this
}

select(v: string) {
this.selectedElements.forEach((el: any) => {
const option: any = Array.from(el.querySelectorAll('option')).find((o: any) => o.textContent === v)
option.setAttribute('selected', '')
el.value = option.value
})
return this
}

last() {
this.selectedElements = this.selectedElements.slice(-1)
return this
}

then(fn: (...args: unknown[]) => unknown) {
fn(this.selectedElements)
return this
}

click() {
return this
}

submit() {
return this
}

attachFile(filename: string) {
const contents = readFileSync(join('cypress', 'fixtures', filename))
// TODO
contents.toString('binary')
return this
}
}

export default class JSDOMDummyCypress {
private currentElement = window.document;

get(q: string) {
return new JSDOMJQuery(this.currentElement.querySelectorAll(q));
}
}

+ 3
- 3
package.json 查看文件

@@ -9,13 +9,13 @@
],
"publishing": {
"github": {
"repository": "https://github.com/TheoryOfNekomata/formxtr.git",
"repository": "https://github.com/TheoryOfNekomata/formxtra.git",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
},
"master": {
"repository": "https://code.modal.sh/TheoryOfNekomata/formxtr.git",
"repository": "https://code.modal.sh/TheoryOfNekomata/formxtra.git",
"publishConfig": {
"registry": "https://js.pack.modal.sh"
}
@@ -51,7 +51,7 @@
"singleQuote": true,
"trailingComma": "es5"
},
"name": "@theoryofnekomata/formxtr",
"name": "@theoryofnekomata/formxtra",
"author": "TheoryOfNekomata",
"module": "dist/get-form-values.esm.js",
"size-limit": [


+ 248
- 100
src/index.ts 查看文件

@@ -1,137 +1,285 @@
/**
* Type for valid field elements.
*
*
*/

type HTMLFieldElement
export type HTMLFieldElement
= HTMLInputElement
| HTMLButtonElement
| HTMLSelectElement
| HTMLTextAreaElement

/**
* Line ending.
*/
export enum LineEnding {
/**
* Carriage return.
*/
CR = '\r',
/**
* Line feed.
*/
LF = '\n',
/**
* Carriage return/line feed combination.
*/
CRLF = '\r\n',
}

/**
* Type for valid submitter elements.
*
* Only the <button> and <input> elements can be submitter elements.
* Only the `<button>` and `<input>` elements can be submitter elements.
*/
type HTMLSubmitterElement
export type HTMLSubmitterElement
= HTMLButtonElement
| HTMLInputElement

const isFormFieldElement = (el: HTMLFieldElement) => {
const htmlEl = el as HTMLElement
const tagName = htmlEl.tagName
/**
* Options for getting a `<textarea>` element field value.
*/
type GetTextAreaValueOptions = {
/**
* Line ending used for the element's value.
*/
lineEndings?: LineEnding,
}

/**
* Options for getting a `<select>` element field value.
*/
type GetSelectValueOptions = {}

/**
* Options for getting an `<input type="checkbox">` element field value.
*/
type GetInputCheckboxFieldValueOptions = {
/**
* Should we consider the `checked` attribute of checkboxes with no `value` attributes instead of the default value
* "on" when checked?
*
* This forces the field to get the `false` value when unchecked.
*/
booleanValuelessCheckbox?: true,
}

/**
* Options for getting an `<input type="radio">` element field value.
*/
type GetInputRadioFieldValueOptions = {}

/**
* Options for getting an `<input type="file">` element field value.
*/
type GetInputFileFieldValueOptions = {
/**
* Should we retrieve the `files` attribute of file inputs instead of the currently selected file names?
*/
getFileObjects?: true,
}

/**
* Options for getting an `<input>` element field value.
*/
type GetInputFieldValueOptions
= GetInputCheckboxFieldValueOptions
& GetInputFileFieldValueOptions
& GetInputRadioFieldValueOptions

/**
* Options for getting a field value.
*/
type GetFieldValueOptions
= GetTextAreaValueOptions
& GetSelectValueOptions
& GetInputFieldValueOptions

/**
* Options for getting form values.
*/
type GetFormValuesOptions = GetFieldValueOptions & {
/**
* The element that triggered the submission of the form.
*/
submitter?: HTMLSubmitterElement,
}

/**
* Checks if an element can hold a field value.
* @param el - The element.
*/
export const isFormFieldElement = (el: HTMLElement) => {
const { tagName } = el
if (['SELECT', 'TEXTAREA'].includes(tagName)) {
return true
}
if (tagName !== 'INPUT') {
return false
}
const inputEl = htmlEl as HTMLInputElement
const type = inputEl.type
const checkedValue = inputEl.getAttribute('value')
if (type === 'checkbox' && checkedValue !== null) {
return inputEl.checked
}
const inputEl = el as HTMLInputElement
const { type } = inputEl
if (type === 'submit' || type === 'reset') {
return false
}
return Boolean(inputEl.name)
}

const getTextAreaFieldValue = (textareaEl: HTMLTextAreaElement, options = {} as GetTextAreaValueOptions) => {
const { lineEndings = LineEnding.CRLF, } = options
return textareaEl.value.replace(/\n/g, lineEndings)
}

/**
* Gets the value of a field element.
* @param el - The field element node.
* Gets the value of a `<select>` element.
* @param selectEl - The element.
* @param options - The options.
* @returns Value of the select element.
*/
const getFieldValue = (el: HTMLFieldElement) => {
const fieldEl = el as HTMLFieldElement
const tagName = fieldEl.tagName
const type = fieldEl.type

if (tagName === 'TEXTAREA') {
return fieldEl.value.replace(/\n/g, '\r\n')
const getSelectFieldValue = (selectEl: HTMLSelectElement, options = {} as GetSelectValueOptions) => {
if (selectEl.multiple) {
return Array.from(selectEl.options).filter(o => o.selected).map(o => o.value)
}
if (typeof options !== 'object' || options === null) {
throw new Error('Invalid options.')
}
return selectEl.value
}

if (tagName === 'SELECT') {
if (fieldEl.value === '') {
return null
}
const selectEl = fieldEl as HTMLSelectElement
if (selectEl.multiple) {
return Array.from(selectEl.options).filter(o => o.selected).map(o => o.value)
/**
* Type for an `<input type="checkbox">` element.
*/
export type HTMLInputCheckboxElement = HTMLInputElement & { type: 'checkbox' }

/**
* Type for an `<input type="radio">` element.
*/
export type HTMLInputRadioElement = HTMLInputElement & { type: 'radio' }

/**
* Type for an `<input type="file">` element.
*/
export type HTMLInputFileElement = HTMLInputElement & { type: 'file' }

/**
* Gets the value of an `<input type="radio">` element.
* @param inputEl - The element.
* @param options - The options.
* @returns Value of the input element.
*/
const getInputRadioFieldValue = (inputEl: HTMLInputRadioElement, options = {} as GetInputRadioFieldValueOptions) => {
if (inputEl.checked) {
return inputEl.value
}
if (typeof options !== 'object' || options === null) {
throw new Error('Invalid options.')
}
return null
}
/**
* Gets the value of an `<input type="checkbox">` element.
* @param inputEl - The element.
* @param options - The options.
* @returns Value of the input element.
*/
const getInputCheckboxFieldValue = (inputEl: HTMLInputCheckboxElement, options = {} as GetInputCheckboxFieldValueOptions) => {
const checkedValue = inputEl.getAttribute('value')
if (checkedValue !== null) {
if (inputEl.checked) {
return inputEl.value
}
return selectEl.value
return null
}
if (options.booleanValuelessCheckbox) {
return inputEl.checked
}
if (inputEl.checked) {
return 'on'
}
return null
}

if (tagName === 'INPUT') {
switch (type) {
case 'checkbox':
const checkboxEl = fieldEl as HTMLInputElement
const checkedValue = checkboxEl.getAttribute('value')
if (checkedValue !== null) {
if (checkboxEl.checked) {
return checkboxEl.value
}
return null
}
return 'on' // default value
case 'radio':
const radioEl = fieldEl as HTMLInputElement
if (radioEl.checked) {
return radioEl.value
}
return null
case 'file':
const fileUploadEl = fieldEl as HTMLInputElement
const { files } = fileUploadEl
if ((files as unknown) !== null) {
const [file = null] = Array.from(files as FileList)
if (file !== null) {
return file.name
}
return ''
}
return null
default:
break
}
/**
* Gets the value of an `<input type="file">` element.
* @param inputEl - The element.
* @param options - The options.
* @returns Value of the input element.
*/
const getInputFileFieldValue = (inputEl: HTMLInputFileElement, options = {} as GetInputFileFieldValueOptions) => {
const { files } = inputEl
if ((files as unknown) === null) {
return null
}
if (options.getFileObjects) {
return files
}
const filesArray = Array.from(files as FileList)
if (filesArray.length > 1) {
return filesArray.map(f => f.name)
}
if (filesArray.length === 1) {
return filesArray[0].name
}
return ''
}

/**
* Gets the value of an `<input>` element.
* @param inputEl - The element.
* @param options - The options.
* @returns Value of the input element.
*/
const getInputFieldValue = (inputEl: HTMLInputElement, options = {} as GetInputFieldValueOptions) => {
switch (inputEl.type) {
case 'checkbox':
return getInputCheckboxFieldValue(inputEl as HTMLInputCheckboxElement, options)
case 'radio':
return getInputRadioFieldValue(inputEl as HTMLInputRadioElement, options)
case 'file':
return getInputFileFieldValue(inputEl as HTMLInputFileElement, options)
default:
break
}
return inputEl.value
}

return fieldEl.value
/**
* Gets the value of a field element.
* @param el - The field element.
* @param options - The options.
* @returns Value of the field element.
*/
export const getFieldValue = (el: HTMLElement, options = {} as GetFieldValueOptions) => {
switch (el.tagName) {
case 'TEXTAREA':
return getTextAreaFieldValue(el as HTMLTextAreaElement, options)
case 'SELECT':
return getSelectFieldValue(el as HTMLSelectElement, options)
case 'INPUT':
return getInputFieldValue(el as HTMLInputElement, options)
default:
break
}

const fieldEl = el as HTMLFieldElement
return fieldEl.value || null
}

/**
* Returns only named form field elements.
* Determines if an element is a named and enabled form field.
* @param el - The element.
* @returns Value determining if the element is a named and enabled form field.
*/
const isValidFormField = (el: HTMLFieldElement) => {
export const isNamedEnabledFormFieldElement = (el: HTMLElement) => {
if (typeof el['name'] !== 'string') {
return false
}
const namedEl = el as HTMLElement & { name: string, disabled: unknown }
return (
'name' in el
&& typeof el['name'] === 'string'
&& el['name'].length > 0
&& !('disabled' in el && Boolean(el['disabled']))
&& isFormFieldElement(el)
namedEl.name.length > 0
&& !('disabled' in namedEl && Boolean(namedEl.disabled))
&& isFormFieldElement(namedEl)
)
}

type GetFormValuesOptions = {
/**
* The element that triggered the submission of the form.
*/
submitter?: HTMLSubmitterElement,
/**
* Should we consider the `checked` attribute of checkboxes with no `value` attributes instead of the default value
* "on" when checked?
*/
booleanValuelessCheckbox?: true,
/**
* Should we retrieve the `files` attribute of file inputs instead of the currently selected file names?
*/
hasFiles?: true,
}

/**
* Gets the values of all the fields within the form through accessing the DOM nodes.
* @param form - The form.
@@ -144,16 +292,13 @@ const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptio
}
const formElements = form.elements as unknown as Record<string | number, HTMLFieldElement>
const allFormFieldElements = Object.entries<HTMLFieldElement>(formElements)
const formFieldElements = allFormFieldElements.filter(([k, el]) => {
return (
// get only indexed forms
!isNaN(Number(k))
&& isValidFormField(el)
)
})
const fieldValues = formFieldElements.reduce(
const indexedNamedEnabledFormFieldElements = allFormFieldElements.filter(([k, el]) => (
!isNaN(Number(k))
&& isNamedEnabledFormFieldElement(el)
))
const fieldValues = indexedNamedEnabledFormFieldElements.reduce(
(theFormValues, [,el]) => {
const fieldValue = getFieldValue(el)
const fieldValue = getFieldValue(el, options)
if (fieldValue === null) {
return theFormValues
}
@@ -183,9 +328,12 @@ const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptio
{} as any
)
if (Boolean(options.submitter as unknown)) {
return {
...fieldValues,
[(options.submitter as HTMLSubmitterElement).name]: (options.submitter as HTMLSubmitterElement).value,
const submitter = options.submitter as HTMLSubmitterElement
if (submitter.name.length > 0) {
return {
...fieldValues,
[submitter.name]: submitter.value,
}
}
}
return fieldValues


+ 0
- 145
test/utils/index.ts 查看文件

@@ -1,145 +0,0 @@
/// <reference types="cypress" />
/// <reference types="cypress-jest-adapter" />
/// <reference types="node" />

import * as fs from 'fs'
import * as path from 'path'

type TestFn = (form: HTMLFormElement, submitter: HTMLButtonElement | HTMLInputElement, after: Record<string, string> | string) => unknown

class JSDOMJQuery {
private selectedElements: Node[]
constructor(elements: NodeList) {
this.selectedElements = Array.from(elements)
}

type(s: string) {
this.selectedElements.forEach((el: any) => {
if (el.tagName === 'TEXTAREA') {
el.innerText = s
el.value = s
return
}
el.setAttribute('value', s)
el.value = s
})
return this
}

check() {
this.selectedElements.forEach((el: any) => {
el.setAttribute('checked', '')
el.checked = true
})
return this
}

select(v: string) {
this.selectedElements.forEach((el: any) => {
const option: any = Array.from(el.querySelectorAll('option')).find((o: any) => o.textContent === v)
option.setAttribute('selected', '')
el.value = option.value
})
return this
}

last() {
this.selectedElements = this.selectedElements.slice(-1)
return this
}

then(fn: (...args: unknown[]) => unknown) {
fn(this.selectedElements)
return this
}

click() {
return this
}

submit() {
return this
}
}

class JSDOMDummyCypress {
private currentElement = window.document;

get(q: string) {
return new JSDOMJQuery(this.currentElement.querySelectorAll(q));
}
}

export const setup = (template: string) => {
if (typeof cy !== 'undefined') {
return () => {
cy.intercept({ url: '/' }, { fixture: `templates/${template}.html` });
cy.intercept({ url: '/?*' }, { fixture: `templates/${template}.html` }).as('submitted');
}
}
return async () => {
const templatePath = path.join('test', 'fixtures', 'templates', `${template}.html`)
const templateRaw = await fs.promises.readFile(templatePath)
window.document.open(undefined, undefined, undefined, true)
window.document.write(templateRaw.toString('utf-8'))
window.document.close()
}
}

export const test = (retrieveSubmitterFn: (wrapper: any) => any, testFn: TestFn, expectedValue: Record<string, string> | string) => {
let form: HTMLFormElement
let submitter: HTMLButtonElement | HTMLInputElement
if (typeof cy !== 'undefined') {
cy
.visit('/')
.get('form')
.then((formResult: any) => {
[form] = Array.from(formResult);
})

retrieveSubmitterFn(cy)
.then((submitterQueryEl: any) => {
[submitter] = Array.from(submitterQueryEl as any[])
})
.click()

cy
.wait('@submitted')
.location('search')
.then(search => {
testFn(form, submitter, search)
})
} else {
retrieveSubmitterFn(new JSDOMDummyCypress())
.then((submitterQueryEl: any) => {
[submitter] = Array.from(submitterQueryEl as any[]);
[form] = Array.from(window.document.getElementsByTagName('form'))
testFn(form, submitter, expectedValue)
})
.click();
}
}

export const makeSearchParams = (beforeValues: Record<string, unknown> | string) => {
switch (typeof (beforeValues as unknown)) {
case 'string':
return new URLSearchParams(beforeValues as string)
case 'object':
return Object
.entries(beforeValues)
.filter(([k]) => k.trim().length > 0)
.reduce(
(beforeSearchParams, [key, value]) => {
const theValue = !Array.isArray(value) ? [value] : value
theValue.forEach(v => {
beforeSearchParams.append(key, v)
})
return beforeSearchParams
},
new URLSearchParams()
)
default:
break
}
throw new TypeError('Invalid parameter.')
}

+ 1
- 1
tsconfig.json 查看文件

@@ -6,7 +6,7 @@
"importHelpers": true,
"declaration": true,
"sourceMap": true,
"strict": true,
"strict": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,


+ 221
- 211
yarn.lock 查看文件

@@ -9,25 +9,25 @@
dependencies:
"@babel/highlight" "^7.12.13"
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.14.0":
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.15", "@babel/compat-data@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.0.tgz#a901128bce2ad02565df95e6ecbf195cf9465919"
integrity sha512-vu9V3uMM/1o5Hl5OekMUowo3FqXLJSw+s+66nt0fSWVWTtmosdzn45JHOB3cPtZoe6CTBDzvSw0RdOY85Q37+Q==
"@babel/core@^7.1.0", "@babel/core@^7.4.4", "@babel/core@^7.7.5":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.0.tgz#47299ff3ec8d111b493f1a9d04bf88c04e728d88"
integrity sha512-8YqpRig5NmIHlMLw09zMlPTvUVMILjqCOtVgu+TVNWEBvy9b5I3RRyhqnrV4hjgEK7n8P9OqvkWJAFmEL6Wwfw==
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.2.tgz#54e45334ffc0172048e5c93ded36461d3ad4c417"
integrity sha512-OgC1mON+l4U4B4wiohJlQNUU3H73mpTyYY3j/c8U9dr9UagGGSm+WFpzjy/YLdoyjiG++c1kIDgxCo/mLwQJeQ==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.14.0"
"@babel/generator" "^7.14.2"
"@babel/helper-compilation-targets" "^7.13.16"
"@babel/helper-module-transforms" "^7.14.0"
"@babel/helper-module-transforms" "^7.14.2"
"@babel/helpers" "^7.14.0"
"@babel/parser" "^7.14.0"
"@babel/parser" "^7.14.2"
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
"@babel/traverse" "^7.14.2"
"@babel/types" "^7.14.2"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
@@ -35,12 +35,12 @@
semver "^6.3.0"
source-map "^0.5.0"
"@babel/generator@^7.14.0":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.1.tgz#1f99331babd65700183628da186f36f63d615c93"
integrity sha512-TMGhsXMXCP/O1WtQmZjpEYDhCYC9vFhayWZPJSZCGkPJgUqX0rF0wwtrYvnzVxIjcF80tkUertXVk5cwqi5cAQ==
"@babel/generator@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.2.tgz#d5773e8b557d421fd6ce0d5efa5fd7fc22567c30"
integrity sha512-OnADYbKrffDVai5qcpkMxQ7caomHOoEwjkouqnN2QhydAjowFAZcsdecFIRUBdb+ZcruwYE4ythYmF1UBZU5xQ==
dependencies:
"@babel/types" "^7.14.1"
"@babel/types" "^7.14.2"
jsesc "^2.5.1"
source-map "^0.5.0"
@@ -59,7 +59,7 @@
"@babel/helper-explode-assignable-expression" "^7.12.13"
"@babel/types" "^7.12.13"
"@babel/helper-compilation-targets@^7.10.4", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16", "@babel/helper-compilation-targets@^7.13.8":
"@babel/helper-compilation-targets@^7.10.4", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.16":
version "7.13.16"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.16.tgz#6e91dccf15e3f43e5556dffe32d860109887563c"
integrity sha512-3gmkYIrpqsLlieFwjkGgLaSHmhnvlAYzZLlYVjlW+QwI+1zE17kGxuJGmIqDQdYp56XdmGeD+Bswx0UTyG18xA==
@@ -70,12 +70,12 @@
semver "^6.3.0"
"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.0":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.1.tgz#1fe11b376f3c41650ad9fedc665b0068722ea76c"
integrity sha512-r8rsUahG4ywm0QpGcCrLaUSOuNAISR3IZCg4Fx05Ozq31aCUrQsTLH6KPxy0N5ULoQ4Sn9qjNdGNtbPWAC6hYg==
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.2.tgz#4e455b0329af29c2d3ad254b5dd5aed34595385d"
integrity sha512-6YctwVsmlkchxfGUogvVrrhzyD3grFJyluj5JgDlQrwfMLJSt5tdAzFZfPf4H2Xoi5YLcQ6BxfJlaOBHuctyIw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.12.13"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-function-name" "^7.14.2"
"@babel/helper-member-expression-to-functions" "^7.13.12"
"@babel/helper-optimise-call-expression" "^7.12.13"
"@babel/helper-replace-supers" "^7.13.12"
@@ -124,14 +124,14 @@
dependencies:
"@babel/types" "^7.13.0"
"@babel/helper-function-name@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a"
integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA==
"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2"
integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==
dependencies:
"@babel/helper-get-function-arity" "^7.12.13"
"@babel/template" "^7.12.13"
"@babel/types" "^7.12.13"
"@babel/types" "^7.14.2"
"@babel/helper-get-function-arity@^7.12.13":
version "7.12.13"
@@ -162,10 +162,10 @@
dependencies:
"@babel/types" "^7.13.12"
"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.0.tgz#8fcf78be220156f22633ee204ea81f73f826a8ad"
integrity sha512-L40t9bxIuGOfpIGA3HNkJhU9qYrf4y5A5LUSw7rGMSn+pcG8dfJ0g6Zval6YJGd2nEjI7oP00fRdnhLKndx6bw==
"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.14.0", "@babel/helper-module-transforms@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.2.tgz#ac1cc30ee47b945e3e0c4db12fa0c5389509dfe5"
integrity sha512-OznJUda/soKXv0XhpvzGWDnml4Qnwp16GN+D/kZIdLsWoHj05kyu8Rm5kXmMef+rVJZ0+4pSGLkeixdqNUATDA==
dependencies:
"@babel/helper-module-imports" "^7.13.12"
"@babel/helper-replace-supers" "^7.13.12"
@@ -173,8 +173,8 @@
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/helper-validator-identifier" "^7.14.0"
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
"@babel/traverse" "^7.14.2"
"@babel/types" "^7.14.2"
"@babel/helper-optimise-call-expression@^7.12.13":
version "7.12.13"
@@ -197,7 +197,7 @@
"@babel/helper-wrap-function" "^7.13.0"
"@babel/types" "^7.13.0"
"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.13.12":
"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.12":
version "7.13.12"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804"
integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==
@@ -266,10 +266,10 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.14.0", "@babel/parser@^7.7.0":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.1.tgz#1bd644b5db3f5797c4479d89ec1817fe02b84c47"
integrity sha512-muUGEKu8E/ftMTPlNp+mc6zL3E9zKWmF5sDHZ5MSsoTP9Wyz64AhEf9kD08xYJ7w6Hdcu8H550ircnPyWSIF0Q==
"@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.14.2", "@babel/parser@^7.7.0":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.2.tgz#0c1680aa44ad4605b16cbdcc5c341a61bde9c746"
integrity sha512-IoVDIHpsgE/fu7eXBeRWt8zLbDrSvD7H1gpomOkPpBoEN8KCruCqSDdqo8dddwQQrui30KSvQBaMUOJiuFu6QQ==
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
version "7.13.12"
@@ -280,10 +280,10 @@
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
"@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions@^7.13.15":
version "7.13.15"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz#80e549df273a3b3050431b148c892491df1bcc5b"
integrity sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==
"@babel/plugin-proposal-async-generator-functions@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.14.2.tgz#3a2085abbf5d5f962d480dbc81347385ed62eb1e"
integrity sha512-b1AM4F6fwck4N8ItZ/AtC4FP/cqZqmKRQ4FaTDutwSYyjuhtvsGEMLK4N/ztV/ImP40BjIDyMgBQAeAMsQYVFQ==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-remap-async-to-generator" "^7.13.0"
@@ -305,77 +305,77 @@
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-class-static-block" "^7.12.13"
"@babel/plugin-proposal-dynamic-import@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d"
integrity sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==
"@babel/plugin-proposal-dynamic-import@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.2.tgz#01ebabd7c381cff231fa43e302939a9de5be9d9f"
integrity sha512-oxVQZIWFh91vuNEMKltqNsKLFWkOIyJc95k2Gv9lWVyDfPUQGSSlbDEgWuJUU1afGE9WwlzpucMZ3yDRHIItkA==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
"@babel/plugin-proposal-export-namespace-from@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz#393be47a4acd03fa2af6e3cde9b06e33de1b446d"
integrity sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw==
"@babel/plugin-proposal-export-namespace-from@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz#62542f94aa9ce8f6dba79eec698af22112253791"
integrity sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-export-namespace-from" "^7.8.3"
"@babel/plugin-proposal-json-strings@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b"
integrity sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==
"@babel/plugin-proposal-json-strings@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.2.tgz#830b4e2426a782e8b2878fbfe2cba85b70cbf98c"
integrity sha512-w2DtsfXBBJddJacXMBhElGEYqCZQqN99Se1qeYn8DVLB33owlrlLftIbMzn5nz1OITfDVknXF433tBrLEAOEjA==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-json-strings" "^7.8.3"
"@babel/plugin-proposal-logical-assignment-operators@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a"
integrity sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==
"@babel/plugin-proposal-logical-assignment-operators@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.2.tgz#222348c080a1678e0e74ea63fe76f275882d1fd7"
integrity sha512-1JAZtUrqYyGsS7IDmFeaem+/LJqujfLZ2weLR9ugB0ufUPjzf8cguyVT1g5im7f7RXxuLq1xUxEzvm68uYRtGg==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3"
integrity sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==
"@babel/plugin-proposal-nullish-coalescing-operator@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.2.tgz#425b11dc62fc26939a2ab42cbba680bdf5734546"
integrity sha512-ebR0zU9OvI2N4qiAC38KIAK75KItpIPTpAtd2r4OZmMFeKbKJpUFLYP2EuDut82+BmYi8sz42B+TfTptJ9iG5Q==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
"@babel/plugin-proposal-numeric-separator@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db"
integrity sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w==
"@babel/plugin-proposal-numeric-separator@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz#82b4cc06571143faf50626104b335dd71baa4f9e"
integrity sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
"@babel/plugin-proposal-object-rest-spread@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a"
integrity sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g==
"@babel/plugin-proposal-object-rest-spread@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.2.tgz#e17d418f81cc103fedd4ce037e181c8056225abc"
integrity sha512-hBIQFxwZi8GIp934+nj5uV31mqclC1aYDhctDu5khTi9PCCUOczyy0b34W0oE9U/eJXiqQaKyVsmjeagOaSlbw==
dependencies:
"@babel/compat-data" "^7.13.8"
"@babel/helper-compilation-targets" "^7.13.8"
"@babel/compat-data" "^7.14.0"
"@babel/helper-compilation-targets" "^7.13.16"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-transform-parameters" "^7.13.0"
"@babel/plugin-transform-parameters" "^7.14.2"
"@babel/plugin-proposal-optional-catch-binding@^7.13.8":
version "7.13.8"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107"
integrity sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==
"@babel/plugin-proposal-optional-catch-binding@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.2.tgz#150d4e58e525b16a9a1431bd5326c4eed870d717"
integrity sha512-XtkJsmJtBaUbOxZsNk0Fvrv8eiqgneug0A6aqLFZ4TSkar2L5dSXWcnUKHgmjJt49pyB/6ZHvkr3dPgl9MOWRQ==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
"@babel/plugin-proposal-optional-chaining@^7.13.12":
version "7.13.12"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866"
integrity sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==
"@babel/plugin-proposal-optional-chaining@^7.13.12", "@babel/plugin-proposal-optional-chaining@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.2.tgz#df8171a8b9c43ebf4c1dabe6311b432d83e1b34e"
integrity sha512-qQByMRPwMZJainfig10BoaDldx/+VDtNcrA7qdNaEOAj6VXud+gfrkA8j4CRAU5HjnWREXqIpSpH30qZX1xivA==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
@@ -542,23 +542,23 @@
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/plugin-transform-block-scoping@^7.14.1":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.1.tgz#ac1b3a8e3d8cbb31efc6b9be2f74eb9823b74ab2"
integrity sha512-2mQXd0zBrwfp0O1moWIhPpEeTKDvxyHcnma3JATVP1l+CctWBuot6OJG8LQ4DnBj4ZZPSmlb/fm4mu47EOAnVA==
"@babel/plugin-transform-block-scoping@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz#761cb12ab5a88d640ad4af4aa81f820e6b5fdf5c"
integrity sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-transform-classes@^7.13.0":
version "7.13.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b"
integrity sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==
"@babel/plugin-transform-classes@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.14.2.tgz#3f1196c5709f064c252ad056207d87b7aeb2d03d"
integrity sha512-7oafAVcucHquA/VZCsXv/gmuiHeYd64UJyyTYU+MPfNu0KeNlxw06IeENBO8bJjXVbolu+j1MM5aKQtH1OMCNg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.12.13"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-function-name" "^7.14.2"
"@babel/helper-optimise-call-expression" "^7.12.13"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-replace-supers" "^7.13.0"
"@babel/helper-replace-supers" "^7.13.12"
"@babel/helper-split-export-declaration" "^7.12.13"
globals "^11.1.0"
@@ -628,12 +628,12 @@
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/plugin-transform-modules-amd@^7.14.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.0.tgz#589494b5b290ff76cf7f59c798011f6d77026553"
integrity sha512-CF4c5LX4LQ03LebQxJ5JZes2OYjzBuk1TdiF7cG7d5dK4lAdw9NZmaxq5K/mouUdNeqwz3TNjnW6v01UqUNgpQ==
"@babel/plugin-transform-modules-amd@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.2.tgz#6622806fe1a7c07a1388444222ef9535f2ca17b0"
integrity sha512-hPC6XBswt8P3G2D1tSV2HzdKvkqOpmbyoy+g73JG0qlF/qx2y3KaMmXb1fLrpmWGLZYA0ojCvaHdzFWjlmV+Pw==
dependencies:
"@babel/helper-module-transforms" "^7.14.0"
"@babel/helper-module-transforms" "^7.14.2"
"@babel/helper-plugin-utils" "^7.13.0"
babel-plugin-dynamic-import-node "^2.3.3"
@@ -688,10 +688,10 @@
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/helper-replace-supers" "^7.12.13"
"@babel/plugin-transform-parameters@^7.13.0":
version "7.13.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz#8fa7603e3097f9c0b7ca1a4821bc2fb52e9e5007"
integrity sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw==
"@babel/plugin-transform-parameters@^7.14.2":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.2.tgz#e4290f72e0e9e831000d066427c4667098decc31"
integrity sha512-NxoVmA3APNCC1JdMXkdYXuQS+EMdqy0vIwyDHeKHiJKRxmp1qGSdb0JLEIoPRhkx6H/8Qi3RJ3uqOCYw8giy9A==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
@@ -768,27 +768,27 @@
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/preset-env@^7.11.0":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.1.tgz#b55914e2e68885ea03f69600b2d3537e54574a93"
integrity sha512-0M4yL1l7V4l+j/UHvxcdvNfLB9pPtIooHTbEhgD/6UGyh8Hy3Bm1Mj0buzjDXATCSz3JFibVdnoJZCrlUCanrQ==
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.14.2.tgz#e80612965da73579c84ad2f963c2359c71524ed5"
integrity sha512-7dD7lVT8GMrE73v4lvDEb85cgcQhdES91BSD7jS/xjC6QY8PnRhux35ac+GCpbiRhp8crexBvZZqnaL6VrY8TQ==
dependencies:
"@babel/compat-data" "^7.14.0"
"@babel/helper-compilation-targets" "^7.13.16"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-validator-option" "^7.12.17"
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions" "^7.13.15"
"@babel/plugin-proposal-async-generator-functions" "^7.14.2"
"@babel/plugin-proposal-class-properties" "^7.13.0"
"@babel/plugin-proposal-class-static-block" "^7.13.11"
"@babel/plugin-proposal-dynamic-import" "^7.13.8"
"@babel/plugin-proposal-export-namespace-from" "^7.12.13"
"@babel/plugin-proposal-json-strings" "^7.13.8"
"@babel/plugin-proposal-logical-assignment-operators" "^7.13.8"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.13.8"
"@babel/plugin-proposal-numeric-separator" "^7.12.13"
"@babel/plugin-proposal-object-rest-spread" "^7.13.8"
"@babel/plugin-proposal-optional-catch-binding" "^7.13.8"
"@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-dynamic-import" "^7.14.2"
"@babel/plugin-proposal-export-namespace-from" "^7.14.2"
"@babel/plugin-proposal-json-strings" "^7.14.2"
"@babel/plugin-proposal-logical-assignment-operators" "^7.14.2"
"@babel/plugin-proposal-nullish-coalescing-operator" "^7.14.2"
"@babel/plugin-proposal-numeric-separator" "^7.14.2"
"@babel/plugin-proposal-object-rest-spread" "^7.14.2"
"@babel/plugin-proposal-optional-catch-binding" "^7.14.2"
"@babel/plugin-proposal-optional-chaining" "^7.14.2"
"@babel/plugin-proposal-private-methods" "^7.13.0"
"@babel/plugin-proposal-private-property-in-object" "^7.14.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.12.13"
@@ -809,8 +809,8 @@
"@babel/plugin-transform-arrow-functions" "^7.13.0"
"@babel/plugin-transform-async-to-generator" "^7.13.0"
"@babel/plugin-transform-block-scoped-functions" "^7.12.13"
"@babel/plugin-transform-block-scoping" "^7.14.1"
"@babel/plugin-transform-classes" "^7.13.0"
"@babel/plugin-transform-block-scoping" "^7.14.2"
"@babel/plugin-transform-classes" "^7.14.2"
"@babel/plugin-transform-computed-properties" "^7.13.0"
"@babel/plugin-transform-destructuring" "^7.13.17"
"@babel/plugin-transform-dotall-regex" "^7.12.13"
@@ -820,14 +820,14 @@
"@babel/plugin-transform-function-name" "^7.12.13"
"@babel/plugin-transform-literals" "^7.12.13"
"@babel/plugin-transform-member-expression-literals" "^7.12.13"
"@babel/plugin-transform-modules-amd" "^7.14.0"
"@babel/plugin-transform-modules-amd" "^7.14.2"
"@babel/plugin-transform-modules-commonjs" "^7.14.0"
"@babel/plugin-transform-modules-systemjs" "^7.13.8"
"@babel/plugin-transform-modules-umd" "^7.14.0"
"@babel/plugin-transform-named-capturing-groups-regex" "^7.12.13"
"@babel/plugin-transform-new-target" "^7.12.13"
"@babel/plugin-transform-object-super" "^7.12.13"
"@babel/plugin-transform-parameters" "^7.13.0"
"@babel/plugin-transform-parameters" "^7.14.2"
"@babel/plugin-transform-property-literals" "^7.12.13"
"@babel/plugin-transform-regenerator" "^7.13.15"
"@babel/plugin-transform-reserved-words" "^7.12.13"
@@ -839,7 +839,7 @@
"@babel/plugin-transform-unicode-escapes" "^7.12.13"
"@babel/plugin-transform-unicode-regex" "^7.12.13"
"@babel/preset-modules" "^0.1.4"
"@babel/types" "^7.14.1"
"@babel/types" "^7.14.2"
babel-plugin-polyfill-corejs2 "^0.2.0"
babel-plugin-polyfill-corejs3 "^0.2.0"
babel-plugin-polyfill-regenerator "^0.2.0"
@@ -881,24 +881,24 @@
"@babel/parser" "^7.12.13"
"@babel/types" "^7.12.13"
"@babel/traverse@^7.1.0", "@babel/traverse@^7.11.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.7.0":
version "7.14.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.0.tgz#cea0dc8ae7e2b1dec65f512f39f3483e8cc95aef"
integrity sha512-dZ/a371EE5XNhTHomvtuLTUyx6UEoJmYX+DT5zBCQN3McHemsuIaKKYqsc/fs26BEkHs/lBZy0J571LP5z9kQA==
"@babel/traverse@^7.1.0", "@babel/traverse@^7.11.5", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.7.0":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b"
integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.14.0"
"@babel/helper-function-name" "^7.12.13"
"@babel/generator" "^7.14.2"
"@babel/helper-function-name" "^7.14.2"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/parser" "^7.14.0"
"@babel/types" "^7.14.0"
"@babel/parser" "^7.14.2"
"@babel/types" "^7.14.2"
debug "^4.1.0"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.1", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.14.1"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.1.tgz#095bd12f1c08ab63eff6e8f7745fa7c9cc15a9db"
integrity sha512-S13Qe85fzLs3gYRUnrpyeIrBJIMYv33qSTg1qoBwiG6nPKwUWAD9odSzWhEedpwOIzSEI6gbdQIWEMiCI42iBA==
"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.14.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.2.tgz#4208ae003107ef8a057ea8333e56eb64d2f6a2c3"
integrity sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==
dependencies:
"@babel/helper-validator-identifier" "^7.14.0"
to-fast-properties "^2.0.0"
@@ -1412,14 +1412,14 @@
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/node@*":
version "15.0.2"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.2.tgz#51e9c0920d1b45936ea04341aa3e2e58d339fb67"
integrity sha512-p68+a+KoxpoB47015IeYZYRrdqMUcpbK8re/zpFB8Ld46LHC1lPEbp3EXgkEhAYEcPvjJF6ZO+869SQ0aH1dcA==
version "15.3.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26"
integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ==
"@types/node@^14.14.31":
version "14.14.44"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.44.tgz#df7503e6002847b834371c004b372529f3f85215"
integrity sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA==
version "14.14.45"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.45.tgz#ec2dfb5566ff814d061aef7e141575aedba245cf"
integrity sha512-DssMqTV9UnnoxDWu959sDLZzfvqCF0qDNRjaWeYSui9xkFe61kKo4l1TWNTQONpuXEm+gLMRvdlzvNHBamzmEw==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -1914,7 +1914,7 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
array.prototype.flat@^1.2.3:
array.prototype.flat@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==
@@ -2878,10 +2878,13 @@ constants-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=
contains-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
contains-path@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-1.0.0.tgz#3458b332185603e8eed18f518d4a10888a3abc91"
integrity sha1-NFizMhhWA+ju0Y9RjUoQiIo6vJE=
dependencies:
normalize-path "^2.1.1"
path-starts-with "^1.0.0"
convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.7.0"
@@ -3296,7 +3299,7 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@^3.1.0:
debug@^3.1.0, debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -3415,14 +3418,6 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
doctrine@1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=
dependencies:
esutils "^2.0.2"
isarray "^1.0.0"
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
@@ -3513,9 +3508,9 @@ ecc-jsbn@~0.1.1:
safer-buffer "^2.1.0"
electron-to-chromium@^1.3.723:
version "1.3.727"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf"
integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg==
version "1.3.728"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.728.tgz#dbedd6373f595ae10a13d146b66bece4c1afa5bd"
integrity sha512-SHv4ziXruBpb1Nz4aTuqEHBYi/9GNCJMYIJgDEXrp/2V01nFXMNFUTli5Z85f5ivSkioLilQatqBYFB44wNJrA==
elegant-spinner@^1.0.1:
version "1.0.1"
@@ -3590,7 +3585,7 @@ errno@^0.1.3, errno@~0.1.7:
dependencies:
prr "~1.0.1"
error-ex@^1.2.0, error-ex@^1.3.1:
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
@@ -3694,12 +3689,12 @@ eslint-import-resolver-node@^0.3.4:
debug "^2.6.9"
resolve "^1.13.1"
eslint-module-utils@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6"
integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==
eslint-module-utils@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.1.tgz#b51be1e473dd0de1c5ea638e22429c2490ea8233"
integrity sha512-ZXI9B8cxAJIH4nfkhTwcRTEAnrVfobYqwjWy/QMCZ8rHkZHFjf9yO4BzpiF9kCSfNlMG54eKigISHpX0+AaT4A==
dependencies:
debug "^2.6.9"
debug "^3.2.7"
pkg-dir "^2.0.0"
eslint-plugin-flowtype@^3.13.0:
@@ -3710,22 +3705,25 @@ eslint-plugin-flowtype@^3.13.0:
lodash "^4.17.15"
eslint-plugin-import@^2.18.2:
version "2.22.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702"
integrity sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==
version "2.23.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.23.2.tgz#ee15dd68fc7a1a1ba4c653c734e0d01c100d3484"
integrity sha512-LmNoRptHBxOP+nb0PIKz1y6OSzCJlB+0g0IGS3XV4KaKk2q4szqQ6s6F1utVf5ZRkxk/QOTjdxe7v4VjS99Bsg==
dependencies:
array-includes "^3.1.1"
array.prototype.flat "^1.2.3"
contains-path "^0.1.0"
array-includes "^3.1.3"
array.prototype.flat "^1.2.4"
contains-path "^1.0.0"
debug "^2.6.9"
doctrine "1.5.0"
doctrine "^2.1.0"
eslint-import-resolver-node "^0.3.4"
eslint-module-utils "^2.6.0"
eslint-module-utils "^2.6.1"
find-up "^2.0.0"
has "^1.0.3"
is-core-module "^2.4.0"
minimatch "^3.0.4"
object.values "^1.1.1"
read-pkg-up "^2.0.0"
resolve "^1.17.0"
object.values "^1.1.3"
pkg-up "^2.0.0"
read-pkg-up "^3.0.0"
resolve "^1.20.0"
tsconfig-paths "^3.9.0"
eslint-plugin-jsx-a11y@^6.2.3:
@@ -4912,7 +4910,7 @@ is-color-stop@^1.0.0:
rgb-regex "^1.0.1"
rgba-regex "^1.0.0"
is-core-module@^2.2.0:
is-core-module@^2.2.0, is-core-module@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1"
integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==
@@ -5965,14 +5963,14 @@ listr@^0.14.3:
p-map "^2.0.0"
rxjs "^6.3.3"
load-json-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=
load-json-file@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
dependencies:
graceful-fs "^4.1.2"
parse-json "^2.2.0"
pify "^2.0.0"
parse-json "^4.0.0"
pify "^3.0.0"
strip-bom "^3.0.0"
loader-runner@^2.4.0:
@@ -6471,9 +6469,9 @@ node-notifier@^6.0.0:
which "^1.3.1"
node-releases@^1.1.71:
version "1.1.71"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
version "1.1.72"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
version "2.5.0"
@@ -6615,7 +6613,7 @@ object.pick@^1.3.0:
dependencies:
isobject "^3.0.1"
object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3:
object.values@^1.1.0, object.values@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee"
integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw==
@@ -6817,13 +6815,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-json@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=
dependencies:
error-ex "^1.2.0"
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -6905,12 +6896,19 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-type@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=
path-starts-with@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-1.0.0.tgz#b28243015e8b138de572682ac52da42e646ad84e"
integrity sha1-soJDAV6LE43lcmgqxS2kLmRq2E4=
dependencies:
normalize-path "^2.1.1"
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
dependencies:
pify "^2.0.0"
pify "^3.0.0"
path-type@^4.0.0:
version "4.0.0"
@@ -6943,11 +6941,16 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d"
integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==
pify@^2.0.0, pify@^2.2.0:
pify@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw=
pify@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
pify@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
@@ -6981,6 +6984,13 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f"
integrity sha1-yBmscoBZpGHKscOImivjxJoATX8=
dependencies:
find-up "^2.1.0"
pn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
@@ -7264,9 +7274,9 @@ postcss-selector-parser@^3.0.0:
uniq "^1.0.1"
postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
version "6.0.5"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.5.tgz#042d74e137db83e6f294712096cb413f5aa612c4"
integrity sha512-aFYPoYmXbZ1V6HZaSvat08M97A8HqO6Pjz+PiNpw/DhuRrC72XWAdp3hL6wusDCN31sSmcZyMGa2hZEuX+Xfhg==
version "6.0.6"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
@@ -7518,13 +7528,13 @@ react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=
read-pkg-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=
dependencies:
find-up "^2.0.0"
read-pkg "^2.0.0"
read-pkg "^3.0.0"
read-pkg-up@^7.0.1:
version "7.0.1"
@@ -7535,14 +7545,14 @@ read-pkg-up@^7.0.1:
read-pkg "^5.2.0"
type-fest "^0.8.1"
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=
read-pkg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
dependencies:
load-json-file "^2.0.0"
load-json-file "^4.0.0"
normalize-package-data "^2.3.2"
path-type "^2.0.0"
path-type "^3.0.0"
read-pkg@^5.2.0:
version "5.2.0"
@@ -7791,7 +7801,7 @@ resolve@1.17.0:
dependencies:
path-parse "^1.0.6"
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0:
resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@@ -8733,9 +8743,9 @@ timsort@^0.3.0:
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
tiny-glob@^0.2.6:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.8.tgz#b2792c396cc62db891ffa161fe8b33e76123e531"
integrity sha512-vkQP7qOslq63XRX9kMswlby99kyO5OvKptw7AMwBVMjXEI7Tb61eoI5DydyEMOseyGS5anDN1VPoVxEvH01q8w==
version "0.2.9"
resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2"
integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==
dependencies:
globalyzer "0.1.0"
globrex "^0.1.2"


Loading…
取消
儲存