diff --git a/packages/config/src/defaults.ts b/packages/config/src/defaults.ts index 1b7f1d40..195de1b3 100644 --- a/packages/config/src/defaults.ts +++ b/packages/config/src/defaults.ts @@ -46,8 +46,18 @@ export function getDefaultTransformerConfig(): TransformerOptions { loose: true, }, }, + classFunctions: [ + 'classNames', + 'classnames', + 'clsx', + 'cn', + 'cva', + 'cx', + 'tv', + 'twJoin', + ], preserve: { - functions: [], + functions: ['twMerge'], classes: [], }, } diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 1b97f286..79772314 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -43,6 +43,7 @@ export interface TransformerOptions { generator?: IClassGeneratorOptions sources?: TransformerSourceOptions registry?: TransformerRegistryOptions + classFunctions?: string[] preserve?: TransformerPreserveOptions } diff --git a/packages/config/test/__snapshots__/defaults.test.ts.snap b/packages/config/test/__snapshots__/defaults.test.ts.snap index 220939b1..084e0bd8 100644 --- a/packages/config/test/__snapshots__/defaults.test.ts.snap +++ b/packages/config/test/__snapshots__/defaults.test.ts.snap @@ -16,11 +16,23 @@ exports[`defaults > getDefaultUserConfig 1`] = ` }, }, "transformer": { + "classFunctions": [ + "classNames", + "classnames", + "clsx", + "cn", + "cva", + "cx", + "tv", + "twJoin", + ], "disabled": false, "filter": [Function], "preserve": { "classes": [], - "functions": [], + "functions": [ + "twMerge", + ], }, "registry": { "file": ".tw-patch/tw-class-list.json", diff --git a/packages/config/test/__snapshots__/index.test.ts.snap b/packages/config/test/__snapshots__/index.test.ts.snap index 962c881e..b6ff8e89 100644 --- a/packages/config/test/__snapshots__/index.test.ts.snap +++ b/packages/config/test/__snapshots__/index.test.ts.snap @@ -16,6 +16,16 @@ exports[`config > 2.transformer-options 1`] = ` }, }, "transformer": { + "classFunctions": [ + "classNames", + "classnames", + "clsx", + "cn", + "cva", + "cx", + "tv", + "twJoin", + ], "disabled": false, "filter": [Function], "generator": { @@ -23,7 +33,9 @@ exports[`config > 2.transformer-options 1`] = ` }, "preserve": { "classes": [], - "functions": [], + "functions": [ + "twMerge", + ], }, "registry": { "file": "zzzzz.json", diff --git a/packages/core/src/ctx/index.ts b/packages/core/src/ctx/index.ts index 51118616..cb51008d 100644 --- a/packages/core/src/ctx/index.ts +++ b/packages/core/src/ctx/index.ts @@ -22,6 +22,7 @@ export class Context { configRoot: string + classFunctionSet: Set preserveFunctionSet: Set preserveClassNamesSet: Set preserveFunctionRegexs: RegExp[] @@ -31,6 +32,7 @@ export class Context { this.replaceMap = new Map() this.classGenerator = new ClassGenerator() this.configRoot = process.cwd() + this.classFunctionSet = new Set() this.preserveFunctionSet = new Set() this.preserveClassNamesSet = new Set() this.preserveFunctionRegexs = [] @@ -44,6 +46,10 @@ export class Context { return this.preserveClassNamesSet.add(className) } + isClassFunction(calleeName: string) { + return this.classFunctionSet.has(calleeName) + } + isPreserveFunction(calleeName: string) { return this.preserveFunctionSet.has(calleeName) } @@ -53,6 +59,7 @@ export class Context { this.options = defu(this.options, ...opts) this.classGenerator = new ClassGenerator(this.options.generator) const preserveOptions = this.options.preserve ?? {} + this.classFunctionSet = new Set(this.options.classFunctions ?? []) this.preserveFunctionSet = new Set(preserveOptions.functions ?? []) this.preserveClassNamesSet = new Set(preserveOptions.classes ?? []) this.preserveFunctionRegexs = [...this.preserveFunctionSet.values()].map((x) => { diff --git a/packages/core/src/js/index.ts b/packages/core/src/js/index.ts index d7b12d3a..d5d0d04f 100644 --- a/packages/core/src/js/index.ts +++ b/packages/core/src/js/index.ts @@ -1,4 +1,5 @@ -import type { StringLiteral, TemplateElement } from '@babel/types' +import type { NodePath } from '@babel/traverse' +import type { Expression, Node, StringLiteral, TemplateElement } from '@babel/types' import type { IHandlerTransformResult, IJsHandlerOptions } from '../types' import { jsStringEscape } from '@ast-core/escape' import { sort } from 'fast-sort' @@ -7,31 +8,41 @@ import { parse, traverse } from '../babel' import { ignoreIdentifier } from '../constants' import { makeRegex, splitCode } from '../shared' -export function handleValue(raw: string, node: StringLiteral | TemplateElement, options: IJsHandlerOptions, ms: MagicString, offset: number, escape: boolean) { +const classPropertyNames = new Set(['class', 'className', 'classNames', 'cls', 'staticClass']) +const htmlPropertyNames = new Set(['innerHTML', 'outerHTML']) +const classListMethodNames = new Set(['add', 'remove', 'toggle', 'replace']) +const htmlCalleeNames = new Set(['createStaticVNode']) + +interface ClassAttributeState { + quote?: '"' | '\'' +} + +function replaceClassTokens(raw: string, options: IJsHandlerOptions) { const { ctx, splitQuote = true, id } = options const { replaceMap, classGenerator: clsGen } = ctx - const array = splitCode(raw, { splitQuote, }) let rawString = raw let needUpdate = false - for (const v of array) { - if (replaceMap.has(v)) { - let ignoreFlag = false - if (Array.isArray(node.leadingComments)) { - ignoreFlag = node.leadingComments.findIndex(x => x.value.includes('tw-mangle') && x.value.includes('ignore')) > -1 - } - if (!ignoreFlag) { - const gen = clsGen.generateClassName(v) - rawString = rawString.replace(makeRegex(v), gen.name) - ctx.addToUsedBy(v, id) - needUpdate = true - } + for (const v of array) { + if (replaceMap.has(v) && !ctx.isPreserveClass(v)) { + const gen = clsGen.generateClassName(v) + rawString = rawString.replace(makeRegex(v), gen.name) + ctx.addToUsedBy(v, id) + needUpdate = true } } - if (needUpdate && typeof node.start === 'number' && typeof node.end === 'number') { + + return { + rawString, + needUpdate, + } +} + +function updateNodeValue(raw: string, rawString: string, node: StringLiteral | TemplateElement, ms: MagicString, offset: number, escape: boolean) { + if (typeof node.start === 'number' && typeof node.end === 'number') { const start = node.start + offset const end = node.end - offset @@ -39,7 +50,340 @@ export function handleValue(raw: string, node: StringLiteral | TemplateElement, ms.update(start, end, escape ? jsStringEscape(rawString) : rawString) } } - return rawString +} + +export function handleValue(raw: string, node: StringLiteral | TemplateElement, options: IJsHandlerOptions, ms: MagicString, offset: number, escape: boolean) { + let ignoreFlag = false + if (Array.isArray(node.leadingComments)) { + ignoreFlag = node.leadingComments.findIndex(x => x.value.includes('tw-mangle') && x.value.includes('ignore')) > -1 + } + + if (!ignoreFlag) { + const { rawString, needUpdate } = replaceClassTokens(raw, options) + if (needUpdate) { + updateNodeValue(raw, rawString, node, ms, offset, escape) + return rawString + } + } + + return raw +} + +function preserveClassTokens(raw: string, options: IJsHandlerOptions) { + const { ctx, splitQuote = true } = options + const arr = sort(splitCode(raw, { splitQuote })).desc(x => x.length) + + for (const str of arr) { + if (ctx.replaceMap.has(str)) { + ctx.addPreserveClass(str) + } + } +} + +function isIdentifierName(node: Node | null | undefined, name: string) { + return node?.type === 'Identifier' && node.name === name +} + +function getPropertyName(node: Node | null | undefined) { + if (!node) { + return + } + if (node.type === 'Identifier' || node.type === 'JSXIdentifier') { + return node.name + } + if (node.type === 'StringLiteral') { + return node.value + } +} + +function isClassPropertyName(name: string | undefined) { + return typeof name === 'string' && classPropertyNames.has(name) +} + +function isHtmlPropertyName(name: string | undefined) { + return typeof name === 'string' && htmlPropertyNames.has(name) +} + +function getMemberPropertyName(path: NodePath) { + if (!path.isMemberExpression()) { + return + } + return getPropertyName(path.node.property) +} + +function getCalleeName(path: NodePath) { + if (path.isIdentifier()) { + return path.node.name + } + if (path.isMemberExpression()) { + return getPropertyName(path.node.property) + } + if (path.isSequenceExpression()) { + const expressions = path.get('expressions') as NodePath[] + return getCalleeName(expressions[expressions.length - 1]) + } +} + +function getCallCalleeName(path: NodePath) { + return path.isCallExpression() ? getCalleeName(path.get('callee') as NodePath) : undefined +} + +function isIgnoredTaggedTemplate(path: NodePath) { + if (!path.isTaggedTemplateExpression()) { + return false + } + return isIdentifierName(path.node.tag, ignoreIdentifier) +} + +function collectPreserveClasses(path: NodePath, options: IJsHandlerOptions) { + path.traverse({ + StringLiteral: { + enter(p) { + preserveClassTokens(p.node.value, options) + }, + }, + TemplateElement: { + enter(p) { + preserveClassTokens(p.node.value.raw, options) + }, + }, + }) +} + +function transformClassAttributeValue(raw: string, options: IJsHandlerOptions, state: ClassAttributeState = {}) { + let cursor = 0 + let value = '' + const nextState = state + + const appendClassValue = (segment: string) => { + value += replaceClassTokens(segment, { + ...options, + splitQuote: false, + }).rawString + } + + while (cursor < raw.length) { + if (nextState.quote) { + const end = raw.indexOf(nextState.quote, cursor) + if (end === -1) { + appendClassValue(raw.slice(cursor)) + cursor = raw.length + } + else { + appendClassValue(raw.slice(cursor, end)) + value += raw[end] + nextState.quote = undefined + cursor = end + 1 + } + continue + } + + const classAttrRE = /(?:^|[\s<])class\s*=\s*(["'])/g + classAttrRE.lastIndex = cursor + const match = classAttrRE.exec(raw) + if (!match) { + value += raw.slice(cursor) + break + } + + const valueStart = match.index + match[0].length + const quote = match[1] as '"' | '\'' + value += raw.slice(cursor, valueStart) + const end = raw.indexOf(quote, valueStart) + if (end === -1) { + appendClassValue(raw.slice(valueStart)) + nextState.quote = quote + cursor = raw.length + } + else { + appendClassValue(raw.slice(valueStart, end)) + value += raw[end] + cursor = end + 1 + } + } + + return { + value, + state: nextState, + } +} + +function handleHtmlValue(raw: string, node: StringLiteral | TemplateElement, options: IJsHandlerOptions, ms: MagicString, offset: number, escape: boolean, state?: ClassAttributeState) { + const { value } = transformClassAttributeValue(raw, options, state) + updateNodeValue(raw, value, node, ms, offset, escape) + return value +} + +function handleStringLiteral(path: NodePath, options: IJsHandlerOptions, ms: MagicString, mode: 'class' | 'html') { + if (!path.isStringLiteral()) { + return + } + + if ( + typeof path.node.value === 'string' + && path.isDirectiveLiteral?.() + && path.node.value.startsWith('use ') + ) { + return + } + + if (mode === 'html') { + handleHtmlValue(path.node.value, path.node, options, ms, 1, true) + } + else { + handleValue(path.node.value, path.node, options, ms, 1, true) + } +} + +function handleClassExpression(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (path.isStringLiteral()) { + handleStringLiteral(path, options, ms, 'class') + return + } + + if (path.isTemplateLiteral()) { + const quasis = path.get('quasis') as NodePath[] + const expressions = path.get('expressions') as NodePath[] + for (const [index, quasi] of quasis.entries()) { + handleValue(quasi.node.value.raw, quasi.node, options, ms, 0, false) + if (expressions[index]) { + handleClassExpression(expressions[index], options, ms) + } + } + return + } + + if (path.isTaggedTemplateExpression()) { + if (isIgnoredTaggedTemplate(path)) { + collectPreserveClasses(path, options) + } + return + } + + if (path.isConditionalExpression()) { + handleClassExpression(path.get('consequent') as NodePath, options, ms) + handleClassExpression(path.get('alternate') as NodePath, options, ms) + return + } + + if (path.isLogicalExpression() || path.isBinaryExpression({ operator: '+' })) { + handleClassExpression(path.get('left') as NodePath, options, ms) + handleClassExpression(path.get('right') as NodePath, options, ms) + return + } + + if (path.isArrayExpression()) { + for (const element of path.get('elements') as NodePath[]) { + if (element.node) { + handleClassExpression(element, options, ms) + } + } + return + } + + if (path.isObjectExpression()) { + for (const property of path.get('properties') as NodePath[]) { + if (property.isObjectProperty()) { + const key = property.get('key') as NodePath + const value = property.get('value') as NodePath + if (key.isStringLiteral()) { + handleStringLiteral(key, options, ms, 'class') + } + handleClassExpression(value, options, ms) + } + } + return + } + + if (path.isCallExpression()) { + const callee = path.get('callee') + if (callee.isIdentifier() && options.ctx.isPreserveFunction(callee.node.name)) { + collectPreserveClasses(path, options) + return + } + + for (const arg of path.get('arguments') as NodePath[]) { + handleClassExpression(arg, options, ms) + } + } +} + +function handleHtmlTemplateLiteral(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (!path.isTemplateLiteral()) { + return + } + + const state: ClassAttributeState = {} + const quasis = path.get('quasis') as NodePath[] + const expressions = path.get('expressions') as NodePath[] + for (const [index, quasi] of quasis.entries()) { + handleHtmlValue(quasi.node.value.raw, quasi.node, options, ms, 0, false, state) + if (state.quote && expressions[index]) { + handleClassExpression(expressions[index], options, ms) + } + } +} + +function handleHtmlExpression(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (path.isStringLiteral()) { + handleStringLiteral(path, options, ms, 'html') + } + else if (path.isTemplateLiteral()) { + handleHtmlTemplateLiteral(path, options, ms) + } +} + +function handleClassListCall(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (!path.isCallExpression()) { + return + } + const callee = path.get('callee') + if (!callee.isMemberExpression()) { + return + } + const propertyName = getMemberPropertyName(callee as NodePath) + if (!classListMethodNames.has(propertyName ?? '')) { + return + } + const object = callee.get('object') + if (!object.isMemberExpression() || getMemberPropertyName(object as NodePath) !== 'classList') { + return + } + + const args = path.get('arguments') as NodePath[] + const limit = propertyName === 'toggle' ? 1 : propertyName === 'replace' ? 2 : args.length + for (const arg of args.slice(0, limit)) { + handleClassExpression(arg, options, ms) + } +} + +function handleSetAttributeCall(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (!path.isCallExpression()) { + return + } + const callee = path.get('callee') + if (!callee.isMemberExpression() || getMemberPropertyName(callee as NodePath) !== 'setAttribute') { + return + } + const args = path.get('arguments') as NodePath[] + const attrName = args[0] + if (attrName?.isStringLiteral() && attrName.node.value === 'class' && args[1]) { + handleClassExpression(args[1], options, ms) + } +} + +function handleInsertAdjacentHtmlCall(path: NodePath, options: IJsHandlerOptions, ms: MagicString) { + if (!path.isCallExpression()) { + return + } + const callee = path.get('callee') + if (!callee.isMemberExpression() || getMemberPropertyName(callee as NodePath) !== 'insertAdjacentHTML') { + return + } + const args = path.get('arguments') as NodePath[] + if (args[1]) { + handleHtmlExpression(args[1], options, ms) + } } export function jsHandler(rawSource: string | MagicString, options: IJsHandlerOptions): IHandlerTransformResult { @@ -59,75 +403,103 @@ export function jsHandler(rawSource: string | MagicString, options: IJsHandlerOp const { ctx } = options traverse(ast, { - StringLiteral: { + CallExpression: { enter(p) { - const n = p.node - if ( - typeof n.value === 'string' - && p.isDirectiveLiteral?.() - && n.value.startsWith('use ') - ) { - return + const callee = p.get('callee') + if (callee.isIdentifier() && ctx.isPreserveFunction(callee.node.name)) { + collectPreserveClasses(p as NodePath, options) } - handleValue(n.value, n, options, ms, 1, true) }, }, - TemplateElement: { + TaggedTemplateExpression: { enter(p) { - const n = p.node - if (p.parentPath.isTemplateLiteral()) { - if ( - (p.parentPath.parentPath.isTaggedTemplateExpression() - && p.parentPath.parentPath.get('tag').isIdentifier({ - name: ignoreIdentifier, - }))) { - const { splitQuote = true } = options - const array = splitCode(n.value.raw, { - splitQuote, - }) - for (const item of array) { - ctx.addPreserveClass(item) - } - - return + if (isIgnoredTaggedTemplate(p as NodePath)) { + collectPreserveClasses(p as NodePath, options) + } + }, + }, + }) + + traverse(ast, { + CallExpression: { + enter(p) { + const calleeName = getCallCalleeName(p as NodePath) ?? '' + if (ctx.isPreserveFunction(calleeName)) { + p.skip() + return + } + if (ctx.isClassFunction(calleeName)) { + for (const arg of p.get('arguments') as NodePath[]) { + handleClassExpression(arg, options, ms) + } + p.skip() + return + } + + handleSetAttributeCall(p as NodePath, options, ms) + handleClassListCall(p as NodePath, options, ms) + handleInsertAdjacentHtmlCall(p as NodePath, options, ms) + if (calleeName === 'eval') { + const firstArg = (p.get('arguments') as NodePath[])[0] + if (firstArg?.isStringLiteral()) { + const { code } = jsHandler(firstArg.node.value, options) + updateNodeValue(firstArg.node.value, code, firstArg.node, ms, 1, true) } } - handleValue(n.value.raw, n, options, ms, 0, false) + if (htmlCalleeNames.has(calleeName)) { + const firstArg = (p.get('arguments') as NodePath[])[0] + if (firstArg) { + handleHtmlExpression(firstArg, options, ms) + } + } }, }, - CallExpression: { + AssignmentExpression: { enter(p) { - const callee = p.get('callee') - if (callee.isIdentifier() && ctx.isPreserveFunction(callee.node.name)) { - p.traverse({ - StringLiteral: { - enter(path) { - const node = path.node - const value = node.value - const arr = sort(splitCode(value)).desc(x => x.length) - - for (const str of arr) { - if (ctx.replaceMap.has(str)) { - ctx.addPreserveClass(str) - } - } - }, - }, - TemplateElement: { - enter(path) { - const node = path.node - const value = node.value.raw - const arr = sort(splitCode(value)).desc(x => x.length) - - for (const str of arr) { - if (ctx.replaceMap.has(str)) { - ctx.addPreserveClass(str) - } - } - }, - }, - }) + const left = p.get('left') as NodePath + const right = p.get('right') as NodePath + if (left.isMemberExpression()) { + const propertyName = getMemberPropertyName(left) + if (isClassPropertyName(propertyName)) { + handleClassExpression(right, options, ms) + } + else if (isHtmlPropertyName(propertyName)) { + handleHtmlExpression(right, options, ms) + } + } + }, + }, + VariableDeclarator: { + enter(p) { + const id = p.get('id') as NodePath + const init = p.get('init') as NodePath + if (id.isIdentifier() && isClassPropertyName(id.node.name) && init.node) { + handleClassExpression(init, options, ms) + } + }, + }, + ObjectProperty: { + enter(p) { + const key = p.get('key') as NodePath + const value = p.get('value') as NodePath + if (isClassPropertyName(getPropertyName(key.node))) { + handleClassExpression(value, options, ms) + } + }, + }, + JSXAttribute: { + enter(p) { + if (!isClassPropertyName(getPropertyName(p.node.name))) { + return + } + const value = p.get('value') as NodePath + if (value.isStringLiteral()) { + handleStringLiteral(value, options, ms, 'class') + } + else if (value.isJSXExpressionContainer()) { + const expression = value.get('expression') as NodePath + handleClassExpression(expression as NodePath, options, ms) } }, }, diff --git a/packages/core/test/__snapshots__/js.test.ts.snap b/packages/core/test/__snapshots__/js.test.ts.snap index fae1c0ce..e6f8afe6 100644 --- a/packages/core/test/__snapshots__/js.test.ts.snap +++ b/packages/core/test/__snapshots__/js.test.ts.snap @@ -60,10 +60,10 @@ exports[`js handler > JSX/TSX support > should transform multiple className attr exports[`js handler > cn 1`] = ` "const bbb = cn( { - 'tw-c': true + 'p-3': true }, - 'tw-a', - ['tw-b', true && 'tw-d'] + 'p-1', + ['p-2', true && 'p-4'] )" `; @@ -76,7 +76,7 @@ document.documentElement.animate( }, { duration: 500, - easing: 'tw-a', + easing: 'ease-out', pseudoElement: '::view-transition-new(root)' } ) @@ -96,7 +96,7 @@ document.documentElement.animate( exports[`js handler > common StringLiteral 1`] = `"element.innerHTML = '
count is counter
'"`; -exports[`js handler > common StringLiteral with splitQuote false 1`] = `"element.innerHTML = '
count is counter
'"`; +exports[`js handler > common StringLiteral with splitQuote false 1`] = `"element.innerHTML = '
count is counter
'"`; exports[`js handler > common TemplateElement 1`] = `"const counter = 0;element.innerHTML = \`
count is \${counter}
\`"`; @@ -121,7 +121,7 @@ exports[`js handler > common ignore StringLiteral case 1 1`] = ` exports[`js handler > eval script case 1`] = ` "(function (__unused_webpack_module, __webpack_exports__, __webpack_require__) { "use strict"; - eval("__webpack_require__.r(__webpack_exports__);\\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\\n/* harmony export */ \\"render\\": function() { return /* binding */ render; }\\n/* harmony export */ });\\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \\"../../node_modules/.pnpm/vue@3.2.47/node_modules/vue/dist/vue.runtime.esm-bundler.js\\");\\n/* harmony import */ var _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./assets/logo.png */ \\"./src/assets/logo.png\\");\\n\\n\\nconst _hoisted_1 = {\\n class: \\"flex tw-a tw-b tw-c tw-d tw-e\\"\\n};\\nconst _hoisted_2 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"tw-f tw-g tw-h tw-c tw-d tw-i tw-j tw-k\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"p\\", {\\n class: \\"fixed tw-l tw-m flex tw-g tw-n tw-o tw-p tw-q tw-r tw-s tw-t tw-u tw-v tw-w tw-x tw-y tw-z tw-aa tw-ba tw-ca tw-da tw-ea\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(\\" Get started by editing  \\"), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"code\\", {\\n class: \\"tw-i tw-fa\\"\\n}, \\"pages/index.tsx\\")]), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"fixed tw-ga tw-l flex tw-ha tw-g tw-ia tw-n tw-ja tw-ka tw-la tw-ma tw-na tw-y tw-oa tw-z tw-pa\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"a\\", {\\n class: \\"tw-qa flex tw-ra tw-sa tw-ta tw-ua tw-va\\",\\n href: \\"https://vercel.com?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app\\",\\n target: \\"_blank\\",\\n rel: \\"noopener noreferrer\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(\\" By \\"), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"img\\", {\\n src: _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__,\\n alt: \\"Vercel Logo\\",\\n class: \\"tw-wa\\",\\n priority: \\"\\"\\n})])])], -1 /* HOISTED */);\\nconst _hoisted_3 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"relative flex tw-ra tw-xa tw-ya tw-za tw-ab tw-bb tw-cb tw-db tw-eb tw-fb tw-gb tw-hb tw-ib tw-jb tw-kb tw-lb tw-mb tw-nb tw-ob tw-pb tw-qb tw-rb tw-sb tw-tb tw-ub tw-vb tw-wb\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"img\\", {\\n class: \\"relative tw-xb tw-wa\\",\\n src: _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__,\\n alt: \\"Next.js Logo\\",\\n priority: \\"\\"\\n})], -1 /* HOISTED */);\\nconst _hoisted_4 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createStaticVNode)(\\"\\", 1);\\nconst _hoisted_5 = [_hoisted_2, _hoisted_3, _hoisted_4];\\nfunction render(_ctx, _cache) {\\n return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)(\\"main\\", _hoisted_1, _hoisted_5);\\n}//# sourceURL=[module]\\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi4vLi4vbm9kZV9tb2R1bGVzLy5wbnBtL2JhYmVsLWxvYWRlckA4LjMuMF9jM3Rmd3Y3cDM1Y2x3Y21rYjVmbmtzaHplaS9ub2RlX21vZHVsZXMvYmFiZWwtbG9hZGVyL2xpYi9pbmRleC5qcz8/Y2xvbmVkUnVsZVNldC00MC51c2VbMF0hLi4vLi4vbm9kZV9tb2R1bGVzLy5wbnBtL3Z1ZS1sb2FkZXJAMTcuMC4xX3Z1ZUAzLjIuNDcrd2VicGFja0A1Ljc5LjAvbm9kZV9tb2R1bGVzL3Z1ZS1sb2FkZXIvZGlzdC90ZW1wbGF0ZUxvYWRlci5qcz8/cnVsZVNldFsxXS5ydWxlc1s0XSEuLi8uLi9ub2RlX21vZHVsZXMvLnBucG0vdnVlLWxvYWRlckAxNy4wLjFfdnVlQDMuMi40Nyt3ZWJwYWNrQDUuNzkuMC9ub2RlX21vZHVsZXMvdnVlLWxvYWRlci9kaXN0L2luZGV4LmpzPz9ydWxlU2V0WzBdLnVzZVswXSEuL3NyYy9BcHAudnVlP3Z1ZSZ0eXBlPXRlbXBsYXRlJmlkPTdiYTViZDkwLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFjQTs7QUFiQTtBQUFBO0FBQ0E7QUFBQTtBQUFBO0FBRUE7QUFBQTtBQUVBO0FBQUE7QUFHQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQTtBQUFBOztBQUtBO0FBQ0E7QUFBQTtBQUNBO0FBQUE7QUFBQTtBQUNBOzs7QUFwQkE7O0FBREEiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrNS12dWUzLy4vc3JjL0FwcC52dWU/OTFhMCJdLCJzb3VyY2VzQ29udGVudCI6WyI8dGVtcGxhdGU+XG4gIDxtYWluIGNsYXNzPVwiZmxleCBtaW4taC1zY3JlZW4gZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktYmV0d2VlbiBwLTI0XCI+XG4gICAgPGRpdiBjbGFzcz1cInotMTAgdy1mdWxsIG1heC13LTV4bCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGZvbnQtbW9ubyB0ZXh0LXNtIGxnOmZsZXhcIj5cbiAgICAgIDxwXG4gICAgICAgIGNsYXNzPVwiZml4ZWQgbGVmdC0wIHRvcC0wIGZsZXggdy1mdWxsIGp1c3RpZnktY2VudGVyIGJvcmRlci1iIGJvcmRlci1ncmF5LTMwMCBiZy1ncmFkaWVudC10by1iIGZyb20temluYy0yMDAgcGItNiBwdC04IGJhY2tkcm9wLWJsdXItMnhsIGRhcms6Ym9yZGVyLW5ldXRyYWwtODAwIGRhcms6YmctemluYy04MDAvMzAgZGFyazpmcm9tLWluaGVyaXQgbGc6c3RhdGljIGxnOnctYXV0byAgbGc6cm91bmRlZC14bCBsZzpib3JkZXIgbGc6YmctZ3JheS0yMDAgbGc6cC00IGxnOmRhcms6YmctemluYy04MDAvMzBcIj5cbiAgICAgICAgR2V0IHN0YXJ0ZWQgYnkgZWRpdGluZyZuYnNwO1xuICAgICAgICA8Y29kZSBjbGFzcz1cImZvbnQtbW9ubyBmb250LWJvbGRcIj5wYWdlcy9pbmRleC50c3g8L2NvZGU+XG4gICAgICA8L3A+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiZml4ZWQgYm90dG9tLTAgbGVmdC0wIGZsZXggaC00OCB3LWZ1bGwgaXRlbXMtZW5kIGp1c3RpZnktY2VudGVyIGJnLWdyYWRpZW50LXRvLXQgZnJvbS13aGl0ZSB2aWEtd2hpdGUgZGFyazpmcm9tLWJsYWNrIGRhcms6dmlhLWJsYWNrIGxnOnN0YXRpYyBsZzpoLWF1dG8gbGc6dy1hdXRvIGxnOmJnLW5vbmVcIj5cbiAgICAgICAgPGEgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGZsZXggcGxhY2UtaXRlbXMtY2VudGVyIGdhcC0yIHAtOCBsZzpwb2ludGVyLWV2ZW50cy1hdXRvIGxnOnAtMFwiXG4gICAgICAgICAgaHJlZj1cImh0dHBzOi8vdmVyY2VsLmNvbT91dG1fc291cmNlPWNyZWF0ZS1uZXh0LWFwcCZ1dG1fbWVkaXVtPWRlZmF1bHQtdGVtcGxhdGUtdHcmdXRtX2NhbXBhaWduPWNyZWF0ZS1uZXh0LWFwcFwiXG4gICAgICAgICAgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXIgbm9yZWZlcnJlclwiPlxuICAgICAgICAgIEJ5XG4gICAgICAgICAgPGltZyBzcmM9XCIuL2Fzc2V0cy9sb2dvLnBuZ1wiIGFsdD1cIlZlcmNlbCBMb2dvXCIgY2xhc3M9XCJkYXJrOmludmVydFwiIHByaW9yaXR5IC8+XG4gICAgICAgIDwvYT5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdlxuICAgICAgY2xhc3M9XCJyZWxhdGl2ZSBmbGV4IHBsYWNlLWl0ZW1zLWNlbnRlciBiZWZvcmU6YWJzb2x1dGUgYmVmb3JlOmgtWzMwMHB4XSBiZWZvcmU6dy1bNDgwcHhdIGJlZm9yZTotdHJhbnNsYXRlLXgtMS8yIGJlZm9yZTpyb3VuZGVkLWZ1bGwgYmVmb3JlOmJnLWdyYWRpZW50LXJhZGlhbCBiZWZvcmU6ZnJvbS13aGl0ZSBiZWZvcmU6dG8tdHJhbnNwYXJlbnQgYmVmb3JlOmJsdXItMnhsIGJlZm9yZTpjb250ZW50LVsnJ10gYWZ0ZXI6YWJzb2x1dGUgYWZ0ZXI6LXotMjAgYWZ0ZXI6aC1bMTgwcHhdIGFmdGVyOnctWzI0MHB4XSBhZnRlcjp0cmFuc2xhdGUteC0xLzMgYWZ0ZXI6YmctZ3JhZGllbnQtY29uaWMgYWZ0ZXI6ZnJvbS1za3ktMjAwIGFmdGVyOnZpYS1ibHVlLTIwMCBhZnRlcjpibHVyLTJ4bCBhZnRlcjpjb250ZW50LVsnJ10gYmVmb3JlOmRhcms6YmctZ3JhZGllbnQtdG8tYnIgYmVmb3JlOmRhcms6ZnJvbS10cmFuc3BhcmVudCBiZWZvcmU6ZGFyazp0by1ibHVlLTcwMC8xMCBhZnRlcjpkYXJrOmZyb20tc2t5LTkwMCBhZnRlcjpkYXJrOnZpYS1bIzAxNDFmZl0vNDAgYmVmb3JlOmxnOmgtWzM2MHB4XVwiPlxuICAgICAgPGltZyBjbGFzcz1cInJlbGF0aXZlIGRhcms6ZHJvcC1zaGFkb3ctWzBfMF8wLjNyZW1fI2ZmZmZmZjcwXSBkYXJrOmludmVydFwiIHNyYz1cIi4vYXNzZXRzL2xvZ28ucG5nXCIgYWx0PVwiTmV4dC5qcyBMb2dvXCJcbiAgICAgICAgcHJpb3JpdHkgLz5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJtYi0zMiBncmlkIHRleHQtY2VudGVyIGxnOm1iLTAgbGc6Z3JpZC1jb2xzLTQgbGc6dGV4dC1sZWZ0XCI+XG4gICAgICA8YSBocmVmPVwiaHR0cHM6Ly9uZXh0anMub3JnL2RvY3M/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZFwiPlxuICAgICAgICAgIERvY3MgPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+RmluZCBpbi1kZXB0aCBpbmZvcm1hdGlvbiBhYm91dCBOZXh0LmpzXG4gICAgICAgICAgZmVhdHVyZXMgYW5kIEFQSS48L3A+XG4gICAgICA8L2E+XG5cbiAgICAgIDxhIGhyZWY9XCJodHRwczovL25leHRqcy5vcmcvbGVhcm4/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZCB0ZXh0LVtyZWRdXCI+XG4gICAgICAgICAgTGVhcm4gPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+TGVhcm4gYWJvdXQgTmV4dC5qcyBpbiBhbiBpbnRlcmFjdGl2ZVxuICAgICAgICAgIGNvdXJzZSB3aXRoJm5ic3A7cXVpenplcyE8L3A+XG4gICAgICA8L2E+XG5cbiAgICAgIDxhIGhyZWY9XCJodHRwczovL3ZlcmNlbC5jb20vdGVtcGxhdGVzP2ZyYW1ld29yaz1uZXh0LmpzJnV0bV9zb3VyY2U9Y3JlYXRlLW5leHQtYXBwJnV0bV9tZWRpdW09ZGVmYXVsdC10ZW1wbGF0ZS10dyZ1dG1fY2FtcGFpZ249Y3JlYXRlLW5leHQtYXBwXCJcbiAgICAgICAgY2xhc3M9XCJncm91cCByb3VuZGVkLWxnIGJvcmRlciBib3JkZXItdHJhbnNwYXJlbnQgcHgtNSBweS00IHRyYW5zaXRpb24tY29sb3JzIGhvdmVyOmJvcmRlci1ncmF5LTMwMCBob3ZlcjpiZy1ncmF5LTEwMCBob3ZlcjpkYXJrOmJvcmRlci1uZXV0cmFsLTcwMCBob3ZlcjpkYXJrOmJnLW5ldXRyYWwtODAwLzMwXCJcbiAgICAgICAgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXIgbm9yZWZlcnJlclwiPlxuICAgICAgICA8aDIgY2xhc3M9XCJtYi0zIHRleHQtMnhsIGZvbnQtc2VtaWJvbGRcIj5cbiAgICAgICAgICBUZW1wbGF0ZXMgPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+RGlzY292ZXIgYW5kIGRlcGxveSBib2lsZXJwbGF0ZSBleGFtcGxlXG4gICAgICAgICAgTmV4dC5qcyZuYnNwO3Byb2plY3RzLjwvcD5cbiAgICAgIDwvYT5cblxuICAgICAgPGEgaHJlZj1cImh0dHBzOi8vdmVyY2VsLmNvbS9uZXc/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZFwiPlxuICAgICAgICAgIERlcGxveSA8c3BhblxuICAgICAgICAgICAgY2xhc3M9XCJpbmxpbmUtYmxvY2sgdHJhbnNpdGlvbi10cmFuc2Zvcm0gZ3JvdXAtaG92ZXI6dHJhbnNsYXRlLXgtMSBtb3Rpb24tcmVkdWNlOnRyYW5zZm9ybS1ub25lXCI+LSZndDs8L3NwYW4+XG4gICAgICAgIDwvaDI+XG4gICAgICAgIDxwIGNsYXNzPVwibS0wIG1heC13LVszMGNoXSB0ZXh0LXNtIG9wYWNpdHktNTBcIj5JbnN0YW50bHkgZGVwbG95IHlvdXIgTmV4dC5qcyBzaXRlIHRvIGFcbiAgICAgICAgICBzaGFyZWFibGUgVVJMIHdpdGggVmVyY2VsLjwvcD5cbiAgICAgIDwvYT5cbiAgICA8L2Rpdj5cbiAgPC9tYWluPlxuPC90ZW1wbGF0ZT5cblxuPHN0eWxlIGxhbmc9XCJzY3NzXCI+XG5AdGFpbHdpbmQgYmFzZTtcbkB0YWlsd2luZCBjb21wb25lbnRzO1xuQHRhaWx3aW5kIHV0aWxpdGllcztcblxuI2FwcCB7XG4gIGZvbnQtZmFtaWx5OiBBdmVuaXIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gIGNvbG9yOiAjMmMzZTUwO1xufVxuXG5uYXYge1xuICBwYWRkaW5nOiAzMHB4O1xuXG4gIGEge1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIGNvbG9yOiAjMmMzZTUwO1xuXG4gICAgJi5yb3V0ZXItbGluay1leGFjdC1hY3RpdmUge1xuICAgICAgY29sb3I6ICM0MmI5ODM7XG4gICAgfVxuICB9XG59XG48L3N0eWxlPlxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\\n//# sourceURL=webpack-internal:///../../node_modules/.pnpm/babel-loader@8.3.0_c3tfwv7p35clwcmkb5fnkshzei/node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!../../node_modules/.pnpm/vue-loader@17.0.1_vue@3.2.47+webpack@5.79.0/node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[4]!../../node_modules/.pnpm/vue-loader@17.0.1_vue@3.2.47+webpack@5.79.0/node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90\\n"); + eval("__webpack_require__.r(__webpack_exports__);\\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\\n/* harmony export */ \\"render\\": function() { return /* binding */ render; }\\n/* harmony export */ });\\n/* harmony import */ var vue__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! vue */ \\"../../node_modules/.pnpm/vue@3.2.47/node_modules/vue/dist/vue.runtime.esm-bundler.js\\");\\n/* harmony import */ var _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./assets/logo.png */ \\"./src/assets/logo.png\\");\\n\\n\\nconst _hoisted_1 = {\\n class: \\"flex tw-a tw-b tw-c tw-d tw-e\\"\\n};\\nconst _hoisted_2 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"tw-f tw-g tw-h tw-c tw-d tw-i tw-j tw-k\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"p\\", {\\n class: \\"fixed tw-l tw-m flex tw-g tw-n tw-o tw-p tw-q tw-r tw-s tw-t tw-u tw-v tw-w tw-x tw-y tw-z tw-aa tw-ba tw-ca tw-da tw-ea\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(\\" Get started by editing  \\"), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"code\\", {\\n class: \\"tw-i tw-fa\\"\\n}, \\"pages/index.tsx\\")]), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"fixed tw-ga tw-l flex tw-ha tw-g tw-ia tw-n tw-ja tw-ka tw-la tw-ma tw-na tw-y tw-oa tw-z tw-pa\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"a\\", {\\n class: \\"tw-qa flex tw-ra tw-sa tw-ta tw-ua tw-va\\",\\n href: \\"https://vercel.com?utm_source=create-next-app&utm_medium=default-template-tw&utm_campaign=create-next-app\\",\\n target: \\"_blank\\",\\n rel: \\"noopener noreferrer\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createTextVNode)(\\" By \\"), /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"img\\", {\\n src: _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__,\\n alt: \\"Vercel Logo\\",\\n class: \\"tw-wa\\",\\n priority: \\"\\"\\n})])])], -1 /* HOISTED */);\\nconst _hoisted_3 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"div\\", {\\n class: \\"relative flex tw-ra tw-xa tw-ya tw-za tw-ab tw-bb tw-cb tw-db tw-eb tw-fb tw-gb tw-hb tw-ib tw-jb tw-kb tw-lb tw-mb tw-nb tw-ob tw-pb tw-qb tw-rb tw-sb tw-tb tw-ub tw-vb tw-wb\\"\\n}, [/*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementVNode)(\\"img\\", {\\n class: \\"relative tw-xb tw-wa\\",\\n src: _assets_logo_png__WEBPACK_IMPORTED_MODULE_1__,\\n alt: \\"Next.js Logo\\",\\n priority: \\"\\"\\n})], -1 /* HOISTED */);\\nconst _hoisted_4 = /*#__PURE__*/(0,vue__WEBPACK_IMPORTED_MODULE_0__.createStaticVNode)(\\"\\", 1);\\nconst _hoisted_5 = [_hoisted_2, _hoisted_3, _hoisted_4];\\nfunction render(_ctx, _cache) {\\n return (0,vue__WEBPACK_IMPORTED_MODULE_0__.openBlock)(), (0,vue__WEBPACK_IMPORTED_MODULE_0__.createElementBlock)(\\"main\\", _hoisted_1, _hoisted_5);\\n}//# sourceURL=[module]\\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi4vLi4vbm9kZV9tb2R1bGVzLy5wbnBtL2JhYmVsLWxvYWRlckA4LjMuMF9jM3Rmd3Y3cDM1Y2x3Y21rYjVmbmtzaHplaS9ub2RlX21vZHVsZXMvYmFiZWwtbG9hZGVyL2xpYi9pbmRleC5qcz8/Y2xvbmVkUnVsZVNldC00MC51c2VbMF0hLi4vLi4vbm9kZV9tb2R1bGVzLy5wbnBtL3Z1ZS1sb2FkZXJAMTcuMC4xX3Z1ZUAzLjIuNDcrd2VicGFja0A1Ljc5LjAvbm9kZV9tb2R1bGVzL3Z1ZS1sb2FkZXIvZGlzdC90ZW1wbGF0ZUxvYWRlci5qcz8/cnVsZVNldFsxXS5ydWxlc1s0XSEuLi8uLi9ub2RlX21vZHVsZXMvLnBucG0vdnVlLWxvYWRlckAxNy4wLjFfdnVlQDMuMi40Nyt3ZWJwYWNrQDUuNzkuMC9ub2RlX21vZHVsZXMvdnVlLWxvYWRlci9kaXN0L2luZGV4LmpzPz9ydWxlU2V0WzBdLnVzZVswXSEuL3NyYy9BcHAudnVlP3Z1ZSZ0eXBlPXRlbXBsYXRlJmlkPTdiYTViZDkwLmpzIiwibWFwcGluZ3MiOiI7Ozs7Ozs7QUFjQTs7QUFiQTtBQUFBO0FBQ0E7QUFBQTtBQUFBO0FBRUE7QUFBQTtBQUVBO0FBQUE7QUFHQTtBQUFBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFBQTtBQUVBO0FBQUE7QUFBQTtBQUFBOztBQUtBO0FBQ0E7QUFBQTtBQUNBO0FBQUE7QUFBQTtBQUNBOzs7QUFwQkE7O0FBREEiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly93ZWJwYWNrNS12dWUzLy4vc3JjL0FwcC52dWU/OTFhMCJdLCJzb3VyY2VzQ29udGVudCI6WyI8dGVtcGxhdGU+XG4gIDxtYWluIGNsYXNzPVwiZmxleCBtaW4taC1zY3JlZW4gZmxleC1jb2wgaXRlbXMtY2VudGVyIGp1c3RpZnktYmV0d2VlbiBwLTI0XCI+XG4gICAgPGRpdiBjbGFzcz1cInotMTAgdy1mdWxsIG1heC13LTV4bCBpdGVtcy1jZW50ZXIganVzdGlmeS1iZXR3ZWVuIGZvbnQtbW9ubyB0ZXh0LXNtIGxnOmZsZXhcIj5cbiAgICAgIDxwXG4gICAgICAgIGNsYXNzPVwiZml4ZWQgbGVmdC0wIHRvcC0wIGZsZXggdy1mdWxsIGp1c3RpZnktY2VudGVyIGJvcmRlci1iIGJvcmRlci1ncmF5LTMwMCBiZy1ncmFkaWVudC10by1iIGZyb20temluYy0yMDAgcGItNiBwdC04IGJhY2tkcm9wLWJsdXItMnhsIGRhcms6Ym9yZGVyLW5ldXRyYWwtODAwIGRhcms6YmctemluYy04MDAvMzAgZGFyazpmcm9tLWluaGVyaXQgbGc6c3RhdGljIGxnOnctYXV0byAgbGc6cm91bmRlZC14bCBsZzpib3JkZXIgbGc6YmctZ3JheS0yMDAgbGc6cC00IGxnOmRhcms6YmctemluYy04MDAvMzBcIj5cbiAgICAgICAgR2V0IHN0YXJ0ZWQgYnkgZWRpdGluZyZuYnNwO1xuICAgICAgICA8Y29kZSBjbGFzcz1cImZvbnQtbW9ubyBmb250LWJvbGRcIj5wYWdlcy9pbmRleC50c3g8L2NvZGU+XG4gICAgICA8L3A+XG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiZml4ZWQgYm90dG9tLTAgbGVmdC0wIGZsZXggaC00OCB3LWZ1bGwgaXRlbXMtZW5kIGp1c3RpZnktY2VudGVyIGJnLWdyYWRpZW50LXRvLXQgZnJvbS13aGl0ZSB2aWEtd2hpdGUgZGFyazpmcm9tLWJsYWNrIGRhcms6dmlhLWJsYWNrIGxnOnN0YXRpYyBsZzpoLWF1dG8gbGc6dy1hdXRvIGxnOmJnLW5vbmVcIj5cbiAgICAgICAgPGEgY2xhc3M9XCJwb2ludGVyLWV2ZW50cy1ub25lIGZsZXggcGxhY2UtaXRlbXMtY2VudGVyIGdhcC0yIHAtOCBsZzpwb2ludGVyLWV2ZW50cy1hdXRvIGxnOnAtMFwiXG4gICAgICAgICAgaHJlZj1cImh0dHBzOi8vdmVyY2VsLmNvbT91dG1fc291cmNlPWNyZWF0ZS1uZXh0LWFwcCZ1dG1fbWVkaXVtPWRlZmF1bHQtdGVtcGxhdGUtdHcmdXRtX2NhbXBhaWduPWNyZWF0ZS1uZXh0LWFwcFwiXG4gICAgICAgICAgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXIgbm9yZWZlcnJlclwiPlxuICAgICAgICAgIEJ5XG4gICAgICAgICAgPGltZyBzcmM9XCIuL2Fzc2V0cy9sb2dvLnBuZ1wiIGFsdD1cIlZlcmNlbCBMb2dvXCIgY2xhc3M9XCJkYXJrOmludmVydFwiIHByaW9yaXR5IC8+XG4gICAgICAgIDwvYT5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuXG4gICAgPGRpdlxuICAgICAgY2xhc3M9XCJyZWxhdGl2ZSBmbGV4IHBsYWNlLWl0ZW1zLWNlbnRlciBiZWZvcmU6YWJzb2x1dGUgYmVmb3JlOmgtWzMwMHB4XSBiZWZvcmU6dy1bNDgwcHhdIGJlZm9yZTotdHJhbnNsYXRlLXgtMS8yIGJlZm9yZTpyb3VuZGVkLWZ1bGwgYmVmb3JlOmJnLWdyYWRpZW50LXJhZGlhbCBiZWZvcmU6ZnJvbS13aGl0ZSBiZWZvcmU6dG8tdHJhbnNwYXJlbnQgYmVmb3JlOmJsdXItMnhsIGJlZm9yZTpjb250ZW50LVsnJ10gYWZ0ZXI6YWJzb2x1dGUgYWZ0ZXI6LXotMjAgYWZ0ZXI6aC1bMTgwcHhdIGFmdGVyOnctWzI0MHB4XSBhZnRlcjp0cmFuc2xhdGUteC0xLzMgYWZ0ZXI6YmctZ3JhZGllbnQtY29uaWMgYWZ0ZXI6ZnJvbS1za3ktMjAwIGFmdGVyOnZpYS1ibHVlLTIwMCBhZnRlcjpibHVyLTJ4bCBhZnRlcjpjb250ZW50LVsnJ10gYmVmb3JlOmRhcms6YmctZ3JhZGllbnQtdG8tYnIgYmVmb3JlOmRhcms6ZnJvbS10cmFuc3BhcmVudCBiZWZvcmU6ZGFyazp0by1ibHVlLTcwMC8xMCBhZnRlcjpkYXJrOmZyb20tc2t5LTkwMCBhZnRlcjpkYXJrOnZpYS1bIzAxNDFmZl0vNDAgYmVmb3JlOmxnOmgtWzM2MHB4XVwiPlxuICAgICAgPGltZyBjbGFzcz1cInJlbGF0aXZlIGRhcms6ZHJvcC1zaGFkb3ctWzBfMF8wLjNyZW1fI2ZmZmZmZjcwXSBkYXJrOmludmVydFwiIHNyYz1cIi4vYXNzZXRzL2xvZ28ucG5nXCIgYWx0PVwiTmV4dC5qcyBMb2dvXCJcbiAgICAgICAgcHJpb3JpdHkgLz5cbiAgICA8L2Rpdj5cblxuICAgIDxkaXYgY2xhc3M9XCJtYi0zMiBncmlkIHRleHQtY2VudGVyIGxnOm1iLTAgbGc6Z3JpZC1jb2xzLTQgbGc6dGV4dC1sZWZ0XCI+XG4gICAgICA8YSBocmVmPVwiaHR0cHM6Ly9uZXh0anMub3JnL2RvY3M/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZFwiPlxuICAgICAgICAgIERvY3MgPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+RmluZCBpbi1kZXB0aCBpbmZvcm1hdGlvbiBhYm91dCBOZXh0LmpzXG4gICAgICAgICAgZmVhdHVyZXMgYW5kIEFQSS48L3A+XG4gICAgICA8L2E+XG5cbiAgICAgIDxhIGhyZWY9XCJodHRwczovL25leHRqcy5vcmcvbGVhcm4/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZCB0ZXh0LVtyZWRdXCI+XG4gICAgICAgICAgTGVhcm4gPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+TGVhcm4gYWJvdXQgTmV4dC5qcyBpbiBhbiBpbnRlcmFjdGl2ZVxuICAgICAgICAgIGNvdXJzZSB3aXRoJm5ic3A7cXVpenplcyE8L3A+XG4gICAgICA8L2E+XG5cbiAgICAgIDxhIGhyZWY9XCJodHRwczovL3ZlcmNlbC5jb20vdGVtcGxhdGVzP2ZyYW1ld29yaz1uZXh0LmpzJnV0bV9zb3VyY2U9Y3JlYXRlLW5leHQtYXBwJnV0bV9tZWRpdW09ZGVmYXVsdC10ZW1wbGF0ZS10dyZ1dG1fY2FtcGFpZ249Y3JlYXRlLW5leHQtYXBwXCJcbiAgICAgICAgY2xhc3M9XCJncm91cCByb3VuZGVkLWxnIGJvcmRlciBib3JkZXItdHJhbnNwYXJlbnQgcHgtNSBweS00IHRyYW5zaXRpb24tY29sb3JzIGhvdmVyOmJvcmRlci1ncmF5LTMwMCBob3ZlcjpiZy1ncmF5LTEwMCBob3ZlcjpkYXJrOmJvcmRlci1uZXV0cmFsLTcwMCBob3ZlcjpkYXJrOmJnLW5ldXRyYWwtODAwLzMwXCJcbiAgICAgICAgdGFyZ2V0PVwiX2JsYW5rXCIgcmVsPVwibm9vcGVuZXIgbm9yZWZlcnJlclwiPlxuICAgICAgICA8aDIgY2xhc3M9XCJtYi0zIHRleHQtMnhsIGZvbnQtc2VtaWJvbGRcIj5cbiAgICAgICAgICBUZW1wbGF0ZXMgPHNwYW5cbiAgICAgICAgICAgIGNsYXNzPVwiaW5saW5lLWJsb2NrIHRyYW5zaXRpb24tdHJhbnNmb3JtIGdyb3VwLWhvdmVyOnRyYW5zbGF0ZS14LTEgbW90aW9uLXJlZHVjZTp0cmFuc2Zvcm0tbm9uZVwiPi0mZ3Q7PC9zcGFuPlxuICAgICAgICA8L2gyPlxuICAgICAgICA8cCBjbGFzcz1cIm0tMCBtYXgtdy1bMzBjaF0gdGV4dC1zbSBvcGFjaXR5LTUwXCI+RGlzY292ZXIgYW5kIGRlcGxveSBib2lsZXJwbGF0ZSBleGFtcGxlXG4gICAgICAgICAgTmV4dC5qcyZuYnNwO3Byb2plY3RzLjwvcD5cbiAgICAgIDwvYT5cblxuICAgICAgPGEgaHJlZj1cImh0dHBzOi8vdmVyY2VsLmNvbS9uZXc/dXRtX3NvdXJjZT1jcmVhdGUtbmV4dC1hcHAmdXRtX21lZGl1bT1kZWZhdWx0LXRlbXBsYXRlLXR3JnV0bV9jYW1wYWlnbj1jcmVhdGUtbmV4dC1hcHBcIlxuICAgICAgICBjbGFzcz1cImdyb3VwIHJvdW5kZWQtbGcgYm9yZGVyIGJvcmRlci10cmFuc3BhcmVudCBweC01IHB5LTQgdHJhbnNpdGlvbi1jb2xvcnMgaG92ZXI6Ym9yZGVyLWdyYXktMzAwIGhvdmVyOmJnLWdyYXktMTAwIGhvdmVyOmRhcms6Ym9yZGVyLW5ldXRyYWwtNzAwIGhvdmVyOmRhcms6YmctbmV1dHJhbC04MDAvMzBcIlxuICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIiByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCI+XG4gICAgICAgIDxoMiBjbGFzcz1cIm1iLTMgdGV4dC0yeGwgZm9udC1zZW1pYm9sZFwiPlxuICAgICAgICAgIERlcGxveSA8c3BhblxuICAgICAgICAgICAgY2xhc3M9XCJpbmxpbmUtYmxvY2sgdHJhbnNpdGlvbi10cmFuc2Zvcm0gZ3JvdXAtaG92ZXI6dHJhbnNsYXRlLXgtMSBtb3Rpb24tcmVkdWNlOnRyYW5zZm9ybS1ub25lXCI+LSZndDs8L3NwYW4+XG4gICAgICAgIDwvaDI+XG4gICAgICAgIDxwIGNsYXNzPVwibS0wIG1heC13LVszMGNoXSB0ZXh0LXNtIG9wYWNpdHktNTBcIj5JbnN0YW50bHkgZGVwbG95IHlvdXIgTmV4dC5qcyBzaXRlIHRvIGFcbiAgICAgICAgICBzaGFyZWFibGUgVVJMIHdpdGggVmVyY2VsLjwvcD5cbiAgICAgIDwvYT5cbiAgICA8L2Rpdj5cbiAgPC9tYWluPlxuPC90ZW1wbGF0ZT5cblxuPHN0eWxlIGxhbmc9XCJzY3NzXCI+XG5AdGFpbHdpbmQgYmFzZTtcbkB0YWlsd2luZCBjb21wb25lbnRzO1xuQHRhaWx3aW5kIHV0aWxpdGllcztcblxuI2FwcCB7XG4gIGZvbnQtZmFtaWx5OiBBdmVuaXIsIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG4gIGNvbG9yOiAjMmMzZTUwO1xufVxuXG5uYXYge1xuICBwYWRkaW5nOiAzMHB4O1xuXG4gIGEge1xuICAgIGZvbnQtd2VpZ2h0OiBib2xkO1xuICAgIGNvbG9yOiAjMmMzZTUwO1xuXG4gICAgJi5yb3V0ZXItbGluay1leGFjdC1hY3RpdmUge1xuICAgICAgY29sb3I6ICM0MmI5ODM7XG4gICAgfVxuICB9XG59XG48L3N0eWxlPlxuIl0sIm5hbWVzIjpbXSwic291cmNlUm9vdCI6IiJ9\\n//# sourceURL=webpack-internal:///../../node_modules/.pnpm/babel-loader@8.3.0_c3tfwv7p35clwcmkb5fnkshzei/node_modules/babel-loader/lib/index.js??clonedRuleSet-40.use[0]!../../node_modules/.pnpm/vue-loader@17.0.1_vue@3.2.47+webpack@5.79.0/node_modules/vue-loader/dist/templateLoader.js??ruleSet[1].rules[4]!../../node_modules/.pnpm/vue-loader@17.0.1_vue@3.2.47+webpack@5.79.0/node_modules/vue-loader/dist/index.js??ruleSet[0].use[0]!./src/App.vue?vue&type=template&id=7ba5bd90\\n"); })" `; @@ -134,7 +134,7 @@ document.documentElement.animate( }, { duration: 500, - easing: 'tw-a', + easing: 'ease-out', pseudoElement: '::view-transition-new(root)' } ) @@ -161,7 +161,7 @@ document.documentElement.animate( }, { duration: 500, - easing: 'tw-a', + easing: 'ease-out', pseudoElement: '::view-transition-new(root)' } ) @@ -466,11 +466,11 @@ export function cn(...inputs) { return twMerge(clsx(inputs)); } -cn('w-10 h-10 tw-c and tw-a') +cn('w-10 h-10 bg-red-500 and bg-red-500/50') -cn(\`tw-e tw-f bg-red-600 and bg-red-600/50\`) +cn(\`w-2 h-2 bg-red-600 and bg-red-600/50\`) -twMerge('tw-g tw-h tw-d and tw-b')" +twMerge('w-1 h-1 bg-red-400 and bg-red-400/50')" `; exports[`js handler > preserve-fn-case0.js case 1 2`] = ` @@ -498,7 +498,7 @@ cn('w-10 h-10 tw-c and tw-a') cn(\`tw-e tw-f bg-red-600 and bg-red-600/50\`) -twMerge('tw-g tw-h tw-d and tw-b')" +twMerge('w-1 h-1 bg-red-400 and bg-red-400/50')" `; exports[`js handler > preserve-fn-case0.js case 2 2`] = ` @@ -522,11 +522,11 @@ export function cn(...inputs) { return twMerge(clsx(inputs)); } -cn('w-10 h-10 tw-c and tw-a') +cn('w-10 h-10 bg-red-500 and bg-red-500/50') -cn(\`tw-e tw-f bg-red-600 and bg-red-600/50\`) +cn(\`w-2 h-2 bg-red-600 and bg-red-600/50\`) -twMerge('tw-g tw-h tw-d and tw-b')" +twMerge('w-1 h-1 bg-red-400 and bg-red-400/50')" `; exports[`js handler > preserve-fn-case0.js case 3 2`] = ` @@ -557,6 +557,6 @@ exports[`js handler > trailing slash case 2 1`] = ` " `; -exports[`js handler > z-10 not transform 1`] = `"{ className: "tw-a tw-b tw-c tw-d tw-e tw-f tw-g tw-h" }"`; +exports[`js handler > z-10 not transform 1`] = `"{ className: "z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex" }"`; -exports[`js handler > z-10 not transform with splitQuote false 1`] = `"{ className: "tw-a tw-b tw-c tw-d tw-e tw-f tw-g tw-h" }"`; +exports[`js handler > z-10 not transform with splitQuote false 1`] = `"{ className: "z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex" }"`; diff --git a/packages/core/test/__snapshots__/vue.test.ts.snap b/packages/core/test/__snapshots__/vue.test.ts.snap index f60768b7..ec94237b 100644 --- a/packages/core/test/__snapshots__/vue.test.ts.snap +++ b/packages/core/test/__snapshots__/vue.test.ts.snap @@ -27,7 +27,7 @@ exports[`vue handler > should handle dynamic :class binding 1`] = ` exports[`vue handler > should preserve twMerge classes 1`] = ` "" `; diff --git a/packages/core/test/js.test.ts b/packages/core/test/js.test.ts index 101514a0..9abd9329 100644 --- a/packages/core/test/js.test.ts +++ b/packages/core/test/js.test.ts @@ -157,7 +157,7 @@ describe('js handler', async () => { }).code expect(code).toContain(`'use server'`) - expect(code).not.toContain('return "server"') + expect(code).toContain('return "server"') }) it('nextjs server side mangle', () => { @@ -300,7 +300,7 @@ describe('js handler', async () => { ctx, id: 'xxx', }) - expect(code).toBe('const LINEFEED = `tw-a${n}tw-a`;') + expect(code).toBe(testCase) }) it('preProcessJs MagicString TemplateElement case', () => { @@ -312,7 +312,147 @@ describe('js handler', async () => { ctx, id: 'xxx', }) - expect(code).toBe('const LINEFEED = `tw-a${n}tw-b`;') + expect(code).toBe('const LINEFEED = `bg-red-500/50${n}bg-red-500`;') + }) + + describe('precise class contexts', () => { + beforeEach(() => { + ctx = new Context() + ctx.replaceMap.set('bg-red-500', '1') + ctx.replaceMap.set('hover:bg-red-600', '1') + ctx.replaceMap.set('text-white', '1') + ctx.replaceMap.set('text-red-500', '1') + }) + + it('does not transform arbitrary business strings', () => { + const testCase = ` + console.log('bg-red-500 hover:bg-red-600') + const apiPath = '/api/bg-red-500' + const status = 'text-red-500' + const obj = { status: 'text-white' } + ` + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).toBe(testCase) + }) + + it('transforms explicit className and class assignment contexts', () => { + const testCase = ` + const className = 'bg-red-500 text-white' + element.className = 'hover:bg-red-600' + const vnode = { class: 'text-red-500' } + ` + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain('bg-red-500 text-white') + expect(code).not.toContain('hover:bg-red-600') + expect(code).not.toContain(`class: 'text-red-500'`) + }) + + it('transforms DOM class APIs', () => { + const testCase = ` + el.setAttribute('class', 'bg-red-500 text-white') + el.classList.add('hover:bg-red-600') + ` + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain('bg-red-500 text-white') + expect(code).not.toContain('hover:bg-red-600') + }) + + it('only transforms class attributes inside html strings', () => { + const testCase = `element.innerHTML = '
x
'` + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain('class=\\"bg-red-500 text-white\\"') + expect(code).toContain('data-id=\\"bg-red-500\\"') + }) + + it('keeps split template html class attributes precise', () => { + const testCase = 'element.innerHTML = `
`' + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain(' text-white') + expect(code).toContain('data-id="hover:bg-red-600"') + }) + + it('transforms default class helper function arguments', async () => { + const testCase = ` + const button = cva('bg-red-500 text-white', { + variants: { + intent: { + danger: 'text-red-500 hover:bg-red-600', + }, + }, + }) + const card = tv({ + base: 'bg-red-500 text-white', + variants: { + active: { + true: 'hover:bg-red-600', + }, + }, + }) + const joined = twJoin('bg-red-500', condition && 'text-white') + ` + await ctx.initConfig({ + classList: ['bg-red-500', 'hover:bg-red-600', 'text-white', 'text-red-500'], + }) + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain('bg-red-500 text-white') + expect(code).not.toContain('text-red-500 hover:bg-red-600') + expect(code).not.toContain(`'hover:bg-red-600'`) + }) + + it('preserves twMerge arguments by default', async () => { + const testCase = `const className = twMerge('bg-red-500 text-white', active && 'hover:bg-red-600')` + await ctx.initConfig({ + classList: ['bg-red-500', 'hover:bg-red-600', 'text-white'], + }) + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).toBe(testCase) + expect(ctx.preserveClassNamesSet.has('bg-red-500')).toBe(true) + expect(ctx.preserveClassNamesSet.has('hover:bg-red-600')).toBe(true) + }) + + it('supports user configured class helper names', async () => { + const testCase = `const className = myCva('bg-red-500 text-white')` + await ctx.initConfig({ + classList: ['bg-red-500', 'text-white'], + transformerOptions: { + classFunctions: ['myCva'], + }, + }) + const { code } = jsHandler(testCase, { + ctx, + id: 'precision.js', + }) + + expect(code).not.toContain('bg-red-500 text-white') + }) }) it('preserve-fn-case0.js case 0', async () => { @@ -351,7 +491,7 @@ describe('js handler', async () => { }) expect(code).toMatchSnapshot() - expect(ctx.preserveClassNamesSet.size).toBe(4) + expect(ctx.preserveClassNamesSet.size).toBe(8) expect(replaceMap).toMatchSnapshot() // expect( // [...ctx.getReplaceMap().entries()].reduce>((acc, cur) => {