reflex 0.7.12a1__py3-none-any.whl → 0.7.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of reflex might be problematic. Click here for more details.

Files changed (55) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +1 -0
  2. reflex/.templates/web/postcss.config.js +0 -1
  3. reflex/.templates/web/utils/state.js +1 -1
  4. reflex/__init__.py +1 -0
  5. reflex/__init__.pyi +1 -0
  6. reflex/app.py +101 -29
  7. reflex/compiler/compiler.py +11 -62
  8. reflex/compiler/templates.py +12 -3
  9. reflex/compiler/utils.py +20 -4
  10. reflex/components/component.py +366 -88
  11. reflex/components/core/helmet.pyi +66 -0
  12. reflex/components/datadisplay/code.py +1 -1
  13. reflex/components/datadisplay/shiki_code_block.py +97 -86
  14. reflex/components/datadisplay/shiki_code_block.pyi +4 -2
  15. reflex/components/el/elements/forms.py +1 -1
  16. reflex/components/lucide/icon.py +2 -1
  17. reflex/components/lucide/icon.pyi +1 -0
  18. reflex/components/plotly/plotly.py +2 -2
  19. reflex/components/plotly/plotly.pyi +2 -3
  20. reflex/components/radix/primitives/accordion.py +1 -1
  21. reflex/components/radix/primitives/drawer.py +1 -1
  22. reflex/components/radix/primitives/form.py +1 -1
  23. reflex/components/radix/themes/base.py +4 -11
  24. reflex/components/radix/themes/components/icon_button.py +2 -2
  25. reflex/components/radix/themes/components/text_field.py +3 -0
  26. reflex/components/radix/themes/components/text_field.pyi +2 -0
  27. reflex/components/radix/themes/layout/list.py +1 -1
  28. reflex/components/tags/iter_tag.py +3 -5
  29. reflex/config.py +57 -7
  30. reflex/constants/__init__.py +0 -2
  31. reflex/event.py +154 -93
  32. reflex/experimental/client_state.py +3 -1
  33. reflex/plugins/__init__.py +7 -0
  34. reflex/plugins/base.py +101 -0
  35. reflex/plugins/tailwind_v3.py +255 -0
  36. reflex/plugins/tailwind_v4.py +258 -0
  37. reflex/state.py +24 -3
  38. reflex/utils/build.py +1 -1
  39. reflex/utils/console.py +1 -1
  40. reflex/utils/exec.py +23 -0
  41. reflex/utils/path_ops.py +26 -6
  42. reflex/utils/prerequisites.py +21 -90
  43. reflex/utils/pyi_generator.py +12 -2
  44. reflex/utils/types.py +15 -1
  45. reflex/vars/base.py +59 -4
  46. reflex/vars/object.py +8 -0
  47. {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/METADATA +2 -2
  48. {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/RECORD +52 -50
  49. scripts/hatch_build.py +17 -0
  50. reflex/.templates/jinja/web/tailwind.config.js.jinja2 +0 -66
  51. reflex/.templates/web/styles/tailwind.css +0 -6
  52. reflex/constants/style.py +0 -16
  53. {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/WHEEL +0 -0
  54. {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/entry_points.txt +0 -0
  55. {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/licenses/LICENSE +0 -0
reflex/config.py CHANGED
@@ -22,6 +22,7 @@ from typing import (
22
22
  TYPE_CHECKING,
23
23
  Annotated,
24
24
  Any,
25
+ ClassVar,
25
26
  Generic,
26
27
  TypeVar,
27
28
  get_args,
@@ -34,6 +35,7 @@ import pydantic.v1 as pydantic
34
35
  from reflex import constants
35
36
  from reflex.base import Base
36
37
  from reflex.constants.base import LogLevel
38
+ from reflex.plugins import Plugin, TailwindV3Plugin, TailwindV4Plugin
37
39
  from reflex.utils import console
38
40
  from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
39
41
  from reflex.utils.types import (
@@ -748,6 +750,9 @@ class EnvironmentVariables:
748
750
  # The timeout to wait for a pong from the websocket server in seconds.
749
751
  REFLEX_SOCKET_TIMEOUT: EnvVar[int] = env_var(constants.Ping.TIMEOUT)
750
752
 
753
+ # Whether to run Granian in a spawn process. This enables Reflex to pick up on environment variable changes between hot reloads.
754
+ REFLEX_STRICT_HOT_RELOAD: EnvVar[bool] = env_var(False)
755
+
751
756
 
752
757
  environment = EnvironmentVariables()
753
758
 
@@ -891,6 +896,11 @@ class Config(Base):
891
896
  # Extra overlay function to run after the app is built. Formatted such that `from path_0.path_1... import path[-1]`, and calling it with no arguments would work. For example, "reflex.components.moment.moment".
892
897
  extra_overlay_function: str | None = None
893
898
 
899
+ # List of plugins to use in the app.
900
+ plugins: list[Plugin] = []
901
+
902
+ _prefixes: ClassVar[list[str]] = ["REFLEX_"]
903
+
894
904
  def __init__(self, *args, **kwargs):
895
905
  """Initialize the config values.
896
906
 
@@ -903,29 +913,59 @@ class Config(Base):
903
913
  """
904
914
  super().__init__(*args, **kwargs)
905
915
 
906
- # Set the log level for this process
907
- env_loglevel = os.environ.get("LOGLEVEL")
916
+ # Clean up this code when we remove plain envvar in 0.8.0
917
+ show_deprecation = False
918
+ env_loglevel = os.environ.get("REFLEX_LOGLEVEL")
919
+ if not env_loglevel:
920
+ env_loglevel = os.environ.get("LOGLEVEL")
921
+ if env_loglevel:
922
+ show_deprecation = True
908
923
  if env_loglevel is not None:
909
924
  env_loglevel = LogLevel(env_loglevel.lower())
910
925
  if env_loglevel or self.loglevel != LogLevel.DEFAULT:
911
926
  console.set_log_level(env_loglevel or self.loglevel)
912
927
 
928
+ if show_deprecation:
929
+ console.deprecate(
930
+ "Usage of deprecated LOGLEVEL env var detected.",
931
+ reason="Prefer `REFLEX_` prefix when setting env vars.",
932
+ deprecation_version="0.7.13",
933
+ removal_version="0.8.0",
934
+ )
935
+
913
936
  # Update the config from environment variables.
914
937
  env_kwargs = self.update_from_env()
915
938
  for key, env_value in env_kwargs.items():
916
939
  setattr(self, key, env_value)
917
940
 
918
- # Update default URLs if ports were set
941
+ # Update default URLs if ports were set
919
942
  kwargs.update(env_kwargs)
920
943
  self._non_default_attributes.update(kwargs)
921
944
  self._replace_defaults(**kwargs)
922
945
 
946
+ if self.tailwind is not None and not any(
947
+ isinstance(plugin, (TailwindV3Plugin, TailwindV4Plugin))
948
+ for plugin in self.plugins
949
+ ):
950
+ console.deprecate(
951
+ "Inferring tailwind usage",
952
+ reason="""
953
+
954
+ If you are using tailwind, add `rx.plugins.TailwindV3Plugin()` to the `plugins=[]` in rxconfig.py.
955
+
956
+ If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
957
+ deprecation_version="0.7.13",
958
+ removal_version="0.8.0",
959
+ dedupe=True,
960
+ )
961
+ self.plugins.append(TailwindV3Plugin())
962
+
923
963
  if (
924
964
  self.state_manager_mode == constants.StateManagerMode.REDIS
925
965
  and not self.redis_url
926
966
  ):
927
967
  raise ConfigError(
928
- "REDIS_URL is required when using the redis state manager."
968
+ f"{self._prefixes[0]}REDIS_URL is required when using the redis state manager."
929
969
  )
930
970
 
931
971
  @property
@@ -966,7 +1006,18 @@ class Config(Base):
966
1006
  # Iterate over the fields.
967
1007
  for key, field in self.__fields__.items():
968
1008
  # The env var name is the key in uppercase.
969
- env_var = os.environ.get(key.upper())
1009
+ for prefix in self._prefixes:
1010
+ if env_var := os.environ.get(f"{prefix}{key.upper()}"):
1011
+ break
1012
+ else:
1013
+ # Default to non-prefixed env var if other are not found.
1014
+ if env_var := os.environ.get(key.upper()):
1015
+ console.deprecate(
1016
+ f"Usage of deprecated {key.upper()} env var detected.",
1017
+ reason=f"Prefer `{self._prefixes[0]}` prefix when setting env vars.",
1018
+ deprecation_version="0.7.13",
1019
+ removal_version="0.8.0",
1020
+ )
970
1021
 
971
1022
  # If the env var is set, override the config value.
972
1023
  if env_var and env_var.strip():
@@ -986,7 +1037,6 @@ class Config(Base):
986
1037
  f"Overriding config value {key} with env var {key.upper()}={env_var}",
987
1038
  dedupe=True,
988
1039
  )
989
-
990
1040
  return updated_values
991
1041
 
992
1042
  def get_event_namespace(self) -> str:
@@ -1038,7 +1088,7 @@ class Config(Base):
1038
1088
  """
1039
1089
  for key, value in kwargs.items():
1040
1090
  if value is not None:
1041
- os.environ[key.upper()] = str(value)
1091
+ os.environ[self._prefixes[0] + key.upper()] = str(value)
1042
1092
  setattr(self, key, value)
1043
1093
  self._non_default_attributes.update(kwargs)
1044
1094
  self._replace_defaults(**kwargs)
@@ -59,7 +59,6 @@ from .route import (
59
59
  RouteVar,
60
60
  )
61
61
  from .state import StateManagerMode
62
- from .style import Tailwind
63
62
 
64
63
  __all__ = [
65
64
  "ALEMBIC_CONFIG",
@@ -115,6 +114,5 @@ __all__ = [
115
114
  "RouteVar",
116
115
  "SocketEvent",
117
116
  "StateManagerMode",
118
- "Tailwind",
119
117
  "Templates",
120
118
  ]
reflex/event.py CHANGED
@@ -1262,6 +1262,118 @@ def get_hydrate_event(state: BaseState) -> str:
1262
1262
  return get_event(state, constants.CompileVars.HYDRATE)
1263
1263
 
1264
1264
 
1265
+ def _values_returned_from_event(
1266
+ event_spec: ArgsSpec | Sequence[ArgsSpec],
1267
+ ) -> list[Any]:
1268
+ return [
1269
+ event_spec_return_type
1270
+ for arg_spec in (
1271
+ [event_spec] if not isinstance(event_spec, Sequence) else list(event_spec)
1272
+ )
1273
+ if (event_spec_return_type := get_type_hints(arg_spec).get("return", None))
1274
+ is not None
1275
+ and get_origin(event_spec_return_type) is tuple
1276
+ ]
1277
+
1278
+
1279
+ def _check_event_args_subclass_of_callback(
1280
+ callback_params_names: list[str],
1281
+ provided_event_types: list[Any],
1282
+ callback_param_name_to_type: dict[str, Any],
1283
+ callback_name: str = "",
1284
+ key: str = "",
1285
+ ):
1286
+ """Check if the event handler arguments are subclass of the callback.
1287
+
1288
+ Args:
1289
+ callback_params_names: The names of the callback parameters.
1290
+ provided_event_types: The event types.
1291
+ callback_param_name_to_type: The callback parameter name to type mapping.
1292
+ callback_name: The name of the callback.
1293
+ key: The key.
1294
+
1295
+ Raises:
1296
+ TypeError: If the event handler arguments are invalid.
1297
+ EventHandlerArgTypeMismatchError: If the event handler arguments do not match the callback.
1298
+
1299
+ # noqa: DAR401 delayed_exceptions[]
1300
+ # noqa: DAR402 EventHandlerArgTypeMismatchError
1301
+ """
1302
+ type_match_found: dict[str, bool] = {}
1303
+ delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
1304
+
1305
+ for event_spec_index, event_spec_return_type in enumerate(provided_event_types):
1306
+ args = get_args(event_spec_return_type)
1307
+
1308
+ args_types_without_vars = [
1309
+ arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
1310
+ ]
1311
+
1312
+ # check that args of event handler are matching the spec if type hints are provided
1313
+ for i, arg in enumerate(callback_params_names[: len(args_types_without_vars)]):
1314
+ if arg not in callback_param_name_to_type:
1315
+ continue
1316
+
1317
+ type_match_found.setdefault(arg, False)
1318
+
1319
+ try:
1320
+ compare_result = typehint_issubclass(
1321
+ args_types_without_vars[i], callback_param_name_to_type[arg]
1322
+ )
1323
+ except TypeError as te:
1324
+ callback_name_context = f" of {callback_name}" if callback_name else ""
1325
+ key_context = f" for {key}" if key else ""
1326
+ raise TypeError(
1327
+ f"Could not compare types {args_types_without_vars[i]} and {callback_param_name_to_type[arg]} for argument {arg}{callback_name_context}{key_context}."
1328
+ ) from te
1329
+
1330
+ if compare_result:
1331
+ type_match_found[arg] = True
1332
+ continue
1333
+ else:
1334
+ type_match_found[arg] = False
1335
+ as_annotated_in = (
1336
+ f" as annotated in {callback_name}" if callback_name else ""
1337
+ )
1338
+ delayed_exceptions.append(
1339
+ EventHandlerArgTypeMismatchError(
1340
+ f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {callback_param_name_to_type[arg]}{as_annotated_in} instead."
1341
+ )
1342
+ )
1343
+
1344
+ if all(type_match_found.values()):
1345
+ delayed_exceptions.clear()
1346
+ if event_spec_index:
1347
+ args = get_args(provided_event_types[0])
1348
+
1349
+ args_types_without_vars = [
1350
+ arg if get_origin(arg) is not Var else get_args(arg)[0]
1351
+ for arg in args
1352
+ ]
1353
+
1354
+ expect_string = ", ".join(
1355
+ repr(arg) for arg in args_types_without_vars
1356
+ ).replace("[", "\\[")
1357
+
1358
+ given_string = ", ".join(
1359
+ repr(callback_param_name_to_type.get(arg, Any))
1360
+ for arg in callback_params_names
1361
+ ).replace("[", "\\[")
1362
+
1363
+ as_annotated_in = (
1364
+ f" as annotated in {callback_name}" if callback_name else ""
1365
+ )
1366
+
1367
+ console.warn(
1368
+ f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> (){as_annotated_in} instead. "
1369
+ f"This may lead to unexpected behavior but is intentionally ignored for {key}."
1370
+ )
1371
+ break
1372
+
1373
+ if delayed_exceptions:
1374
+ raise delayed_exceptions[0]
1375
+
1376
+
1265
1377
  def call_event_handler(
1266
1378
  event_callback: EventHandler | EventSpec,
1267
1379
  event_spec: ArgsSpec | Sequence[ArgsSpec],
@@ -1278,17 +1390,13 @@ def call_event_handler(
1278
1390
  event_spec: The lambda that define the argument(s) to pass to the event handler.
1279
1391
  key: The key to pass to the event handler.
1280
1392
 
1281
- Raises:
1282
- EventHandlerArgTypeMismatchError: If the event handler arguments do not match the event spec. #noqa: DAR402
1283
- TypeError: If the event handler arguments are invalid.
1284
-
1285
1393
  Returns:
1286
1394
  The event spec from calling the event handler.
1287
-
1288
- #noqa: DAR401
1289
1395
  """
1290
1396
  event_spec_args = parse_args_spec(event_spec)
1291
1397
 
1398
+ event_spec_return_types = _values_returned_from_event(event_spec)
1399
+
1292
1400
  if isinstance(event_callback, EventSpec):
1293
1401
  check_fn_match_arg_spec(
1294
1402
  event_callback.handler.fn,
@@ -1297,6 +1405,32 @@ def call_event_handler(
1297
1405
  bool(event_callback.handler.state_full_name) + len(event_callback.args),
1298
1406
  event_callback.handler.fn.__qualname__,
1299
1407
  )
1408
+
1409
+ event_callback_spec_args = list(
1410
+ inspect.signature(event_callback.handler.fn).parameters.keys()
1411
+ )
1412
+
1413
+ try:
1414
+ type_hints_of_provided_callback = get_type_hints(event_callback.handler.fn)
1415
+ except NameError:
1416
+ type_hints_of_provided_callback = {}
1417
+
1418
+ argument_names = [str(arg) for arg, value in event_callback.args]
1419
+
1420
+ _check_event_args_subclass_of_callback(
1421
+ [
1422
+ arg
1423
+ for arg in event_callback_spec_args[
1424
+ bool(event_callback.handler.state_full_name) :
1425
+ ]
1426
+ if arg not in argument_names
1427
+ ],
1428
+ event_spec_return_types,
1429
+ type_hints_of_provided_callback,
1430
+ event_callback.handler.fn.__qualname__,
1431
+ key or "",
1432
+ )
1433
+
1300
1434
  # Handle partial application of EventSpec args
1301
1435
  return event_callback.add_args(*event_spec_args)
1302
1436
 
@@ -1308,98 +1442,23 @@ def call_event_handler(
1308
1442
  event_callback.fn.__qualname__,
1309
1443
  )
1310
1444
 
1311
- all_acceptable_specs = (
1312
- [event_spec] if not isinstance(event_spec, Sequence) else event_spec
1313
- )
1314
-
1315
- event_spec_return_types = list(
1316
- filter(
1317
- lambda event_spec_return_type: event_spec_return_type is not None
1318
- and get_origin(event_spec_return_type) is tuple,
1319
- (
1320
- get_type_hints(arg_spec).get("return", None)
1321
- for arg_spec in all_acceptable_specs
1322
- ),
1323
- )
1324
- )
1325
- type_match_found: dict[str, bool] = {}
1326
- delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
1327
-
1328
- try:
1329
- type_hints_of_provided_callback = get_type_hints(event_callback.fn)
1330
- except NameError:
1331
- type_hints_of_provided_callback = {}
1332
-
1333
1445
  if event_spec_return_types:
1334
1446
  event_callback_spec_args = list(
1335
1447
  inspect.signature(event_callback.fn).parameters.keys()
1336
1448
  )
1337
1449
 
1338
- for event_spec_index, event_spec_return_type in enumerate(
1339
- event_spec_return_types
1340
- ):
1341
- args = get_args(event_spec_return_type)
1342
-
1343
- args_types_without_vars = [
1344
- arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
1345
- ]
1346
-
1347
- # check that args of event handler are matching the spec if type hints are provided
1348
- for i, arg in enumerate(
1349
- event_callback_spec_args[1 : len(args_types_without_vars) + 1]
1350
- ):
1351
- if arg not in type_hints_of_provided_callback:
1352
- continue
1353
-
1354
- type_match_found.setdefault(arg, False)
1355
-
1356
- try:
1357
- compare_result = typehint_issubclass(
1358
- args_types_without_vars[i], type_hints_of_provided_callback[arg]
1359
- )
1360
- except TypeError as te:
1361
- raise TypeError(
1362
- f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
1363
- ) from te
1364
-
1365
- if compare_result:
1366
- type_match_found[arg] = True
1367
- continue
1368
- else:
1369
- type_match_found[arg] = False
1370
- delayed_exceptions.append(
1371
- EventHandlerArgTypeMismatchError(
1372
- f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} as annotated in {event_callback.fn.__qualname__} instead."
1373
- )
1374
- )
1375
-
1376
- if all(type_match_found.values()):
1377
- delayed_exceptions.clear()
1378
- if event_spec_index:
1379
- args = get_args(event_spec_return_types[0])
1380
-
1381
- args_types_without_vars = [
1382
- arg if get_origin(arg) is not Var else get_args(arg)[0]
1383
- for arg in args
1384
- ]
1385
-
1386
- expect_string = ", ".join(
1387
- repr(arg) for arg in args_types_without_vars
1388
- ).replace("[", "\\[")
1389
-
1390
- given_string = ", ".join(
1391
- repr(type_hints_of_provided_callback.get(arg, Any))
1392
- for arg in event_callback_spec_args[1:]
1393
- ).replace("[", "\\[")
1394
-
1395
- console.warn(
1396
- f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
1397
- f"This may lead to unexpected behavior but is intentionally ignored for {key}."
1398
- )
1399
- break
1400
-
1401
- if delayed_exceptions:
1402
- raise delayed_exceptions[0]
1450
+ try:
1451
+ type_hints_of_provided_callback = get_type_hints(event_callback.fn)
1452
+ except NameError:
1453
+ type_hints_of_provided_callback = {}
1454
+
1455
+ _check_event_args_subclass_of_callback(
1456
+ event_callback_spec_args[1:],
1457
+ event_spec_return_types,
1458
+ type_hints_of_provided_callback,
1459
+ event_callback.fn.__qualname__,
1460
+ key or "",
1461
+ )
1403
1462
 
1404
1463
  return event_callback(*event_spec_args)
1405
1464
 
@@ -1958,6 +2017,8 @@ class EventCallback(Generic[Unpack[P]], EventActionsMixin):
1958
2017
  class LambdaEventCallback(Protocol[Unpack[P]]):
1959
2018
  """A protocol for a lambda event callback."""
1960
2019
 
2020
+ __code__: types.CodeType
2021
+
1961
2022
  @overload
1962
2023
  def __call__(self: LambdaEventCallback[()]) -> Any: ...
1963
2024
 
@@ -158,7 +158,9 @@ class ClientStateVar(Var):
158
158
  hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
159
159
  hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
160
160
  hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
161
- hooks[f"{_client_state_ref_dict(var_name)}[{id_name}] = {var_name}"] = None
161
+ hooks[
162
+ f"{_client_state_ref_dict(var_name)}[{id_name}] = {_client_state_ref(var_name)}"
163
+ ] = None
162
164
  hooks[
163
165
  f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
164
166
  ] = None
@@ -0,0 +1,7 @@
1
+ """Reflex Plugin System."""
2
+
3
+ from .base import CommonContext as CommonContext
4
+ from .base import Plugin as Plugin
5
+ from .base import PreCompileContext as PreCompileContext
6
+ from .tailwind_v3 import Plugin as TailwindV3Plugin
7
+ from .tailwind_v4 import Plugin as TailwindV4Plugin
reflex/plugins/base.py ADDED
@@ -0,0 +1,101 @@
1
+ """Base class for all plugins."""
2
+
3
+ from collections.abc import Callable, Sequence
4
+ from pathlib import Path
5
+ from typing import ParamSpec, Protocol, TypedDict
6
+
7
+ from typing_extensions import Unpack
8
+
9
+
10
+ class CommonContext(TypedDict):
11
+ """Common context for all plugins."""
12
+
13
+
14
+ P = ParamSpec("P")
15
+
16
+
17
+ class AddTaskProtcol(Protocol):
18
+ """Protocol for adding a task to the pre-compile context."""
19
+
20
+ def __call__(
21
+ self,
22
+ task: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
23
+ /,
24
+ *args: P.args,
25
+ **kwargs: P.kwargs,
26
+ ) -> None:
27
+ """Add a task to the pre-compile context.
28
+
29
+ Args:
30
+ task: The task to add.
31
+ args: The arguments to pass to the task
32
+ kwargs: The keyword arguments to pass to the task
33
+ """
34
+
35
+
36
+ class PreCompileContext(CommonContext):
37
+ """Context for pre-compile hooks."""
38
+
39
+ add_save_task: AddTaskProtcol
40
+ add_modify_task: Callable[[str, Callable[[str], str]], None]
41
+
42
+
43
+ class Plugin:
44
+ """Base class for all plugins."""
45
+
46
+ def get_frontend_development_dependencies(
47
+ self, **context: Unpack[CommonContext]
48
+ ) -> list[str] | set[str] | tuple[str, ...]:
49
+ """Get the NPM packages required by the plugin for development.
50
+
51
+ Args:
52
+ context: The context for the plugin.
53
+
54
+ Returns:
55
+ A list of packages required by the plugin for development.
56
+ """
57
+ return []
58
+
59
+ def get_frontend_dependencies(
60
+ self, **context: Unpack[CommonContext]
61
+ ) -> list[str] | set[str] | tuple[str, ...]:
62
+ """Get the NPM packages required by the plugin.
63
+
64
+ Args:
65
+ context: The context for the plugin.
66
+
67
+ Returns:
68
+ A list of packages required by the plugin.
69
+ """
70
+ return []
71
+
72
+ def get_static_assets(
73
+ self, **context: Unpack[CommonContext]
74
+ ) -> Sequence[tuple[Path, str | bytes]]:
75
+ """Get the static assets required by the plugin.
76
+
77
+ Args:
78
+ context: The context for the plugin.
79
+
80
+ Returns:
81
+ A list of static assets required by the plugin.
82
+ """
83
+ return []
84
+
85
+ def get_stylesheet_paths(self, **context: Unpack[CommonContext]) -> Sequence[str]:
86
+ """Get the paths to the stylesheets required by the plugin relative to the styles directory.
87
+
88
+ Args:
89
+ context: The context for the plugin.
90
+
91
+ Returns:
92
+ A list of paths to the stylesheets required by the plugin.
93
+ """
94
+ return []
95
+
96
+ def pre_compile(self, **context: Unpack[PreCompileContext]) -> None:
97
+ """Called before the compilation of the plugin.
98
+
99
+ Args:
100
+ context: The context for the plugin.
101
+ """