datamade / how-to

📚 Doing all sorts of things, the DataMade way
MIT License
88 stars 12 forks source link

Add third-party css files to Django asset pipeline #236

Closed smcalilly closed 2 years ago

smcalilly commented 2 years ago

Description

I'm unable to use CSS from an npm package because our current asset pipeline isn't configured to do this. I'm not familiar enough with the pipeline to fix it within a project's budget/scope (I've tried quickly but gave up), so I usually just grab it from a CDN or find the CSS files and add them to the repo's local code. This doesn't always work, however, when the npm package uses a CSS pre-processor different from scss (like less).

Steps to reproduce

This bug report was prompted by rc-slider, but I've also had the issue with react-leaflet.

Request Method: GET

http://localhost:8000/projects-map/ 3.2.7 FilterError /app/node_modules/rc-slider/assets/index.css:1 .rc-slider { ^ ParseError: Unexpected token /usr/local/lib/python3.9/site-packages/compressor/filters/base.py, line 206, in input /usr/local/bin/python 3.9.7 ['/app', '/usr/local/lib/python39.zip', '/usr/local/lib/python3.9', '/usr/local/lib/python3.9/lib-dynload', '/usr/local/lib/python3.9/site-packages']

smcalilly commented 2 years ago

I'm not sure how to fix this.

It seems like a common problem based on this issue in the browserify-css library. We don't use that, but I tried with no success. Here are some concrete steps in their documentation to fix, but

  1. using @import doesn't work with django-compressor

  2. adding the global flag to the settings.py COMPRESS_PRECOMPILERS commands fails on this error:

    #16 1.719 CommandError: An error occurred during rendering /app/asset_dashboard/templates/asset_dashboard/planner.html: internal/validators.js:120
    #16 1.719     throw new ERR_INVALID_ARG_TYPE(name, 'string', value);
    #16 1.719     ^
    #16 1.719 
    #16 1.719 TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined
    #16 1.719     at validateString (internal/validators.js:120:11)
    #16 1.719     at Object.resolve (path.js:980:7)
    #16 1.719     at Deps._transform (/app/node_modules/module-deps/index.js:110:32)
    #16 1.719     at Deps.Transform._read (/app/node_modules/readable-stream/lib/_stream_transform.js:184:10)
    #16 1.719     at Deps.Transform._write (/app/node_modules/readable-stream/lib/_stream_transform.js:172:83)
    #16 1.719     at Labeled.Pipeline._write (/app/node_modules/stream-splicer/index.js:70:22)
    #16 1.719     at doWrite (/app/node_modules/readable-stream/lib/_stream_writable.js:428:64)
    #16 1.719     at writeOrBuffer (/app/node_modules/readable-stream/lib/_stream_writable.js:417:5)
    #16 1.719     at Labeled.Writable.write (/app/node_modules/readable-stream/lib/_stream_writable.js:334:11)
    #16 1.719     at Labeled.ondata (/app/node_modules/readable-stream/lib/_stream_readable.js:619:20) {
    #16 1.719   code: 'ERR_INVALID_ARG_TYPE'
    #16 1.719 }
  3. fails on the same error as no. 2

I've searched the error in the browserify github issues but haven't had any luck either.

smcalilly commented 2 years ago

My big question: how does something like the react-bootstrap css not have this problem but other npm libraries do? Is it because we already have bootstrap CSS loaded into the static files (seems unlikely)?

smcalilly commented 2 years ago

My big question: how does something like the react-bootstrap css not have this problem but other npm libraries do? Is it because we already have bootstrap CSS loaded into the static files (seems unlikely)?

To answer this question, the react-bootstrap library doesn't package the CSS with the library. They recommend a few different approaches in their docs for dealing with the CSS. You can see an example in their source code where it's not importing the CSS, but only referencing the bootstrap classes.

I created a fresh app and installed react-bootstrap. Since our cookiecutter template has bootstrap out of the box, the react-bootstrap components worked without having to import any CSS in the JS file. If I remove the reference to the bootstrap stylesheet in my code, then the bootstrap component isn't styled.

Interestingly, in an app where we're using react-bootstrap, the bootstrap css in node_modules is imported via an scss file: https://github.com/fpdcc/ccfp-asset-dashboard/blob/0a53b96f85676eec8059cc867741932cec87c7d2/asset_dashboard/static/scss/custom.scss#L16

We could perhaps (maybe?) do something like that for any npm packages where we have this issue, but I would rather find an automated process using our browserify and compressor pipeline. I think that importing any npm css like this by hand could be confusing and overlooked by future developers, even if we documented it.

smcalilly commented 2 years ago

Just to try to articulate what is happening and what i know/don't know, so I can understand it:

smcalilly commented 2 years ago

This is the TLDR I've always needed and now the compressor finally makes a little more sense!

smcalilly commented 2 years ago

maybe we could add a script that searches node_modules for *.css files and copies them to a css file that can be processed by the compressor?

one problem with this approach: it will copy over all the css in node_modules, which will bring over css that our app won't use. so, per http request, it will download a ton of css that isn't used. not the best approach! there is a way to use postcss and purgecss to remove unused css, see this example where i did that with tailwind css: https://github.com/data-mississippi/data-ms-app/blob/a1f8006af0492e4962f72333bb355eaec317ce02/frontend/postcss.config.js#L3. not sure this is possible with our config, nor do i know if we want to add another build tool.

smcalilly commented 2 years ago

I have a WIP test app here: https://github.com/smcalilly/fix-css

smcalilly commented 2 years ago

leaving these notes to pick up in a bit:

when using styles from javascript packages, include them this way

two cases:

what’s the difference between require and import

add this to the cookie cutter -g browserify-css

COMPRESS_PRECOMPILERS = (
    ('module', 'export NODE_PATH=/app/node_modules && npx browserify -g browserify-css {infile} -t [ babelify --global ] > {outfile}'),
    ('text/jsx', 'export NODE_PATH=/app/node_modules && npx browserify -g browserify-css {infile} -t [ babelify --global --presets [ @babel/preset-react ] ] > {outfile}')
)

here’s how you do a custom bootstrap

smcalilly commented 2 years ago

https://github.com/cheton/browserify-css#1-how-do-i-include-css-files-located-inside-the-node_modules-folder