-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreate-web-app
More file actions
390 lines (322 loc) · 8.79 KB
/
create-web-app
File metadata and controls
390 lines (322 loc) · 8.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
#!/usr/bin/env python3
import argparse
import os
def getPath(dest):
return os.path.abspath(dest)
def create_dir(path, verbose):
os.makedirs(path)
if verbose > 0:
print('Created directory {}'.format(path))
def writeFile(path, content, verbose):
# todo: write file
with open(path, 'w') as ofs:
ofs.write(content)
if verbose > 0:
print('Created file {}'.format(path))
def createFileTree(name, path, verbose):
# create_dir(path + '/src', verbose)
# create_dir(path + '/src/api', verbose)
# create_dir(path + '/src/js', verbose)
# create_dir(path + '/src/static', verbose)
# create_dir(path + '/src/static/css', verbose)
# create_dir(path + '/src/static/images', verbose)
# create_dir(path + '/src/static/js', verbose)
# create_dir(path + '/src/templates', verbose)
# create_dir(path + '/src/util', verbose)
# create_dir(path + '/src/views', verbose)
for p, content in allFiles.items():
filePath = path + p
filePathDir = filePath[:filePath.rfind('/')]
if not os.path.exists(filePathDir):
create_dir(filePathDir, verbose)
writeFile(filePath, content.replace('{{name}}', name), verbose)
def create(name, destination, verbose):
print('Creating React web app', name, 'at', destination)
path = getPath(destination)
fullpath = path + '/' + name
if not os.path.exists(path):
try:
create_dir(path, verbose)
except PermissionError:
print('Permission denied.')
exit(1)
else:
if os.path.exists(fullpath):
print('Project at {} already exists.'.format(fullpath))
exit(1)
create_dir(fullpath, verbose)
createFileTree(name, fullpath, verbose)
print('Done.')
def main():
parser = argparse.ArgumentParser(description='Create a Web App!')
parser.add_argument('name', help='Name of the to-be-created app')
parser.add_argument('destination', default='.', help='Destination to create app')
parser.add_argument('--verbose', '-v', action='count', default=0, help='Show verbose output')
params = parser.parse_args()
create(params.name, params.destination, params.verbose)
#
#
# Files
#
#
allFiles = {
# .gitignore
'/.gitignore' : '''*.DS_Store
*.pyc
*.swp
*__pycache__*
*egg-info*
Session.vim
cookies.txt
env/*
node_modules
session.json
tmp
var
*.log
starter_files*
sftp-config.json
package-lock.json
*_compiled.js
''',
# requirements.txt
'/requirements.txt' : '''Flask==1.1.1
Flask-Testing==0.7.1
nodeenv==1.3.3
sh==1.12.14
''',
# main.py
'/main.py' : '''from src import app
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
''',
# config.py
'/config.py' : '''APPLICATION_ROOT = '/'
DEBUG = False
''',
# package.json
'/package.json' : '''{
"name": "{{name}}",
"version": "1.0.0",
"description": "",
"main": "",
"dependencies": {
"@babel/core": "^7.13.10",
"@babel/preset-env": "^7.13.12",
"@babel/preset-react": "^7.12.13",
"bootstrap": "^4.6.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-localizer": "^2.1.3"
},
"devDependencies": {
"babel-core": "^7.0.0-0",
"babel-loader": "==7.1.2",
"babel-preset-env": "==1.7.0",
"babel-preset-react": "==6.24.1",
"webpack": "==4.30.0",
"webpack-cli": "==3.3.0"
},
"scripts": {
"test": "echo \\"Error: no test specified\\" && exit 1",
"dev": "webpack --mode development --display-error-details --watch",
"build": "webpack --mode production"
},
"keywords": []
}
''',
# webpack
'/webpack.config.js' : '''const path = require('path');
module.exports = {
entry: {
index: './src/js/index.jsx'
},
output: {
path: path.join(__dirname, '/src/static/js/'),
filename: '[name]_compiled.js',
},
module: {
rules: [
{
// Test for js or jsx files
test: /\\.jsx?$/,
loader: 'babel-loader',
query: {
// Convert ES6 syntax to ES5 for browser compatibility
presets: ["@babel/preset-react", '@babel/preset-env'],
compact: false,
},
},
{
test: /\\.css$/i,
use: ['style-loader', 'css-loader'],
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
''',
# runServer
'/runServer' : '''source env/bin/activate
if [ $1 = "build" ]; then
echo "Building javascript..."
npm run build
fi
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
export FLASK_DEBUG=True
export FLASK_APP=src
export APP_SETTINGS=config.py
flask run --host 0.0.0.0 --port 8000
''',
# setup.sh
'/setup.sh' : '''echo "Setting up {{name}} project"
echo "Adding runServer script permissions..."
chmod +x runServer
echo "Done"
echo "Setting up Python virtual environment..."
python3 -m venv env
echo "Done"
echo "Entering virtual environment..."
source env/bin/activate
echo "Installing Python dependencies..."
pip3 install -r requirements.txt
echo "Done"
echo "Installing Javascript dependencies..."
npm install . --legacy-peer-deps
echo "Done"
echo "Finished setup"
''',
# README
'/README.md' : '''# {{name}}
## Getting started
Run the setup script. This script assumes that you have the following already installed:
- Python 3
- pip
- npm
```sh
sudo sh setup.sh
```
The script will set up a virtual environment for the backend, as well as install all JS/Python dependencies.
## Running the server
```sh
./runServer build
```
Then visit the webpage from your browser at url [0.0.0.0:8000](0.0.0.0:8000)!
*Alternativtely*, if you don't want to rebuild the javascript and restart the server everytime you make a JS change, you can split it into two terminal commands:
```sh
npm run dev
```
This will cause the JS compiler to watch for changes indefinitely and update as you save your files (until you Ctrl-C). Then, on a fresh terminal, run the server without the build command:
```sh
./runServer
```
''',
# src/init
'/src/__init__.py' : '''import flask
app = flask.Flask(__name__)
app.config.from_object('config')
app.config.from_envvar('APP_SETTINGS', silent=True)
# import pyrebase
# firebase = pyrebase.initialize_app({
# TODO: uncomment if using firebase
# })
# db = firebase.database()
import src.views
import src.api
''',
# src/api/init
'/src/api/__init__.py' : '''from src.api.api import *
''',
# src/api/api.py
'/src/api/api.py' : '''from src import app
from src.util import *
import flask
@app.route("/api/", methods=['GET'])
def get_id():
new_hash = gen_hash(recipe_id_length_c)
return flask.jsonify({'id': new_hash})
''',
# src/js/index.jsx
'/src/js/index.jsx' : '''import React from 'react';
import ReactDOM from 'react-dom';
function Index(props) {
return (
<div>Hello! Welcome to {{name}} :)</div>
);
}
ReactDOM.render(
<Index url="/api/" />,
document.getElementById('reactEntryPoint'),
);
''',
# src/static/css/style.css
'/src/static/css/style.css' : '''.title {
}
.data {
display:none;
}
''',
# src/templates/base.html
'/src/templates/base.html' : '''{% from "macros.html" import declare %}
<!DOCTYPE html>
<html lang="en">
<head>
<title class="title">{% block title %}{{ title }}{% endblock %}</title>
<div class="data">{% block data %}{% endblock %}</div>
<link rel="stylesheet" href="{{url_for('static', filename='css/style.css')}}">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
{% block database %}{% endblock %}
{% block content %}{% endblock %}
{% block entry %}<div id="reactEntryPoint"></div>{% endblock %}
<script
type="text/javascript"
src="{% block script %}{{ url_for('static', filename='js/') }}{{ jsfile }}_compiled.js{% endblock %}">
</script>
<div class="footer"></div>
</body>
</html>
''',
# src/templates/macros.html
'/src/templates/macros.html' : '''{% macro declare(var, name) -%}
<div id={{var}} class="data">{{ name }}</div>
{%- endmacro %}
''',
# src/util/init
'/src/util/__init__.py' : '''from src.util.util import *
from src.util.consts import *
''',
# src/util/util.py
'/src/util/util.py' : '''import string
import random
# Source: https://stackoverflow.com/questions/13484726/safe-enough-8-character-short-unique-random-string
def gen_hash(length):
alphabet = string.ascii_lowercase + string.digits
return ''.join(random.choices(alphabet, k=length))
''',
# src/util/consts.py
'/src/util/consts.py' : '''main_jsfile_c = 'index'
''',
# src/views/init
'/src/views/__init__.py' : '''from src.views.main import *
''',
# src/views/main.py
'/src/views/main.py' : '''from src import app
from src.util import *
import flask
@app.route("/")
def home_page():
context = {
'jsfile': main_jsfile_c,
'title': '{{name}}'
}
return flask.render_template("base.html", **context)
''',
}
if __name__ == '__main__':
main()