protibimbok / django-vite-plugin

This plugin configures Vite for use with Django backend.
103 stars 13 forks source link

Could we remove declaration from `Vite` section #1

Open baseplate-admin opened 1 year ago

baseplate-admin commented 1 year ago

Hi there,

Thanks for making this ( i was the one who asked you the question in the forum )

//vite.config.js
import { defineConfig } from 'vite'
import djangoVite from 'django-vite-plugin'

export default defineConfig({
    plugins: [
        djangoVite([
            // Can we not declare the files in here?
            'home/js/app.js',
            'home/css/style.css',
        ])
    ],
});

So i was thinking can we remove this section so that it can auto detect the path when we call the template tag.

with all being said Mashallah for this package brother and ramadan mubarak

protibimbok commented 1 year ago

This section is for providing the build entrypoints to the vite build tools. Removing this configuration is not worth the effort. We'll have to scan through all template files and collect the used assets and so on. Even after this, there's a possibility of dynamic assets, which is impossible to track with certainty. Another approach might be just to compile everything. Given that the static file count can be really large, this couldn't be any less optimal.

But currently I am working on a helper function that'll help the developers to pass these assets more elegantly.

// Possible syntax
...
import { inputHelper } from 'django-vite-plugin'
djangoVite(inputHelper([
     '@app': {
          'dir1': ['file1.js', 'file2.js'],
          'dir2': {}
      }
]))
...

Even this does not seem like that much of a help. But there's place for improvements.

Thank you for your feedback. "Ramadan Kareem"

baseplate-admin commented 1 year ago

Even after this, there's a possibility of dynamic assets, which is impossible to track with certainty.

I dont get this. Dynamic assets?

Given that the static file count can be really large, this couldn't be any less optimal.

Forgive me but Doesn't vite offer source imports ?

Even this does not seem like that much of a help. But there's place for improvements.

One possibility i see is aliasing all page under one namespace.

import { inputHelper } from 'django-vite-plugin'
djangoVite(inputHelper([     
          'home_page': ['file1.js', 'file2.js'],
          'another_page': {}     
]))

প্রতিবিম্বক। nice name :]

protibimbok commented 1 year ago

I dont get this. Dynamic assets?

Any link to css/js you send from the view (see the example project.).

def home(req):
    return render(req, 'index.html', {
        'dyn_js' : 'a_dynamic_js.js',
        'dyn_css' : 'a_dynamic_css.css',
        'dyn_attr': 'A dynamic attr'
    })

Forgive me but doesn't vite offer source imports ?

It's about passing the inputs to rollupOptions everything you give here will result in a separate output file - which is not optimal. But if you want to do this, you may just use glob

//vite.config.js
import { defineConfig } from 'vite'
import djangoVite from 'django-vite-plugin'
import glob from 'glob'

export default defineConfig({
    plugins: [
        djangoVite(glob.sync(
             '/**/*.{js,css}'
        ))
    ],
});

And the inputHelper is still under consideration. Thank you

protibimbok commented 1 year ago

As I've stated earlier, to remove this we'll have to scan through all of the template files to get the references. Even though it's not the best way to go, but seems like I should give it a try...

baseplate-admin commented 1 year ago

As I've stated earlier, to remove this we'll have to scan through all of the template files to get the references. Even though it's not the best way to go, but seems like I should give it a try...

thanks a bunch for reconsidering..

def home(req):
    return render(req, 'index.html', {
        'dyn_js' : 'a_dynamic_js.js',
        'dyn_css' : 'a_dynamic_css.css',
        'dyn_attr': 'A dynamic attr'
    })

I have never seen ( nor used ) this pattern from django app. Django ( views ) is not a good candidate to write logics for assets IMHO ( templates is ).

And the inputHelper is still under consideration.

Thanks again.

Lmk if you need any help ( i dont know much but i will try to help )

Love from Bangladesh :D

protibimbok commented 1 year ago

I have never seen ( nor used ) this pattern from django app. Django ( views ) is not a good candidate to write logics for assets IMHO ( templates is ).

Neither did I. But when you write something for mass developer you shouldn't take away any feature.

Thank you for the support.

jbuerklin commented 10 months ago

I added a function to my vite.config.js that scans through the project directory in order to find *.html files with {% vite %} template tags. It works quite well for my uses. You could also easily add entrypoints manually if needed (e.g. for dynamic assets as discussed earlier)

import { defineConfig } from 'vite'
import { djangoVitePlugin } from 'django-vite-plugin'
import fs from 'fs'
import path from 'path'

/**
 * Searches through the project's HTML files and extracts the entrypoints from the {% vite %} tag.
 *
 * @returns {Array<string>} list of entrypoints
 */
const findEntrypoints = () => {
    let folders = ['.']
    let files = []
    while (folders.length > 0) {
        const folder = folders.pop()
        if (
            ['node_modules', 'venv', 'vite_build', 'static'].includes(
                path.basename(folder)
            )
        ) {
            continue
        }
        let results
        try {
            results = readDirectory(folder)
        } catch (error) {
            continue
        }
        files = files.concat(results.files)
        folders = folders.concat(results.folders)
    }
    const entrypoints = new Set()
    for (const file of files) {
        if (file.endsWith('.html')) {
            const content = fs.readFileSync(file, 'utf8')

            const matches = content.matchAll(
                /{%\s+vite\s+('|")(.+?)\1\s+.*?%}/g
            )
            for (const match of matches) {
                entrypoints.add(match[2])
            }
        }
    }
    const entrypointsList = Array.from(entrypoints)
    console.log(`found entrypoints:\n${entrypointsList.join('\n')}`)
    return entrypointsList
}

/**
 * Parses the given directory and extracts a list of all files and all subdirectories.
 *
 * @typedef ReadDirectoryReturnObject
 * @property {Array<string>} folders - list of all subdirectories
 * @property {Array<string>} files - list of all files
 * @param {string} dirPath - directory path to read
 * @returns {ReadDirectoryReturnObject} two list; all files and all subdirectories
 */
const readDirectory = (dirPath) => {
    const data = fs.readdirSync(dirPath)
    const folders = []
    const files = []
    data.forEach((file) => {
        const filepath = path.join(dirPath, file)
        if (file.includes('.')) {
            files.push(filepath)
        } else {
            folders.push(filepath)
        }
    })
    return { folders, files }
}

export default defineConfig({
    plugins: [
        djangoVitePlugin({
            input: findEntrypoints(),
            addAliases: true,
        }),
    ],
})
protibimbok commented 10 months ago

This is amazing. Some suggestions:

  1. No need to scan in dev mode. Only scan during the build command.
  2. Create a caching system. (I.e. storing the included files in a file as json and instead of scanning every time for {% vite %} you will scan only for the changed files)
  3. Maybe do it in Python
  4. Maybe use multi-threading as file count can be really large.

After completing these, please create a pull request.

Keep this functionality in a new file vite/src/lookup.ts and export from there.

elnygren commented 7 months ago

I'm using this library without the django-vite-plugin JS side vite plugin. My setup is the following:

  1. Vite runs completely without Django or any knowledge of it
  2. .hotfile is created by hand (content: http://127.0.0.1:5173)
  3. Django side settings:
    DJANGO_VITE_PLUGIN = {
    # 'npm run build' output directory; collectstatic will copy all files from here
    # must match vite.config.ts -> build -> outDir
    "BUILD_DIR": "src/static/vite",
    # prefix has to point to the outDir with manifest.json
    "BUILD_URL_PREFIX": "/static/vite/",
    # we don't keep the source code in static dirs so this can be False
    "STATIC_LOOKUP": False,
    "MANIFEST": "src/static/vite/manifest.json",  # can drop this with vite5
    }
  4. Vite side settings:
    export default defineConfig(({ mode }) => ({
    plugins: [react()],
    server: {
    host: '127.0.0.1',  // django is calling 127.0.0.1 instead of localhost
    },
    base: mode === 'development' ? '/' : '/static/vite/', // DJANGO_VITE_PLUGIN->BUILD_URL_PREFIX
    build: {
    manifest: true,  // generate manifest.json in outDir
    outDir: 'src/static/vite',  // must match DJANGO_VITE_PLUGIN-> BUILD_DIR
    rollupOptions: {
      input: [...glob.sync('src/vite/**/*.{ts,tsx,css}')], // <- where vite-processed code lives
    },
    },
    }))

Sure the downside is having to keep some settings in sync by hand... but that's a lot simpler than having Django-magic-code inside Vite and needing Django to do a vite build (think about CI/CD and containers...).

Not sure why I'd want more complexity than that? 🤔