Skip to content
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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

tngflx
Copy link

@tngflx tngflx commented Sep 18, 2024

Make tailwindcss centralized css production settings, without creating postcss config in every package.json
Added multiple entryfiles just for content-ui

Priority*

  • High: This PR needs to be merged first for other tasks.
  • Middle: This PR should be merged quickly to prevent conflicts due to common changes. (default)
  • Low: This PR does not affect other tasks, so it can be merged later.

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*

  1. 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

  2. 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

image
All the css is still working but with leaner size

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
Copy link
Collaborator

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

Copy link
Author

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?

Copy link
Collaborator

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

Comment on lines +2 to +3
import fs from 'fs/promises';
import path from 'path';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import fs from 'fs/promises';
import path from 'path';
import fs from 'node:fs/promises';
import path from 'node:path';

Copy link
Author

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

Copy link
Collaborator

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'));
Copy link
Collaborator

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe sth like that:

Suggested change
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())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not like that?

Suggested change
.filter((item): item is Dirent => item.isDirectory())
.filter((item: : Dirent) => item.isDirectory())

I don't really know this kind of typing, that's interesting

Copy link
Owner

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.

Copy link
Author

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');
Copy link
Collaborator

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'],
Copy link
Collaborator

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

Copy link
Author

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

Copy link
Collaborator

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`
Copy link
Collaborator

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?

Copy link
Author

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?

Copy link
Collaborator

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';
Copy link
Collaborator

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.

Copy link
Author

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');
Copy link
Collaborator

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?

Copy link
Owner

@Jonghakseo Jonghakseo left a 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

  1. centralization of tailwindcss bundling
  2. add multiple entry points for content-ui
  3. 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.

Comment on lines +17 to +19
"generate-i8n": "node generate-i18n.mjs",
"ready": "pnpm generate-i8n && node build.dev.mjs",
"build": "pnpm generate-i8n && node build.prod.mjs",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼

Copy link
Owner

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

Copy link
Author

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

Copy link
Owner

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';
Copy link
Owner

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.

Copy link
Author

@tngflx tngflx Sep 21, 2024

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

Comment on lines +9 to +19
/**
* 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
});
Copy link
Owner

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.

Copy link
Author

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())
Copy link
Owner

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');
Copy link
Owner

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.

Copy link
Author

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'],
Copy link
Owner

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

Copy link
Author

@tngflx tngflx Sep 21, 2024

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?

@tngflx
Copy link
Author

tngflx commented Sep 21, 2024

There are three changes to this PR

  1. centralization of tailwindcss bundling
  2. add multiple entry points for content-ui
  3. 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.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants