-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Template): Introduce template for react extensions (#69)
Co-authored-by: Max <[email protected]> Co-authored-by: Dominik Schubert <[email protected]> Co-authored-by: Thomas Rausch <[email protected]>
- Loading branch information
1 parent
3ff9fab
commit 3f055ec
Showing
29 changed files
with
769 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
Extension Template | ||
================== | ||
|
||
This is a [cookiecutter](https://github.com/cookiecutter/cookiecutter) template that is used when you invoke. | ||
|
||
```console | ||
localstack extensions dev new --template=react | ||
``` | ||
|
||
It contains a simple python distribution config, and some boilerplate extension code. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"project_name": "My LocalStack Extension", | ||
"project_short_description": "All the boilerplate you need to create a LocalStack extension.", | ||
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '-') }}", | ||
"module_name": "{{ cookiecutter.project_slug.replace('-', '_') }}", | ||
"class_name": "{{ cookiecutter.project_name.replace('-', ' ').replace('_', ' ').title().replace(' ', '') }}", | ||
"full_name": "Jane Doe", | ||
"email": "[email protected]", | ||
"github_username": "janedoe", | ||
"version": "0.1.0" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
.venv | ||
frontend/node_modules | ||
frontend/.yarn | ||
dist | ||
build | ||
**/*.egg-info | ||
.eggs | ||
__pycache__ | ||
*.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
VENV_BIN = python3 -m venv | ||
VENV_DIR ?= .venv | ||
VENV_ACTIVATE = $(VENV_DIR)/bin/activate | ||
VENV_RUN = . $(VENV_ACTIVATE) | ||
FRONTEND_FOLDER = frontend | ||
BACKEND_FOLDER = backend | ||
COREPACK_EXISTS := $(shell command -v corepack) | ||
YARN_EXISTS := $(shell command -v yarn) | ||
|
||
|
||
INFO_COLOR = \033[0;36m | ||
NO_COLOR = \033[m | ||
|
||
venv: $(VENV_ACTIVATE) | ||
|
||
$(VENV_ACTIVATE): | ||
test -d .venv || $(VENV_BIN) .venv | ||
$(VENV_RUN); pip install --upgrade pip setuptools plux build wheel | ||
$(VENV_RUN); pip install -e .[dev] | ||
touch $(VENV_DIR)/bin/activate | ||
|
||
check-frontend-deps: | ||
@if [ -z "$(YARN_EXISTS)" ]; then \ | ||
npm install --global yarn; \ | ||
fi | ||
@if [ -z "$(COREPACK_EXISTS)" ]; then \ | ||
npm install -g corepack; \ | ||
fi | ||
|
||
clean: ## Clean the project | ||
rm -rf .venv/ | ||
rm -rf build/ | ||
rm -rf .eggs/ | ||
rm -rf $(BACKEND_FOLDER)/*.egg-info/ | ||
|
||
install-backend: venv ## Install dependencies of the extension | ||
$(VENV_RUN); python -m plux entrypoints | ||
|
||
install-frontend: venv check-frontend-deps ## Install dependencies of the frontend | ||
cd $(FRONTEND_FOLDER) && yarn install | ||
|
||
build-frontend: # Build the React app | ||
@if [ ! -d "$(FRONTEND_FOLDER)/node_modules" ]; then \ | ||
$(MAKE) install-frontend; \ | ||
fi | ||
cd $(FRONTEND_FOLDER); rm -rf build && REACT_APP_DEVELOPMENT_ENVIRONMENT=false NODE_ENV=prod npm run build | ||
|
||
start-frontend: ## Start the frontend in dev mode (hot reload) | ||
cd $(FRONTEND_FOLDER); REACT_APP_DEVELOPMENT_ENVIRONMENT=true yarn start | ||
|
||
install: venv install-backend install-frontend ## Install dependencies | ||
|
||
dist: venv build-frontend ## Create distribution files | ||
$(VENV_RUN); python -m build | ||
|
||
publish: clean-dist venv dist ## Build and upload package to pypi | ||
$(VENV_RUN); pip install --upgrade twine; twine upload dist/* | ||
|
||
clean-dist: clean ## Remove dist folder | ||
rm -rf dist/ | ||
|
||
help: ## Show this help | ||
@echo Please specify a build target. The choices are: | ||
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(INFO_COLOR)%-30s$(NO_COLOR) %s\n", $$1, $$2}' | ||
|
||
.PHONY: clean clean-dist dist install install-backend install-frontend build-frontend start-frontend publish venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{{ cookiecutter.project_name }} | ||
=============================== | ||
|
||
{{ cookiecutter.project_short_description }} | ||
|
||
## Install local development version | ||
|
||
To install the extension into localstack in developer mode, you will need Python 3.10+, and create a virtual environment in the extensions project. | ||
You will also need to install [yarn](https://yarnpkg.com/getting-started/install) as package manager if you haven't already | ||
In the newly generated project, simply run | ||
|
||
```bash | ||
make install | ||
``` | ||
|
||
Then, to enable the extension for LocalStack, run | ||
|
||
```bash | ||
localstack extensions dev enable . | ||
``` | ||
|
||
You can then start LocalStack with `EXTENSION_DEV_MODE=1` to load all enabled extensions: | ||
|
||
```bash | ||
EXTENSION_DEV_MODE=1 localstack start | ||
``` | ||
|
||
## Developing UI | ||
With this template is generated also a UI made in react that is available at either {{ cookiecutter.project_name }}.localhost.localstack.cloud:4566/ or http://localhost.localstack.cloud:4566/_extension/{{ cookiecutter.project_name }}/. | ||
|
||
There are a few make commands available that will help your journey with the UI: | ||
- **build-frontend**: will build the react app into the frontend/build folder which will then be passed into the extension itself allowing the UI to be seen. Remember to always execute this command when you wish to see new changes when using the extension. | ||
- **start-frontend**: will start a live server on port 3000 (by default) that will allow you to have hot reloading when developing locally outside the extension (it will also build the frontend) | ||
|
||
|
||
## Install from GitHub repository | ||
|
||
To distribute your extension, simply upload it to your github account. Your extension can then be installed via: | ||
|
||
```bash | ||
localstack extensions install "git+https://github.com/{{cookiecutter.github_username }}/{{ cookiecutter.project_slug }}/#egg={{ cookiecutter.project_slug }}" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
backend |
1 change: 1 addition & 0 deletions
1
...ates/react/{{cookiecutter.project_slug}}/backend/{{cookiecutter.module_name}}/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
name = "{{ cookiecutter.module_name }}" |
12 changes: 12 additions & 0 deletions
12
...lates/react/{{cookiecutter.project_slug}}/backend/{{cookiecutter.module_name}}/api/web.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from localstack.http import route, Request, Response | ||
|
||
from .. import static | ||
|
||
class WebApp: | ||
@route("/") | ||
def index(self, request: Request, *args, **kwargs): | ||
return Response.for_resource(static, "index.html") | ||
|
||
@route("/<path:path>") | ||
def index2(self, request: Request, path: str, **kwargs): | ||
return Response.for_resource(static, path) |
19 changes: 19 additions & 0 deletions
19
...tes/react/{{cookiecutter.project_slug}}/backend/{{cookiecutter.module_name}}/extension.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import logging | ||
import typing as t | ||
|
||
from localstack.extensions.patterns.webapp import WebAppExtension | ||
|
||
from .api.web import WebApp | ||
|
||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
class {{ cookiecutter.class_name }}(WebAppExtension): | ||
name = "{{ cookiecutter.project_slug }}" | ||
|
||
def __init__(self): | ||
super().__init__(template_package_path=None) | ||
|
||
def collect_routes(self, routes: list[t.Any]): | ||
routes.append(WebApp()) | ||
|
Empty file.
99 changes: 99 additions & 0 deletions
99
templates/react/{{cookiecutter.project_slug}}/frontend/.esbuild/esbuild.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* eslint-disable global-require */ | ||
|
||
const esbuild = require('esbuild'); | ||
const path = require('path'); | ||
|
||
const SvgrPlugin = require('esbuild-plugin-svgr'); | ||
const CopyPlugin = require('esbuild-plugin-copy').default; | ||
const CleanPlugin = require('esbuild-plugin-clean').default; | ||
const { NodeModulesPolyfillPlugin } = require('@esbuild-plugins/node-modules-polyfill'); | ||
|
||
const packageJson = require('../package.json'); | ||
const HtmlPlugin = require('./plugins/html'); | ||
const { writeFileSync } = require('fs'); | ||
|
||
const CURRENT_ENV = process.env.NODE_ENV || 'development.local'; | ||
const BUILD_PATH = path.join(__dirname, '..', '..', 'backend', '{{cookiecutter.module_name}}', 'static'); | ||
|
||
const BUILD_CONFIG = { | ||
entryPoints: [ | ||
path.join(__dirname, '..', 'src', 'index.tsx'), | ||
path.join(__dirname, '..', 'src', 'index.html'), | ||
], | ||
assetNames: '[name]-[hash]', | ||
entryNames: '[name]-[hash]', | ||
outdir: BUILD_PATH, | ||
bundle: true, | ||
minify: !CURRENT_ENV.includes('development.local'), | ||
sourcemap: true, | ||
target: 'es2015', | ||
metafile: true, | ||
// splitting: true, | ||
// set in case file loader is added below | ||
plugins: [ | ||
CleanPlugin({ | ||
patterns: [`${BUILD_PATH}/*`, `!${BUILD_PATH}/index.html`], | ||
sync: true, | ||
verbose: false, | ||
options: { | ||
force: true | ||
} | ||
}), | ||
SvgrPlugin({ | ||
prettier: false, | ||
svgo: false, | ||
svgoConfig: { | ||
plugins: [{ removeViewBox: false }], | ||
}, | ||
titleProp: true, | ||
ref: true, | ||
}), | ||
CopyPlugin({ | ||
copyOnStart: true, | ||
// https://github.com/LinbuduLab/nx-plugins/issues/57 | ||
assets: [ | ||
{ | ||
from: ['./public/*'], | ||
to: ['./'], | ||
}, | ||
], | ||
}), | ||
NodeModulesPolyfillPlugin(), | ||
HtmlPlugin({ | ||
filename: path.join(BUILD_PATH, 'index.html'), | ||
env: true, | ||
}), | ||
], | ||
inject: [path.join(__dirname, 'esbuild.shims.js')], | ||
define: { | ||
// Define replacements for env vars starting with `REACT_APP_` | ||
...Object.entries(process.env).reduce( | ||
(memo, [name, value]) => name.startsWith('REACT_APP_') ? | ||
{ ...memo, [`process.env.${name}`]: JSON.stringify(value) } : | ||
memo, | ||
{}, | ||
), | ||
'process.cwd': 'dummyProcessCwd', | ||
global: 'window', | ||
}, | ||
external: [ | ||
...Object.keys(packageJson.devDependencies || {}), | ||
], | ||
loader: { | ||
'.md': 'text', | ||
'.gif': 'dataurl', | ||
} | ||
}; | ||
|
||
const build = async (overrides = {}) => { | ||
try { | ||
await esbuild.build({ ...BUILD_CONFIG, ...overrides }); | ||
writeFileSync(path.join(BUILD_PATH, '__init__.py'),'') | ||
console.log('done building'); | ||
} catch (e) { | ||
console.error(e); | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
module.exports = { build }; |
7 changes: 7 additions & 0 deletions
7
templates/react/{{cookiecutter.project_slug}}/frontend/.esbuild/esbuild.shims.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import * as React from 'react'; | ||
|
||
export { React }; | ||
|
||
export function dummyProcessCwd() { | ||
return ''; | ||
}; |
11 changes: 11 additions & 0 deletions
11
templates/react/{{cookiecutter.project_slug}}/frontend/.esbuild/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
const { build, serve } = require('./esbuild.config'); | ||
|
||
(async () => { | ||
if (process.argv.includes('--serve')) { | ||
await serve(); | ||
} else if (process.argv.includes('--watch')) { | ||
await build({ watch: true }); | ||
} else { | ||
await build(); | ||
} | ||
})(); |
84 changes: 84 additions & 0 deletions
84
templates/react/{{cookiecutter.project_slug}}/frontend/.esbuild/plugins/html/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const crypto = require('crypto'); | ||
|
||
/** | ||
* @param {object} config | ||
* @param {string} config.filename - HTML file to process and override | ||
* @param {boolean} config.env - Whether to replace env vars or not (default - `false`) | ||
* @param {string} config.envPrefix - Limit env vars to pick (default - `REACT_APP_`) | ||
*/ | ||
const HtmlPlugin = (config) => ({ | ||
name: 'html', | ||
setup(build) { | ||
build.onResolve({ filter: /\.html$/ }, args => ({ | ||
path: path.resolve(args.resolveDir, args.path), | ||
namespace: 'html', | ||
})); | ||
build.onLoad({ filter: /.html/, namespace: 'html' }, (args) => { | ||
let htmlContent = fs.readFileSync(args.path).toString('utf-8'); | ||
|
||
// replace env vars | ||
if (config.env) { | ||
const envPrefix = config.envPrefix || 'REACT_APP_'; | ||
const envVars = Object.entries(process.env || {}).filter(([name]) => name.startsWith(envPrefix)); | ||
htmlContent = envVars.reduce( | ||
(memo, [name, value]) => memo.replace(new RegExp(`%${name}%`, 'igm'), value), | ||
htmlContent, | ||
); | ||
} | ||
|
||
return { | ||
contents: htmlContent, | ||
loader: 'file' | ||
}; | ||
}); | ||
|
||
build.onEnd((result) => { | ||
const outFiles = Object.keys((result.metafile || {}).outputs); | ||
const jsFiles = outFiles.filter((p) => p.endsWith('.js')); | ||
const cssFiles = outFiles.filter((p) => p.endsWith('.css')); | ||
const htmlFiles = outFiles.filter((p) => p.endsWith('.html')); | ||
|
||
const headerAppends = cssFiles.reduce( | ||
(memo, p) => { | ||
const filename = p.split(path.sep).slice(-1)[0]; | ||
return [...memo, `<link href="${filename}" rel="stylesheet">`]; | ||
}, | ||
[], | ||
); | ||
|
||
const bodyAppends = jsFiles.reduce( | ||
(memo, p) => { | ||
const filename = p.split(path.sep).slice(-1)[0]; | ||
return [...memo, `<script src="${filename}"></script>`]; | ||
}, | ||
[], | ||
); | ||
|
||
for (const htmlFile of htmlFiles) { | ||
let htmlContent = fs.readFileSync(htmlFile).toString('utf-8'); | ||
|
||
// replace env vars | ||
if (config.env) { | ||
const envPrefix = config.envPrefix || 'REACT_APP_'; | ||
const envVars = Object.entries(process.env).filter(([name]) => name.startsWith(envPrefix)); | ||
|
||
htmlContent = envVars.reduce( | ||
(memo, [name, value]) => memo.replace(new RegExp(`%${name}%`, 'igm'), value), | ||
htmlContent, | ||
); | ||
} | ||
|
||
// inject references to js and css files | ||
htmlContent = htmlContent | ||
.replace('</head>', [...headerAppends, '</head>'].join("\n")) | ||
.replace('</body>', [...bodyAppends, '</body>'].join("\n")); | ||
|
||
fs.writeFileSync(config.filename.replace('-[^.]+', ''), htmlContent); | ||
} | ||
}); | ||
}, | ||
}); | ||
|
||
module.exports = HtmlPlugin; |
1 change: 1 addition & 0 deletions
1
templates/react/{{cookiecutter.project_slug}}/frontend/.yarnrc.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
nodeLinker: node-modules |
Empty file.
Oops, something went wrong.