If you expect all depender apps to utilize tailwind, you can use tailwind classes in your library HTML and have them configure a content
path of ./node_modules/my-lib/esm2020/**/*.mjs
.
It finds the inlined/escaped classes in the Ivy compiled files.
esm2020
to scope the scan.
Update 11/30/22 - allowing the use of @apply
in the library
@apply
s are not resolved in precompiled library code as these files are not processed in that lifecycle.
As a workaround, you can pre-process your components to resolve @apply
styles before building the library.
- Create a
tailwind.config.js
to use in the compilation
- If your library project has a demo-app (highly suggest for impl testing), could utilize it's config file, unless you've got some crazy config in there. Since we're not rendering
@tailwind components
or anything, we won't get any excess styles
projects/my-lib/tailwind.config.js
module.exports = {
content: [
'./projects/my-lib/**/*.{html,ts,css,scss}',
],
};
Note the content
path is still relative from project root as that's the context it's ran at
- Create precompiler process
- Tailwind resolve into a new file (mostly so we don't mess things up accidentally locally)
- Point component at the new file
import { readFile, writeFile } from "fs";
import { sync } from 'glob';
import { exec } from 'child_process';
const libRoot = 'projects/my-lib/src/lib';
const tailwindConf = 'tailwind.config.js'; // may be apps/demo when using NX
const processedExt = '.precompiled.scss';
const styleRegex = /styleUrls:\s*\[([^\]]+)]/;
// Find all `.scss` files and tailwind process them
sync(`${libRoot}/**/*.component.scss`).forEach(file => {
const cssFile = file.replace(/\.scss$/, processedExt);
exec(`npx tailwind -c ${tailwindConf} -i ${file} -o ${cssFile}`, (err, stdout, stderr) => {
if (err) {
console.error(stderr);
throw err;
}
});
});
// .component.ts update
// Find all components with `styleUrls` and switch `.scss` extension to our precompiled file names
sync(`${libRoot}/**/*.component.ts`).forEach(file => {
readFile(file, (err, data) => {
if (err) throw err;
const content = data.toString();
const match = content.match(styleRegex);
if (match) {
const styleUrls = match[1]
.split(',')
.map(s => s.trim().replace('.scss', processedExt))
.join(', ');
writeFile(file, content.replace(styleRegex, `styleUrls: [${styleUrls}]`), (err) => {
if (err) throw err;
});
}
});
});
This should only be ran by your CI process and never committed.
Also this could easily be switched to javascript instead of typescript
Other possible ways to do this (untested) without the .component.ts
update:
- Utilize
environment.prod.ts
's production: true
flag to decide the style file to use
styleUrls: [ environment.prod ? 'my.component.precompiled.scss' : 'my.component.scss' ],
- Gotta remember this for all new components
- Change the tailwind compile to output to the same scss file
- Less moving parts - I liked the separate file so I'd realize quickly if it were accidentally ran/committed
- Add CI precompile command to
package.json
"build:ci": "node --require ts-node/register projects/my-lib/src/precompile.ts && npm run build:my-lib"
- Very rough implementation - remove
--require ts-node/register
if converted to javascript
I use NX workspace, so I added a new target in the library's project.json
:
"ci": {
"executor": "nx:run-commands",
"options": {
"command": "node --require ts-node/register libs/my-lib/src/precompile.ts"
}
},
and added a the package.json
entry as:
"build": "nx run-many --all --target build",
"build:ci": "npx nx ci && npm run build",
allowing build
to still be used locally.
- Build and Package/Release as normal
- With
@apply
's resolved, all should flow well
- If you used tailwind utility classes in HTML, be sure to see the very beginning of this answer
Tailwindless Depender
If you want applications to be able to utilize your library without them installing tailwind you could supply a stylesheet containing all the helper classes you used.
- Create a stylesheet to contain all used utilities
projects/my-lib/style.scss
@tailwind utilities;
- Add a
postbuild
to your package.json
to produce the stylesheet, assuming you use npm run build
to build the library.
"postbuild": "npx tailwind -c projects/my-lib/tailwind.config.js -i projects/my-lib/style.scss -o dist/my-lib/style.scss",
- Direct depender projects to then include this compiled stylesheet:
@import 'my-lib/style.scss'
Note tailwind does not compile SCSS into CSS - need to run through a SASS processor if you want to supply CSS.
Downside of this is all utility classes used in all components are produced, even if the depender app doesn't use them (same happens for projects using tailwind, so not so bad).
Also the depender project may produce duplicate utility classes if using tailwind itself.
Plus side is your library doesn't require the depender to have tailwind.
Note that you still need the above process to resolve @apply
's - this only gathers the utility classes used in the HTML