Skip to content

Commit

Permalink
chore: done server mode
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Jun 25, 2024
1 parent dfc2667 commit b227b49
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 127 deletions.
1 change: 1 addition & 0 deletions src/plugins/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function stylexBuild(plugin: Plugin, ctx: PluginContext, cssPlugins: Plug
}
},
renderChunk: {
// By declare order we can get better performance for generate styles.
async handler(_, chunk) {
if (!cssHooks.size) {
cssPlugins.forEach((p) => {
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function createForViteServer(ctx: PluginContext) {
}
cssPlugins.push(...conf.plugins.filter(p => CONSTANTS.CSS_PLUGINS.includes(p.name)))
cssPlugins.sort((a, b) => a.name.length < b.name.length ? -1 : 1)
ctx.env === 'build' ? stylexBuild(plugin, ctx, cssPlugins) : stylexServer(plugin, ctx)
}
ctx.env === 'build' ? stylexBuild(plugin, ctx, cssPlugins) : stylexServer(plugin, ctx)
}
}
148 changes: 79 additions & 69 deletions src/plugins/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PluginContext, parseRequest } from '../context'
import { hash, hijackHook } from '../shared'

export const CONSTANTS = {
WS_EVENT_PREFIX: 'stylex:hmr',
WS_EVENT: 'stylex:hmr',
VIRTUAL_STYLEX_ID: 'virtual:stylex.css',
RESOLVED_ID_WITH_QUERY_REG: /[/\\]__stylex(_.*?)?\.css(\?.*)?$/,
RESOLVED_ID_REG: /[/\\]__stylex(?:_(.*?))?\.css$/,
Expand All @@ -21,18 +21,34 @@ export function resolveId(id: string) {
for (const alias of CONSTANTS.VIRTUAL_ENTRY_ALIAS) {
const matched = id.match(alias)
if (matched) {
return matched[1]
? `/__stylex_${matched[1]}.css`
: '/__stylex.css'
return '/__stylex.css'
}
}
}

let hmr = `
try {
let hash = __vite__css.match(/__stylex_hash_(\\w{${CONSTANTS.HASH_LENGTH}})/)
hash = hash && hash[1]
if (!hash) {
console.log('[vite-plugin-stylex]', 'Failed to get stylex hash, hmr might not work!')
} else {
await import.meta.hot.send('${CONSTANTS.WS_EVENT}', hash)
}
} catch (e) {
console.warn('[vite-plugin0-stylex]', e)
}
if (!import.meta.url.include('?')) {
await new Promise(r => setTimeout(r, 100))
}
`

hmr = `\nif (import.meta.hot) { ${hmr} }`

export function stylexServer(plugin: Plugin, ctx: PluginContext) {
let viteDevServer: ViteDevServer | null = null
// const resolved = false
let lastServerTime = Date.now()
const cssCaches: Map<string, string> = new Map()
const modules = new Set<string>()

const generateCSS = () => {
Expand All @@ -48,6 +64,8 @@ export function stylexServer(plugin: Plugin, ctx: PluginContext) {
return { css, hash: hash(css) }
}

// const onInvalidate

const update = (ids: Set<string>) => {
if (!viteDevServer) return
viteDevServer.ws.send({
Expand All @@ -65,80 +83,72 @@ export function stylexServer(plugin: Plugin, ctx: PluginContext) {
})
}

const entries = new Set<string>()
plugin.enforce = 'post'
let invalidateTimer: any

plugin.configureServer = function configureServer(server) {
viteDevServer = server
viteDevServer.ws.on(CONSTANTS.WS_EVENT_PREFIX, () => {
update(entries)
})
}
plugin.resolveId = function handler(id: string) {
const entry = resolveId(id)
if (entry) {
entries.add(entry)
return entry
const invalidate = (ids: Set<string>) => {
for (const id of ids) {
const mod = viteDevServer?.moduleGraph.getModuleById(id)
if (!mod) continue
viteDevServer?.moduleGraph.invalidateModule(mod)
}
clearTimeout(invalidateTimer)
invalidateTimer = setTimeout(() => {
update(ids)
}, 10)
}

plugin.load = async function handler(id: string) {
const { original } = parseRequest(id)
const matched = original.match(CONSTANTS.RESOLVED_ID_REG)
if (matched) {
const { hash, css } = generateCSS()
cssCaches.set(hash, css)
return {
code: `${css}__stylex_hash_${hash}{--:'';}`,
map: { mappings: '' }
const entries = new Set<string>()

const schedule = <Partial<Plugin>> {
enforce: 'pre',
name: 'stylex:server',
apply: 'serve',
resolveId(id: string) {
const entry = resolveId(id)
if (entry) {
entries.add(entry)
return entry
}
},
load(id: string) {
const { original } = parseRequest(id)
const matched = original.match(CONSTANTS.RESOLVED_ID_REG)
if (matched) {
const { hash, css } = generateCSS()
return {
code: `${css}__stylex_hash_${hash}{--:'';}`,
map: { mappings: '' }
}
}
},
configureServer(server) {
viteDevServer = server
viteDevServer.ws.on(CONSTANTS.WS_EVENT, () => {
update(entries)
})
}
}

hijackHook(plugin, 'configResolved', (fn, _, c) => {
const pos = c[0].plugins.findIndex(p => p.name === 'stylex')
// @ts-expect-error
c[0].plugins.splice(pos, 0, schedule)
fn(...c)
})
//
hijackHook(plugin, 'transform', async (fn, c, args) => {
const [code, id] = args
const id = args[1]
const { original } = parseRequest(id)
const result = await fn.apply(c, args)
if (ctx.styleRules.has(original) && result) {
modules.add(id)

return result
} else {
// inject css modules to send callback on css load
if (original.match(CONSTANTS.RESOLVED_ID_REG)) {
if (code.includes('import.meta.hot')) {
let hmr = `try {
let hash = __vite__css.match(/__stylex_hash_(\\w{${CONSTANTS.HASH_LENGTH}})/)
hash = hash && hash[1]
if (!hash) {
console.warn('[vite-plugin-stylex]', 'Failed to get stylex hash, hmr might not work!')
} else {
await import.meta.hot.send('${CONSTANTS.WS_EVENT_PREFIX}', hash)
}
} catch (e) {
console.warn('[vite-plugin-stylex]', e)
}
if (!import.meta.url.includes('?')) {
await new Promise(resolve => setTimeout(resolve, 100))
}
`
hmr = `\nif (import.meta.hot) {${hmr}}`
const s = code + hmr
// const placeholder = 'const __vite__css = '
// const pos = s.indexOf(placeholder) + placeholder.length
// const length = s.length
// let n = pos
// for (let i = pos; i < length; i++) {
// if (s[i] === '\n') {
// break
// }
// n++
// }
// const { css, hash } = generateCSS()
// s = s.replace(s.substring(pos, n), `"${css}__stylex_hash_${hash}{--:'';}"`)
return { code: s, map: { mappings: '' } }
}
}
if (ctx.styleRules.has(original)) {
// record affect module.
modules.add(original)
invalidate(new Set([...entries, ...modules]))
}
if (original.match(CONSTANTS.RESOLVED_ID_REG)) {
const code = args[0] + hmr
return { code, map: { mappings: '' } }
}
return result
})
}
66 changes: 9 additions & 57 deletions src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,62 +137,14 @@ export function searchForWorkspaceRoot(
return searchForWorkspaceRoot(dir, root)
}

/* eslint-disable no-fallthrough */

/**
* JS Implementation of MurmurHash2
*
* @author <a href="mailto:[email protected]">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:[email protected]">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} str ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
function murmurhash2_32_gc(str: string, seed: number = 0) {
let l = str.length
let h = seed ^ l
let i = 0
let k

while (l >= 4) {
k = (str.charCodeAt(i) & 0xff) |
((str.charCodeAt(++i) & 0xff) << 8) |
((str.charCodeAt(++i) & 0xff) << 16) |
((str.charCodeAt(++i) & 0xff) << 24)

k = (k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)
k ^= k >>> 24
k = (k & 0xffff) * 0x5bd1e995 + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16)

h = ((h & 0xffff) * 0x5bd1e995 +
((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^
k

l -= 4 // eslint-disable-next-line stylistic/semi
;++i
}

switch (l) {
case 3:
h ^= (str.charCodeAt(i + 2) & 0xff) << 16
case 2:
h ^= (str.charCodeAt(i + 1) & 0xff) << 8
case 1:
h ^= str.charCodeAt(i) & 0xff
h = (h & 0xffff) * 0x5bd1e995 +
((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)
export function hash(str: string) {
let i
let l
let hval = 0x811C9DC5

for (i = 0, l = str.length; i < l; i++) {
hval ^= str.charCodeAt(i)
hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24)
}

h ^= h >>> 13
h = (h & 0xffff) * 0x5bd1e995 + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)
h ^= h >>> 15

return h >>> 0
}

export function hash(str: string): string {
return murmurhash2_32_gc(str, 1).toString(36)
return (`00000${(hval >>> 0).toString(36)}`).slice(-6)
}

0 comments on commit b227b49

Please sign in to comment.