-
-
Notifications
You must be signed in to change notification settings - Fork 356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Streamlining production of tailwindcss bundled css, introducing multiple entryfiles for content-ui foldesr #750
base: main
Are you sure you want to change the base?
Conversation
2) fixed hmr crash with internalizing tailwindcss build file 3) added multi entrypoints for content-ui folder only 4) added tailwindcss-bundler to be shared across pages
**/vite.config.mts.timestamp-* | ||
pnpm-lock.yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can't be ignored
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops i forgot ive done that, will remove it.. i just have to push another commit right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that's your normal workflow, PR it's only for tracking it
import fs from 'fs/promises'; | ||
import path from 'path'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import fs from 'fs/promises'; | |
import path from 'path'; | |
import fs from 'node:fs/promises'; | |
import path from 'node:path'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohh node must be included? what happen if don't
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
writeFileSync(entryFileTSX, readFileSync(existingEntryFileTSX, 'utf8')); | ||
|
||
try { | ||
// renameSync(existingEntryFileTSX, resolve(srcDir, 'App-deleteMe!.tsx')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't leave comments like this for review
.map(item => item.name); | ||
|
||
foldersToProcess.forEach(folderName => { | ||
const shouldExclude = options.exclude && options.exclude.includes(folderName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe sth like that:
const shouldExclude = options.exclude && options.exclude.includes(folderName); | |
const shouldExclude = options.exclude?.includes(folderName); |
const foldersToProcess = | ||
options.include || | ||
readdirSync(srcDir, { withFileTypes: true }) | ||
.filter((item): item is Dirent => item.isDirectory()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not like that?
.filter((item): item is Dirent => item.isDirectory()) | |
.filter((item: : Dirent) => item.isDirectory()) |
I don't really know this kind of typing, that's interesting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unnecessary assertion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unnecessary assertion.
erm we only want folder name, and not file names, readdirsync will read filenames too which we don't want.
const theme = useStorage(exampleThemeStorage); | ||
|
||
useEffect(() => { | ||
console.log('content ui loaded'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't be the same as in first script, but anyway.
1 example is enough
name: 'contentUI', | ||
formats: ['iife'], | ||
fileName: 'index', | ||
formats: ['es'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't have it to es
because it isn't working good, i've checked it XD
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm my repo worked fine, but im really puzzled how the test all failed, i didn't even touched the content-runtime stuff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try to e.g. inject runtime script several times, you will have an error
fileName: 'index', | ||
formats: ['es'], | ||
fileName: (format, entryName) => | ||
isDev ? `${entryName}/index_dev.js` : `${entryName}/index.js` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you need to have 2 different files?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filename should be different in dev and production mode, perhaps hashes should be applied in production? that's the convention no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, i don't think it's really necessary to add prefix
If there's some differences between dev and prod versions, let's add if statement or sth inside code, and you'll have other output file.
@@ -1,6 +1,6 @@ | |||
import { createRoot } from 'react-dom/client'; | |||
import '@src/index.css'; | |||
import '@extension/ui/lib/global.css'; | |||
import '../dist/global-out.css'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe your plugin are able to add this import during build to .js
bundled file?
We can avoid those imports in each file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh that's a great idea, perhaps will solve my build time error not found this file during build
name: 'process-css', | ||
async configResolved(config) { | ||
const inputFile = path.resolve(rootDir, 'ui/lib/global.css'); | ||
const outputFile = path.resolve(config.root, 'dist/global-out.css'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's really necessary to change name to -out
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are three changes to this PR
- centralization of tailwindcss bundling
- add multiple entry points for content-ui
- update package (?)
First of all, the centralization of the CSS bundling process into the build process seems like a good idea. Basically I support your idea, but I'm concerned that we need to make sure there are no side effects or unexpected issues, that it's not a common implementation and users may struggle when adding the plugin from existing settings(by some tutorials, guide...), and that it's difficult to set the settings separately on each page.
Second, I'm skeptical that adding an entry point for content-ui is a necessary task. Users can achieve the same thing by copy/pasting the pages they want, such as content, content-ui, and registering them in the manifest if they want. My understanding is that this is a convenience feature, is that correct? Please let me know if I'm missing something.
"generate-i8n": "node generate-i18n.mjs", | ||
"ready": "pnpm generate-i8n && node build.dev.mjs", | ||
"build": "pnpm generate-i8n && node build.prod.mjs", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏼
pages/content-ui/src/page1/page1.tsx
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be page1/App.tsx
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeap i agree. i just want to show example
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
page2/App.tsx
👀
import { isDev, withPageConfig } from '@extension/vite-config'; | ||
import { processCssPlugin } from '@extension/hmr'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO some people might wonder why this plugin include hmr package... 🤔
It would be nice to refactor it to collect packages used in the development environment in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
erm bro this is your own method ... haha you've written like this for your own plugin, so i just follow your convention :
import { makeEntryPointPlugin } from '@extension/hmr';
import { withPageConfig, isDev } from '@extension/vite-config';
const rootDir = resolve(__dirname);
const libDir = resolve(rootDir, 'lib');
export default withPageConfig({
resolve: {
alias: {
'@lib': libDir,
},
},
publicDir: resolve(rootDir, 'public'),
plugins: [isDev && makeEntryPointPlugin()],
i follow your makeentrypointplugin
/** | ||
* Here is the place to add new entry points, u can straight away add new folders here to include | ||
* without creating folders in src dir manually. | ||
* @param {string[]} options.include - Folders to include !!MODIFY ME!! | ||
* @param {string[]} options.exclude - Folders to exclude !!MODIFY ME!! | ||
* @param {boolean} options.createRecursively - Create folders recursively | ||
* @returns {Record<string, string>} | ||
*/ | ||
const entryPoints = getEntryPoints({ | ||
include: ['page1', 'page2'], createRecursively: true | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems more like makeEntryponits
than getEntryPoints
, and shouldn't it be possible to run it as a separate script? I'm curious why it's in the vite config together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cause getentrypoints/makeentrypoints is resolving to the array of folder names to be used in vite.config entries. that's why its here. if not vite config don't know which folder to process. And yes this is more of convenience feature and people can add to manifest.json later
const foldersToProcess = | ||
options.include || | ||
readdirSync(srcDir, { withFileTypes: true }) | ||
.filter((item): item is Dirent => item.isDirectory()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an unnecessary assertion.
const entryFolder = resolve(srcDir, folderName); | ||
const entryFileTS = resolve(entryFolder, 'index.ts'); | ||
const entryFileTSX = resolve(entryFolder, `${folderName}.tsx`); | ||
const existingEntryFileTSX = resolve(srcDir, 'App.tsx'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The details of creating an existing App.tsx as an entry point for pages is a very specific implementation that is difficult to understand from the outside.
App.tsx is not currently used anywhere, so it seems like a good idea to rename it to something like Template.tsx.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahhh i just added that, cause user can choose between just single page or multi page, if user chooses to have multipage, he can always just add stuff into getentrypoints.includes which will generate those template for them. so its easier for them to understand.
@@ -51,7 +51,7 @@ const manifest = deepmerge( | |||
}, | |||
{ | |||
matches: ['http://*/*', 'https://*/*', '<all_urls>'], | |||
js: ['content-ui/index.iife.js'], | |||
js: ['content-ui/page1/index_dev.js'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of missing implementation about shadow dom injection, this is totally not working right now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
erm bro.. basically what you need to do is just this :
import { createRoot } from 'react-dom/client';
import '@src/index.css';
import '../dist/global-out.css';
import NewTab from '@src/NewTab';
ive included in every page that needed this css bundle.. it worked outright for me without pasting
@tailwind base @tailwind components all over the place
you couldn't see it, because of the fail build? did you try local version?
based on ur first question, since the original code was already opinionated on tailwindcss i just help u to internalize the bundle process which is the exact same as running the build:tailwindcss script, your approach doesnt provide hmr and mine does. we can ofc work on different ui package which people ccan just install whtever ui library they want. but the step of bundling should be the same |
Make tailwindcss centralized css production settings, without creating postcss config in every package.json
Added multiple entryfiles just for content-ui
Priority*
Purpose of the PR*
Streamlining css production for shadowdom, which included minification and treeshaking to be used for shadowdom
Added multiple entryfiles just for content-ui
Changes*
Tailwindcss bundle production is a mess, i created a plugin to be shared accross all pages folder, without the need of redundant @tailwind base ... etc, to be pasted all over the code, the plugin itself contains the postcss config, and thereby internalizing the css production for shadowdom. And hence the command https://github.com/Jonghakseo/chrome-extension-boilerplate-react-vite/blob/a7d51c3266803703e211223e49de675d119fdfa6/pages/content-ui/package.json#L15C1-L16C1 can be removed,
and this also fixes hmr crashes upon live editing of tailwindcss classess
Multi entry files for content-ui folder, as a normal dev will have multiple subdomains to inject across, its smarter to have multiple entry files injected for different subdomains, I've made the script to autogenerate folders and files inside content-ui, but u have to pay attention as well to make changes in manifest.js
How to check the feature
Everything works even without the postcss config in package.json, and every pages now have its own generated global-out.css
just fork my repo and install as usual, you'll see all the tailwindcss still works perfectly
https://discord.com/channels/1263404974830915637/1264514968288759889/1285558712941023345
as you see above hmr doesn't crash
All the css is still working but with leaner size