flask-commands 0.1.0__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.
Files changed (41) hide show
  1. flask_commands/__init__.py +1 -0
  2. flask_commands/cli.py +12 -0
  3. flask_commands/commands/controller.py +0 -0
  4. flask_commands/commands/form.py +0 -0
  5. flask_commands/commands/model.py +0 -0
  6. flask_commands/commands/new.py +60 -0
  7. flask_commands/commands/route.py +0 -0
  8. flask_commands/commands/view.py +171 -0
  9. flask_commands/project/.env +6 -0
  10. flask_commands/project/.env.example +6 -0
  11. flask_commands/project/app/__init__.py +31 -0
  12. flask_commands/project/app/controllers/__init__.py +1 -0
  13. flask_commands/project/app/controllers/main_controller.py +6 -0
  14. flask_commands/project/app/models/__init__.py +1 -0
  15. flask_commands/project/app/models/user.py +51 -0
  16. flask_commands/project/app/routes/mains/__init__.py +5 -0
  17. flask_commands/project/app/routes/mains/routes.py +6 -0
  18. flask_commands/project/app/static/src/input.css +1 -0
  19. flask_commands/project/app/templates/mains/index.html +14 -0
  20. flask_commands/project/config/__init__.py +8 -0
  21. flask_commands/project/config/base_config.py +12 -0
  22. flask_commands/project/config/development_config.py +8 -0
  23. flask_commands/project/config/production_config.py +7 -0
  24. flask_commands/project/run.py +62 -0
  25. flask_commands/project/run.sh +64 -0
  26. flask_commands/utils/__init__.py +1 -0
  27. flask_commands/utils/controllers.py +148 -0
  28. flask_commands/utils/css.py +70 -0
  29. flask_commands/utils/databases.py +32 -0
  30. flask_commands/utils/files.py +88 -0
  31. flask_commands/utils/models.py +119 -0
  32. flask_commands/utils/naming.py +98 -0
  33. flask_commands/utils/routes.py +312 -0
  34. flask_commands/utils/scaffold.py +54 -0
  35. flask_commands/utils/venv.py +48 -0
  36. flask_commands/utils/views.py +65 -0
  37. flask_commands-0.1.0.dist-info/METADATA +80 -0
  38. flask_commands-0.1.0.dist-info/RECORD +41 -0
  39. flask_commands-0.1.0.dist-info/WHEEL +4 -0
  40. flask_commands-0.1.0.dist-info/entry_points.txt +3 -0
  41. flask_commands-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1 @@
1
+ from flask_commands.cli import cli
flask_commands/cli.py ADDED
@@ -0,0 +1,12 @@
1
+ import click
2
+ from flask_commands.commands.new import new
3
+ from flask_commands.commands.view import make_view
4
+
5
+ @click.group()
6
+ def cli() -> None:
7
+ """Flask command line tools that will help you build a flask application with blueprints quickly."""
8
+ pass # pragma: no cover
9
+
10
+ # Add commands to the CLI
11
+ cli.add_command(new)
12
+ cli.add_command(make_view)
File without changes
File without changes
File without changes
@@ -0,0 +1,60 @@
1
+ import os
2
+ import click
3
+ import shutil
4
+ from flask_commands.utils.venv import create_venv
5
+ from flask_commands.utils.files import copy_templates
6
+ from flask_commands.utils.css import install_tailwind
7
+ from flask_commands.utils.databases import install_sqlitedb
8
+
9
+ @click.command()
10
+ @click.argument("project_name")
11
+ @click.option("--db/--no-db", default=None, help="Include a database with your project")
12
+ def new(project_name, db):
13
+ """Create a new Flask project"""
14
+ project_path = os.path.abspath(project_name)
15
+ project_started = False
16
+ if os.path.exists(project_path):
17
+ click.secho(f"💣 Error: Folder Already Exists.", fg="red", bold=True)
18
+ click.secho(f" - Folder '{project_name}' already exists in this directory", fg="red")
19
+ click.secho(f" - Please choose a different project name or change to a new directory", fg="red")
20
+ return
21
+ try:
22
+ project_started = True
23
+ include_db = db if db is not None else click.confirm("Include a Sqlite Database?", default=True)
24
+ os.makedirs(project_path)
25
+
26
+ # Create a Virtual Enviroment and install dependancies and
27
+ # generate a requirments file
28
+ packages = ["Flask", "python-dotenv"]
29
+ if include_db:
30
+ packages.extend(["Flask-Login", "Flask-Migrate", "Flask-SQLAlchemy"])
31
+ create_venv(
32
+ project_path,
33
+ packages=packages,
34
+ freeze_requirements=True)
35
+
36
+ copy_templates(
37
+ project_path,
38
+ include_db=include_db,
39
+ replacements={"project_name": project_name, "project_path": project_path})
40
+
41
+ # Make run.sh executable
42
+ os.chmod(os.path.join(project_path, "run.sh"), 0o755)
43
+
44
+ install_tailwind(project_path)
45
+
46
+ if include_db:
47
+ install_sqlitedb(project_path)
48
+
49
+ click.secho(
50
+ f"{project_name.title()} is ready!!! Run the following:",
51
+ bold=True, underline=True)
52
+
53
+
54
+ click.secho(f"cd {project_name}", fg="cyan")
55
+ click.secho("./run.sh", fg="cyan")
56
+ except Exception as exception:
57
+ if project_started and os.path.exists(project_name):
58
+ shutil.rmtree(project_name, ignore_errors=True)
59
+ click.secho("💣 Error: Project Creation Failed 😤", bold=True, fg="red")
60
+ raise click.ClickException(f"exception:\n{exception}") from exception
File without changes
@@ -0,0 +1,171 @@
1
+ import os
2
+ import click
3
+
4
+ from flask_commands.utils.controllers import (
5
+ controller_add_method,
6
+ controller_infer_name_from,
7
+ controller_make_file
8
+ )
9
+ from flask_commands.utils.models import (
10
+ model_infer_name_from,
11
+ model_make_file
12
+ )
13
+ from flask_commands.utils.naming import camel_to_snake
14
+ from flask_commands.utils.routes import (
15
+ route_add_method,
16
+ route_infer_name_from,
17
+ route_make_directory_and_register_blueprint,
18
+ generate_route_folder_path_and_blueprint_name
19
+ )
20
+
21
+ from flask_commands.utils.scaffold import split_dotted_path
22
+
23
+ from flask_commands.utils.views import view_make_file
24
+
25
+
26
+ @click.command(name="make:view")
27
+ @click.argument("dotted_path_with_name")
28
+ @click.option("--controller", "controller_name", default=None,
29
+ help="Optional controller class name (example PostController).")
30
+ @click.option("-c", "--generate-controller", is_flag=True,
31
+ help="Optional controller flag to generate an inferred controller from the dotted path name.")
32
+ @click.option("--route", "route_name", default=None,
33
+ help="Optional route class name (example /posts).")
34
+ @click.option("-r", "--generate-route", is_flag=True,
35
+ help="Optional route flag to generate an inferred route from the dotted path name.")
36
+ @click.option("--model", "model_name", default=None,
37
+ help="Optional model name (example Post which makes the database table 'posts').")
38
+ @click.option("-m", "--generate-model", is_flag=True,
39
+ help="Optional model flag to generate an inferred model from the dotted path name.")
40
+ def make_view(
41
+ dotted_path_with_name: str,
42
+ controller_name: str | None,
43
+ generate_controller: bool,
44
+ route_name: str | None,
45
+ generate_route: bool,
46
+ model_name: str | None,
47
+ generate_model: bool) -> None:
48
+ """
49
+ \b
50
+ Create a template view file under app/templates/<folder>/<name>.html.
51
+ You can also optionally connect this view to a controller, route, and model.
52
+ \b
53
+ ─── Understanding DOTTED_PATH_WITH_NAME ───
54
+ The dotted path defines the folder and file name:
55
+ <folder>.<name> → app/templates/<folder>/<name>.html
56
+ Example: posts.index → app/templates/posts/index.html
57
+ \b
58
+ You can also nest folders for relationships:
59
+ admin.users.index → app/templates/admin/users/index.html
60
+ posts.images.index → app/templates/posts/images/index.html
61
+ \b
62
+ ─── Simple Component Views ───
63
+ For standalone components like a button:
64
+ flask make:view button
65
+ \b
66
+ ─── CRUD Views ───
67
+ For RESTful actions (index, show, create, store, edit, update, destroy/delete):
68
+ Initial CRUD setup (controller, route, and model):
69
+ flask make:view posts.index -crm
70
+ flask make:view posts.index --controller PostController --route /posts --model Post
71
+ \b
72
+ Additional CRUD actions (e.g., show):
73
+ flask make:view posts.show -cr
74
+ flask make:view posts.show --controller PostController --route /posts/<int:post_id>
75
+ \b
76
+ ─── Flags ───
77
+ Optional flags can be combined as seen above:
78
+ -c / --generate-controller generate inferred controller
79
+ -r / --generate-route generate inferred route
80
+ -m / --generate-model generate inferred model
81
+ \b
82
+ If you prefer explicit control:
83
+ --controller CONTROLLER_NAME set a specific controller
84
+ --route ROUTE_NAME set a specific route
85
+ --model MODEL_NAME set a specific model
86
+ """
87
+ relative_path, action = split_dotted_path(dotted_path_with_name)
88
+
89
+ # Infer controller name if not provided
90
+ if generate_controller and controller_name is None:
91
+ if relative_path != '':
92
+ controller_name = controller_infer_name_from(relative_path)
93
+ click.secho(f"💡 Info: Inferred controller name as {click.style(controller_name, bold=True)}", fg="cyan")
94
+ else:
95
+ click.secho(f"⚠️ Warning: Could not infer the controller name "
96
+ f"from {dotted_path_with_name}", fg="yellow", bold=True)
97
+
98
+ # Infer route name if not provided
99
+ if generate_route and route_name is None:
100
+ route_name = route_infer_name_from(dotted_path_with_name)
101
+ click.secho("💡 Info: Inferred route name as "
102
+ f"{click.style(route_name, bold=True)}", fg="cyan")
103
+
104
+ # Infer model name if not provided
105
+ if generate_model and model_name is None:
106
+ model_name = model_infer_name_from(relative_path, dotted_path_with_name)
107
+ click.secho(f"💡 Info: Inferred model name as "
108
+ f"{click.style(model_name, bold=True)}", fg="cyan")
109
+
110
+ click.echo("\n")
111
+
112
+ relative_view_file_path = os.path.join(relative_path, f"{action}.html")
113
+ destination_file_path = \
114
+ os.path.join("app", "templates", relative_view_file_path)
115
+
116
+ is_successful, message = view_make_file(destination_file_path)
117
+ click.echo(message)
118
+
119
+ # If a controller_name was provided or inferred
120
+ if controller_name:
121
+ controller_file_path = \
122
+ os.path.join(
123
+ "app",
124
+ "controllers",
125
+ f"{camel_to_snake(controller_name)}.py")
126
+
127
+ # if controller exist just add the method
128
+ if os.path.exists(controller_file_path):
129
+ is_successful, message = controller_add_method(
130
+ controller_name, action, relative_view_file_path, route_name)
131
+ # else create the controller and the method
132
+ else:
133
+ is_successful, message = controller_make_file(
134
+ controller_name, action, relative_view_file_path, route_name)
135
+ click.echo(message)
136
+
137
+ # If a controller_name was provided or inferred
138
+ if route_name:
139
+ route_folder_path, blueprint_name = \
140
+ generate_route_folder_path_and_blueprint_name(
141
+ dotted_path_with_name, relative_path)
142
+ try:
143
+ if os.path.exists(route_folder_path):
144
+ is_successful, message = \
145
+ route_add_method(
146
+ relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
147
+ action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
148
+ route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
149
+ blueprint_name, # posts or mains or posts_comments
150
+ route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
151
+ controller_name) # contoller_name is like post_controller
152
+ else:
153
+ is_successful, message = \
154
+ route_make_directory_and_register_blueprint(
155
+ # relative_path, # this is everything before the last part of dotted_path_with_name replacing . with /
156
+ action, # in CRUD this is index, create, update, show... else this is just the last part of dotted_path_with_name
157
+ route_folder_path, # this is app/routes/{relative_path} or app/routes/main if relative path is ''
158
+ blueprint_name, # posts or mains or posts_comments
159
+ route_name, # this is the url path like /posts/<int:post_id> or /admin/posts/comments
160
+ controller_name) # contoller_name is like post_controller
161
+ click.echo(message)
162
+ except Exception as exception:
163
+ click.secho(f"💣 Error:\n {exception}", fg="red")
164
+
165
+ # If a model_name was provided or inferred
166
+ if model_name:
167
+ model_init_path = os.path.join("app", "models", "__init__.py")
168
+ model_file_path = os.path.join("app", "models", f"{model_name.lower()}.py")
169
+ is_successful, message = model_make_file(
170
+ model_name, model_init_path, model_file_path)
171
+ click.echo(message)
@@ -0,0 +1,6 @@
1
+ SECRET_KEY=PUT_SOMETHING_SECRET_HERE
2
+ FLASK_APP=run.py
3
+ FLASK_CONFIG=development
4
+ APP_NAME=project_name
5
+ SQLALCHEMY_DEVELOPMENT_DATABASE_URI=sqlite:///project_name_dev.db
6
+ SQLALCHEMY_PRODUCTION_DATABASE_URI=mysql+pymysql://username:password@localhost:3306/project_name_prod
@@ -0,0 +1,6 @@
1
+ SECRET_KEY=
2
+ FLASK_APP=
3
+ FLASK_CONFIG=
4
+ APP_NAME=
5
+ SQLALCHEMY_DEVELOPMENT_DATABASE_URI=
6
+ SQLALCHEMY_PRODUCTION_DATABASE_URI=
@@ -0,0 +1,31 @@
1
+ from config import config
2
+ from flask import Flask
3
+ from flask_login import LoginManager
4
+ from flask_migrate import Migrate
5
+ from flask_sqlalchemy import SQLAlchemy
6
+
7
+ # Logging a user's authentication state handler
8
+ login_manager = LoginManager()
9
+
10
+ # ORM Database handler
11
+ db = SQLAlchemy()
12
+ migrate = Migrate()
13
+
14
+ def create_app(config_name) -> Flask:
15
+ """Creates a Flask application Instance."""
16
+ app = Flask(__name__)
17
+
18
+ # apply configuration
19
+ app.config.from_object(config[config_name])
20
+
21
+ # initialize extensions: order matters
22
+ login_manager.init_app(app)
23
+ db.init_app(app)
24
+ migrate.init_app(app, db)
25
+
26
+ from app import models
27
+
28
+ from app.routes.mains import bp as mains_blueprint
29
+ app.register_blueprint(mains_blueprint)
30
+
31
+ return app
@@ -0,0 +1 @@
1
+ from .main_controller import MainController
@@ -0,0 +1,6 @@
1
+ from flask import render_template
2
+
3
+ class MainController(object):
4
+ @staticmethod
5
+ def index() -> str:
6
+ return render_template('mains/index.html')
@@ -0,0 +1 @@
1
+ from .user import User
@@ -0,0 +1,51 @@
1
+ from app import db, login_manager
2
+ from flask_login import UserMixin
3
+ from werkzeug.security import generate_password_hash, check_password_hash
4
+ from datetime import datetime, timezone
5
+
6
+ class User(UserMixin, db.Model):
7
+ __tablename__ = 'users'
8
+ # Columns
9
+ id = db.Column(db.Integer, primary_key=True)
10
+ username = db.Column(db.String(64), index=True,
11
+ unique=True, nullable=False)
12
+ password_hash = db.Column(db.String(256), nullable=True)
13
+ created_at = db.Column(db.DateTime(timezone=True),
14
+ index=True,
15
+ default=lambda: datetime.now(timezone.utc))
16
+ updated_at = db.Column(db.DateTime(timezone=True),
17
+ default=lambda: datetime.now(timezone.utc),
18
+ onupdate=lambda: datetime.now(timezone.utc))
19
+
20
+ @property
21
+ def password(self):
22
+ """Throw an error when trying to access password attribute"""
23
+ raise AttributeError('password is not a readable attribute')
24
+
25
+ @password.setter
26
+ def password(self, password):
27
+ """Hash and set password"""
28
+ self.password_hash = generate_password_hash(password)
29
+
30
+ def verify_password(self, password):
31
+ """Returns true or false by verifing a given password"""
32
+ return check_password_hash(self.password_hash, password)
33
+
34
+ def store_in_database(self):
35
+ db.session.add(self)
36
+ db.session.commit()
37
+
38
+ def delete_from_database(self):
39
+ db.session.delete(self)
40
+ db.session.commit()
41
+
42
+ def __repr__(self):
43
+ """Model representation for code debugging"""
44
+ return f'<User id:{self.id} username:{self.username}>'
45
+
46
+
47
+ @login_manager.user_loader
48
+ def load_user(user_id):
49
+ """Load the user from the database, given the id stored
50
+ in the session"""
51
+ return User.query.get(int(user_id))
@@ -0,0 +1,5 @@
1
+ from flask import Blueprint
2
+
3
+ bp = Blueprint('mains', __name__)
4
+
5
+ from app.routes.mains import routes
@@ -0,0 +1,6 @@
1
+ from app.controllers import MainController
2
+ from app.routes.mains import bp
3
+
4
+ @bp.route('/', methods=['GET'])
5
+ def index():
6
+ return MainController.index()
@@ -0,0 +1 @@
1
+ @import 'tailwindcss';
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Document</title>
7
+ {%- block styles %}
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='tailwind.min.css', v=time.time()) }}">
9
+ {%- endblock styles %}
10
+ </head>
11
+ <body>
12
+ Hello World
13
+ </body>
14
+ </html>
@@ -0,0 +1,8 @@
1
+ from .base_config import BaseConfig
2
+ from .development_config import DevelopmentConfig
3
+ from .production_config import ProductionConfig
4
+
5
+ config = {
6
+ 'development': DevelopmentConfig,
7
+ 'production': ProductionConfig
8
+ }
@@ -0,0 +1,12 @@
1
+ import os
2
+ from datetime import timedelta
3
+ from dotenv import load_dotenv
4
+
5
+ # Import Environment variables
6
+
7
+ load_dotenv() # reads variables from a .env file and sets them in os.environ
8
+
9
+ class BaseConfig():
10
+ FLASK_CONFIG = os.environ.get('FLASK_CONFIG')
11
+ SECRET_KEY = os.environ.get('SECRET_KEY')
12
+ APP_NAME = os.environ.get('APP_NAME')
@@ -0,0 +1,8 @@
1
+ import os
2
+ from .base_config import BaseConfig
3
+
4
+
5
+ class DevelopmentConfig(BaseConfig):
6
+ # MySQL DataBase Configuration
7
+ SQLALCHEMY_RECORD_QUERIES = True
8
+ SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DEVELOPMENT_DATABASE_URI')
@@ -0,0 +1,7 @@
1
+ import os
2
+ from .base_config import BaseConfig
3
+
4
+
5
+ class ProductionConfig(BaseConfig):
6
+ # MySQL DataBase Configuration
7
+ SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_PRODUCTION_DATABASE_URI')
@@ -0,0 +1,62 @@
1
+ import os
2
+ import time
3
+ import logging
4
+ from logging.handlers import SMTPHandler, RotatingFileHandler
5
+ from app import create_app
6
+
7
+ app = create_app(os.getenv('FLASK_CONFIG') or 'development')
8
+
9
+ # These are global variable for your jinja2 templates
10
+ @app.context_processor
11
+ def inject_globals():
12
+ return {
13
+ 'time': time}
14
+
15
+ with app.app_context():
16
+ if not os.path.exists('logs'):
17
+ os.makedirs('logs', exist_ok=True)
18
+
19
+ max_bytes = 10 * 1024 * 1024 # 10 MB
20
+ backup_count = 10 # this will create 10 files .log.1 .log.2 .log.3 ...
21
+
22
+ # Logging levels to: NOTSET, DEBUG, INFO, WARNING, ERROR, CRITICAL
23
+
24
+ file_handler = RotatingFileHandler(
25
+ f"logs/{app.config['APP_NAME']}-{app.config['FLASK_CONFIG']}.log",
26
+ maxBytes=max_bytes,
27
+ backupCount=backup_count)
28
+ file_handler.setFormatter(logging.Formatter(
29
+ '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
30
+ if file_handler not in app.logger.handlers:
31
+ app.logger.addHandler(file_handler)
32
+
33
+ # SQL log file (separate from app logs)
34
+ sql_file_handler = RotatingFileHandler(
35
+ f"logs/sql-{app.config['FLASK_CONFIG']}.log",
36
+ maxBytes=max_bytes,
37
+ backupCount=backup_count)
38
+ sql_file_handler.setFormatter(logging.Formatter(
39
+ '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
40
+ sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
41
+ sqlalchemy_logger.propagate = False
42
+ if sql_file_handler not in sqlalchemy_logger.handlers:
43
+ sqlalchemy_logger.addHandler(sql_file_handler)
44
+
45
+ if app.config['FLASK_CONFIG'] == 'development':
46
+ # this sets what the application will emit defaults to WARNING
47
+ app.logger.setLevel(logging.DEBUG)
48
+ sqlalchemy_logger.setLevel(logging.INFO)
49
+
50
+ # this sets what is writting to the logs
51
+ file_handler.setLevel(logging.DEBUG)
52
+ sql_file_handler.setLevel(logging.INFO)
53
+
54
+
55
+ if app.config['FLASK_CONFIG'] == 'production':
56
+ app.logger.setLevel(logging.INFO)
57
+ sqlalchemy_logger.setLevel(logging.WARNING)
58
+
59
+ file_handler.setLevel(logging.INFO)
60
+ sql_file_handler.setLevel(logging.WARNING)
61
+
62
+ app.logger.info(f"{app.config['APP_NAME']} started up in {app.config['FLASK_CONFIG']} mode")
@@ -0,0 +1,64 @@
1
+ # The main Terminal will eventually run the Hot Reloading
2
+
3
+ # Start Up Shell
4
+ osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
5
+ osascript -e 'tell application "Terminal" to do script "cd project_path && source venv/bin/activate" in front window'
6
+ osascript -e 'tell application "Terminal" to do script "flask shell" in front window'
7
+
8
+ # Start Up Flask Server
9
+ osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
10
+ osascript -e 'tell application "Terminal" to do script "cd project_path && source venv/bin/activate" in front window'
11
+ osascript -e 'tell application "Terminal" to do script "flask run --debug" in front window'
12
+
13
+ # Building your tailwind.css file
14
+ osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
15
+ osascript -e 'tell application "Terminal" to do script "cd project_path && npm run watch:css" in front window'
16
+
17
+ # Building your tailwind.min.css file
18
+ osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
19
+ osascript -e 'tell application "Terminal" to do script "cd project_path && npm run build:css" in front window'
20
+
21
+ # Open up VS code Text
22
+ osascript -e 'tell application "System Events" to tell process "Terminal" to keystroke "t" using command down'
23
+ osascript -e 'tell application "Terminal" to do script "cd project_path && code ." in front window'
24
+
25
+ sleep 5
26
+
27
+ # Open Safari and navigate to http://127.0.0.1:5000
28
+ open -a "Safari" "http://127.0.0.1:5000"
29
+
30
+
31
+ #!/bin/bash
32
+
33
+ # Define the folder to watch
34
+ WATCH_FOLDER_TEMPLATES="$(pwd)/app/templates"
35
+ WATCH_FOLDER_CONTROLLER="$(pwd)/app/controllers"
36
+ WATCH_FOLDER_FORMS="$(pwd)/app/forms"
37
+ WATCH_FOLDER_MODELS="$(pwd)/app/models"
38
+ WATCH_FOLDER_ROUTES="$(pwd)/app/routes"
39
+
40
+ # Function to refresh Chrome
41
+ refresh_safari() {
42
+ osascript <<EOF
43
+ tell application "Safari"
44
+ repeat with w in windows
45
+ repeat with t in tabs of w
46
+ if (URL of t contains "http://127.0.0.1:5000/") then
47
+ tell t to do JavaScript "window.location.reload();"
48
+ end if
49
+ end repeat
50
+ end repeat
51
+ end tell
52
+ EOF
53
+ }
54
+
55
+
56
+ # Watch for file changes in the folder and its subfolders
57
+ fswatch -0 "$WATCH_FOLDER_TEMPLATES" "$WATCH_FOLDER_CONTROLLER" "$WATCH_FOLDER_FORMS" "$WATCH_FOLDER_MODELS" "$WATCH_FOLDER_ROUTES" | while read -d "" event
58
+
59
+ do
60
+ echo "Change detected: $event"
61
+
62
+ # Call the function to refresh Chrome
63
+ refresh_safari
64
+ done
@@ -0,0 +1 @@
1
+ # intentionally empty