Skip to content

Commit

Permalink
export vite plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
nitedani committed Jan 20, 2024
1 parent 42a533a commit e875995
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 106 deletions.
7 changes: 2 additions & 5 deletions examples/zustand/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,5 @@
"vite": "^5.0.10",
"zustand": "^4.4.7"
},
"type": "module",
"devDependencies": {
"es-module-lexer": "^1.4.1"
}
}
"type": "module"
}
92 changes: 3 additions & 89 deletions examples/zustand/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,8 @@
import react from '@vitejs/plugin-react'
import { init, parse } from 'es-module-lexer'
import vike from 'vike/plugin'
import type { Plugin, UserConfig } from 'vite'
import type { UserConfig } from 'vite'
import { vikeReactZustand } from 'vike-react-zustand/plugin'

export default {
plugins: [react(), vike(), vikeReactZustandPlugin()]
plugins: [react(), vike(), vikeReactZustand()]
} satisfies UserConfig

function vikeReactZustandPlugin(): Plugin {
const idToStoreKeys: { [id: string]: Set<string> } = {}
return {
name: 'vikeReactZustand',
enforce: 'post',
transform(code, id) {
if (id.includes('node_modules')) {
return
}
const res = parse(code)
const match = res[0].find((line) => line.n === 'vike-react-zustand')
if (!match) {
return
}
const importLine = code.slice(match.ss, match.se)
const imports = importLine
.substring(importLine.indexOf('{') + 1, importLine.indexOf('}'))
.split(',')
.map((s) => s.trim())
.filter((s) => {
const split = s.split(' as ')
return (
split.length === 1 ||
// create as create
split[0] === split[1]
)
})
if (!imports.includes('create')) {
return
}

// Playground: https://regex101.com/r/oDNRzp/1
const matches = code.matchAll(/(?<=[\s:=,;])create\s*?\(/g)
let idx = 0
for (const match of matches) {
if (!match.index || !match.input) {
continue
}
const key = simpleHash(`${id}:${idx}`)
idToStoreKeys[id] ??= new Set([key])
idToStoreKeys[id].add(key)
code =
match.input.substring(0, match.index) +
`${match[0]}'${key}',` +
match.input.substring(match.index + match[0].length)
idx++
}

return code
},
async buildStart() {
await init
},
async handleHotUpdate(ctx) {
const modules = ctx.modules.filter((m) => m.id && m.id in idToStoreKeys)
if (!modules.length) return

for (const module of modules) {
if (!module.id) {
continue
}
const storeKeysInFile = idToStoreKeys[module.id]
for (const key of storeKeysInFile) {
//@ts-ignore
if (globalThis.__vite_plugin_ssr?.['VikeReactZustandContext.ts']) {
//@ts-ignore
delete globalThis.__vite_plugin_ssr['VikeReactZustandContext.ts'].initializers[key]
}
}
delete idToStoreKeys[module.id]
}

ctx.server.ws.send({ type: 'full-reload' })
return []
}
}
}

function simpleHash(str: string) {
let hash = 0
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0
}
return (hash >>> 0).toString(36)
}
18 changes: 12 additions & 6 deletions packages/vike-react-zustand/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"types": "dist/index.d.ts",
"exports": {
".": "./dist/index.js",
"./config": "./dist/renderer/+config.h.js",
"./renderer/VikeReactZustandWrapper": "./dist/renderer/VikeReactZustandWrapper.js"
"./config": "./dist/renderer/+config.js",
"./renderer/VikeReactZustandWrapper": "./dist/renderer/VikeReactZustandWrapper.js",
"./plugin": "./dist/plugin.js"
},
"scripts": {
"dev": "tsc --watch",
Expand All @@ -20,7 +21,8 @@
"react-dom": "^18.0.0",
"vike": "^0.4.151",
"vike-react": "^0.3.5",
"zustand": "^4.0.0"
"zustand": "^4.0.0",
"es-module-lexer": "^1.4.1"
},
"devDependencies": {
"@brillout/release-me": "^0.1.13",
Expand All @@ -33,16 +35,20 @@
"vike": "^0.4.151",
"vike-react": "^0.3.8",
"vite": "^5.0.10",
"zustand": "^4.4.7"
"zustand": "^4.4.7",
"es-module-lexer": "^1.4.1"
},
"dependencies": {},
"typesVersions": {
"*": {
"config": [
"dist/renderer/+config.h.d.ts"
"dist/renderer/+config.d.ts"
],
"renderer/VikeReactZustandWrapper": [
"dist/renderer/VikeReactZustandWrapper.d.ts"
],
"plugin": [
"dist/plugin.d.ts"
]
}
},
Expand All @@ -51,4 +57,4 @@
],
"repository": "https://github.com/vikejs/vike-react/tree/main/packages/vike-react-zustand",
"license": "MIT"
}
}
87 changes: 87 additions & 0 deletions packages/vike-react-zustand/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
export { vikeReactZustand }

import type { Plugin } from 'vite'
import { initializers_remove } from './renderer/context.js'
import { assert } from './utils.js'
import { init, parse } from 'es-module-lexer'

function vikeReactZustand(): Plugin {
const idToStoreKeys: { [id: string]: Set<string> } = {}
return {
name: 'vikeReactZustand',
enforce: 'post',
async transform(code, id) {
if (id.includes('node_modules')) {
return
}
const res = parse(code)
const match = res[0].find((line) => line.n === 'vike-react-zustand')
if (!match) {
return
}
const importLine = code.slice(match.ss, match.se)
const imports = importLine
.substring(importLine.indexOf('{') + 1, importLine.indexOf('}'))
.split(',')
.map((s) => s.trim())
.filter((s) => {
const split = s.split(' as ')
return (
split.length === 1 ||
// create as create
split[0] === split[1]
)
})
if (!imports.includes('create')) {
return
}

// Playground: https://regex101.com/r/oDNRzp/1
const matches = code.matchAll(/(?<=[\s:=,;])create\s*?\(/g)
let idx = 0
for (const match of matches) {
if (!match.index || !match.input) {
continue
}
const key = simpleHash(`${id}:${idx}`)
idToStoreKeys[id] ??= new Set([key])
idToStoreKeys[id]!.add(key)
code =
match.input.substring(0, match.index) +
`${match[0]}'${key}',` +
match.input.substring(match.index + match[0].length)
idx++
}

return code
},
async buildStart() {
await init
},
handleHotUpdate(ctx) {
const modules = ctx.modules.filter((m) => m.id && m.id in idToStoreKeys)
if (!modules.length) return

for (const module of modules.filter((m) => m.id)) {
assert(module.id)
const storeKeysInFile = idToStoreKeys[module.id]
assert(storeKeysInFile)
for (const key of storeKeysInFile) {
initializers_remove(key)
}
delete idToStoreKeys[module.id]
}

ctx.server.ws.send({ type: 'full-reload' })
return []
}
}
}

function simpleHash(str: string) {
let hash = 0
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) - hash + str.charCodeAt(i)) | 0
}
return (hash >>> 0).toString(36)
}
4 changes: 4 additions & 0 deletions packages/vike-react-zustand/src/renderer/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { initializers_set }
export { initializers_get }
export { initializers_remove }
export { setPageContext }
export { getPageContext }
export { getReactStoreContext }
Expand Down Expand Up @@ -33,6 +34,9 @@ function initializers_set(key: string, initializer: any) {
[key]: initializer
}
}
function initializers_remove(key: string) {
delete globalObject.initializers[key]
}
function initializers_get() {
return globalObject.initializers
}
54 changes: 48 additions & 6 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 e875995

Please sign in to comment.