Comments (16)
Looks good. Can't wait to see it all come together π.
from create-vite-plugin-ssr.
This sounds interessting, but unfortunately I have worked with import.meta
just in one scenario until now.
Can you please provide a more detailed example of the concept?
from create-vite-plugin-ssr.
import.meta
doesn't matter here. It's only about replacing/removing static strings. It could be MACRO_PREACT
instead of import.meta.IS_PREACT
.
We could, for example, use Vite to do this. We can implement a Vite plugin that does these transformations.
In dev, our transfomer replaces these macros with true
/fasle
.
In prod, our transformer removes all macros and picks only one if-block.
from create-vite-plugin-ssr.
For example:
// renderer/_default.page.server.tsx
import { escapeInject, dangerouslySkipEscape } from 'vite-plugin-ssr'
let renderToString: (element: unknown) => string
let PageShell: unknown
if (UI === 'react') {
renderToString = (await import('react-dom/server')).renderToString
PageShell = (await import('./PageShell.react.tsx')).PageShell
}
if (UI === 'preact') {
renderToString = (await import('preact-render-to-string'))
PageShell = (await import('./PageShell.preact.tsx')).PageShell
}
// We can reuse a single boilerplate code for both the react and preact variants π.
export function render(pageContext) {
const { Page, pageProps } = pageContext
const pageHtml = renderToString(
<PageShell>
<Page {...pageProps} />
</PageShell>,
)
return escapeInject`<!DOCTYPE html>
<html>
<body>
<div id="page-view">${dangerouslySkipEscape(pageHtml)}</div>
</body>
</html>`
}
(I think if (UI === 'react')
is better than if (MACRO_PREACT)
for better TypeScript ergonomics.)
So yes, multiple variants in one file. We can still have variant files such as PageShell.react.tsx
and PageShell.preact.tsx
.
The Vite transformer would prune if-blocks, so that only one if-block remains.
I'd suggest we try to prune if-blocks without AST first.
Without AST because we can then publish an npm package create-vite-plugin-ssr
that contains only:
- The "big boilerplate" that contains all variants
- The variant pruning code
That way the npm package create-vite-plugin-ssr
stays light.
Note that this scaffoling technology we are developing is more than just about vite-plugin-ssr.
It will enable things like https://divjoy.com/ but 10x better. Last time I checked it seemed like the author of Divjoy abandoned the project. I'm guessing because Divjoy become unmaintainable. (Scaffolding logic is not easy, as we can see in this very thread :-).)
This is exciting.
from create-vite-plugin-ssr.
Scaffolding logic is not easy, as we can see in this very thread :-)
This is exciting.
Yeah, this is totally interessting and fun!β‘Yesterday i was researching for more then 10 hours the topics micro-frontends
& single-SPA
. I have read many examples and strategies about and now I can follow your thoughts.
That way the npm package create-vite-plugin-ssr stays light.
This will be great!
I optimized my code a lot, but I still want to spent some more time, switching to pnpm
and setting up a new project structure. I will update the repo asap. π―
from create-vite-plugin-ssr.
π
from create-vite-plugin-ssr.
Oki, so I'm a bit stuck at the moment, cause I don't understand totally the concept you have in mind.
When I look at micro-frontend solutions like micro-app, or micro-zoe, it seems like a different approach.
When I look at create-vike, it also seems different, since it uses static content to generate a boilerplate/template.
When we use Vite
with conditionally dynamic imports, I don't understand how we want to build/generate a boilerplate, which then installs, or can be used by a user. As far as I understand, if you run vite build
with the conditionally dynamic imports, it will compile the source to javascript
, what can't be actually used for development, since its no source code.
I have setup a kind of monorepo with the CLI-app, and a second app with vite, the "big boilerplate", but I don't know how to continue. What I was thinking, that the CLI could generate a .env
file, with the selectedOptions
, which then can be used as import.meta inside the boilerplate and as .env vars in vite.
from create-vite-plugin-ssr.
Have a look at https://vitejs.dev/guide/api-plugin.html. Play around with it, e.g. implement a toy Vite plugin and apply it on a vanilla Vite app (without vite-plugin-ssr). Alternatively, we can also simply use import.meta.env
as you suggested π.
We use Vite only for dev (when we work on developing the big boilerplate).
We don't use Vite to build. Instead we simply use JavaScript. For example:
// generateBoilerplate.ts
type Options = { framework: 'react' | 'preact', clientRouting: boolean }
export function generateBoilerplate(options: Options) {
let boilerplateFiles = findBoilerplateFiles()
boilerplateFiles = boilerplateFiles.map(file => {
file.code = removeIfBlocks(file.code, options)
return file
})
// ...
}
function findBoilerplateFiles(): { filePath: string, code: string }[] {
// Crawl and get all the boilerplate `.ts` files
}
function removeIfBlocks(code: string, options: Options) {
Object.entries(options).forEach(([optionName, optionValue]) => {
const lines = code.split('\n')
let state: 'OUTSIDE_IF_BLOCK' | 'INSIDE_IF_BLOCK' = 'OUTSIDE_IF_BLOCK'
let whitespacePadding: null | number = null
lines = lines.filter((line, i) => {
const idx = lines.findIndex(`if (import.meta.env.${optionName}`)
if (idx !== -1) {
state = 'INSIDE_IF_BLOCK'
whitespacePadding = idx
return false // We always remove the if condition lines
}
if (
state === 'INSIDE_IF_BLOCK' && line.trim() === '}' &&
line.length === whitespacePadding.length + 1
) {
state = 'OUTSIDE_IF_BLOCK'
whitespacePadding = null
return false
}
// We keep the lines that are outside the if-block.
if (state === 'OUTSIDE_IF_BLOCK') {
return true
}
if (state === 'INSIDE_IF_BLOCK') {
// TODO: only remove if value is `!== optionValue`: if the value is `=== optionValue`
// we should keep the block content.
return false
}
})
code = lines.join('\n')
})
// We should have no `import.meta.env` left after we removed the if-blocks. (We remove the
// if-condition for the one if-block we keep.)
assert(!code.includes('import.meta.env'), "Wrong `import.meta.env` syntax. Make sure to apply prettier.")
return code
}
AFAICT we don't need an AST.
from create-vite-plugin-ssr.
I edited this post, since I remember you said we can have single files for the components e.g.
index.page.preact.tsx
index.page.react.tsx
index.page.vue
I think we even must do this, since its not possible to dynamic import hooks
inside a condition.
// TypeError: useState is not a function or its return value is not iterable
let useState: any
// OR
let useState: (element: any) => any
if (import.meta.env.VITE_APP_FRAMEWORK === 'React') {
useState = (await import('react')).useState
}
So all good, I will continue tomorrow.
from create-vite-plugin-ssr.
π―
from create-vite-plugin-ssr.
Today was very successful. I'll clean up the code and update the repo for sure if I'm ready.
As long, I couldn't get your pruning code example to work with array.IndexOd
and wildcards, but with line.includes
.
lines.findIndex(`if (import.meta.env.${optionName}`)
Nevermind, to keep this up to date here is the working code snippet I got so far, with a test string. π
If you have any ideas how to optimize let me know.
import assert from 'assert'
const frameworks = ['Preact', 'React', 'Vue'] as const
type Frameworks = typeof frameworks[number]
type Framework = Partial<Frameworks>
type Options = { VITE_APP_FRAMEWORK: Framework }
const VITE_APP_FRAMEWORK = 'React'
const options: Options = {
VITE_APP_FRAMEWORK
}
const rExp = new RegExp(VITE_APP_FRAMEWORK, 'm') as unknown as Framework
generateBoilerplate(options)
export function generateBoilerplate(options: Options) {
let boilerplateFiles = findBoilerplateFiles()
boilerplateFiles = boilerplateFiles.map((file) => {
file.code = removeIfBlocks(file.code, options)
return file
})
// ...
}
function findBoilerplateFiles(): { filePath: string; code: string }[] {
return [
{
filePath: `./boilerplate.ts`,
code: `if (import.meta.env.VITE_APP_FRAMEWORK === 'React') {
console.log('SELECTED REACT')
}
if (import.meta.env.VITE_APP_FRAMEWORK === 'Vue') {
console.log('SELECTED VUE')
}
if (import.meta.env.VITE_APP_FRAMEWORK === 'Preact') {
console.log('SELECTED PREACT')
}
console.log('END1')
console.log('END2')
console.log('END3')`
}
]
}
function removeIfBlocks(code: string, options: Options) {
Object.entries(options).forEach(([optionName, optionValue]) => {
let lines = code.split('\n')
let state: 'OUTSIDE_IF_BLOCK' | 'INSIDE_IF_BLOCK' = 'OUTSIDE_IF_BLOCK'
let whitespacePadding: null | number = null
let frameworkValue: any = null
lines = lines.filter((line, i) => {
const idx = line.includes(`if (import.meta.env.${optionName}`)
if (idx) {
frameworkValue = line.match(rExp)
state = 'INSIDE_IF_BLOCK'
whitespacePadding = i
return false // We always remove the if condition lines
}
if (state === 'INSIDE_IF_BLOCK' && line.trim() === '}' && i === whitespacePadding + 2) {
state = 'OUTSIDE_IF_BLOCK'
whitespacePadding = null
return false
}
// We keep the lines that are outside the if-block.
if (state === 'OUTSIDE_IF_BLOCK') {
return true
}
if (state === 'INSIDE_IF_BLOCK') {
// TODO: only remove if value is `!== optionValue`: if the value is `=== optionValue`
// we should keep the block content.
return frameworkValue !== null && frameworkValue[0] === optionValue
}
})
code = lines.join('\n')
})
console.log(code)
// We should have no `import.meta.env` left after we removed the if-blocks. (We remove the
// if-condition for the one if-block we keep.)
assert(!code.includes('import.meta.env'), 'Wrong `import.meta.env` syntax. Make sure to apply prettier.')
return code
}
from create-vite-plugin-ssr.
I updated the repo.
Really like the CLI code style now! π With the reducer it's sweet.
Next I integrate the generator & optimize the boilerplate package, cause that's a bit dirty. π
from create-vite-plugin-ssr.
Nice, will have a look at it later today.
Ok π.
from create-vite-plugin-ssr.
New commit is done! Still some stuff todo β‘
from create-vite-plugin-ssr.
β‘β‘β‘Neat neat neat.
:-).
All-in-all it's great. Only thing:
- I don't know if we need this
<TaskList/>
thing. Although I really like when the CLI is showing concurrent loading icons. Looks cute, neat, clean, and powerful. Just wondering whether we are overengineering here. But if we do have slow tasks (e.g. runningpnpm install
on behalf of the user), then yea it does make sense.
As said in PM, I love many details about your code.
It's interesting that you put the reducers along TypeScript types in types.ts
. Didn't like it at first, but actually it kind of makes sense. Looking forward to see how it's going to evolve.
from create-vite-plugin-ssr.
Awesome that you like it π
I don't know if we need this thing. Although I really like when the CLI is showing concurrent loading icons. Looks cute, neat, clean, and powerful. Just wondering whether we are overengineering here. But if we do have slow tasks (e.g. running pnpm install on behalf of the user), then yea it does make sense.
I was thinking the same while developing, but realized when I replaced the delay
placeholder with real promises. We will see if I add more tasks like detype
, which might take longer, or like you say, if we might add a install dependencies
option, it will make sense. One thing this is really cool for, when a promise is rejected, so an error happens, you can see it in the progress with icon (I will also add an error message when this happens, in the completed message as next).
This is really making fun, I will continue afap.
from create-vite-plugin-ssr.
Related Issues (7)
- Directory Conflict Resolving HOT 8
- MVP
- CLI selectOption conditions HOT 1
- Restructuring concepts and implementation HOT 6
- Monorepo architecture HOT 3
- Use Vitest HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. πππ
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from create-vite-plugin-ssr.