Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom path #5

Merged
merged 2 commits into from
Jul 12, 2023
Merged
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,32 @@ This will generate 2 handlers in `server/.generated/api` :

All HTTP methods are supported.

### Custom route

You can override the default route convention with the `path` attribute :

```html
<server lang="ts" path="/not-api/this/is/cool">
export const GET = defineEventHandler((event) => {
return "We're here now."
})
</server>

<script setup lang="ts">
const { data } = useFetch("/not-api/this/is/cool")
</script>

<template>
<h1>Hello</h1>
<div> {{ data }} </div>
</template>

```

A `.gitignore` file will be generated for you. Do not commit the generated files in your repository.

## TODO

- [ ] Support multiple server blocks in a single file
- [ ] Integrates with form-actions & loaders
- [ ] Add useFetch typings
32 changes: 29 additions & 3 deletions packages/extract-sfc-block/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import ExtractSFCBlock from "@hebilicious/extract-sfc-block"
const VitePlugin = ExtractSFCBlock({
output: "server/.generated", // This is relative to the vite config directory.
sourceDir: "pages", // This is relative to the vite config directory.
blockType: "server" // This will match <server></server> blocks.
blockType: "server", // This will match <server></server> blocks.
defaultPath: "api" // This will only be used if no path attribute is provided.
})
```

Expand All @@ -33,11 +34,36 @@ const message = "Hello World!!!"
</template>
```

This plugin will create a `output/hello.ts` file:
This plugin will create a `[output]/[defaultPath]/hello.ts` file:

//
```ts
const message = "Hello World!!!"
```

The file extension will use the lang attribute, or default to `.ts`.

### Custom Path

You can customize the output path at the block level:

```html
<server lang="ts" path="somewhere/else/another-message">
const message = "Hello World!!!"
</server>
<template>
<div> This is a template.</div>
</template>
```

This plugin will create a `somewhere/else/another-message.ts` file:

```ts
const message = "Hello World!!!"
```

## TODO

- [ ] Support multiple server blocks in a single file
- [ ] Refactor to unplugin
- [ ] Write tests
- [ ] Write docs
4 changes: 2 additions & 2 deletions packages/extract-sfc-block/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { defineBuildConfig } from "unbuild"
export default defineBuildConfig({
entries: ["src/index"],
externals: [
"vite"
"vite",
"@vue/compiler-sfc"
// "vue/compiler-sfc"
// "@vue/compiler-sfc"
// "@volar/vue-language-core"
// "@vue/shared"
// "@vitejs/plugin-vue"
Expand Down
15 changes: 2 additions & 13 deletions packages/extract-sfc-block/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,18 @@ export default function vueExtractSFCServer(pluginConfig: PluginConfig): PluginO
configResolved(resolvedConfig) {
// logger.info("Resolved config !")
config = resolvedConfig
// Write a gitignore file in the output directory
// const directoryPath = path.resolve(config.root, pluginConfig.output)
// if (existsSync(directoryPath)) rmSync(directoryPath, { recursive: true, force: true })
// if (!existsSync(directoryPath)) mkdirSync(directoryPath)
// writeFileSync(path.resolve(directoryPath, ".gitignore"), "*")
},

buildStart() {
// logger.info("Starting build")
cache = createCache(config)
},
load(id) {
// if (!cache.isNestedComponent(id)) {
// return
// }
// Only match vue files
const match = id.match(/^(.*)\/([^/]+)\.vue$/)
if (!match) {
return
}
// logger.info("Loading vue", id)
// let filename = match[1]
// const component = match[2]
let filename = id
if (!filename.startsWith(config.root)) {
filename = path.resolve(config.root, filename)
Expand All @@ -51,7 +40,7 @@ export default function vueExtractSFCServer(pluginConfig: PluginConfig): PluginO
if (extractBlock) {
logger.info(`Extracting block @ ${filename}`)
// logger.info(extractBlock.content)
const { outputDirectory, outputPath } = getExtractionInfo(config.root, filename, pluginConfig, extractBlock.lang)
const { outputDirectory, outputPath } = getExtractionInfo(config.root, filename, pluginConfig, extractBlock)
if (outputDirectory && !existsSync(outputDirectory)) mkdirSync(outputDirectory, { recursive: true })
if (outputPath) {
writeFileSync(outputPath, `${GENERATED_TEXT}${extractBlock.content}`)
Expand Down Expand Up @@ -82,7 +71,7 @@ export default function vueExtractSFCServer(pluginConfig: PluginConfig): PluginO
const sfc = cache.getSFC(file)
const extractBlock = sfc.customBlocks.find(findBlockType)
if (extractBlock) {
const { outputPath } = getExtractionInfo(config.root, file, pluginConfig, extractBlock.lang)
const { outputPath } = getExtractionInfo(config.root, file, pluginConfig, extractBlock)
if (outputPath) {
const newSFC = cache.updateCodeSFC(file, await read())
writeFileSync(outputPath, `${GENERATED_TEXT}${newSFC.customBlocks.find(findBlockType)?.content}`)
Expand Down
20 changes: 14 additions & 6 deletions packages/extract-sfc-block/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from "node:path"
import type { VueQuery } from "@vitejs/plugin-vue"
import type { SFCBlock } from "vue/compiler-sfc"

import { createConsola } from "consola"
import {
Expand All @@ -11,6 +12,7 @@ export interface PluginConfig {
output: string
sourceDir: string
blockType: string
defaultPath?: string
}
export const pluginName = "extract-sfc-block" as const
export const GENERATED_TEXT = `/** This file is auto-generated by the [${pluginName}] plugin. /!\\ Do not modify it manually ! */ \n` as const
Expand Down Expand Up @@ -41,12 +43,18 @@ function extractVueFileName(str: string, dir: string) {
return str.startsWith(dir) ? str.split(dir)[1].slice(0, -4) : null
}

export function getExtractionInfo(root: string, file: string, pluginOptions: any, lang = "ts") {
const directoryPath = path.resolve(root, pluginOptions.output)
const sourceDirectory = `${root}/${pluginOptions.sourceDir}/`
const vueFileName = extractVueFileName(file, sourceDirectory)
const outputPath = vueFileName ? `${directoryPath}/${vueFileName}.${lang}` : null
// console.log({ vueFileName, sourceDirectory, file, directoryPath, outputPath })
function removeLeadingAndTrailingSlash(str: string) {
const start = str.startsWith("/") ? 1 : 0
const end = str.endsWith("/") ? -1 : undefined
return str.slice(start, end)
}
export function getExtractionInfo(root: string, file: string, pluginOptions: PluginConfig, sfc: SFCBlock) {
const { output, sourceDir, defaultPath } = pluginOptions
const directoryPath = path.resolve(root, output)
const sourceDirectory = `${root}/${sourceDir}/`
const nameAndPath = typeof sfc.attrs.path === "string" ? removeLeadingAndTrailingSlash(sfc.attrs.path) : `${defaultPath ? `${defaultPath}/` : ""}${extractVueFileName(file, sourceDirectory)}`
const outputPath = nameAndPath ? `${directoryPath}/${nameAndPath}.${sfc.lang ?? "ts"}` : null
// console.log({ nameAndPath, sourceDirectory, file, directoryPath, outputPath })
return {
outputDirectory: outputPath ? path.dirname(outputPath) : null,
outputPath
Expand Down
3 changes: 2 additions & 1 deletion packages/server-block-nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"devDependencies": {
"@hebilicious/sfc-server-volar": "latest",
"@nuxt/module-builder": "^0.4.0",
"h3": "^1.7.1"
"h3": "^1.7.1",
"nitropack": "^2.5.2"
}
}
27 changes: 13 additions & 14 deletions packages/server-block-nuxt/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { addServerHandler, createResolver, defineNuxtModule, useNitro } from "@n
import ExtractSFCBlock from "@hebilicious/extract-sfc-block"

import { loadFile } from "magicast"
import type { NitroEventHandler } from "nitropack"
import { SupportedMethods, getRoute, logger, makePathShortener, writeHandlers } from "./runtime/utils"

const name = "server-block"

const serverOutput = "server/.generated/api" as const
const serverOutput = "server/.generated" as const

export default defineNuxtModule({
meta: {
Expand Down Expand Up @@ -41,12 +42,14 @@ export default defineNuxtModule({
ExtractSFCBlock({
output: serverOutput,
sourceDir: "pages",
blockType: "server"
blockType: "server",
defaultPath: "api"
})
)
})

// 3.Watch directories, split handlers and add them to Nitro/Nuxt
const allHandlers = new Map<string, NitroEventHandler>()
nuxt.hook("builder:watch", async (event, path) => {
try {
if (!existsSync(path)) return // Return early if the path doesn't exist.
Expand All @@ -61,23 +64,19 @@ export default defineNuxtModule({
const handlers = await writeHandlers(file, path)
for (const handler of handlers) {
logger.success(`[update] Wrote ${handler.method} handler @${handler.route} : ${shortened(handler.handler)}`)
if (!useNitro().scannedHandlers.some(h => h.handler === handler.handler)) {
useNitro().scannedHandlers.push({
...handler,
lazy: true
})
logger.success(`[update] Nitro handler updated : ${handler.route}`)
}
allHandlers.set(handler.handler, handler)
if (!nuxt.options.serverHandlers.some(h => h.handler === handler.handler)) {
addServerHandler({
...handler,
lazy: true
})
addServerHandler({ ...handler, lazy: true })
logger.success(`[update] Nuxt handler updated : ${handler.route}`)
}
}
for (const [path, handler] of allHandlers.entries()) {
if (useNitro().scannedHandlers.find(h => h.handler === path)) continue
useNitro().scannedHandlers.push({ ...handler, lazy: true })
}
}
// logger.info("[update]: Handlers", nuxt.options.serverHandlers)
logger.info("[update]: Nitro Handlers \n", useNitro().scannedHandlers.map(h => h.handler))
logger.info("[update]: Nuxt Handlers \n", nuxt.options.serverHandlers.map(h => h.handler))
}
}
catch (error) {
Expand Down
9 changes: 0 additions & 9 deletions playgrounds/.gitignore

This file was deleted.

6 changes: 3 additions & 3 deletions playgrounds/pages/nested/hello.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<server lang="ts">
<server lang="ts" path="/not-api/this/is/cool">
export const GET = defineEventHandler((event) => {
return "Hello World"
return "We're here now."
})
</server>

<script setup lang="ts">
const { data } = useFetch("/api/nested/hello")
const { data } = useFetch("/not-api/this/is/cool")
</script>

<template>
Expand Down
Loading