Flask-Node
Flask-Node manages an isolated Node/npm project for Flask applications and extensions. It contains no JavaScript library integrations, bundler, asset server, or frontend framework.
Requires Python 3.10+ and Flask 2.2–3.x. Install Node.js (including npm/npx) separately when executing commands; importing and initializing the Flask extension does not require it.
pip install Flask-Node
Application setup
from flask import Flask
from flask_node import Node
node = Node()
def create_app():
app = Flask(__name__)
node.init_app(app)
return app
Node(app) is also supported. init_app() registers defaults and the CLI,
without creating directories or launching commands. Each application owns a
separate NodeManager in app.extensions["node"]; a shared Node facade uses
the current application context. node.get_manager(app) gives explicit access
outside a context. Duplicate registration raises ConfigurationError.
Configuration
| Setting | Default | Meaning |
|---|---|---|
NODE_DIR |
.node |
Relative to app.root_path, or an absolute path |
NODE_BIN |
node |
Node executable name or path |
NODE_NPM_BIN |
npm |
npm executable name or path |
NODE_NPX_BIN |
npx |
npx executable name or path |
Configure before calling init_app(). Configuration is captured per app.
For a directory beside an application package, configure an absolute project
path. No behavior depends on the shell's current directory.
Lifecycle and dependencies
with app.app_context():
node.require("example", version="^1")
node.require("@scope/build-tool", version="^2", dev=True)
node.initialize()
node.install()
node.install("another-package", version="^3")
node.uninstall("another-package")
require() only records an in-memory declaration. Consumer extensions may call
it during application setup. It creates no files and does not check or install
packages. Identical declarations are idempotent; differing versions or dependency
sections raise DependencyConflictError. Version declarations are compared as
strings; Flask-Node does not solve semver ranges.
initialize() creates .node/package.json with private: true and empty
dependencies / devDependencies. It preserves an existing valid manifest
without rewriting it. Invalid manifests fail clearly. npm creates the lockfile
and installed modules later:
.node/
package.json
package-lock.json
node_modules/
install() initializes if necessary, merges active declarations into the
manifest, preserves unrelated fields and dependencies, and runs npm install.
Declarations take precedence over persisted entries for the same package.
install(name, version=None, dev=False) additionally installs a registry package;
without a version, npm chooses it unless an active declaration supplies one.
Conflicting explicit options fail before installation. Uninstalling an actively
required package is rejected. npm failures may leave manifest changes or partial
installation artifacts; operations are not transactional.
ci() requires package-lock.json and declarations matching the manifest, then
runs npm ci without rewriting the manifest. npm validates lockfile consistency.
Commit .node/package.json and .node/package-lock.json for reproducible builds;
ignore .node/node_modules/.
Commands and diagnostics
with app.app_context():
result = node.npm("run", "custom-script")
result = node.npx("some-tool", "--help", capture_output=False)
print(node.status())
Raw commands require an initialized environment and run with its directory as
cwd. Arguments are passed individually with shell=False. Python calls capture
output by default; use capture_output=False to inherit terminal streams.
CommandResult exposes args, returncode, stdout, and stderr.
status() reports the directory, manifest presence, and executable locations
(or None). It launches no processes and does not validate executable versions.
Missing executables raise ExecutableNotFoundError; nonzero exits and other
launch failures raise CommandExecutionError, which retains command, cwd,
returncode, stdout, and stderr. All public errors derive from NodeError.
npm/npx may access the network and run package scripts. The managed working directory is not a process sandbox; invoked tools can write elsewhere.
Flask CLI
flask --app your_app node init
flask --app your_app node install
flask --app your_app node install example --version '^1' --dev
flask --app your_app node uninstall example
flask --app your_app node npm -- run custom-script --flag
flask --app your_app node npx -- some-tool --help
flask --app your_app node ci
flask --app your_app node status
CLI commands use the same Python operations, stream subprocess output, and
report extension errors with a nonzero exit status. -- separates forwarding
arguments from Click's own options.
Installed assets and extension consumers
manager = app.extensions["node"]
manager.require("example", "^1") # Safe during consumer init_app().
# After an explicit installation step:
asset = manager.resolve("example", "dist/example.js")
package = manager.package("example")
assert package.resolve("dist/example.js") == asset
print(package.name, package.version, package.root)
The consumer must initialize Flask-Node first. It owns its library-specific configuration, rendering, asset copying/serving, and build/watch commands. Flask-Node only declares/installs dependencies, executes commands, and locates files. No registration protocol is needed.
Package lookup supports ordinary and scoped registry names. Asset lookup returns
an existing Path; absolute paths, parent traversal, Windows-style paths, and
symlinks escaping the package or managed environment are rejected. Externally
linked packages are intentionally unsupported. Missing or malformed installed
packages raise PackageNotFoundError; unsafe or missing assets raise
AssetResolutionError. This is filesystem path validation, not protection
against concurrent malicious filesystem changes.
Development
python -m venv .venv
.venv/bin/python -m pip install -e '.[test]' build
.venv/bin/python -m pytest
.venv/bin/python -m build
Tests inject/mock the runner and subprocess boundary. They never download npm
packages or require Node. A custom runner can be supplied to Node(runner=...)
or NodeManager(..., runner=...) for testing. This dependency injection is not a
consumer plugin protocol.
Inspired by Flask-Tailwind-Manager.
Metadata
Release files for Flask-Node 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| flask_node-0.1.0.tar.gz | 12.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| flask_node-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.5 kB
Release files / flask_node-0.1.0.tar.gz
| Download URL | flask_node-0.1.0.tar.gz |
|---|---|
| Size | 12.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
48e2f180459086d4bb6ead9cce70ea091e29d75ef1d03cbc5ab622eea33b95dc
|
|
BLAKE2b-256 checksum How to use checksums |
54efee688cfb37f1ac3e8a10eb89a8751bf5c80614fb6d4aad9f4dc940c6cf85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / flask_node-0.1.0-py3-none-any.whl
| Download URL | flask_node-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
065a179b984a6dfe8356e1598043199bbc6bbcc6d48f082919cfb302fc963b3b
|
|
BLAKE2b-256 checksum How to use checksums |
d910ccf8f696cb9f983e596c7326958251bb3406c077989e3c60293d16e2cce9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|