Skip to content
This repository was archived by the owner on Jul 6, 2025. It is now read-only.

Add Tailwindcss example using windi plugin #375

Merged
merged 3 commits into from
Sep 1, 2021
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
2 changes: 1 addition & 1 deletion bundler/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ export class Bundler {
entry.map((specifier) => {
let mod = this.#aleph.getModule(specifier)
if (mod) {
hasher.update(this.#aleph.gteModuleHash(mod))
hasher.update(this.#aleph.computeModuleHash(mod))
}
})
const bundleFilename = `${name}.bundle.${hasher.toString().slice(0, 8)}.js`
Expand Down
6 changes: 6 additions & 0 deletions examples/tailwindcss/aleph.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Config } from 'aleph/types'
import windicss from 'https://deno.land/x/[email protected]/plugin.ts'

export default <Config>{
plugins: [windicss]
}
12 changes: 12 additions & 0 deletions examples/tailwindcss/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React, { ComponentType } from 'react'

export default function App({ Page, pageProps }: { Page: ComponentType<any>, pageProps: any }) {
return (
<main>
<head>
<meta name="viewport" content="width=device-width" />
</head>
<Page {...pageProps} />
</main>
)
}
23 changes: 23 additions & 0 deletions examples/tailwindcss/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

export default function Index() {
return (
<div className="w-screen h-screen flex items-center justify-center">
<div className="py-8 px-8 max-w-md mx-auto bg-white rounded-xl shadow-md space-y-6 sm:(py-4 space-y-0 space-x-6)">
<img className="block mx-auto h-24 rounded-full sm:(mx-0 flex-shrink-0)" src="/logo.svg" alt="Aleph.js" />
<div className="text-center space-y-2 sm:text-left">
<div className="space-y-0.1">
<p className="text-lg text-black font-semibold">Aleph.js</p>
<p className="text-gray-500 font-medium">CSS Powered by Windi.</p>
</div>
<a
href="https://alephjs.org/docs/get-started"
className="inline-block px-4 py-1 text-sm text-purple-600 font-semibold rounded-full border border-purple-200 hover:(text-white bg-purple-600 border-transparent) focus:(outline-none ring-2 ring-purple-600 ring-offset-2)"
>
Get started
</a>
</div>
</div>
</div>
)
}
15 changes: 15 additions & 0 deletions examples/tailwindcss/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 6 additions & 4 deletions framework/core/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ export function applyCSS(url: string, { css, href }: { css?: string, href?: stri
return el.getAttribute('data-module-id') === url
})
const clean = () => {
if (prevEls.length > 0) {
prevEls.forEach(el => document.head.removeChild(el))
}
setTimeout(() => {
if (prevEls.length > 0) {
prevEls.forEach(el => document.head.removeChild(el))
}
}, 0)
}
let el: any
if (util.isFilledString(css)) {
el = document.createElement('style')
el.type = 'text/css'
el.appendChild(document.createTextNode(css))
Promise.resolve().then(clean)
clean()
} else if (util.isFilledString(href)) {
el = document.createElement('link')
el.rel = 'stylesheet'
Expand Down
59 changes: 38 additions & 21 deletions server/aleph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import cssPlugin, { cssLoader } from '../plugins/css.ts'
import { ensureTextFile, existsDir, existsFile, lazyRemove } from '../shared/fs.ts'
import log, { Measure } from '../shared/log.ts'
import util from '../shared/util.ts'
import type { Aleph as IAleph, DependencyDescriptor, ImportMap, LoadInput, LoadOutput, Module, HtmlDescriptor, RouterURL, ResolveResult, TransformOutput, SSRData, RenderOutput } from '../types.d.ts'
import type { Aleph as IAleph, DependencyDescriptor, ImportMap, LoadInput, LoadOutput, Module, RouterURL, ResolveResult, TransformInput, TransformOutput, SSRData, RenderOutput } from '../types.d.ts'
import { VERSION } from '../version.ts'
import { Analyzer } from './analyzer.ts'
import { cache } from './cache.ts'
Expand All @@ -38,6 +38,7 @@ type CompileOptions = {
forceRefresh?: boolean,
ignoreDeps?: boolean,
httpExternal?: boolean
virtual?: boolean
}

type ResolveListener = {
Expand All @@ -52,7 +53,7 @@ type LoadListener = {

type TransformListener = {
test: RegExp | 'hmr' | 'main',
transform(input: TransformOutput & { module: Module }): TransformOutput | void | Promise<TransformOutput> | Promise<void>,
transform(input: TransformInput): TransformOutput | void | Promise<TransformOutput | void>,
}

type RenderListener = (input: RenderOutput & { path: string }) => void | Promise<void>
Expand Down Expand Up @@ -318,7 +319,7 @@ export class Aleph implements IAleph {
const module = await this.compile(specifier, {
forceRefresh: true,
ignoreDeps: true,
httpExternal: specifier.startsWith('/api/')
httpExternal: prevModule.httpExternal
})
const refreshPage = (
this.#config.ssr &&
Expand Down Expand Up @@ -526,7 +527,7 @@ export class Aleph implements IAleph {
}

/** add a module by given path and optional source code. */
async addModule(specifier: string, sourceCode: string): Promise<Module> {
async addModule(specifier: string, sourceCode: string, forceRefresh?: boolean): Promise<Module> {
let sourceType = getSourceType(specifier)
if (sourceType === SourceType.Unknown) {
throw new Error("addModule: unknown souce type")
Expand All @@ -535,7 +536,8 @@ export class Aleph implements IAleph {
source: {
code: sourceCode,
type: sourceType,
}
},
forceRefresh,
})
if (specifier.startsWith('pages/') || specifier.startsWith('api/')) {
specifier = '/' + specifier
Expand Down Expand Up @@ -707,8 +709,7 @@ export class Aleph implements IAleph {
specifier: '/main.js',
deps: [],
sourceHash: '',
jsFile: '',
ready: Promise.resolve()
jsFile: ''
},
code,
})
Expand Down Expand Up @@ -775,7 +776,7 @@ export class Aleph implements IAleph {
]
}

gteModuleHash(module: Module) {
computeModuleHash(module: Module) {
const hasher = createHash('md5').update(module.sourceHash)
this.lookupDeps(module.specifier, dep => {
const depMod = this.getModule(dep.specifier)
Expand Down Expand Up @@ -939,7 +940,7 @@ export class Aleph implements IAleph {

async importModule<T = any>(module: Module): Promise<T> {
const path = join(this.#buildDir, module.jsFile)
const hash = this.gteModuleHash(module)
const hash = this.computeModuleHash(module)
if (existsFile(path)) {
return await import(`file://${path}#${(hash).slice(0, 6)}`)
}
Expand Down Expand Up @@ -979,7 +980,8 @@ export class Aleph implements IAleph {
}
for (const { test, transform } of this.#transformListeners) {
if (test === 'hmr') {
const ret = await transform({ module: { ...module }, code })
const { jsBuffer, ready, ...rest } = module
const ret = await transform({ module: structuredClone(rest), code })
if (util.isFilledString(ret?.code)) {
code = ret!.code
}
Expand Down Expand Up @@ -1077,7 +1079,7 @@ export class Aleph implements IAleph {
/** init the module by given specifier, don't transpile the code when the returned `source` is equal to null */
private async initModule(
specifier: string,
{ source: customSource, forceRefresh, httpExternal }: CompileOptions = {}
{ source: customSource, forceRefresh, httpExternal, virtual }: CompileOptions = {}
): Promise<[Module, ModuleSource | null]> {
let external = false
let data: any = null
Expand Down Expand Up @@ -1148,7 +1150,7 @@ export class Aleph implements IAleph {
this.#appModule = mod
}

if (await existsFile(metaFp)) {
if (!forceRefresh && await existsFile(metaFp)) {
try {
const { specifier: _specifier, sourceHash, deps, isStyle, ssrPropsFn, ssgPathsFn, denoHooks } = JSON.parse(await Deno.readTextFile(metaFp))
if (_specifier === specifier && util.isFilledString(sourceHash) && util.isArray(deps)) {
Expand All @@ -1165,6 +1167,11 @@ export class Aleph implements IAleph {
} catch (e) { }
}

if (virtual) {
defer()
return [mod, null]
}

if (!isRemote || this.#reloading || mod.sourceHash === '' || !await existsFile(cacheFp)) {
try {
const src = customSource || await this.resolveModuleSource(specifier, data)
Expand Down Expand Up @@ -1272,15 +1279,20 @@ export class Aleph implements IAleph {
}
}

let extraDeps: DependencyDescriptor[] = []
for (const { test, transform } of this.#transformListeners) {
if (test instanceof RegExp && test.test(specifier)) {
const ret = await transform({ module: { ...module }, code: jsCode, map: sourceMap })
const { jsBuffer, ready, ...rest } = module
const ret = await transform({ module: { ...structuredClone(rest) }, code: jsCode, map: sourceMap })
if (util.isFilledString(ret?.code)) {
jsCode = ret!.code
}
if (util.isFilledString(ret?.map)) {
sourceMap = ret!.map
}
if (Array.isArray(ret?.extraDeps)) {
extraDeps.push(...ret!.extraDeps)
}
}
}

Expand All @@ -1291,7 +1303,7 @@ export class Aleph implements IAleph {

module.jsBuffer = encoder.encode(jsCode)
module.deps = deps.filter(({ specifier }) => specifier !== module.specifier).map(({ specifier, resolved, isDynamic }) => {
const dep: DependencyDescriptor = { specifier }
const dep: DependencyDescriptor = { specifier, }
if (isDynamic) {
dep.isDynamic = true
}
Expand All @@ -1303,7 +1315,7 @@ export class Aleph implements IAleph {
}
}
return dep
})
}).concat(extraDeps)

ms.stop(`transpile '${specifier}'`)

Expand All @@ -1312,11 +1324,16 @@ export class Aleph implements IAleph {

if (module.deps.length > 0) {
let fsync = false
await Promise.all(module.deps.map(async ({ specifier, hashLoc }) => {
let depModule: Module | null
if (ignoreDeps) {
await Promise.all(module.deps.map(async ({ specifier, hashLoc, virtual }) => {
let depModule: Module | null = null
if (ignoreDeps || virtual) {
depModule = this.getModule(specifier)
} else {
if (depModule == null && virtual) {
const [mod] = await this.initModule(specifier, { virtual: true })
depModule = mod
}
}
if (depModule === null) {
const [mod, src] = await this.initModule(specifier, { httpExternal })
if (!mod.external) {
await this.transpileModule(mod, src, false, __tracing)
Expand All @@ -1325,7 +1342,7 @@ export class Aleph implements IAleph {
}
if (depModule) {
if (hashLoc !== undefined) {
const hash = this.gteModuleHash(depModule)
const hash = this.computeModuleHash(depModule)
if (await this.replaceDepHash(module, hashLoc, hash)) {
fsync = true
}
Expand Down Expand Up @@ -1356,7 +1373,7 @@ export class Aleph implements IAleph {
const { specifier, hashLoc } = dep
if (specifier === by.specifier && hashLoc !== undefined) {
if (hash == null) {
hash = this.gteModuleHash(by)
hash = this.computeModuleHash(by)
}
if (await this.replaceDepHash(mod, hashLoc, hash)) {
fsync = true
Expand Down
2 changes: 1 addition & 1 deletion server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ export class Server {
if (module) {
const content = await aleph.getModuleJS(module, aleph.isDev)
if (content) {
const hash = aleph.gteModuleHash(module)
const hash = aleph.computeModuleHash(module)
if (hash === req.headers.get('If-None-Match')) {
end(304)
return
Expand Down
15 changes: 13 additions & 2 deletions types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ export interface Aleph {
readonly workingDir: string
readonly config: RequiredConfig
addDist(path: string, content: Uint8Array): Promise<void>
addModule(specifier: string, sourceCode: string): Promise<{ specifier: string, jsFile: string }>
addModule(specifier: string, sourceCode: string, forceRefresh?: boolean): Promise<Module>
fetchModule(specifier: string): Promise<{ content: Uint8Array, contentType: string | null }>
onResolve(test: RegExp, resolve: (specifier: string) => ResolveResult): void
onLoad(test: RegExp, load: (input: LoadInput) => LoadOutput | Promise<LoadOutput>): void
onTransform(test: 'hmr' | 'main' | RegExp, transform: (input: TransformOutput & { module: Module }) => TransformOutput | void | Promise<TransformOutput> | Promise<void>): void
onTransform(test: 'hmr' | 'main' | RegExp, transform: (input: TransformInput) => TransformOutput | void | Promise<TransformOutput | void>): void
onRender(callback: (input: RenderOutput) => Promise<void> | void): void
}

Expand Down Expand Up @@ -85,11 +85,21 @@ export type LoadOutput = {
map?: string
}

/**
* The input of the `onTransform` hook.
*/
export type TransformInput = {
module: Omit<Module, 'jsBuffer' | 'ready'>
code: string
map?: string
}

/**
* The output of the `onTransform` hook.
*/
export type TransformOutput = {
code: string
extraDeps?: DependencyDescriptor[]
map?: string
}

Expand All @@ -113,6 +123,7 @@ export type Module = {
/** The Dependency Descriptor. */
type DependencyDescriptor = {
readonly specifier: string
virtual?: boolean
isDynamic?: boolean
hashLoc?: number
}
Expand Down