Using Tailwind3 in Flask application without manually (re-)generating css - html

I'm currently trying to set up a flask project using tailwindcss 3.0.23. For templating I'm using jinja. Furthermore yarn is used. During previous projects when working on frontend components I was used to an automatic adoption of styling by the usage of inline HTML classes. As I worked myself through this tutorial, I just realized I have to re-run npx tailwindcss -i ./static/src/style.css -o ./static/css/main.css to generate the most recent version of my tailwind css classes, that I defined in my style.css. As I'm now a lazy developer I would like to configure the project in a way that introduces two things.
#1 automatic generation of most recent css
This should allow me to add tailwind classes, which are automatically applied after saving my .css file and reloading my localhost:3000/index page.
#2 inline tailwind html classes for styling
As described earlier, I need to put all my tailwind classes into the style.css file which looks like the following code snippet, to define a class todo-text that is then later used in my templates/index.html. Instead I would be more flexible and also be able to add tailwind classes to my exisitng index.html like this. <p class="text-xl font-mono font-bold">text</p>
#tailwind base;
#tailwind components;
.todo-text {
#apply text-xl font-mono font-bold;
}
#tailwind utilities;
I have already read about the just-in-time engine of tailwind, but I'm not really sure how to configure my project so that it will work using tailwind 3.0.23. I further do not want to use a CDN as solution and I would appreciate anybody that would also add some explanation about the inner workings, why my current process is so cumbersome and furthermore, which role nodejs plays in this whole topic. Lastly, I've heard of the Flask Assets package but I'm not sure if this is even an option to solve my issues.
Config: My tailwind.config.js looks like this:
module.exports = {
content: ["./templates/*.{html,js,jsx}"],
theme: {
extend: {},
},
plugins: [],
};
Update: As a limited answer to "Why node? What is node used for?" I want to reference this post. But want to encourage you to add more elaborate sources to understand the background of using nodejs better.

I came across the -watch flag that automatically regenerates the latest .css after changes occur.
So just open a new terminal and run npx tailwindcss -i ./static/src/input.css -o ./static/dist/output.css --watch to activate automatic updates after changes in your template files.
Hope that helps!

I was recently struggling with the same problem and was determined to get the compiler automatically started. After some research I found a way to plug-in a subprocess that is automatically torn down when the flask server shuts down.
The compiler is configured to only start in debug mode (flask --debug run).
Assumptions for this code to work:
tailwind was installed with npm
your npm package.json and tailwind.config.js are located in the parent folder of your flask app folder
you define the command to run tailwindcss (e.g. npx tailwindcss -i {src} -o {dst} --minify --watch) within your package.json file as a child of scripts.
package.json
{
"dependencies": {. . .},
"scripts": {
"watch": "npx tailwindcss -i ./app/static/src/main.css -o ./app/static/dist/main.min.css --minify --watch"
}
}
app/utils/compiler.py
import atexit
import json
import os
import shlex
import subprocess
from pathlib import Path
from typing import Optional
import flask
import werkzeug
class TailwindCompiler:
proc: Optional[subprocess.Popen] = None
def __init__(
self,
app: flask.Flask,
npm_script_name: str,
debugmode_only: bool = True,
):
"""Start subprocess to compile TailwindCSS on-the-fly on change.
Prerequisites: flask app is run in debug mode & second instance started
by `werkzeug` is running (this second instance is needed in debug mode
to watch for changes). This ensures that the subprocess is only started
once.
"""
self.app = app
debugmode = app.config["DEBUG"]
is_reloader = werkzeug.serving.is_running_from_reloader()
if debugmode and is_reloader:
self.run(npm_script_name)
elif not debugmode and not debugmode_only:
self.run(npm_script_name)
else:
pass
def run(self, npm_script_name):
"""Run TailwindCSS Compiler as subprocess.
Store the current working dir and assume that tailwind, configs,
etc. are in the apps parent dir. Change the working directory to the
parent dir. Get the command for running tailwind from the package.json.
Start the subprocess. Then change back to the original working dir.
Finally register the subprocess so that it can be shut down on exit.
Parameters
----------
npm_script_name : str
The script that should be run must be defined in a `package.json`
file as a child of the `scripts` key like so:
"scripts": {
"watch": "npx tailwindcss -i ./app/static/src/main.css -o ./app/static/dist/main.min.css --minify --watch"
}
"""
print("=== Starting TailwindCSS Compiler ===")
cwd = os.getcwd()
app_parent_dir = str(Path(self.app.root_path).parent)
os.chdir(app_parent_dir)
with open("package.json") as f:
package = json.load(f)
try:
cmd = shlex.split(package["scripts"][npm_script_name])
except KeyError:
raise ValueError(
f"No script with name '{npm_script_name}' "
"found in `package.json`."
)
TailwindCompiler.proc = subprocess.Popen(cmd)
os.chdir(cwd)
atexit.register(TailwindCompiler.terminate_on_exit)
#classmethod
def terminate_on_exit(cls):
print("=== Closing TailwindCSS Compiler ===")
TailwindCompiler.proc.terminate()
It is now very easy to use! Initialize the class after you created the app object.
app.py
from app import create_app
from app.utils import TailwindCompiler
app = create_app()
# Run TailwindCSS as subprocess; watch for changes; build css on-the-fly
TailwindCompiler(app, npm_script_name="watch", debugmode_only=True)
The output of the compiler is presented between the normal flask server logs.
flask --debug run
* Serving Flask app 'wsgi.py'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
* Restarting with stat
=== Starting TailwindCSS Compiler ===
* Debugger is active!
* Debugger PIN: 143-120-061
Rebuilding...
Done in 168ms.
127.0.0.1 - - [05/Dec/2022 11:48:21] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [05/Dec/2022 11:48:21] "GET /static/dist/main.min.css HTTP/1.1" 200 -
. . .
^C=== Closing TailwindCSS Compiler ===

Related

Install multiple vs code extensions in CICD

My unit test launch looks like this. As you can see I have exploited CLI options to install a VSIX my CICD has already produced, and then also tried to install ms-vscode-remote.remote-ssh because I want to re-run the tests on a remote workspace.
import * as path from 'path';
import * as fs from 'fs';
import { runTests } from '#vscode/test-electron';
async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
// The path to the extension test runner script
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');
const vsixName = fs.readdirSync(extensionDevelopmentPath)
.filter(p => path.extname(p) === ".vsix")
.sort((a, b) => a < b ? 1 : a > b ? -1 : 0)[0];
const launchArgsLocal = [
path.resolve(__dirname, '../../src/test/test-docs'),
"--install-extension",
vsixName,
"--install-extension",
"ms-vscode-remote.remote-ssh"
];
const SSH_HOST = process.argv[2];
const SSH_WORKSPACE = process.argv[3];
const launchArgsRemote = [
"--folder-uri",
`vscode-remote://ssh-remote+testuser#${SSH_HOST}${SSH_WORKSPACE}`
];
// Download VS Code, unzip it and run the integration test
await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: launchArgsLocal });
await runTests({ extensionDevelopmentPath, extensionTestsPath, launchArgs: launchArgsRemote });
} catch (err) {
console.error(err);
console.error('Failed to run tests');
process.exit(1);
}
}
main();
runTests downloads and installs VS Code, and passes through the parameters I supply. For the local file system all the tests pass, so the extension from the VSIX is definitely installed.
But ms-vscode-remote.remote-ssh doesn't seem to be installed - I get this error:
Cannot get canonical URI because no extension is installed to resolve ssh-remote
and then the tests fail because there's no open workspace.
This may be related to the fact that CLI installation of multiple extensions repeats the --install-extension switch. I suspect the switch name is used as a hash key.
What to do? Well, I'm not committed to any particular course of action, just platform independence. If I knew how to do a platform independent headless CLI installation of VS Code:latest in a GitHub Action, that would certainly do the trick. I could then directly use the CLI to install the extensions before the tests, and pass the installation path. Which would also require a unified way to get the path for vs code.
Update 2022-07-20
Having figured out how to do a platform independent headless CLI installation of VS Code:latest in a GitHub Action followed by installation of the required extensions I face new problems.
The test framework options include a path to an existing installation of VS Code. According to the interface documentation, supplying this should cause the test to use the existing installation instead of installing VS Code; this is why I thought the above installation would solve my problems.
However, the option seems to be ignored.
My latest iteration uses an extension dependency on remote-ssh to install it. There's a new problem: how to get the correct version of my extension onto the remote host. By default the remote host uses the marketplace version, which obviously won't be the version we're trying to test.
I would first try with only one --install-extension option, just to check if any extension is installed.
I would also check if the same set of commands works locally (install VSCode and its remote SSH extension)
Testing it locally (with only one extension) also allows to check if that extension has any dependencies (like Remote SSH - Editing)

Storyshots doesn't work on local storybook-static folder

Problem Summary
Storybook snapshot test on static storybook returning blank screenshots even though they look fine on localhost:8080 when I ran npx http-server storybook-static
Tech stack and relevant code
Vue 3
Vite
Storybook
Jest
Storyshots
Puppeteer
I have components and their respective stories. npm run storybook works perfectly fine. My storybook.spec.js is as follows:
import { imageSnapshot } from "#storybook/addon-storyshots-puppeteer"
import initStoryshots from "#storybook/addon-storyshots"
initStoryshots({
suite: "Image storyshots",
test: imageSnapshot(
storybookUrl: 'file://absolute/path/to/my/storybook-static'
)
})
I ran the following. fyi, I did not modify any file in storybook-static after running npm run build-storybook.
npm run build-storybook
npm run test
npm run test constitutes jest --config=jest.config.js test
Problem
Unfortunately, the screenshots I get are all blank and fail the snapshot test.
I suspect it might be due to a CORS error just like other Storybook users when they click <project-root>/storybook-static/index.html after running npm run build-storybook, to which I want to ask for a solution as well, because I wanna run test remotely on a headless server.
Note
I used absolute path because relative path caused a resource not found error during the testing process.
The problem is that you're running the tests from file:// instead of http://. So the URI is file:// and the img url ends up like this after applying some url logic: path.resolve(window.location, '/your-image.png') file:///your-image.png.
If this is the case you could change to http://. You can start a express server and serve the storybook-static folder from setupGlobal and then shut it down in teardownGlobal. Then you will need to change your storybookUrl to http://localhost:<some-port>.
None of the images were loading within my pipeline but worked fine locally, ended up being because the components were fetching images using a relative path <img src="/my-image" /> which apparently is not allowed using the file protocol.
I ended up doing 2 things:
Updating the static dirs directory to use the root by updating the main.js file in storybook
module.exports = {
staticDirs: [{ from: '../static', to: '/' }],
}
Added a script to remove the leading slash of images in the preview-head.html file from storybook
<script>
document.addEventListener('DOMContentLoaded', () => {
Array.from(document.querySelectorAll('img')).forEach((img) => {
const original = img.getAttribute('src');
img.setAttribute('src', original.replace('/', ''));
});
});
</script>
Another (arguably better) approach would be to run the tests through a server where you can access the images

Why npm run serve is throwing ERR_OSSL_EVP_UNSUPPORTED? [duplicate]

This question already has answers here:
Error message "error:0308010C:digital envelope routines::unsupported"
(50 answers)
Closed 12 months ago.
I'm having an issue with a Webpack build process that suddenly broke, resulting in the following error...
<s> [webpack.Progress] 10% building 0/1 entries 0/0 dependencies 0/0 modules
node:internal/crypto/hash:67
this[kHandle] = new _Hash(algorithm, xofLen);
^
Error: error:0308010C:digital envelope routines::unsupported
at new Hash (node:internal/crypto/hash:67:19)
at Object.createHash (node:crypto:130:10)
at BulkUpdateDecorator.hashFactory (/app/node_modules/webpack/lib/util/createHash.js:155:18)
at BulkUpdateDecorator.update (/app/node_modules/webpack/lib/util/createHash.js:46:50)
at OriginalSource.updateHash (/app/node_modules/webpack-sources/lib/OriginalSource.js:131:8)
at NormalModule._initBuildHash (/app/node_modules/webpack/lib/NormalModule.js:888:17)
at handleParseResult (/app/node_modules/webpack/lib/NormalModule.js:954:10)
at /app/node_modules/webpack/lib/NormalModule.js:1048:4
at processResult (/app/node_modules/webpack/lib/NormalModule.js:763:11)
at /app/node_modules/webpack/lib/NormalModule.js:827:5 {
opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
library: 'digital envelope routines',
reason: 'unsupported',
code: 'ERR_OSSL_EVP_UNSUPPORTED'
}
command terminated with exit code 1
I've tried googling ERR_OSSL_EVP_UNSUPPORTED webpack which yielded almost no useful results, but it did highlight an issue using MD4 as provided by OpenSSL (which is apparently deprecated?) to generate hashes.
The webpack.config.js code is as follows:
const path = require('path');
const webpack = require('webpack');
/*
* SplitChunksPlugin is enabled by default and replaced
* deprecated CommonsChunkPlugin. It automatically identifies modules which
* should be splitted of chunk by heuristics using module duplication count and
* module category (i. e. node_modules). And splits the chunks…
*
* It is safe to remove "splitChunks" from the generated configuration
* and was added as an educational example.
*
* https://webpack.js.org/plugins/split-chunks-plugin/
*
*/
/*
* We've enabled TerserPlugin for you! This minifies your app
* in order to load faster and run less javascript.
*
* https://github.com/webpack-contrib/terser-webpack-plugin
*
*/
const TerserPlugin = require('terser-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/js/scripts.js',
output: {
path: path.resolve(__dirname, 'js'),
filename: 'scripts.js'
},
devtool: 'source-map',
plugins: [new webpack.ProgressPlugin()],
module: {
rules: []
},
optimization: {
minimizer: [new TerserPlugin()],
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/
}
},
chunks: 'async',
minChunks: 1,
minSize: 30000,
name: 'true'
}
}
};
How do I change the hashing algorithm used by Webpack to something else?
I was able to fix it via:
export NODE_OPTIONS=--openssl-legacy-provider
sachaw's comment to Node.js v17.0.0 - Error starting project in development mode #30078
But they say they fixed it: ijjk's comment to Node.js v17.0.0 - Error starting project in development mode #30078:
Hi, this has been updated in v11.1.3-canary.89 of Next.js, please update and give it a try!
For me, it worked only with the annotation above.
I also want to point out that npm run start works with -openssl-legacy-provider, but npm run dev won't.
It seems that there is a patch:
Node.js 17: digital envelope routines::unsupported #14532
I personally downgraded to 16-alpine.
I had this problem too. I'd accidentally been running on the latest Node.js (17.0 at time of writing), not the LTS version (14.18) which I'd meant to install. Downgrading my Node.js install to the LTS version fixed the problem for me.
There is a hashing algorithm that comes with Webpack v5.54.0+ that does not rely on OpenSSL.
To use this hash function that relies on a npm-provided dependency instead of an operating system-provided dependency, modify the webpack.config.cjs output key to include the hashFunction: "xxhash64" option.
module.exports = {
output: {
hashFunction: "xxhash64"
}
};
Ryan Brownell's answer is the ideal solution if you are using Webpack v5.54.0+.
If you're using an older version of Webpack, you can still solve this by changing the hash function to one that is not deprecated. (It defaults to the ancient md4, which OpenSSL has removed support for, which is the root cause of the error.) The supported algorithms are any supported by crypto.createHash. For example, to use SHA-256:
module.exports = {
output: {
hashFunction: "sha256"
}
};
Finally, if you are unable to change the Webpack configuration (e.g., if it's a transitive dependency which is running Webpack), you can enable OpenSSL's legacy provider to temporarily enable MD4 during the Webpack build. This is a last resort. Create a file openssl.cnf with this content…
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
…and then set the environment variable OPENSSL_CONF to the path to that file when running Webpack.
It is not my answer really, but I found this workaround /hack/ to fix my problem Code Check in for a GitHub project... see the bug comments here.
I ran into ERR_OSSL_EVP_UNSUPPORTED after updating with npm install.
I added the following to node_modules\react-scripts\config\webpack.config.js
const crypto = require("crypto");
const crypto_orig_createHash = crypto.createHash;
crypto.createHash = algorithm => crypto_orig_createHash(algorithm == "md4" ? "sha256" : algorithm);
I tried Ryan Brownell's solution and ended up with a different error, but this worked...
This error is mentioned in the release notes for Node.js 17.0.0, with a suggested workaround:
If you hit an ERR_OSSL_EVP_UNSUPPORTED error in your application with Node.js 17, it’s likely that your application or a module you’re using is attempting to use an algorithm or key size which is no longer allowed by default with OpenSSL 3.0. A command-line option, --openssl-legacy-provider, has been added to revert to the legacy provider as a temporary workaround for these tightened restrictions.
I ran into this issue using Laravel Mix (Webpack) and was able to fix it within file package.json by adding in the NODE_OPTIONS=--openssl-legacy-provider (referenced in Jan's answer) to the beginning of the script:
package.json:
{
"private": true,
"scripts": {
"production": "cross-env NODE_ENV=production NODE_OPTIONS=--openssl-legacy-provider node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"dependencies": {
...
}
}
Try upgrading your Webpack version to 5.62.2.
I faced the same challenge, but you just need to downgrade Node.js to version 16.13 and everything works well. Download LTS, not the current on Downloads.
I had the same problem with my Vue.js project and I solved it.
macOS and Linux
You should have installed NVM (Node Version Manager). If you never had before, just run this command in your terminal:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
Open your project
Open the terminal in your project
Run the command nvm install 16.13.0 or any older version
After the installation is completed, run nvm use 16.13.0
I faced the same problem in a project I developed with Next.js. For the solution, I ran the project as follows and I solved the problem.
cross-env NODE_OPTIONS='--openssl-legacy-provider' next dev
This means that you have the latest Node.js version. If you are using it for Docker then you need to change the image from
FROM node
to
FROM node:14

Export .MWB to working .SQL file using command line

We recently installed a server dedicated to unit tests, which deploys
updates automatically via Jenkins when commits are done, and sends
mails when a regression is noticed
> This requires our database to always be up-to-date
Since the database-schema-reference is our MWB, we added some scripts
during deploy, which export the .mwb to a .sql (using python) This
worked fine... but still has some issues
Our main concern is that the functions attached to the schema are not exported at all, which makes the DB unusable.
We'd like to hack into the python code to make it export scripts... but didn't find enough informations about it.
Here is the only piece of documentation we found. It's not very clear for us. We didn't find any information about exporting scripts.
All we found is that a db_Script class exists. We don't know where we can find its instances in our execution context, nor if they can be exported easily. Did we miss something ?
For reference, here is the script we currently use for the mwb to sql conversion (mwb2sql.sh).
It calls the MySqlWorkbench from command line (we use a dummy x-server to flush graphical output.)
What we need to complete is the python part passed in our command-line call of workbench.
# generate sql from mwb
# usage: sh mwb2sql.sh {mwb file} {output file}
# prepare: set env MYSQL_WORKBENCH
if [ "$MYSQL_WORKBENCH" = "" ]; then
export MYSQL_WORKBENCH="/usr/bin/mysql-workbench"
fi
export INPUT=$(cd $(dirname $1);pwd)/$(basename $1)
export OUTPUT=$(cd $(dirname $2);pwd)/$(basename $2)
"$MYSQL_WORKBENCH" \
--open $INPUT \
--run-python "
import os
import grt
from grt.modules import DbMySQLFE as fe
c = grt.root.wb.doc.physicalModels[0].catalog
fe.generateSQLCreateStatements(c, c.version, {})
fe.createScriptForCatalogObjects(os.getenv('OUTPUT'), c, {})" \
--quit-when-done
set -e

missing jruby gem for logstash

I have downloaded the latest logstash 1.4, and when I run it with the following config:
input {
eventlog {
}
}
output { stdout {} }
I get this error :
D:\logstash-1.4.0\bin>logstash agent -f simpleConfig.config -l logs.log
Sending logstash logs to agent.log.
←[33mUsing milestone 2 input plugin 'eventlog'. This plugin should be stable, bu
t if you see strange behavior, please let us know! For more information on plugi
n milestones, see http://logstash.net/docs/1.4.0/plugin-milestones {:level=>:war
n}←[0m
LoadError: no such file to load -- jruby-win32ole
require at org/jruby/RubyKernel.java:1085
require at file:/D:/logstash-1.4.0/vendor/jar/jruby-complete-1
.7.11.jar!/META-INF/jruby.home/lib/ruby/shared/rubygems/core_ext/kernel_require.
rb:55
require at file:/D:/logstash-1.4.0/vendor/jar/jruby-complete-1
.7.11.jar!/META-INF/jruby.home/lib/ruby/shared/rubygems/core_ext/kernel_require.
rb:53
require at D:/logstash-1.4.0/lib/logstash/JRUBY-6970.rb:27
require at D:/logstash-1.4.0/vendor/bundle/jruby/1.9/gems/poly
glot-0.3.4/lib/polyglot.rb:65
register at D:/logstash-1.4.0/lib/logstash/inputs/eventlog.rb:3
7
start_inputs at D:/logstash-1.4.0/lib/logstash/pipeline.rb:135
each at org/jruby/RubyArray.java:1613
start_inputs at D:/logstash-1.4.0/lib/logstash/pipeline.rb:134
run at D:/logstash-1.4.0/lib/logstash/pipeline.rb:72
execute at D:/logstash-1.4.0/lib/logstash/agent.rb:136
run at D:\logstash-1.4.0\lib\logstash\runner.rb:190
call at org/jruby/RubyProc.java:271
initialize at D:/logstash-1.4.0/vendor/bundle/jruby/1.9/gems/stud
-0.0.17/lib/stud/task.rb:12
I think that the package win32ole jruby is missing, but I don't know how to add it.
Thanks in advance for your help
I installed the logstash-contrib-1.4.x.tar.gz.
I didn't found a download link so I copy the logstash download link and add "-contrib" in the filename eg: https://download.elasticsearch.org/logstash/logstash/logstash-contrib-1.4.2.tar.gz
This works fine in my case.
The installation is only unzip the file on home directoy and override all files. Now it works.