Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions library/src/engine/engine.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { isHTMLOrSVG } from '../utils/dom'
import { isPojo, pathToObj } from '../utils/paths'
import { camel, snake } from '../utils/text'
import { snake } from '../utils/text'
import { DATASTAR, DSP, DSS } from './consts'
import { initErr, runtimeErr } from './errors'
import type {
Expand Down Expand Up @@ -866,15 +866,15 @@ export function load(...pluginsToLoad: DatastarPlugin[]) {
return a.name.localeCompare(b.name)
})

pluginRegexs = plugins.map((plugin) => RegExp(`^${plugin.name}([A-Z]|_|$)`))
pluginRegexs = plugins.map((plugin) => RegExp(`^${plugin.name}(:|$)`))
}

function applyEls(els: Iterable<HTMLOrSVG>): void {
const ignore = `[${aliasify('ignore')}]`
for (const el of els) {
if (!el.closest(ignore)) {
for (const key in el.dataset) {
applyAttributePlugin(el, key, el.dataset[key]!)
applyAttributePlugin(el, key.replace(/[A-Z]/g, '-$&').toLowerCase(), el.dataset[key]!)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just undo dataset case.

}
}
}
Expand Down Expand Up @@ -918,17 +918,14 @@ function applyAttributePlugin(
attrKey: string,
value: string,
): void {
if (attrKey.startsWith(alias)) {
const rawKey = camel(alias ? attrKey.slice(alias.length) : attrKey)
if (!alias || attrKey.startsWith(alias + '-')) {
const rawKey = alias ? attrKey.slice(alias.length + 1) : attrKey
const plugin = plugins.find((_, i) => pluginRegexs[i].test(rawKey))
if (plugin) {
// Extract the key and modifiers
let [key, ...rawModifiers] = rawKey.slice(plugin.name.length).split(/__+/)
let [key, ...rawModifiers] = rawKey.slice(plugin.name.length + 1).split(/__+/)

const hasKey = !!key
if (hasKey) {
key = camel(key)
}
const hasValue = !!value

// Create the runtime context
Expand Down Expand Up @@ -995,7 +992,7 @@ function applyAttributePlugin(

for (const rawMod of rawModifiers) {
const [label, ...mod] = rawMod.split('.')
ctx.mods.set(camel(label), new Set(mod.map((t) => t.toLowerCase())))
ctx.mods.set(label, new Set(mod))
}

const cleanup = plugin.onLoad(ctx)
Expand Down Expand Up @@ -1041,7 +1038,7 @@ function observe(mutations: MutationRecord[]) {
} else if (type === 'attributes') {
// If el has a parent with data-ignore, skip it
if (isHTMLOrSVG(target) && !target.closest(ignore)) {
const key = camel(attributeName!.slice(5))
const key = attributeName!.slice(5)
const value = target.getAttribute(attributeName!)
if (value === null) {
const cleanups = removals.get(target)
Expand Down
2 changes: 0 additions & 2 deletions library/src/plugins/attributes/attr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// Description: Sets the value of any HTML attribute to an expression, and keeps it in sync.

import type { AttributePlugin } from '../../engine/types'
import { kebab } from '../../utils/text'

export const Attr: AttributePlugin = {
type: 'attribute',
Expand All @@ -23,7 +22,6 @@ export const Attr: AttributePlugin = {
}
}

key = kebab(key)
const update = key
? () => {
observer.disconnect()
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export const Bind: AttributePlugin = {
!(el instanceof HTMLSelectElement && el.multiple)
) {
const inputs = document.querySelectorAll(
`[${aliasify('bind')}-${key}],[${aliasify('bind')}="${value}"]`,
`[${aliasify('bind')}\\:${key}],[${aliasify('bind')}="${value}"]`,
) as NodeListOf<HTMLInputElement>

const paths: Paths = []
Expand Down
4 changes: 2 additions & 2 deletions library/src/plugins/attributes/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Description: Adds or removes a class to or from an element based on an expression.

import type { AttributePlugin } from '../../engine/types'
import { kebab, modifyCasing } from '../../utils/text'
import { modifyCasing } from '../../utils/text'

export const Class: AttributePlugin = {
type: 'attribute',
Expand All @@ -12,7 +12,7 @@ export const Class: AttributePlugin = {
returnsValue: true,
onLoad: ({ key, el, effect, mods, rx }) => {
if (key) {
key = modifyCasing(kebab(key), mods)
key = modifyCasing(key, mods, 'kebab')
}

const callback = () => {
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/jsonSignals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { jsStrToObject } from '../../utils/text'

export const JsonSignals: AttributePlugin = {
type: 'attribute',
name: 'jsonSignals',
name: 'json-signals',
keyReq: 'denied',
onLoad: ({ el, effect, value, filtered, mods }) => {
const spaces = mods.has('terse') ? 0 : 2
Expand Down
5 changes: 2 additions & 3 deletions library/src/plugins/attributes/on.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
type AttributePlugin,
DATASTAR_SIGNAL_PATCH_EVENT,
} from '../../engine/types'
import { kebab, modifyCasing } from '../../utils/text'
import { modifyCasing } from '../../utils/text'
import { modifyTiming } from '../../utils/timing'
import { modifyViewTransition } from '../../utils/view-transitions'
import { DATASTAR_FETCH_EVENT } from '../backend/shared'
Expand Down Expand Up @@ -52,8 +52,7 @@ export const On: AttributePlugin = {
}
}
// Default to kebab-case and allow modifying
let eventName = kebab(key)
eventName = modifyCasing(eventName, mods)
let eventName = modifyCasing(key, mods, 'kebab')
// Listen for Datastar events on the document
if (
eventName === DATASTAR_FETCH_EVENT ||
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/onIntersect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const once = new WeakSet<HTMLOrSVG>()

export const OnIntersect: AttributePlugin = {
type: 'attribute',
name: 'onIntersect',
name: 'on-intersect',
keyReq: 'denied',
onLoad: ({ el, mods, rx, startBatch, endBatch }) => {
let callback = () => {
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/onInterval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { modifyViewTransition } from '../../utils/view-transitions'

export const OnInterval: AttributePlugin = {
type: 'attribute',
name: 'onInterval',
name: 'on-interval',
keyReq: 'denied',
valReq: 'must',
onLoad: ({ mods, rx, startBatch, endBatch }) => {
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/onLoad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { modifyViewTransition } from '../../utils/view-transitions'

export const OnLoad: AttributePlugin = {
type: 'attribute',
name: 'onLoad',
name: 'on-load',
keyReq: 'denied',
valReq: 'must',
onLoad: ({ rx, mods, startBatch, endBatch }) => {
Expand Down
2 changes: 1 addition & 1 deletion library/src/plugins/attributes/onSignalPatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { modifyTiming } from '../../utils/timing'

export const OnSignalPatch: AttributePlugin = {
type: 'attribute',
name: 'onSignalPatch',
name: 'on-signal-patch',
valReq: 'must',
argNames: ['patch'],
returnsValue: true,
Expand Down
2 changes: 0 additions & 2 deletions library/src/plugins/attributes/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ export const Style: AttributePlugin = {
const { style } = el
const initialStyles = new Map<string, string>()

key &&= kebab(key)

const apply = (prop: string, value: any) => {
const initial = initialStyles.get(prop)
if (!value && value !== 0) {
Expand Down
21 changes: 12 additions & 9 deletions library/src/utils/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,8 @@ export const kebab = (str: string) =>
.replace(/([0-9]+)([a-z])/gi, '$1-$2')
.toLowerCase()

export const camel = (str: string) =>
kebab(str).replace(/-./g, (x) => x[1].toUpperCase())

export const snake = (str: string) => kebab(str).replace(/-/g, '_')

export const pascal = (str: string) =>
camel(str).replace(/(^.|(?<=\.).)/g, (x) => x[0].toUpperCase())

export const jsStrToObject = (raw: string) => {
try {
return JSON.parse(raw)
Expand All @@ -27,10 +21,19 @@ export const jsStrToObject = (raw: string) => {
}
}

const caseFns: Record<string, (s: string) => string> = { kebab, snake, pascal }
// The case mods expect the input to be raw attribute names (kebab-case)
export const modCamel = (str: string) =>
str.replace(/-[a-z]/g, (x) => x[1].toUpperCase())

export const modSnake = (str: string) => str.replace(/-/g, '_')

export const modPascal = (str: string) =>
str[0].toUpperCase() + modCamel(str.slice(1))

const caseFns: Record<string, (s: string) => string> = { camel: modCamel, snake: modSnake, pascal: modPascal }

export function modifyCasing(str: string, mods: Modifiers) {
for (const c of mods.get('case') || []) {
export function modifyCasing(str: string, mods: Modifiers, defaultCase: string = 'camel') {
for (const c of mods.get('case') || [defaultCase]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes the default case a param so that only one case modifier is applied. Before the default case would be applied no matter what, then the user case applied on top of that.

const fn = caseFns[c]
if (fn) str = fn(str)
}
Expand Down