Skip to content

Commit

Permalink
feat(setup-block): init
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Nov 22, 2022
1 parent 692f374 commit 66145a7
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/metal-keys-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@vue-macros/setup-block': patch
---

support `<setup>` block for shortcut of `<script setup lang="ts">`
1 change: 1 addition & 0 deletions packages/macros/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"@vue-macros/define-slots": "workspace:*",
"@vue-macros/hoist-static": "workspace:*",
"@vue-macros/named-template": "workspace:*",
"@vue-macros/setup-block": "workspace:*",
"@vue-macros/setup-component": "workspace:*",
"@vue-macros/setup-sfc": "workspace:*",
"@vue-macros/short-emits": "workspace:*",
Expand Down
6 changes: 6 additions & 0 deletions packages/macros/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import VueDefineRender from '@vue-macros/define-render'
import VueDefineSlots from '@vue-macros/define-slots'
import VueHoistStatic from '@vue-macros/hoist-static'
import VueNamedTemplate from '@vue-macros/named-template'
import VueSetupBlock from '@vue-macros/setup-block'
import VueSetupComponent from '@vue-macros/setup-component'
import VueSetupSFC from '@vue-macros/setup-sfc'
import VueShortEmits from '@vue-macros/short-emits'
Expand All @@ -26,6 +27,7 @@ import type { Options as OptionsDefineRender } from '@vue-macros/define-render'
import type { Options as OptionsDefineSlots } from '@vue-macros/define-slots'
import type { Options as OptionsHoistStatic } from '@vue-macros/hoist-static'
import type { Options as OptionsNamedTemplate } from '@vue-macros/named-template'
import type { Options as OptionsSetupBlock } from '@vue-macros/setup-block'
import type { Options as OptionsSetupComponent } from '@vue-macros/setup-component'
import type { Options as OptionsSetupSFC } from '@vue-macros/setup-sfc'
import type { Options as OptionsShortEmits } from '@vue-macros/short-emits'
Expand All @@ -39,6 +41,7 @@ export interface FeatureOptionsMap {
defineSlots: OptionsDefineSlots
hoistStatic: OptionsHoistStatic
namedTemplate: OptionsNamedTemplate
setupBlock: OptionsSetupBlock
setupComponent: OptionsSetupComponent
setupSFC: OptionsSetupSFC
shortEmits: OptionsShortEmits
Expand Down Expand Up @@ -79,6 +82,7 @@ function resolveOptions({
defineSlots,
hoistStatic,
namedTemplate,
setupBlock,
setupComponent,
setupSFC,
shortEmits,
Expand Down Expand Up @@ -112,6 +116,7 @@ function resolveOptions({
defineSlots: resolveSubOptions<'defineSlots'>(defineSlots),
hoistStatic: resolveSubOptions<'hoistStatic'>(hoistStatic),
namedTemplate: resolveSubOptions<'namedTemplate'>(namedTemplate),
setupBlock: resolveSubOptions<'setupBlock'>(setupBlock),
setupComponent: resolveSubOptions<'setupComponent'>(setupComponent, {
root,
}),
Expand Down Expand Up @@ -149,6 +154,7 @@ export default createCombinePlugin((userOptions: Options = {}) => {
const plugins: OptionsPlugin[] = [
resolvePlugin(options.setupSFC, VueSetupSFC),
resolvePlugin(options.setupComponent, VueSetupComponent, 0),
resolvePlugin(options.setupBlock, VueSetupBlock),
resolvePlugin(options.hoistStatic, VueHoistStatic),
resolvePlugin(options.namedTemplate, VueNamedTemplate, 0),
resolvePlugin(options.defineProps, VueDefineProps),
Expand Down
3 changes: 3 additions & 0 deletions packages/setup-block/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @vue-macros/setup-block [![npm](https://img.shields.io/npm/v/@vue-macros/setup-block.svg)](https://npmjs.com/package/@vue-macros/setup-block)

Please refer to [README.md](https://github.com/sxzz/unplugin-vue-macros#readme)
75 changes: 75 additions & 0 deletions packages/setup-block/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"name": "@vue-macros/setup-block",
"version": "0.0.0",
"packageManager": "pnpm@7.17.0",
"description": "",
"keywords": [
"unplugin",
"vue",
"sfc",
"setup",
"macros",
"script-setup",
"setup-block"
],
"license": "MIT",
"homepage": "https://github.com/sxzz/unplugin-vue-macros#readme",
"bugs": {
"url": "https://github.com/sxzz/unplugin-vue-macros/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sxzz/unplugin-vue-macros.git"
},
"author": "三咲智子 <sxzz@sxzz.moe>",
"files": [
"dist"
],
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "index.d.ts",
"exports": {
".": {
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
"./vite": {
"require": "./dist/vite.js",
"import": "./dist/vite.mjs"
},
"./webpack": {
"require": "./dist/webpack.js",
"import": "./dist/webpack.mjs"
},
"./rollup": {
"require": "./dist/rollup.js",
"import": "./dist/rollup.mjs"
},
"./esbuild": {
"require": "./dist/esbuild.js",
"import": "./dist/esbuild.mjs"
},
"./*": "./*"
},
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./*"
]
}
},
"scripts": {
"build": "tsup && tsx ../../scripts/postbuild.mts",
"dev": "DEV=1 tsup"
},
"dependencies": {
"@rollup/pluginutils": "^4.2.1",
"@vue-macros/common": "workspace:~",
"@vue/compiler-dom": "^3.2.45",
"unplugin": "^1.0.0"
},
"engines": {
"node": ">=14.19.0"
}
}
59 changes: 59 additions & 0 deletions packages/setup-block/src/core/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { MagicString, getTransformResult } from '@vue-macros/common'
import { parse } from '@vue/compiler-dom'

export const transformSetupBlock = (
code: string,
id: string,
lang?: string
) => {
const s = new MagicString(code)

const node = parse(code, {
// there are no components at SFC parsing level
isNativeTag: () => true,
// preserve all whitespaces
isPreTag: () => true,
getTextMode: ({ tag, props }, parent) => {
// all top level elements except <template> are parsed as raw text
// containers
if (
(!parent && tag !== 'template') ||
// <template lang="xxx"> should also be treated as raw text
(tag === 'template' &&
props.some(
(p) =>
p.type === 6 /* NodeTypes.ATTRIBUTE */ &&
p.name === 'lang' &&
p.value &&
p.value.content &&
p.value.content !== 'html'
))
) {
return 2 /* TextModes.RAWTEXT */
} else {
return 0 /* TextModes.DATA */
}
},
})
for (const child of node.children) {
if (child.type === 1 && child.tag === 'setup') {
const hasLang = child.props.some((p) => p.name === 'lang')
let codegen = 'script setup'
if (!hasLang && lang) {
codegen += ` lang="${lang}"`
}
s.overwrite(
child.loc.start.offset + 1 /* '<'.length */,
child.loc.start.offset + 6 /* '<setup'.length */,
codegen
)
s.overwrite(
child.loc.end.offset - 6 /* `setup>`.length */,
child.loc.end.offset - 1 /* '>'.length */,
'script'
)
}
}

return getTransformResult(s, id)
}
3 changes: 3 additions & 0 deletions packages/setup-block/src/esbuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.esbuild
47 changes: 47 additions & 0 deletions packages/setup-block/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createUnplugin } from 'unplugin'
import { createFilter } from '@rollup/pluginutils'
import { REGEX_SETUP_SFC, REGEX_VUE_SFC } from '@vue-macros/common'
import { transformSetupBlock } from './core'
import type { FilterPattern } from '@rollup/pluginutils'

export interface Options {
include?: FilterPattern
exclude?: FilterPattern
defaultLang?: string
}

export type OptionsResolved = Omit<Required<Options>, 'exclude'> & {
exclude?: FilterPattern
}

function resolveOption(options: Options): OptionsResolved {
return {
include: [REGEX_VUE_SFC, REGEX_SETUP_SFC],
defaultLang: 'ts',
...options,
}
}

const name = 'unplugin-vue-setup-block'

export default createUnplugin((userOptions: Options = {}) => {
const options = resolveOption(userOptions)
const filter = createFilter(options.include, options.exclude)

return {
name,
enforce: 'pre',

transformInclude(id) {
return filter(id)
},

transform(code, id) {
try {
return transformSetupBlock(code, id, options.defaultLang)
} catch (err: unknown) {
this.error(`${name} ${err}`)
}
},
}
})
3 changes: 3 additions & 0 deletions packages/setup-block/src/rollup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.rollup
4 changes: 4 additions & 0 deletions packages/setup-block/src/vite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import unplugin from '.'
import type {} from 'vite'

export default unplugin.vite
3 changes: 3 additions & 0 deletions packages/setup-block/src/webpack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import unplugin from '.'

export default unplugin.webpack
23 changes: 23 additions & 0 deletions packages/setup-block/tests/__snapshots__/fixtures.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Vitest Snapshot v1

exports[`fixtures > ./fixtures/basic.vue 1`] = `
"<script setup lang=\\"magic\\">
import { ref } from 'vue'
console.log('Hello World')
const msg = ref('Hello World')
</script>
"
`;
exports[`fixtures > ./fixtures/with-lang.vue 1`] = `
"<script setup lang=\\"ts\\">
import { ref } from 'vue'
console.log('Hello World')
const msg = ref('Hello World')
</script>
"
`;
21 changes: 21 additions & 0 deletions packages/setup-block/tests/fixtures.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { describe, expect, test } from 'vitest'
import { transformSetupBlock } from '../src/core'

describe('fixtures', () => {
const files = import.meta.glob('./fixtures/*.vue', {
eager: true,
as: 'raw',
})

for (const [id, code] of Object.entries(files)) {
test(id.replace(/\\/g, '/'), () => {
const exec = () => transformSetupBlock(code, id, 'magic')?.code

if (id.includes('error')) {
expect(exec).toThrowErrorMatchingSnapshot()
} else {
expect(exec()).toMatchSnapshot()
}
})
}
})
7 changes: 7 additions & 0 deletions packages/setup-block/tests/fixtures/basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<setup>
import { ref } from 'vue'

console.log('Hello World')

const msg = ref('Hello World')
</setup>
7 changes: 7 additions & 0 deletions packages/setup-block/tests/fixtures/with-lang.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<setup lang="ts">
import { ref } from 'vue'

console.log('Hello World')

const msg = ref('Hello World')
</setup>
3 changes: 3 additions & 0 deletions packages/setup-block/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import cfg from '../common/tsup.config.js'

export default cfg
6 changes: 6 additions & 0 deletions playground/vue3/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DefineModelRuntimeVue from './examples/define-model/runtime/parent.vue'
import DefineSlotsVue from './examples/define-slots/parent.vue'
import BetterDefineVue from './examples/better-define/index.vue'
import NamedTemplateVue from './examples/named-template/index.vue'
import SetupBlockVue from './examples/setup-block/index.vue'
import { SetupComponentFoo } from './examples/setup-component'
Expand Down Expand Up @@ -69,6 +70,11 @@ import Full from './examples/full.setup'
<SetupSFC />
</fieldset>

<fieldset>
<legend>setupBlock</legend>
<SetupBlockVue />
</fieldset>

<fieldset>
<legend>Short v-model</legend>
<short-vmodel />
Expand Down
9 changes: 9 additions & 0 deletions playground/vue3/src/examples/setup-block/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<setup>
import { ref } from 'vue'
const msg = ref('Hello World')
</setup>

<template>
<div>setupBlock</div>
{{ msg }}
</template>
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

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

0 comments on commit 66145a7

Please sign in to comment.