Skip to content

Commit

Permalink
Merge branch 'master' into checkbox-values
Browse files Browse the repository at this point in the history
  • Loading branch information
calebporzio committed Mar 7, 2020
2 parents 6f58523 + 98d26e1 commit 898adec
Show file tree
Hide file tree
Showing 12 changed files with 7,465 additions and 33 deletions.
477 changes: 477 additions & 0 deletions README.ja.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Think of it like [Tailwind](https://tailwindcss.com/) for JavaScript.

> Note: This tool's syntax is almost entirely borrowed from [Vue](https://vuejs.org/) (and by extension [Angular](https://angularjs.org/)). I am forever grateful for the gift they are to the web.
[**日本語ドキュメント**](./README.ja.md)

## Install

**From CDN:** Add the following script to the end of your `<head>` section.
Expand Down
6,900 changes: 6,894 additions & 6 deletions dist/alpine-ie11.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion dist/alpine-ie11.js.map

This file was deleted.

15 changes: 12 additions & 3 deletions dist/alpine.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,17 +109,27 @@
return new Function(['$data', ...Object.keys(additionalHelperVariables)], `var result; with($data) { result = ${expression} }; return result`)(dataContext, ...Object.values(additionalHelperVariables));
}
function saferEvalNoReturn(expression, dataContext, additionalHelperVariables = {}) {
// For the cases when users pass only a function reference to the caller: `x-on:click="foo"`
// Where "foo" is a function. Also, we'll pass the function the event instance when we call it.
if (Object.keys(dataContext).includes(expression)) {
let methodReference = new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { return ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables));

if (typeof methodReference === 'function') {
return methodReference.call(dataContext, additionalHelperVariables['$event']);
}
}

return new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { ${expression} }`)(dataContext, ...Object.values(additionalHelperVariables));
}
const xAttrRE = /^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)\b/;
function isXAttr(attr) {
const name = replaceAtAndColonWithStandardSyntax(attr.name);
const xAttrRE = /x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)/;
return xAttrRE.test(name);
}
function getXAttrs(el, type) {
return Array.from(el.attributes).filter(isXAttr).map(attr => {
const name = replaceAtAndColonWithStandardSyntax(attr.name);
const typeMatch = name.match(/x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)/);
const typeMatch = name.match(xAttrRE);
const valueMatch = name.match(/:([a-zA-Z\-:]+)/);
const modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || [];
return {
Expand Down Expand Up @@ -1603,4 +1613,3 @@
return Alpine;

})));
//# sourceMappingURL=alpine.js.map
1 change: 0 additions & 1 deletion dist/alpine.js.map

This file was deleted.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 5 additions & 10 deletions rollup-ie11.config.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
import babel from 'rollup-plugin-babel';
import filesize from 'rollup-plugin-filesize';
import { terser } from "rollup-plugin-terser";
import resolve from "rollup-plugin-node-resolve"
import commonjs from '@rollup/plugin-commonjs'
import resolve from 'rollup-plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import multi from '@rollup/plugin-multi-entry';
import replace from '@rollup/plugin-replace';

export default {
input: ['src/polyfills.js', 'src/index.js'],
output: {
name: 'Alpine',
file: 'dist/alpine-ie11.js',
format: 'umd',
sourcemap: true,
},
plugins: [
multi(),
commonjs(),
// 'observable-membrane' uses process.env. We don't have that...
replace({ "process.env.NODE_ENV": "'production'" }),
resolve(),
filesize(),
terser({
mangle: false,
compress: {
drop_debugger: false,
}
}),
babel({
babelrc: false,
exclude: 'node_modules/**',
Expand Down
3 changes: 1 addition & 2 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import babel from 'rollup-plugin-babel';
import filesize from 'rollup-plugin-filesize';
import resolve from "rollup-plugin-node-resolve"
import resolve from 'rollup-plugin-node-resolve';
import stripCode from 'rollup-plugin-strip-code';
import replace from '@rollup/plugin-replace';

Expand All @@ -10,7 +10,6 @@ export default {
name: 'Alpine',
file: 'dist/alpine.js',
format: 'umd',
sourcemap: true,
},
plugins: [
// 'observable-membrane' uses process.env. We don't have that...
Expand Down
19 changes: 15 additions & 4 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,27 @@ export function saferEval(expression, dataContext, additionalHelperVariables = {
}

export function saferEvalNoReturn(expression, dataContext, additionalHelperVariables = {}) {
// For the cases when users pass only a function reference to the caller: `x-on:click="foo"`
// Where "foo" is a function. Also, we'll pass the function the event instance when we call it.
if (Object.keys(dataContext).includes(expression)) {
let methodReference = (new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { return ${expression} }`))(
dataContext, ...Object.values(additionalHelperVariables)
)

if (typeof methodReference === 'function') {
return methodReference.call(dataContext, additionalHelperVariables['$event'])
}
}

return (new Function(['dataContext', ...Object.keys(additionalHelperVariables)], `with(dataContext) { ${expression} }`))(
dataContext, ...Object.values(additionalHelperVariables)
)
}

const xAttrRE = /^x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)\b/

export function isXAttr(attr) {
const name = replaceAtAndColonWithStandardSyntax(attr.name)

const xAttrRE = /x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)/

return xAttrRE.test(name)
}

Expand All @@ -83,7 +94,7 @@ export function getXAttrs(el, type) {
.map(attr => {
const name = replaceAtAndColonWithStandardSyntax(attr.name)

const typeMatch = name.match(/x-(on|bind|data|text|html|model|if|for|show|cloak|transition|ref)/)
const typeMatch = name.match(xAttrRE)
const valueMatch = name.match(/:([a-zA-Z\-:]+)/)
const modifiers = name.match(/\.[^.\]]+(?=[^\]]*$)/g) || []

Expand Down
18 changes: 17 additions & 1 deletion test/constructor.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test('auto-detect new components at the top level', async () => {
await wait(() => { expect(document.querySelector('span').innerText).toEqual('bar') })
})

test('auto-detect newsted new components at the top level', async () => {
test('auto-detect nested new components at the top level', async () => {
var runObservers = []

global.MutationObserver = class {
Expand Down Expand Up @@ -279,3 +279,19 @@ test('can clone an existing component to a new element', async () => {

expect(document.querySelector('span').innerText).toEqual('bar')
})

test('x-attributes are matched exactly', async () => {
document.body.innerHTML = `
<div x-data="{ showElement: false }">
<div id="el1" x-show="showElement" />
<div id="el2" xxx-show="showElement" />
<div id="el3" x-showabc="showElement" />
</div>
`

Alpine.start()

expect(document.getElementById('el1').style.display).toEqual('none')
expect(document.getElementById('el2').style.display).not.toEqual('none')
await wait(() => { expect(document.getElementById('el3').style.display).not.toEqual('none') })
})
45 changes: 41 additions & 4 deletions test/on.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ global.MutationObserver = class {
observe() {}
}

test('data modified in event listener updates effected attribute bindings', async () => {
test('data modified in event listener updates affected attribute bindings', async () => {
document.body.innerHTML = `
<div x-data="{ foo: 'bar' }">
<button x-on:click="foo = 'baz'"></button>
Expand All @@ -24,7 +24,7 @@ test('data modified in event listener updates effected attribute bindings', asyn
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})

test('nested data modified in event listener updates effected attribute bindings', async () => {
test('nested data modified in event listener updates affected attribute bindings', async () => {
document.body.innerHTML = `
<div x-data="{ nested: { foo: 'bar' } }">
<button x-on:click="nested.foo = 'baz'"></button>
Expand Down Expand Up @@ -161,7 +161,7 @@ test('.once modifier', async () => {
expect(document.querySelector('span').getAttribute('foo')).toEqual('1')
})

test('.once modifier doest remove listener if false is returned', async () => {
test('.once modifier does not remove listener if false is returned', async () => {
document.body.innerHTML = `
<div x-data="{ count: 0 }">
<button x-on:click.once="return ++count === 2"></button>
Expand Down Expand Up @@ -348,7 +348,6 @@ test('event with colon', async () => {
await wait(() => { expect(document.querySelector('span').getAttribute('foo')).toEqual('baz') })
})


test('prevent default action when an event returns false', async () => {
window.confirm = jest.fn().mockImplementation(() => false)

Expand All @@ -372,3 +371,41 @@ test('prevent default action when an event returns false', async () => {

expect(document.querySelector('input').checked).toEqual(true)
})

test('allow method reference to be passed to listeners', async () => {
document.body.innerHTML = `
<div x-data="{ foo: 'bar', changeFoo() { this.foo = 'baz' } }">
<button x-on:click="changeFoo"></button>
<span x-text="foo"></span>
</div>
`

Alpine.start()

expect(document.querySelector('span').innerText).toEqual('bar')

document.querySelector('button').click()

await new Promise(resolve => setTimeout(resolve, 1))

expect(document.querySelector('span').innerText).toEqual('baz')
})

test('event instance is passed to method reference', async () => {
document.body.innerHTML = `
<div x-data="{ foo: 'bar', changeFoo(e) { this.foo = e.target.id } }">
<button x-on:click="changeFoo" id="baz"></button>
<span x-text="foo"></span>
</div>
`

Alpine.start()

expect(document.querySelector('span').innerText).toEqual('bar')

document.querySelector('button').click()

await new Promise(resolve => setTimeout(resolve, 1))

expect(document.querySelector('span').innerText).toEqual('baz')
})

0 comments on commit 898adec

Please sign in to comment.