Comparing version 2.3.0 to 2.3.1
{ | ||
"editor.fontSize": 16, | ||
"editor.lineHeight": 45 | ||
} |
{ | ||
"main": "dist/alpine.js", | ||
"name": "alpinejs", | ||
"version": "2.3.0", | ||
"version": "2.3.1", | ||
"repository": { | ||
@@ -6,0 +6,0 @@ "type": "git", |
@@ -121,2 +121,9 @@ # Alpine.js | ||
## Sponsors | ||
<img width="33%" src="https://refactoringui.nyc3.cdn.digitaloceanspaces.com/tailwind-logo.svg" alt="Tailwind CSS"> | ||
**Want your logo here? [DM on Twitter](https://twitter.com/calebporzio)** | ||
### Directives | ||
@@ -374,2 +381,6 @@ | ||
> :warning: **Only use on trusted content and never on user-provided content.** :warning: | ||
> | ||
> Dynamically rendering HTML from third parties can easily lead to [XSS](https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting) vulnerabilities. | ||
--- | ||
@@ -586,10 +597,19 @@ | ||
## v3 Roadmap | ||
## Security | ||
If you find a security vulnerability, please send an email to [calebporzio@gmail.com]() | ||
Alpine relies on a custom implementation using the `Function` object to evaluate its directives. Despite being more secure then `eval()`, its use is prohibited in some environments, such as Google Chrome App, using restrictive Content Security Policy (CSP). | ||
If you use Alpine in a website dealing with sensitive data and requiring [CSP](https://csp.withgoogle.com/docs/strict-csp.html), you need to include `unsafe-eval` in your policy. A robust policy correctly configured will help protecting your users when using personal or financial data. | ||
Since a policy applies to all scripts in your page, it's important that other external libraries included in the website are carefully reviewed to ensure that they are trustworthy and they won't introduce any Cross Site Scripting vulnerability either using the `eval()` function or manipulating the DOM to inject malicious code in your page. | ||
## V3 Roadmap | ||
* Move from `x-ref` to `ref` for Vue parity? | ||
* Add `Alpine.directive()` | ||
* Add `Alpine.component('foo', {...})` (With magic `__init()` method) | ||
* Dispatch Alpine events for "loaded", "transition-start", etc... (Original PR: #299) ? | ||
* Remove "object" (and array) syntax from `x-bind:class="{ 'foo': true }"` (PR to add support for object syntax for the `style` attribute: #236) | ||
* Improve `x-for` mutation reactivity (#165) | ||
* Add "deep watching" support in V3 (#294) | ||
* Dispatch Alpine events for "loaded", "transition-start", etc... ([#299](https://github.com/alpinejs/alpine/pull/299)) ? | ||
* Remove "object" (and array) syntax from `x-bind:class="{ 'foo': true }"` ([#236](https://github.com/alpinejs/alpine/pull/236) to add support for object syntax for the `style` attribute) | ||
* Improve `x-for` mutation reactivity ([#165](https://github.com/alpinejs/alpine/pull/165)) | ||
* Add "deep watching" support in V3 ([#294](https://github.com/alpinejs/alpine/pull/294)) | ||
@@ -596,0 +616,0 @@ ## License |
@@ -7,2 +7,3 @@ import babel from 'rollup-plugin-babel'; | ||
import replace from '@rollup/plugin-replace'; | ||
import pkg from './package.json'; | ||
@@ -19,4 +20,8 @@ export default { | ||
commonjs(), | ||
// 'observable-membrane' uses process.env. We don't have that... | ||
replace({ "process.env.NODE_ENV": "'production'" }), | ||
replace({ | ||
// 'observable-membrane' uses process.env. We don't have that... | ||
"process.env.NODE_ENV": "'production'", | ||
// inject Alpine.js package version number | ||
"process.env.PKG_VERSION": `"${pkg.version}"` | ||
}), | ||
resolve(), | ||
@@ -23,0 +28,0 @@ filesize(), |
@@ -6,2 +6,3 @@ import babel from 'rollup-plugin-babel'; | ||
import replace from '@rollup/plugin-replace'; | ||
import pkg from './package.json'; | ||
@@ -16,4 +17,8 @@ export default { | ||
plugins: [ | ||
// 'observable-membrane' uses process.env. We don't have that... | ||
replace({ "process.env.NODE_ENV": "'production'" }), | ||
replace({ | ||
// 'observable-membrane' uses process.env. We don't have that... | ||
"process.env.NODE_ENV": "'production'", | ||
// inject Alpine.js package version number | ||
"process.env.PKG_VERSION": `"${pkg.version}"` | ||
}), | ||
resolve(), | ||
@@ -20,0 +25,0 @@ filesize(), |
@@ -47,6 +47,10 @@ import { arrayUnique , isBooleanAttr } from '../utils' | ||
// Cursor position should be restored back to origin due to a safari bug | ||
const cursorPosition = el.selectionStart | ||
const selectionStart = el.selectionStart | ||
const selectionEnd = el.selectionEnd | ||
const selectionDirection = el.selectionDirection | ||
el.value = value | ||
if(el === document.activeElement) { | ||
el.setSelectionRange(cursorPosition, cursorPosition) | ||
if (el === document.activeElement && selectionStart !== null) { | ||
el.setSelectionRange(selectionStart, selectionEnd, selectionDirection) | ||
} | ||
@@ -53,0 +57,0 @@ } |
@@ -5,2 +5,3 @@ import Component from './component' | ||
const Alpine = { | ||
version: process.env.PKG_VERSION, | ||
start: async function () { | ||
@@ -7,0 +8,0 @@ if (! isTesting()) { |
import Alpine from 'alpinejs' | ||
import { wait } from '@testing-library/dom' | ||
import { fireEvent, wait } from '@testing-library/dom' | ||
@@ -375,4 +375,49 @@ global.MutationObserver = class { | ||
expect(document.querySelector('span').classList.contains('hidden')).toBeTruthy() | ||
expect(document.querySelector('span').classList.contains('text-red')).toBeTruthy | ||
expect(document.querySelector('span').classList.contains('text-red')).toBeTruthy() | ||
}) | ||
}); | ||
test('cursor position is preserved on selectable text input', async () => { | ||
document.body.innerHTML = ` | ||
<div x-data="{ foo: 'bar' }"> | ||
<input type="text" x-model="foo" @select="foo = 'baz'"> | ||
</div> | ||
` | ||
Alpine.start() | ||
document.querySelector('input').focus() | ||
expect(document.querySelector('input').value).toEqual('bar') | ||
expect(document.querySelector('input').selectionStart).toEqual(0) | ||
expect(document.querySelector('input').selectionEnd).toEqual(0) | ||
expect(document.querySelector('input').selectionDirection).toEqual('none') | ||
document.querySelector('input').setSelectionRange(0, 3, 'backward') | ||
await wait(() => { | ||
expect(document.querySelector('input').value).toEqual('baz') | ||
expect(document.querySelector('input').selectionStart).toEqual(0) | ||
expect(document.querySelector('input').selectionEnd).toEqual(3) | ||
expect(document.querySelector('input').selectionDirection).toEqual('backward') | ||
}) | ||
}) | ||
// input elements that are not 'text', 'search', 'url', 'password' types | ||
// will throw an exception when calling their setSelectionRange() method | ||
// see issues #401 #404 #405 | ||
test('setSelectionRange is not called for inapplicable input types', async () => { | ||
document.body.innerHTML = ` | ||
<div x-data="{ foo: 'bar' }"> | ||
<input type="hidden" x-model="foo"> | ||
</div> | ||
` | ||
Alpine.start() | ||
fireEvent.input(document.querySelector('input'), { target: { value: 'baz' } }) | ||
await wait(() => { | ||
expect(document.querySelector('input').value).toEqual('baz') | ||
}) | ||
}) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
576824
56
11818
618
1