Skip to content

Commit

Permalink
feat(vite, webpack): add extraContent option (#1963)
Browse files Browse the repository at this point in the history
* feat(vite): add `watchExternal` option

* chore: lint

* refactor: api

Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
sibbng and antfu committed Dec 21, 2022
1 parent 4d951a1 commit b7a6687
Show file tree
Hide file tree
Showing 22 changed files with 323 additions and 19 deletions.
20 changes: 20 additions & 0 deletions examples/astro-vue/.gitignore
@@ -0,0 +1,20 @@
# build output
dist/
.output/

# dependencies
node_modules/

# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*


# environment variables
.env
.env.production

# macOS-specific files
.DS_Store
10 changes: 10 additions & 0 deletions examples/astro-vue/astro.config.ts
@@ -0,0 +1,10 @@
import { defineConfig } from 'astro/config'
import UnoCSS from 'unocss/astro'
import vue from '@astrojs/vue'

export default defineConfig({
integrations: [
vue(),
UnoCSS(),
],
})
19 changes: 19 additions & 0 deletions examples/astro-vue/package.json
@@ -0,0 +1,19 @@
{
"name": "unocss-astro",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"vue": "^3.2.30"
},
"devDependencies": {
"@astrojs/vue": "^1.2.1",
"astro": "^1.6.11",
"unocss": "link:../../packages/unocss"
}
}
Empty file.
21 changes: 21 additions & 0 deletions examples/astro-vue/src/components/Card.astro
@@ -0,0 +1,21 @@
---
export interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props as Props;
---

<li class="border rounded inline-block px4 py2 m2 hover:shadow">
<a href={href}>
<h2 class="font-bold">
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
11 changes: 11 additions & 0 deletions examples/astro-vue/src/components/Example.vue
@@ -0,0 +1,11 @@
<template>
<div class=":uno: text-(xl red-400) mt-8">
Example Vue component
</div>
</template>

<style scoped>
div {
@apply mt-8;
}
</style>
1 change: 1 addition & 0 deletions examples/astro-vue/src/env.d.ts
@@ -0,0 +1 @@
/// <reference types="astro/client" />
19 changes: 19 additions & 0 deletions examples/astro-vue/src/layouts/Layout.astro
@@ -0,0 +1,19 @@
---
export interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body class="font-sans text-center">
<slot />
</body>
</html>
15 changes: 15 additions & 0 deletions examples/astro-vue/src/pages/foo.astro
@@ -0,0 +1,15 @@
---
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
---

<Layout title="Foo Page">
<div class="text-center text-lg mt-10">Foo</div>
<ul role="list" class="link-card-grid">
<Card
href="/"
title="Home"
body="Go home."
/>
</ul>
</Layout>
25 changes: 25 additions & 0 deletions examples/astro-vue/src/pages/index.astro
@@ -0,0 +1,25 @@
---
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
import Example from '../components/Example.vue';
---

<Layout title="Welcome to Astro.">
<main class="p10 text-center">
<div>
<div class="i-logo"></div>
</div>
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
<p class="instructions">
Check out the <code>src/pages</code> directory to get started.<br />
</p>
<ul role="list" class="link-card-grid">
<Card
href="/foo"
title="/foo"
body="Go to page foo."
/>
<Example client:only="vue" />
</ul>
</main>
</Layout>
10 changes: 10 additions & 0 deletions examples/astro-vue/tsconfig.json
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true
}
}
28 changes: 28 additions & 0 deletions examples/astro-vue/uno.config.ts
@@ -0,0 +1,28 @@
import {
defineConfig,
presetIcons,
presetUno,
transformerCompileClass,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'

export default defineConfig({
shortcuts: [
{ 'i-logo': 'i-logos-astro w-6em h-6em transform transition-800' },
],
transformers: [
transformerDirectives(),
transformerCompileClass(),
transformerVariantGroup(),
],
presets: [
presetUno(),
presetIcons({
extraProperties: {
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
],
})
7 changes: 7 additions & 0 deletions packages/astro/src/index.ts
@@ -1,3 +1,5 @@
import { resolve } from 'path'
import { fileURLToPath } from 'url'
import type { AstroIntegration } from 'astro'
import type { VitePluginConfig } from '@unocss/vite'
import VitePlugin from '@unocss/vite'
Expand Down Expand Up @@ -38,6 +40,11 @@ export default function UnoCSSAstroIntegration<Theme extends {}>(
name: 'unocss',
hooks: {
'astro:config:setup': async ({ config, injectScript }) => {
// Adding components to UnoCSS's extra content
options.extraContent ||= {}
options.extraContent.filesystem ||= []
options.extraContent.filesystem.push(resolve(fileURLToPath(config.root), 'src/components/**/*').replace(/\\/g, '/'))

config.vite.plugins ||= []
config.vite.plugins.push(...VitePlugin(options, defaults) as any)

Expand Down
25 changes: 25 additions & 0 deletions packages/core/src/types.ts
Expand Up @@ -531,6 +531,13 @@ export interface UnocssPluginContext<Config extends UserConfig = UserConfig> {
/** Module IDs that been affected by UnoCSS */
affectedModules: Set<string>

/** Pending promises */
tasks: Promise<any>[]
/**
* Await all pending tasks
*/
flushTasks(): Promise<any>

filter: (code: string, id: string) => boolean
extract: (code: string, id?: string) => Promise<void>

Expand Down Expand Up @@ -581,6 +588,19 @@ export interface SourceCodeTransformer {
transform: (code: MagicString, id: string, ctx: UnocssPluginContext) => Awaitable<void>
}

export interface ExtraContentOptions {
/**
* Glob patterns to match the files to be extracted
* In dev mode, the files will be watched and trigger HMR
*/
filesystem?: string[]

/**
* Plain text to be extracted
*/
plain?: string[]
}

/**
* For other modules to aggregate the options
*/
Expand Down Expand Up @@ -611,6 +631,11 @@ export interface PluginOptions {
* Custom transformers to the source code
*/
transformers?: SourceCodeTransformer[]

/**
* Extra content outside of build pipeline (assets, backend, etc.) to be extracted
*/
extraContent?: ExtraContentOptions
}

export interface UserConfig<Theme extends {} = {}> extends ConfigBase<Theme>, UserOnlyOptions<Theme>, GeneratorOptions, PluginOptions, CliOptions {}
Expand Down
11 changes: 10 additions & 1 deletion packages/shared-integration/src/context.ts
Expand Up @@ -23,6 +23,7 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(

const modules = new BetterMap<string, string>()
const tokens = new Set<string>()
const tasks: Promise<void>[] = []
const affectedModules = new Set<string>()

let ready = reloadConfig()
Expand Down Expand Up @@ -83,7 +84,7 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
invalidate()
}

const filter = (code: string, id: string) => {
function filter(code: string, id: string) {
if (code.includes(IGNORE_COMMENT))
return false
return code.includes(INCLUDE_COMMENT) || code.includes(CSS_PLACEHOLDER) || rollupFilter(id.replace(/\?v=\w+$/, ''))
Expand All @@ -94,13 +95,21 @@ export function createContext<Config extends UserConfig<any> = UserConfig<any>>(
return rawConfig
}

async function flushTasks() {
const _tasks = [...tasks]
await Promise.all(_tasks)
tasks.splice(0, _tasks.length)
}

return {
get ready() {
return ready
},
tokens,
modules,
affectedModules,
tasks,
flushTasks,
invalidate,
onInvalidate(fn: () => void) {
invalidations.push(fn)
Expand Down
55 changes: 55 additions & 0 deletions packages/shared-integration/src/extra-content.ts
@@ -0,0 +1,55 @@
import fs from 'fs/promises'
import { resolve } from 'path'
import fg from 'fast-glob'
import type { UnocssPluginContext } from '@unocss/core'
import { applyTransformers } from './transformers'

export async function setupExtraContent(ctx: UnocssPluginContext, shouldWatch = false) {
const { extraContent } = await ctx.getConfig()
const { extract, tasks, root, filter } = ctx

// plain text
if (extraContent?.plain) {
await Promise.all(
extraContent.plain.map((code, idx) => {
return extract(code, `__extra_content_${idx}__`)
}),
)
}

// filesystem
if (extraContent?.filesystem) {
const files = await fg(extraContent.filesystem, { cwd: root })

async function extractFile(file: string) {
const code = await fs.readFile(file, 'utf-8')
if (!filter(code, file))
return
const preTransform = await applyTransformers(ctx, code, file, 'pre')
const defaultTransform = await applyTransformers(ctx, preTransform?.code || code, file)
await applyTransformers(ctx, defaultTransform?.code || preTransform?.code || code, file, 'post')
return await extract(preTransform?.code || code, file)
}

if (shouldWatch) {
const { watch } = await import('chokidar')
const ignored = ['**/{.git,node_modules}/**']

const watcher = watch(files, {
ignorePermissionErrors: true,
ignored,
cwd: root,
ignoreInitial: true,
})

watcher.on('all', (type, file) => {
if (type === 'add' || type === 'change') {
const absolutePath = resolve(root, file)
tasks.push(extractFile(absolutePath))
}
})
}

await Promise.all(files.map(extractFile))
}
}
2 changes: 2 additions & 0 deletions packages/vite/package.json
Expand Up @@ -53,6 +53,8 @@
"@unocss/inspector": "workspace:*",
"@unocss/scope": "workspace:*",
"@unocss/transformer-directives": "workspace:*",
"chokidar": "^3.5.3",
"fast-glob": "^3.2.12",
"magic-string": "^0.27.0"
},
"devDependencies": {
Expand Down

0 comments on commit b7a6687

Please sign in to comment.