adding packages
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# Update VARIANT in devcontainer.json to pick a Dart version
|
||||
ARG VARIANT=2
|
||||
FROM google/dart:${VARIANT}
|
||||
|
||||
# [Option] Install zsh
|
||||
ARG INSTALL_ZSH="true"
|
||||
# [Option] Upgrade OS packages to their latest versions
|
||||
ARG UPGRADE_PACKAGES="false"
|
||||
|
||||
# Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies.
|
||||
ARG USERNAME=vscode
|
||||
ARG USER_UID=1000
|
||||
ARG USER_GID=$USER_UID
|
||||
COPY library-scripts/*.sh /tmp/library-scripts/
|
||||
RUN apt-get update \
|
||||
&& /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \
|
||||
&& apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts
|
||||
|
||||
# Add bin location to path
|
||||
ENV PUB_CACHE="/usr/local/share/pub-cache"
|
||||
ENV PATH="${PATH}:${PUB_CACHE}/bin"
|
||||
RUN mkdir -p ${PUB_CACHE} \
|
||||
&& chown ${USERNAME}:root ${PUB_CACHE} \
|
||||
&& echo "if [ \"\$(stat -c '%U' ${PUB_CACHE})\" != \"${USERNAME}\" ]; then sudo chown -R ${USER_UID}:root ${PUB_CACHE}; fi" \
|
||||
| tee -a /root/.bashrc /root/.zshrc /home/${USERNAME}/.bashrc >> /home/${USERNAME}/.zshrc
|
||||
|
||||
# [Optional] Uncomment this section to install additional OS packages.
|
||||
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# && apt-get -y install --no-install-recommends <your-package-list-here>
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "Dart",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
// Update VARIANT to pick a Dart version
|
||||
"args": { "VARIANT": "2" }
|
||||
},
|
||||
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"terminal.integrated.shell.linux": "/bin/bash"
|
||||
},
|
||||
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"dart-code.dart-code"
|
||||
]
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "uname -a",
|
||||
|
||||
// Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
|
||||
// "remoteUser": "vscode"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Warning: Folder contents may be replaced
|
||||
|
||||
The contents of this folder will be automatically replaced with a file of the same name in the [vscode-dev-containers](https://github.com/microsoft/vscode-dev-containers) repository's [script-library folder](https://github.com/microsoft/vscode-dev-containers/tree/master/script-library) whenever the repository is packaged.
|
||||
|
||||
To retain your edits, move the file to a different location. You may also delete the files if they are not needed.
|
||||
@@ -0,0 +1,309 @@
|
||||
#!/usr/bin/env bash
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
#
|
||||
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/master/script-library/docs/common.md
|
||||
#
|
||||
# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My *! flag]
|
||||
|
||||
INSTALL_ZSH=${1:-"true"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
USER_UID=${3:-"automatic"}
|
||||
USER_GID=${4:-"automatic"}
|
||||
UPGRADE_PACKAGES=${5:-"true"}
|
||||
INSTALL_OH_MYS=${6:-"true"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If in automatic mode, determine if a user already exists, if not use vscode
|
||||
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
|
||||
USERNAME=""
|
||||
POSSIBLE_USERS=("vscode", "node", "codespace", "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
|
||||
for CURRENT_USER in ${POSSIBLE_USERS[@]}; do
|
||||
if id -u ${CURRENT_USER} > /dev/null 2>&1; then
|
||||
USERNAME=${CURRENT_USER}
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "${USERNAME}" = "" ]; then
|
||||
USERNAME=vscode
|
||||
fi
|
||||
elif [ "${USERNAME}" = "none" ]; then
|
||||
USERNAME=root
|
||||
USER_UID=0
|
||||
USER_GID=0
|
||||
fi
|
||||
|
||||
# Load markers to see which steps have already run
|
||||
MARKER_FILE="/usr/local/etc/vscode-dev-containers/common"
|
||||
if [ -f "${MARKER_FILE}" ]; then
|
||||
echo "Marker file found:"
|
||||
cat "${MARKER_FILE}"
|
||||
source "${MARKER_FILE}"
|
||||
fi
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Function to call apt-get if needed
|
||||
apt-get-update-if-needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies
|
||||
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
|
||||
apt-get-update-if-needed
|
||||
|
||||
PACKAGE_LIST="apt-utils \
|
||||
git \
|
||||
openssh-client \
|
||||
gnupg2 \
|
||||
iproute2 \
|
||||
procps \
|
||||
lsof \
|
||||
htop \
|
||||
net-tools \
|
||||
psmisc \
|
||||
curl \
|
||||
wget \
|
||||
rsync \
|
||||
ca-certificates \
|
||||
unzip \
|
||||
zip \
|
||||
nano \
|
||||
vim-tiny \
|
||||
less \
|
||||
jq \
|
||||
lsb-release \
|
||||
apt-transport-https \
|
||||
dialog \
|
||||
libc6 \
|
||||
libgcc1 \
|
||||
libgssapi-krb5-2 \
|
||||
libicu[0-9][0-9] \
|
||||
liblttng-ust0 \
|
||||
libstdc++6 \
|
||||
zlib1g \
|
||||
locales \
|
||||
sudo \
|
||||
ncdu \
|
||||
man-db"
|
||||
|
||||
# Install libssl1.1 if available
|
||||
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
|
||||
PACKAGE_LIST="${PACKAGE_LIST} libssl1.1"
|
||||
fi
|
||||
|
||||
# Install appropriate version of libssl1.0.x if available
|
||||
LIBSSL=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
|
||||
if [ "$(echo "$LIBSSL" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
|
||||
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
|
||||
# Debian 9
|
||||
PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.2"
|
||||
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
|
||||
# Ubuntu 18.04, 16.04, earlier
|
||||
PACKAGE_LIST="${PACKAGE_LIST} libssl1.0.0"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Packages to verify are installed: ${PACKAGE_LIST}"
|
||||
apt-get -y install --no-install-recommends ${PACKAGE_LIST} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 )
|
||||
|
||||
PACKAGES_ALREADY_INSTALLED="true"
|
||||
fi
|
||||
|
||||
# Get to latest versions of all packages
|
||||
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
|
||||
apt-get-update-if-needed
|
||||
apt-get -y upgrade --no-install-recommends
|
||||
apt-get autoremove -y
|
||||
fi
|
||||
|
||||
# Ensure at least the en_US.UTF-8 UTF-8 locale is available.
|
||||
# Common need for both applications and things like the agnoster ZSH theme.
|
||||
if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
|
||||
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
|
||||
locale-gen
|
||||
LOCALE_ALREADY_SET="true"
|
||||
fi
|
||||
|
||||
# Create or update a non-root user to match UID/GID.
|
||||
if id -u ${USERNAME} > /dev/null 2>&1; then
|
||||
# User exists, update if needed
|
||||
if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -G $USERNAME)" ]; then
|
||||
groupmod --gid $USER_GID $USERNAME
|
||||
usermod --gid $USER_GID $USERNAME
|
||||
fi
|
||||
if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then
|
||||
usermod --uid $USER_UID $USERNAME
|
||||
fi
|
||||
else
|
||||
# Create user
|
||||
if [ "${USER_GID}" = "automatic" ]; then
|
||||
groupadd $USERNAME
|
||||
else
|
||||
groupadd --gid $USER_GID $USERNAME
|
||||
fi
|
||||
if [ "${USER_UID}" = "automatic" ]; then
|
||||
useradd -s /bin/bash --gid $USERNAME -m $USERNAME
|
||||
else
|
||||
useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add add sudo support for non-root user
|
||||
if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then
|
||||
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
|
||||
chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
EXISTING_NON_ROOT_USER="${USERNAME}"
|
||||
fi
|
||||
|
||||
# ** Shell customization section **
|
||||
if [ "${USERNAME}" = "root" ]; then
|
||||
USER_RC_PATH="/root"
|
||||
else
|
||||
USER_RC_PATH="/home/${USERNAME}"
|
||||
fi
|
||||
|
||||
# .bashrc/.zshrc snippet
|
||||
RC_SNIPPET="$(cat << EOF
|
||||
export USER=\$(whoami)
|
||||
|
||||
export PATH=\$PATH:\$HOME/.local/bin
|
||||
|
||||
if type code-insiders > /dev/null 2>&1 && ! type code > /dev/null 2>&1; then
|
||||
alias code=code-insiders
|
||||
fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Codespaces themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme
|
||||
CODESPACES_BASH="$(cat \
|
||||
<<EOF
|
||||
#!/usr/bin/env bash
|
||||
prompt() {
|
||||
if [ "\$?" != "0" ]; then
|
||||
local arrow_color=\${bold_red}
|
||||
else
|
||||
local arrow_color=\${reset_color}
|
||||
fi
|
||||
if [ ! -z "\${GITHUB_USER}" ]; then
|
||||
local USERNAME="gh:@\${GITHUB_USER}"
|
||||
else
|
||||
local USERNAME="\$(whoami)"
|
||||
fi
|
||||
local cwd="\$(pwd | sed "s|^\${HOME}|~|")"
|
||||
PS1="\${green}\${USERNAME} \${arrow_color}➜\${reset_color} \${bold_blue}\${cwd}\${reset_color} \$(scm_prompt_info)\${white}$ \${reset_color}"
|
||||
}
|
||||
SCM_THEME_PROMPT_PREFIX="\${reset_color}\${cyan}(\${bold_red}"
|
||||
SCM_THEME_PROMPT_SUFFIX="\${reset_color} "
|
||||
SCM_THEME_PROMPT_DIRTY=" \${bold_yellow}✗\${reset_color}\${cyan})"
|
||||
SCM_THEME_PROMPT_CLEAN="\${reset_color}\${cyan})"
|
||||
SCM_GIT_SHOW_MINIMAL_INFO="true"
|
||||
safe_append_prompt_command prompt
|
||||
EOF
|
||||
)"
|
||||
CODESPACES_ZSH="$(cat \
|
||||
<<EOF
|
||||
prompt() {
|
||||
if [ ! -z "\${GITHUB_USER}" ]; then
|
||||
local USERNAME="gh:@\${GITHUB_USER}"
|
||||
else
|
||||
local USERNAME="\$(whoami)"
|
||||
fi
|
||||
PROMPT="%{\$fg[green]%}\${USERNAME} %(?:%{\$reset_color%}➜ :%{\$fg_bold[red]%}➜ )"
|
||||
PROMPT+='%{\$fg_bold[blue]%}%~%{\$reset_color%} \$(git_prompt_info)%{\$fg[white]%}$ %{\$reset_color%}'
|
||||
}
|
||||
ZSH_THEME_GIT_PROMPT_PREFIX="%{\$fg_bold[cyan]%}(%{\$fg_bold[red]%}"
|
||||
ZSH_THEME_GIT_PROMPT_SUFFIX="%{\$reset_color%} "
|
||||
ZSH_THEME_GIT_PROMPT_DIRTY=" %{\$fg_bold[yellow]%}✗%{\$fg_bold[cyan]%})"
|
||||
ZSH_THEME_GIT_PROMPT_CLEAN="%{\$fg_bold[cyan]%})"
|
||||
prompt
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Adapted Oh My Zsh! install step to work with both "Oh Mys" rather than relying on an installer script
|
||||
# See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for offical script.
|
||||
install-oh-my()
|
||||
{
|
||||
local OH_MY=$1
|
||||
local OH_MY_INSTALL_DIR="${USER_RC_PATH}/.oh-my-${OH_MY}"
|
||||
local TEMPLATE="${OH_MY_INSTALL_DIR}/templates/$2"
|
||||
local OH_MY_GIT_URL=$3
|
||||
local USER_RC_FILE="${USER_RC_PATH}/.${OH_MY}rc"
|
||||
|
||||
if [ -d "${OH_MY_INSTALL_DIR}" ] || [ "${INSTALL_OH_MYS}" != "true" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
umask g-w,o-w
|
||||
mkdir -p ${OH_MY_INSTALL_DIR}
|
||||
git clone --depth=1 \
|
||||
-c core.eol=lf \
|
||||
-c core.autocrlf=false \
|
||||
-c fsck.zeroPaddedFilemode=ignore \
|
||||
-c fetch.fsck.zeroPaddedFilemode=ignore \
|
||||
-c receive.fsck.zeroPaddedFilemode=ignore \
|
||||
${OH_MY_GIT_URL} ${OH_MY_INSTALL_DIR} 2>&1
|
||||
echo -e "$(cat "${TEMPLATE}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${USER_RC_FILE}
|
||||
if [ "${OH_MY}" = "bash" ]; then
|
||||
sed -i -e 's/OSH_THEME=.*/OSH_THEME="codespaces"/g' ${USER_RC_FILE}
|
||||
mkdir -p ${OH_MY_INSTALL_DIR}/custom/themes/codespaces
|
||||
echo "${CODESPACES_BASH}" > ${OH_MY_INSTALL_DIR}/custom/themes/codespaces/codespaces.theme.sh
|
||||
else
|
||||
sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${USER_RC_FILE}
|
||||
mkdir -p ${OH_MY_INSTALL_DIR}/custom/themes
|
||||
echo "${CODESPACES_ZSH}" > ${OH_MY_INSTALL_DIR}/custom/themes/codespaces.zsh-theme
|
||||
fi
|
||||
# Shrink git while still enabling updates
|
||||
cd ${OH_MY_INSTALL_DIR}
|
||||
git repack -a -d -f --depth=1 --window=1
|
||||
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
cp -rf ${USER_RC_FILE} ${OH_MY_INSTALL_DIR} /root
|
||||
chown -R ${USERNAME}:${USERNAME} ${USER_RC_PATH}
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then
|
||||
echo "${RC_SNIPPET}" >> /etc/bash.bashrc
|
||||
RC_SNIPPET_ALREADY_ADDED="true"
|
||||
fi
|
||||
install-oh-my bash bashrc.osh-template https://github.com/ohmybash/oh-my-bash
|
||||
|
||||
# Optionally install and configure zsh and Oh My Zsh!
|
||||
if [ "${INSTALL_ZSH}" = "true" ]; then
|
||||
if ! type zsh > /dev/null 2>&1; then
|
||||
apt-get-update-if-needed
|
||||
apt-get install -y zsh
|
||||
fi
|
||||
if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
|
||||
echo "${RC_SNIPPET}" >> /etc/zsh/zshrc
|
||||
ZSH_ALREADY_INSTALLED="true"
|
||||
fi
|
||||
install-oh-my zsh zshrc.zsh-template https://github.com/ohmyzsh/ohmyzsh
|
||||
fi
|
||||
|
||||
# Write marker file
|
||||
mkdir -p "$(dirname "${MARKER_FILE}")"
|
||||
echo -e "\
|
||||
PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\
|
||||
LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\
|
||||
EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\
|
||||
RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\
|
||||
ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}"
|
||||
|
||||
echo "Done!"
|
||||
@@ -0,0 +1,42 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/Flutter/.last_build_id
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
# Symbolication related
|
||||
app.*.symbols
|
||||
|
||||
# Obfuscation related
|
||||
app.*.map.json
|
||||
third_party/flutter
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "third_party/flutter_ast_core"]
|
||||
path = third_party/flutter_ast_core
|
||||
url = https://rodydavis@github.com/rodydavis/flutter_ast_core
|
||||
@@ -0,0 +1,7 @@
|
||||
README.md
|
||||
flutter_gen
|
||||
definition-manifest.json
|
||||
.devcontainer/library-scripts/README.md
|
||||
.vscode
|
||||
.npmignore
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Server",
|
||||
"type": "dart",
|
||||
"request": "launch",
|
||||
"program": "bin/server.dart",
|
||||
"cwd": "flutter_gen",
|
||||
"serverReadyAction": {
|
||||
"pattern": "Listening on localhost:([0-9]+)",
|
||||
"uriFormat": "http://localhost:%s",
|
||||
"action": "openExternally"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Rody Davis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,693 @@
|
||||
# Dart/Flutter AST Generator
|
||||
|
||||
Parse a Dart or Flutter file and return a opinionated AST for use to create a dynamic widget or runtime. Works in a browser or native at runtime.
|
||||
|
||||
You can pass an input as a file or directory:
|
||||
```
|
||||
$ dart ./bin/generator.dart -p samples/example.dart
|
||||
|
||||
$ dart ./bin/generator.dart -p samples
|
||||
```
|
||||
|
||||
Or you can call the method directly:
|
||||
```dart
|
||||
final DartResult result = parseSource("Dart Code Here");
|
||||
print(result.toJson());
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ Classes
|
||||
- ✅ Enums
|
||||
- ✅ Logic Tree
|
||||
- ✅ Flutter Support
|
||||
- ✅ Top Level Methods and Variables
|
||||
- ✅ Methods
|
||||
- ✅ Fields
|
||||
- ✅ Constructors
|
||||
|
||||
## Example
|
||||
|
||||
Here is a sample input:
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum MyEnum { one, type, three }
|
||||
|
||||
const int kGlobalField = 1;
|
||||
|
||||
/// This is a doc comment
|
||||
class MyScreen extends StatelessWidget {
|
||||
const MyScreen(this.position, {Key key, this.myField = false, this.mySecondField = 1,
|
||||
this.numField = 3,
|
||||
this.mapField = const {},
|
||||
this.dateField,
|
||||
this.listField = const [],
|
||||
}) : super(key: key);
|
||||
|
||||
const MyScreen.alt(this.position, {Key key, this.mySecondField = double.infinity,
|
||||
this.numField = 3,
|
||||
this.mapField = const {},
|
||||
this.listField = const [],
|
||||
this.dateField,
|
||||
}) : this.myField = true, super(key: key);
|
||||
|
||||
static const String routeName = '/my_route';
|
||||
|
||||
final bool myField;
|
||||
final double mySecondField;
|
||||
final num numField;
|
||||
final Map mapField;
|
||||
final DateTime dateField;
|
||||
final List listField;
|
||||
|
||||
final int position;
|
||||
|
||||
// This is a normal comment
|
||||
Map<String, dynamic> toJson() {
|
||||
return {};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (myField) {
|
||||
return mySecondField == 1 ? Container(color: Colors.red) : Container(color: Colors.blue);
|
||||
}
|
||||
return Container(
|
||||
color: Colors.red,
|
||||
width: 20,
|
||||
child: Center(
|
||||
child: Builder((context) {
|
||||
return Text('Hello World');
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void myGlobalMethod() {
|
||||
|
||||
}
|
||||
|
||||
// Ignore this simple comment
|
||||
class Simple {
|
||||
String value;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
And that would produce this output:
|
||||
|
||||
```json
|
||||
{
|
||||
"file": {
|
||||
"name": null,
|
||||
"imports": [
|
||||
"package:flutter/material.dart"
|
||||
],
|
||||
"classes": [
|
||||
{
|
||||
"name": "MyScreen",
|
||||
"comments": [
|
||||
"This is a doc comment"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "routeName",
|
||||
"type": "String"
|
||||
},
|
||||
{
|
||||
"name": "myField",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"name": "mySecondField",
|
||||
"type": "double"
|
||||
},
|
||||
{
|
||||
"name": "numField",
|
||||
"type": "num"
|
||||
},
|
||||
{
|
||||
"name": "mapField",
|
||||
"type": "Map"
|
||||
},
|
||||
{
|
||||
"name": "dateField",
|
||||
"type": "DateTime"
|
||||
},
|
||||
{
|
||||
"name": "listField",
|
||||
"type": "List"
|
||||
},
|
||||
{
|
||||
"name": "position",
|
||||
"type": "int"
|
||||
}
|
||||
],
|
||||
"constructors": [
|
||||
{
|
||||
"name": "MyScreen",
|
||||
"properties": [
|
||||
{
|
||||
"value": null,
|
||||
"name": "key",
|
||||
"type": "Key",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "false",
|
||||
"name": "myField",
|
||||
"type": "bool",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "1",
|
||||
"name": "mySecondField",
|
||||
"type": "double",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"name": "numField",
|
||||
"type": "num",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "const {}",
|
||||
"name": "mapField",
|
||||
"type": "Map",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": null,
|
||||
"name": "dateField",
|
||||
"type": "DateTime",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "const []",
|
||||
"name": "listField",
|
||||
"type": "List",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "alt",
|
||||
"properties": [
|
||||
{
|
||||
"value": null,
|
||||
"name": "key",
|
||||
"type": "Key",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": null,
|
||||
"name": "mySecondField",
|
||||
"type": "double",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "3",
|
||||
"name": "numField",
|
||||
"type": "num",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "const {}",
|
||||
"name": "mapField",
|
||||
"type": "Map",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": "const []",
|
||||
"name": "listField",
|
||||
"type": "List",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
},
|
||||
{
|
||||
"value": null,
|
||||
"name": "dateField",
|
||||
"type": "DateTime",
|
||||
"isConst": false,
|
||||
"isFinal": false,
|
||||
"isNamed": true,
|
||||
"isOptional": true,
|
||||
"isPositional": false,
|
||||
"isRequired": false,
|
||||
"isRequiredPositional": false,
|
||||
"isSynthetic": false,
|
||||
"isRequiredNamed": false,
|
||||
"isOptionalNamed": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"name": "toJson",
|
||||
"body": {
|
||||
"name": "method_declaration",
|
||||
"values": [
|
||||
{
|
||||
"name": "type",
|
||||
"props": {
|
||||
"0": "Map<String, dynamic>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "declaration",
|
||||
"values": []
|
||||
},
|
||||
{
|
||||
"name": "block_body",
|
||||
"values": [
|
||||
{
|
||||
"name": "block",
|
||||
"values": [
|
||||
{
|
||||
"name": "return",
|
||||
"values": [
|
||||
{
|
||||
"name": "value",
|
||||
"props": {
|
||||
"0": {
|
||||
"type": "Map",
|
||||
"value": "{}"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": []
|
||||
},
|
||||
{
|
||||
"name": "build",
|
||||
"body": {
|
||||
"name": "method_declaration",
|
||||
"values": [
|
||||
{
|
||||
"name": "type",
|
||||
"props": {
|
||||
"0": "Widget"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "declaration",
|
||||
"values": []
|
||||
},
|
||||
{
|
||||
"name": "block_body",
|
||||
"values": [
|
||||
{
|
||||
"name": "block",
|
||||
"values": [
|
||||
{
|
||||
"name": "if",
|
||||
"values": [
|
||||
{
|
||||
"name": "name",
|
||||
"props": {
|
||||
"0": "myField"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "block",
|
||||
"values": [
|
||||
{
|
||||
"name": "return",
|
||||
"values": [
|
||||
{
|
||||
"name": "conditional",
|
||||
"values": [
|
||||
{
|
||||
"name": "binary",
|
||||
"left": {
|
||||
"name": "name",
|
||||
"props": {
|
||||
"0": "mySecondField"
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"name": "value",
|
||||
"props": {
|
||||
"0": {
|
||||
"type": "int",
|
||||
"value": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"operation": "=="
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "return",
|
||||
"values": [
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null,
|
||||
"width": {
|
||||
"name": "value",
|
||||
"props": {
|
||||
"0": {
|
||||
"type": "int",
|
||||
"value": "20"
|
||||
}
|
||||
}
|
||||
},
|
||||
"child": {
|
||||
"name": "constructor",
|
||||
"value": "Center",
|
||||
"arguments": {
|
||||
"child": {
|
||||
"name": "constructor",
|
||||
"value": "Builder",
|
||||
"arguments": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
],
|
||||
"tree": {
|
||||
"name": null,
|
||||
"body": {
|
||||
"name": "block_body",
|
||||
"values": [
|
||||
{
|
||||
"name": "block",
|
||||
"values": [
|
||||
{
|
||||
"name": "if",
|
||||
"values": [
|
||||
{
|
||||
"name": "name",
|
||||
"props": {
|
||||
"0": "myField"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "block",
|
||||
"values": [
|
||||
{
|
||||
"name": "return",
|
||||
"values": [
|
||||
{
|
||||
"name": "conditional",
|
||||
"values": [
|
||||
{
|
||||
"name": "binary",
|
||||
"left": {
|
||||
"name": "name",
|
||||
"props": {
|
||||
"0": "mySecondField"
|
||||
}
|
||||
},
|
||||
"right": {
|
||||
"name": "value",
|
||||
"props": {
|
||||
"0": {
|
||||
"type": "int",
|
||||
"value": "1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"operation": "=="
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "return",
|
||||
"values": [
|
||||
{
|
||||
"name": "constructor",
|
||||
"value": "Container",
|
||||
"arguments": {
|
||||
"color": null,
|
||||
"width": {
|
||||
"name": "value",
|
||||
"props": {
|
||||
"0": {
|
||||
"type": "int",
|
||||
"value": "20"
|
||||
}
|
||||
}
|
||||
},
|
||||
"child": {
|
||||
"name": "constructor",
|
||||
"value": "Center",
|
||||
"arguments": {
|
||||
"child": {
|
||||
"name": "constructor",
|
||||
"value": "Builder",
|
||||
"arguments": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Simple",
|
||||
"comments": [],
|
||||
"fields": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "String"
|
||||
}
|
||||
],
|
||||
"constructors": [],
|
||||
"methods": []
|
||||
}
|
||||
],
|
||||
"enums": [
|
||||
{
|
||||
"name": "MyEnum",
|
||||
"values": [
|
||||
"one",
|
||||
"type",
|
||||
"three"
|
||||
]
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"name": "kGlobalField",
|
||||
"type": "int"
|
||||
}
|
||||
],
|
||||
"methods": [
|
||||
{
|
||||
"name": "myGlobalMethod",
|
||||
"body": {
|
||||
"name": "function_declaration",
|
||||
"values": [
|
||||
{
|
||||
"name": "type",
|
||||
"props": {
|
||||
"0": "void"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "declaration",
|
||||
"values": []
|
||||
},
|
||||
{
|
||||
"name": "function",
|
||||
"values": [
|
||||
{
|
||||
"name": "block_body",
|
||||
"values": [
|
||||
{
|
||||
"name": "block",
|
||||
"values": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameters": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"errors": []
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,4 @@
|
||||
analyzer:
|
||||
exclude:
|
||||
- "build/**"
|
||||
- "samples/*.g.dart"
|
||||
@@ -0,0 +1,270 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter_ast/src/generator/parser.dart';
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
import 'package:build_cli_annotations/build_cli_annotations.dart';
|
||||
import "package:console/console.dart";
|
||||
import 'package:flutter_ast/flutter_ast.dart';
|
||||
import 'package:mustache_template/mustache_template.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
|
||||
part 'generator.g.dart';
|
||||
|
||||
@CliOptions()
|
||||
class Options {
|
||||
@CliOption(
|
||||
abbr: 'p',
|
||||
help: 'Required. The path to the Directory of widgets or Single File',
|
||||
)
|
||||
final String path;
|
||||
|
||||
@CliOption(
|
||||
abbr: 'o',
|
||||
help: 'The path to the Directory output.',
|
||||
defaultsTo: 'build',
|
||||
)
|
||||
final String output;
|
||||
|
||||
@CliOption(
|
||||
negatable: false,
|
||||
help: 'Prints usage information.',
|
||||
)
|
||||
bool help;
|
||||
|
||||
Options(this.path, this.output);
|
||||
}
|
||||
|
||||
const kBasePath =
|
||||
'/Users/rodydavis/Developer/GitHub/protoypes/widget_studio/third_party/flutter_dynamic_widget/third_party/flutter_ast';
|
||||
|
||||
final cache = Cache();
|
||||
// dart ./bin/flutter_gen.dart -p /Users/rodydavis/Developer/GitHub/protoypes/widget_studio/third_party/flutter/packages/flutter/lib/src/material
|
||||
void main(List<String> args) {
|
||||
Console.init();
|
||||
final options = parseOptions(args);
|
||||
final output = Directory(options.output);
|
||||
final inputDir = Directory(options.path);
|
||||
final inputFile = File(options.path);
|
||||
final _paths = <String>[];
|
||||
if (inputDir.existsSync()) {
|
||||
_processDirectory(inputDir, inputDir, output, _paths);
|
||||
} else if (inputFile.existsSync()) {
|
||||
_paths.add(inputFile.path);
|
||||
} else {
|
||||
throw Exception('Not a valid path!');
|
||||
}
|
||||
|
||||
final template = _getTemplate('widget');
|
||||
var progress = ProgressBar(complete: _paths.length);
|
||||
var i = 0;
|
||||
final parser = GenParser();
|
||||
for (final path in _paths) {
|
||||
_processFile(output, File(path), template, parser);
|
||||
progress.update(++i);
|
||||
}
|
||||
_getFile(output.path, 'base.dart').writeAsStringSync(_base);
|
||||
final files = Directory(p.join(output.path, 'classes')).listSync();
|
||||
final imports = files
|
||||
.map((item) => "export 'classes/${p.basename(item.path)}';")
|
||||
.toList();
|
||||
imports.sort();
|
||||
_getFile(output.path, 'index.dart').writeAsStringSync(imports.join('\n'));
|
||||
final sb = StringBuffer();
|
||||
sb.writeln("import 'index.dart';");
|
||||
sb.writeln("import 'base.dart';");
|
||||
sb.writeln();
|
||||
sb.writeln("Map<String, BaseWidget> widgetLibrary = {");
|
||||
final List<String> _names = [];
|
||||
for (final file in files) {
|
||||
final _result = cache.getCache(p.basename(file.path));
|
||||
if (_result == null) continue;
|
||||
for (final item in _result.file.classes) {
|
||||
if (item.isValid) _names.add(item.name);
|
||||
}
|
||||
}
|
||||
_names.sort();
|
||||
for (final name in _names) {
|
||||
sb.writeln(" '${name}': ${name}Base.readOnly(),");
|
||||
}
|
||||
sb.writeln('};');
|
||||
_getFile(output.path, 'library.dart').writeAsStringSync(sb.toString());
|
||||
// writeIndex(_paths, output);
|
||||
print(parser.toString());
|
||||
}
|
||||
|
||||
Template _getTemplate(String name) {
|
||||
final _typePath = File('$kBasePath/templates/$name.dart.mustache');
|
||||
final _template = Template(
|
||||
_typePath.readAsStringSync(),
|
||||
name: _typePath.path,
|
||||
lenient: true,
|
||||
htmlEscapeValues: false,
|
||||
);
|
||||
return _template;
|
||||
}
|
||||
|
||||
void _processDirectory(
|
||||
Directory dir,
|
||||
Directory input,
|
||||
Directory output,
|
||||
List<String> paths,
|
||||
) {
|
||||
for (final file in dir.listSync(recursive: true)) {
|
||||
if (file is Directory) {
|
||||
_processDirectory(file, input, output, paths);
|
||||
} else {
|
||||
paths.add(p.relative(file.path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _processFile(
|
||||
Directory output, File input, Template template, GenParser parser) {
|
||||
final source = input.readAsStringSync();
|
||||
parser.merge(source);
|
||||
final result = parseSource(source, input.path);
|
||||
cache.setCache(p.basename(input.path), result);
|
||||
final _base = result.file;
|
||||
if (_base?.classes != null && _base.classes.isNotEmpty) {
|
||||
for (final item in _base.classes) {
|
||||
if (item.isValid) {
|
||||
if (!cache.addName(item.name)) continue;
|
||||
final _template = _processClass(item, input);
|
||||
if (_template == null) continue;
|
||||
final name = ReCase(item.name).snakeCase;
|
||||
final _path = 'classes/' + name + '.dart';
|
||||
final _file = _getFile(output.path, _path);
|
||||
final _output = template.renderString(_template);
|
||||
if (_output.trim().isEmpty) {
|
||||
_file.deleteSync();
|
||||
return;
|
||||
}
|
||||
_file.writeAsStringSync(_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> _processClass(
|
||||
DartClass item,
|
||||
File input,
|
||||
) {
|
||||
if (!item.name.startsWith('_') &&
|
||||
!input.path.contains('.g.dart') &&
|
||||
!item.isAbstract) {
|
||||
final _comments = item.comments?.map((e) => e.comment)?.toList() ?? [];
|
||||
final _root = <String, dynamic>{
|
||||
"imports": [
|
||||
{'path': "import '../base.dart';"},
|
||||
],
|
||||
'class': item.name,
|
||||
'constructors': [],
|
||||
'fields': [],
|
||||
'static': [],
|
||||
'comments': _comments,
|
||||
'description': _comments.join('/n'),
|
||||
};
|
||||
for (final sub in item.constructors) {
|
||||
final isDefault = item.name == sub.name;
|
||||
final _name = isDefault ? '${item.name}' : '${item.name}.${sub.name}';
|
||||
if (_name.startsWith('_') || _name.contains('._')) continue;
|
||||
_root['constructors'].add(buildConstructor(_name, sub));
|
||||
}
|
||||
for (final field in item.fields) {
|
||||
if (field is DartField) {
|
||||
_root['fields'].add({
|
||||
'key': field?.name ?? '',
|
||||
'type': field?.type ?? 'dynamic',
|
||||
'value': field?.value?.value ?? 'null',
|
||||
});
|
||||
}
|
||||
}
|
||||
_root['constructor_divider'] =
|
||||
List.from(_root['fields']).isEmpty ? '' : ':';
|
||||
if (List.from(_root['constructors']).isNotEmpty) return _root;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, dynamic> buildConstructor(String name, DartConstructor item) {
|
||||
return {
|
||||
'name': name,
|
||||
'widget': '$name()',
|
||||
'json': jsonEncode({
|
||||
'name': '$name',
|
||||
'params': {},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
class Cache {
|
||||
final _files = <String, DartResult>{};
|
||||
final _names = <String>{};
|
||||
|
||||
void setCache(String path, DartResult result) => _files[path] = result;
|
||||
DartResult getCache(String path) => _files[path];
|
||||
|
||||
bool addName(String name) => _names.add(name);
|
||||
List<String> get name => _names.toList();
|
||||
}
|
||||
|
||||
extension on DartClass {
|
||||
String get extendedClasses {
|
||||
final _extends = (this?.extendsClause ?? '').replaceAll('extends ', '');
|
||||
if (_extends.isEmpty) return '';
|
||||
return _extends;
|
||||
}
|
||||
|
||||
bool get isValid {
|
||||
if (this.name.startsWith('_')) return false;
|
||||
if (this.isAbstract) return false;
|
||||
if (extendedClasses.isEmpty) return false;
|
||||
if ([
|
||||
'StatelessWidget',
|
||||
'StatefulWidget',
|
||||
'MaterialButton',
|
||||
'InheritedWidget',
|
||||
'InheritedTheme',
|
||||
'InlineSpan',
|
||||
'RenderObjectWidget',
|
||||
'BoxScrollView',
|
||||
'ScrollView',
|
||||
'SingleChildRenderObjectWidget',
|
||||
].contains(extendedClasses)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
File _getFile(String output, String filename) {
|
||||
final metaData = File('$output/$filename');
|
||||
if (!metaData.existsSync()) metaData.createSync(recursive: true);
|
||||
return metaData;
|
||||
}
|
||||
|
||||
String get _base => '''
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
export 'package:flutter/material.dart';
|
||||
export 'package:flutter/cupertino.dart' hide RefreshCallback;
|
||||
|
||||
abstract class BaseWidget extends ValueNotifier<Map<String, dynamic>> implements Base {
|
||||
BaseWidget() : super({});
|
||||
Map<String, Object> flavors(BuildContext context);
|
||||
List<String> get constructors;
|
||||
Map<String, String> get properties;
|
||||
String get constructor;
|
||||
Object render(BuildContext context) => flavors(context)[constructor];
|
||||
bool isWidget(BuildContext context) => render(context) is Widget;
|
||||
dynamic getProperty(String key);
|
||||
setProperty(String key, dynamic value);
|
||||
}
|
||||
|
||||
abstract class Base {
|
||||
Map<String, dynamic> toJson();
|
||||
String get description;
|
||||
}
|
||||
''';
|
||||
@@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'generator.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// CliGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Options _$parseOptionsResult(ArgResults result) =>
|
||||
Options(result['path'] as String, result['output'] as String)
|
||||
..help = result['help'] as bool;
|
||||
|
||||
ArgParser _$populateOptionsParser(ArgParser parser) => parser
|
||||
..addOption('path',
|
||||
abbr: 'p', help: 'Required. The path to the Directory of widgets.')
|
||||
..addOption('output',
|
||||
abbr: 'o', help: 'The path to the Directory output.', defaultsTo: 'build')
|
||||
..addFlag('help', help: 'Prints usage information.', negatable: false);
|
||||
|
||||
final _$parserForOptions = _$populateOptionsParser(ArgParser());
|
||||
|
||||
Options parseOptions(List<String> args) {
|
||||
final result = _$parserForOptions.parse(args);
|
||||
return _$parseOptionsResult(result);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import 'package:analyzer/dart/analysis/utilities.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'src/index.dart';
|
||||
export 'src/index.dart';
|
||||
|
||||
DartResult parseSource(String source, [String path]) {
|
||||
assert(source != null && source.isNotEmpty);
|
||||
final result = parseString(
|
||||
content: source,
|
||||
path: path,
|
||||
throwIfDiagnostics: false,
|
||||
);
|
||||
final root = result.unit.root;
|
||||
final file = root.toDartFile();
|
||||
final output = DartResult(file);
|
||||
if (result.errors.isNotEmpty) {
|
||||
output.errors.addAll(result.errors);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
class DartResult {
|
||||
DartResult(this.file);
|
||||
final DartFile file;
|
||||
final List<AnalysisError> errors = [];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'file': file,
|
||||
'errors': [
|
||||
for (final error in errors) error,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => toJson().prettyPrint();
|
||||
}
|
||||
|
||||
extension AnalysisErrorUtils on AnalysisError {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'message': this.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void printMembers(CompilationUnit unit) {
|
||||
for (CompilationUnitMember unitMember in unit.declarations) {
|
||||
if (unitMember is ClassDeclaration) {
|
||||
print(unitMember.name.name);
|
||||
for (ClassMember classMember in unitMember.members) {
|
||||
if (classMember is MethodDeclaration) {
|
||||
print(' ${classMember.name}');
|
||||
} else if (classMember is FieldDeclaration) {
|
||||
for (VariableDeclaration field in classMember.fields.variables) {
|
||||
print(' ${field.name.name}');
|
||||
}
|
||||
} else if (classMember is ConstructorDeclaration) {
|
||||
if (classMember.name == null) {
|
||||
print(' ${unitMember.name.name}');
|
||||
} else {
|
||||
print(' ${unitMember.name.name}.${classMember.name.name}');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export 'package:analyzer/dart/ast/ast.dart';
|
||||
export 'package:analyzer/src/dart/ast/ast.dart';
|
||||
export 'package:analyzer/dart/analysis/utilities.dart';
|
||||
export 'package:analyzer/dart/ast/syntactic_entity.dart';
|
||||
export 'package:_fe_analyzer_shared/src/scanner/token_impl.dart';
|
||||
export 'package:analyzer/dart/analysis/results.dart';
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'comment.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension ClauseDeclarationImplUtils on ClassDeclarationImpl {
|
||||
DartClass toDartClass(DartFile parent) {
|
||||
DartClass base = DartClass(name: this.name.toString());
|
||||
final List<DartField> fields = [];
|
||||
for (final item in this.childEntities.whereType<FieldDeclarationImpl>()) {
|
||||
fields.add(item.toDartField());
|
||||
}
|
||||
final List<DartConstructor> constructors = [];
|
||||
for (final item
|
||||
in this.childEntities.whereType<ConstructorDeclarationImpl>()) {
|
||||
constructors.add(item.toDartConstructor(base));
|
||||
}
|
||||
final List<DartMethod> methods = [];
|
||||
for (final item in this.childEntities.whereType<MethodDeclarationImpl>()) {
|
||||
methods.add(item.toDartMethod(base));
|
||||
}
|
||||
final List<DartComment> comments = [];
|
||||
for (final item in this.childEntities.whereType<CommentImpl>()) {
|
||||
comments.add(item.toDartComment());
|
||||
}
|
||||
return base.copyWith(
|
||||
isAbstract: this?.abstractKeyword != null,
|
||||
extendsClause: this?.extendsClause?.toString(),
|
||||
implementsClause: this?.implementsClause?.toString(),
|
||||
withClause: this?.withClause?.toString(),
|
||||
fields: fields,
|
||||
constructors: constructors,
|
||||
methods: methods,
|
||||
comments: comments,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension DartCommentUtils on CommentImpl {
|
||||
DartComment toDartComment() {
|
||||
final _lines = <String>[];
|
||||
for (final child in this.childEntities) {
|
||||
final _desc = child.toString();
|
||||
if (_desc.contains('///')) {
|
||||
final line = _desc.replaceFirst('/// ', '').replaceFirst('///', '');
|
||||
_lines.add(line);
|
||||
}
|
||||
}
|
||||
return DartComment(lines: _lines);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension ConstructorDeclarationImplUtils on ConstructorDeclarationImpl {
|
||||
DartConstructor toDartConstructor(DartClass parent) {
|
||||
DartConstructor base;
|
||||
String _name = '';
|
||||
for (final node in this.childEntities) {
|
||||
if (node is SimpleIdentifierImpl) {
|
||||
_name = node.name;
|
||||
}
|
||||
if (node is DeclaredSimpleIdentifier) {
|
||||
_name = node.name;
|
||||
}
|
||||
base = DartConstructor(name: _name);
|
||||
if (node is FormalParameterListImpl) {
|
||||
for (final child in node.childEntities) {
|
||||
if (child is DefaultFormalParameterImpl) {
|
||||
final _props = List<DartProperty>.from(base.properties);
|
||||
_props.add(child.toDartProperty(parent.fields));
|
||||
base = base.copyWith(properties: _props);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
|
||||
extension LiteralImplUtils on LiteralImpl {
|
||||
DartCore toDartCore() {
|
||||
final value = this;
|
||||
if (value is BooleanLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'bool',
|
||||
value: value.value.toString(),
|
||||
);
|
||||
}
|
||||
if (value is IntegerLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'int',
|
||||
value: value.value.toString(),
|
||||
);
|
||||
}
|
||||
if (value is DoubleLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'double',
|
||||
value: value.value.toString(),
|
||||
);
|
||||
}
|
||||
if (value is StringLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'String',
|
||||
value: value.stringValue.toString(),
|
||||
);
|
||||
}
|
||||
if (value is SetOrMapLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'Map',
|
||||
value: value.toString(),
|
||||
);
|
||||
}
|
||||
if (value is ListLiteralImpl) {
|
||||
return DartCore(
|
||||
type: 'List',
|
||||
value: value.toString(),
|
||||
);
|
||||
}
|
||||
return DartCore(
|
||||
type: null,
|
||||
value: value.toString(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension EnumDeclarationImplUtils on EnumDeclarationImpl {
|
||||
DartEnum toDartEnum() {
|
||||
final _name = this.name.toString();
|
||||
final _values = this.constants.map((e) => e.name.toString()).toList();
|
||||
return DartEnum(
|
||||
name: _name,
|
||||
values: _values,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import 'dart:convert';
|
||||
|
||||
extension Utils on Object {
|
||||
String get description => '${this.runtimeType} -> $this';
|
||||
void debug() => print(description);
|
||||
}
|
||||
|
||||
extension MapUtils on Map {
|
||||
String prettyPrint() {
|
||||
JsonEncoder encoder = new JsonEncoder.withIndent(' ');
|
||||
return encoder.convert(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension FieldDeclarationImplUtils on FieldDeclarationImpl {
|
||||
DartField toDartField() {
|
||||
DartField _base;
|
||||
for (final node in this.root.childEntities) {
|
||||
if (node is VariableDeclarationListImpl) {
|
||||
_base = _process(node);
|
||||
}
|
||||
}
|
||||
return _base;
|
||||
}
|
||||
}
|
||||
|
||||
extension TopLevelVariableDeclarationImplUtils
|
||||
on TopLevelVariableDeclarationImpl {
|
||||
DartField toDartField() {
|
||||
DartField _base;
|
||||
for (final node in this.root.childEntities) {
|
||||
if (node is VariableDeclarationListImpl) {
|
||||
_base = _process(node);
|
||||
}
|
||||
}
|
||||
return _base;
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultFormalParameterImplUtils on DefaultFormalParameterImpl {
|
||||
DartProperty toDartProperty(List<DartField> fields) {
|
||||
DartProperty base;
|
||||
bool _hasValue = false;
|
||||
for (final node in this.root.childEntities) {
|
||||
if (node is SimpleFormalParameterImpl) {
|
||||
base = _processProperty(node);
|
||||
for (final child in node.childEntities) {
|
||||
if (child is DeclaredSimpleIdentifier) {
|
||||
base = base.copyWith(name: child.toString());
|
||||
}
|
||||
if (child is TypeNameImpl) {
|
||||
base = base.copyWith(type: child.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node is FieldFormalParameterImpl) {
|
||||
base = _processProperty(node);
|
||||
for (final child in node.childEntities) {
|
||||
if (child is SimpleIdentifierImpl) {
|
||||
base = base.copyWith(name: child.toString());
|
||||
}
|
||||
}
|
||||
if (fields != null)
|
||||
for (final field in fields) {
|
||||
if (field.name == base.name) {
|
||||
base = base.copyWith(type: field.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node.runtimeType.toString() == 'SimpleToken' &&
|
||||
node.toString() == '=') {
|
||||
_hasValue = true;
|
||||
continue;
|
||||
}
|
||||
if (_hasValue && node is LiteralImpl) {
|
||||
base = base.copyWith(value: node.toDartCore());
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
DartField _processField(FormalParameter node) {
|
||||
return DartField(
|
||||
name: null,
|
||||
type: null,
|
||||
isConst: node.isConst,
|
||||
isFinal: node.isFinal,
|
||||
);
|
||||
}
|
||||
|
||||
DartProperty _processProperty(FormalParameter node) {
|
||||
return DartProperty(
|
||||
name: null,
|
||||
type: null,
|
||||
isNamed: node.isNamed,
|
||||
isOptional: node.isOptional,
|
||||
isPositional: node.isPositional,
|
||||
isRequired: node.isRequired,
|
||||
isRequiredPositional: node.isRequiredPositional,
|
||||
isSynthetic: node.isSynthetic,
|
||||
isRequiredNamed: node.isRequiredNamed,
|
||||
isOptionalNamed: node.isOptionalNamed,
|
||||
);
|
||||
}
|
||||
|
||||
DartField _process(VariableDeclarationListImpl node) {
|
||||
String _type, _name;
|
||||
for (final child in node.childEntities) {
|
||||
if (child is TypeNameImpl) {
|
||||
final TypeNameImpl _node = child;
|
||||
_type = _node.toString();
|
||||
}
|
||||
if (child is VariableDeclarationImpl) {
|
||||
_name = child.name.toString();
|
||||
}
|
||||
}
|
||||
return DartField(
|
||||
type: _type,
|
||||
name: _name,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'index.dart';
|
||||
|
||||
extension AstNodeUtils on AstNode {
|
||||
DartFile toDartFile([String path]) {
|
||||
DartFile base = DartFile(path: path);
|
||||
|
||||
final List<String> imports = [];
|
||||
for (final node in root.childEntities.whereType<ImportDirectiveImpl>()) {
|
||||
final ImportDirectiveImpl _node = node;
|
||||
final _url = _node.uri.stringValue;
|
||||
imports.add(_url);
|
||||
}
|
||||
base = base.copyWith(imports: imports);
|
||||
|
||||
final List<DartField> fields = [];
|
||||
for (final node
|
||||
in root.childEntities.whereType<TopLevelVariableDeclarationImpl>()) {
|
||||
fields.add(node.toDartField());
|
||||
}
|
||||
base = base.copyWith(fields: fields);
|
||||
|
||||
final List<DartMethod> methods = [];
|
||||
for (final node
|
||||
in root.childEntities.whereType<FunctionDeclarationImpl>()) {
|
||||
methods.add(node.toDartMethod());
|
||||
}
|
||||
base = base.copyWith(methods: methods);
|
||||
|
||||
final List<DartClass> classes = [];
|
||||
for (final node in root.childEntities.whereType<ClassDeclarationImpl>()) {
|
||||
final ClassDeclarationImpl _node = node;
|
||||
classes.add(_node.toDartClass(base));
|
||||
}
|
||||
base = base.copyWith(classes: classes);
|
||||
|
||||
final List<DartEnum> enums = [];
|
||||
for (final node in root.childEntities.whereType<EnumDeclarationImpl>()) {
|
||||
final EnumDeclarationImpl _node = node;
|
||||
enums.add(_node.toDartEnum());
|
||||
}
|
||||
base = base.copyWith(enums: enums);
|
||||
|
||||
return base;
|
||||
}
|
||||
}
|
||||
|
||||
extension DartFileUtils on DartFile {
|
||||
String toDart() {
|
||||
final sb = StringBuffer();
|
||||
// TODO: Write back out to Dart
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import '../../flutter_ast.dart';
|
||||
|
||||
class GenParser {
|
||||
GenParser();
|
||||
|
||||
List<DartClass> get classes => _classes.values.toList(growable: false);
|
||||
final Map<String, DartClass> _classes = {};
|
||||
DartClass getClass(String key) =>
|
||||
_classes.containsKey(key) ? _classes[key] : null;
|
||||
|
||||
List<DartEnum> get enums => _enums.toList(growable: false);
|
||||
final Set<DartEnum> _enums = {};
|
||||
|
||||
List<DartField> get fields => _fields.toList(growable: false);
|
||||
final Set<DartField> _fields = {};
|
||||
|
||||
List<DartMethod> get methods => _methods.toList(growable: false);
|
||||
final Set<DartMethod> _methods = {};
|
||||
|
||||
List<String> get imports => _imports.toList(growable: false);
|
||||
final Set<String> _imports = {};
|
||||
|
||||
factory GenParser.fromString(String source) {
|
||||
final base = GenParser();
|
||||
base.merge(source);
|
||||
return base;
|
||||
}
|
||||
|
||||
factory GenParser.fromListString(List<String> sources) {
|
||||
final base = GenParser();
|
||||
for (final source in sources) {
|
||||
base.merge(source);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
void merge(String source) {
|
||||
final DartResult result = parseSource(source);
|
||||
if (result?.file != null) {
|
||||
if (result?.file?.classes != null) {
|
||||
for (final item in result.file.classes) {
|
||||
this._classes.putIfAbsent(item.name, () => item);
|
||||
}
|
||||
}
|
||||
if (result?.file?.enums != null) {
|
||||
this._enums.addAll(result.file.enums);
|
||||
}
|
||||
if (result?.file?.fields != null) {
|
||||
this._fields.addAll(result.file.fields);
|
||||
}
|
||||
if (result?.file?.methods != null) {
|
||||
this._methods.addAll(result.file.methods);
|
||||
}
|
||||
if (result?.file?.imports != null) {
|
||||
this._imports.addAll(result.file.imports);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final sb = StringBuffer();
|
||||
sb.writeln('-- RESULTS --');
|
||||
sb.writeln('Classes: ${this.classes.length}');
|
||||
sb.writeln('Enums: ${this.enums.length}');
|
||||
sb.writeln('Imports: ${this.imports.length}');
|
||||
sb.writeln('Methods: ${this.methods.length}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export 'analyzer.dart';
|
||||
export 'class.dart';
|
||||
export 'comment.dart';
|
||||
export 'constructor.dart';
|
||||
export 'core.dart';
|
||||
export 'enum.dart';
|
||||
export 'extensions.dart';
|
||||
export 'field.dart';
|
||||
export 'file.dart';
|
||||
export 'method.dart';
|
||||
@@ -0,0 +1,225 @@
|
||||
import 'package:flutter_ast_core/flutter_ast_core.dart';
|
||||
|
||||
import 'analyzer.dart';
|
||||
import 'core.dart';
|
||||
|
||||
extension MethodDeclarationImplUtils on MethodDeclarationImpl {
|
||||
DartMethod toDartMethod(DartClass parent) {
|
||||
return DartMethod(
|
||||
name: this.name.toString(),
|
||||
body: _check(this),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionBodyImplUtils on FunctionBodyImpl {
|
||||
DartMethod toDartMethod(DartClass parent) {
|
||||
return DartMethod(
|
||||
name: null,
|
||||
body: _check(this),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension FunctionDeclarationImplUtils on FunctionDeclarationImpl {
|
||||
DartMethod toDartMethod() {
|
||||
return DartMethod(
|
||||
name: this.name.toString(),
|
||||
body: _check(this),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
MethodNode _check(SyntacticEntity node) {
|
||||
if (node is FunctionDeclarationImpl) {
|
||||
return _processFunctionDeclaration(node);
|
||||
}
|
||||
if (node is MethodDeclarationImpl) {
|
||||
return _processMethodDeclarationImpl(node);
|
||||
}
|
||||
if (node is FunctionExpressionImpl) {
|
||||
return _processFunction(node);
|
||||
}
|
||||
if (node is DeclaredSimpleIdentifier) {
|
||||
return _processDeclaration(node);
|
||||
}
|
||||
if (node is MethodInvocationImpl) {
|
||||
return _processMethod(node);
|
||||
}
|
||||
if (node is ReturnStatementImpl) {
|
||||
return _processReturn(node);
|
||||
}
|
||||
if (node is IfStatementImpl) {
|
||||
return _processIfStatement(node);
|
||||
}
|
||||
if (node is ConditionalExpressionImpl) {
|
||||
return _processConditional(node);
|
||||
}
|
||||
if (node is BlockFunctionBodyImpl) {
|
||||
return _processBlockBody(node);
|
||||
}
|
||||
if (node is BlockImpl) {
|
||||
return _processBlock(node);
|
||||
}
|
||||
if (node is BinaryExpressionImpl) {
|
||||
return _processBinary(node);
|
||||
}
|
||||
if (node is SimpleIdentifierImpl) {
|
||||
return MethodNode.simple(
|
||||
name: 'name',
|
||||
value: node.name,
|
||||
);
|
||||
}
|
||||
if (node is LiteralImpl) {
|
||||
return MethodNode.simple(
|
||||
name: 'value',
|
||||
value: node.toDartCore(),
|
||||
);
|
||||
}
|
||||
if (node is TypeNameImpl) {
|
||||
return MethodNode.simple(
|
||||
name: 'type',
|
||||
value: node.toString(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodNode _processFunctionDeclaration(FunctionDeclarationImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'function_declaration',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processMethodDeclarationImpl(MethodDeclarationImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'method_declaration',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processIfStatement(IfStatementImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'if',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processFunction(FunctionExpressionImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'function',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processDeclaration(DeclaredSimpleIdentifier node) {
|
||||
final List<MethodNode> values = [];
|
||||
// Check name meta getter/setter
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'declaration',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processMethod(MethodInvocationImpl node) {
|
||||
final Map<String, MethodNode> arguments = {};
|
||||
final args = node.argumentList;
|
||||
for (var i = 0; i < args.arguments.length; i++) {
|
||||
final arg = args.arguments[i];
|
||||
if (arg is LiteralImpl) {
|
||||
arguments['$i'] = MethodNode.simple(
|
||||
name: 'value',
|
||||
value: arg.toDartCore(),
|
||||
);
|
||||
}
|
||||
if (arg is NamedExpressionImpl) {
|
||||
arguments[arg.name.label.toString()] = _check(arg.expression);
|
||||
}
|
||||
}
|
||||
return MethodNode.constructor(
|
||||
name: 'constructor',
|
||||
value: node.methodName.name,
|
||||
arguments: arguments,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processBinary(BinaryExpressionImpl node) {
|
||||
final _children = node.childEntities.toList();
|
||||
return MethodNode.binary(
|
||||
name: 'binary',
|
||||
left: _check(_children[0]),
|
||||
right: _check(_children[2]),
|
||||
operation: _children[1].toString(),
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processConditional(ConditionalExpressionImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'conditional',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processReturn(ReturnStatementImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'return',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processBlock(BlockImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'block',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
MethodNode _processBlockBody(BlockFunctionBodyImpl node) {
|
||||
final List<MethodNode> values = [];
|
||||
for (final child in node.childEntities) {
|
||||
_checkAndAdd(child, values);
|
||||
}
|
||||
return MethodNode.values(
|
||||
name: 'block_body',
|
||||
values: values,
|
||||
);
|
||||
}
|
||||
|
||||
void _checkAndAdd(SyntacticEntity child, List<MethodNode> values) {
|
||||
final _value = _check(child);
|
||||
if (_value != null && _value.name != null) {
|
||||
values.add(_value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
name: flutter_ast
|
||||
description: A Pure Dart File to Ast Serializer/Deserializer.
|
||||
publish_to: "none"
|
||||
version: 1.0.0+1
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
build_cli_annotations: ^1.0.0
|
||||
analyzer: ^0.39.0
|
||||
path: ^1.7.0
|
||||
console: ^3.1.0
|
||||
mustache_template: ^1.0.0+1
|
||||
_fe_analyzer_shared: ^9.0.0
|
||||
recase: ^3.0.0
|
||||
flutter_ast_core:
|
||||
path: third_party/flutter_ast_core
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^1.6.7
|
||||
build_cli: ^1.3.9
|
||||
@@ -0,0 +1,64 @@
|
||||
// //ignore_for_file: uri_does_not_exist,undefined_class,extends_non_class,undefined_named_parameter,undefined_method,undefined_identifier
|
||||
// import 'package:flutter/material.dart';
|
||||
|
||||
// enum MyEnum { one, type, three }
|
||||
|
||||
// const int kGlobalField = 1;
|
||||
|
||||
// /// This is a doc comment
|
||||
// class MyScreen extends StatelessWidget {
|
||||
// const MyScreen(this.position, {Key key, this.myField = false, this.mySecondField = 1,
|
||||
// this.numField = 3,
|
||||
// this.mapField = const {},
|
||||
// this.dateField,
|
||||
// this.listField = const [],
|
||||
// }) : super(key: key);
|
||||
|
||||
// const MyScreen.alt(this.position, {Key key, this.mySecondField = double.infinity,
|
||||
// this.numField = 3,
|
||||
// this.mapField = const {},
|
||||
// this.listField = const [],
|
||||
// this.dateField,
|
||||
// }) : this.myField = true, super(key: key);
|
||||
|
||||
// static const String routeName = '/my_route';
|
||||
|
||||
// final bool myField;
|
||||
// final double mySecondField;
|
||||
// final num numField;
|
||||
// final Map mapField;
|
||||
// final DateTime dateField;
|
||||
// final List listField;
|
||||
|
||||
// final int position;
|
||||
|
||||
// // This is a normal comment
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {};
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// if (myField) {
|
||||
// return mySecondField == 1 ? Container(color: Colors.red) : Container(color: Colors.blue);
|
||||
// }
|
||||
// return Container(
|
||||
// color: Colors.red,
|
||||
// width: 20,
|
||||
// child: Center(
|
||||
// child: Builder((context) {
|
||||
// return Text('Hello World');
|
||||
// }),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// void myGlobalMethod() {
|
||||
|
||||
// }
|
||||
|
||||
// // Ignore this simple comment
|
||||
// class Simple {
|
||||
// String value;
|
||||
// }
|
||||
@@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
{{#imports}}
|
||||
{{path}}
|
||||
{{/imports}}
|
||||
{{#classes}}
|
||||
|
||||
class {{class}}Render<T> extends StatelessWidget {
|
||||
|
||||
factory {{class}}Render.fromJson(Map<String, dynamic> data, VoidCallback update) {
|
||||
return {{class}}Render(update,
|
||||
{{#fields}}
|
||||
{{name}}Val: BaseCore<{{type}}>(null, update),
|
||||
{{/fields}}
|
||||
);
|
||||
}
|
||||
|
||||
{{class}}Render(this._update, {
|
||||
{{#fields}}
|
||||
@required this.{{name}}Val,
|
||||
{{/fields}}
|
||||
});
|
||||
|
||||
@override
|
||||
final VoidCallback _update;
|
||||
|
||||
{{#fields}}
|
||||
{{core}} {{name}}Val;
|
||||
|
||||
{{type}} get {{name}} {
|
||||
return {{name}}Val.value;
|
||||
}
|
||||
|
||||
set {{name}}({{type}} val) {
|
||||
if (val == this.{{name}}) {
|
||||
return;
|
||||
}
|
||||
{{name}}Val.value = val;
|
||||
}
|
||||
|
||||
{{/fields}}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> get staticFields => {
|
||||
{{#static}}
|
||||
'{{name}}': {{value}},
|
||||
{{/static}}
|
||||
};
|
||||
|
||||
@override
|
||||
List<Core> get props => [
|
||||
{{#fields}}
|
||||
this.{{name}}Val,
|
||||
{{/fields}}
|
||||
];
|
||||
|
||||
@override
|
||||
String get description {
|
||||
final sb = StringBuffer();
|
||||
{{#comments}}
|
||||
sb.writeln("{{comments}}");
|
||||
{{/comments}}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object> get constructors {
|
||||
return {
|
||||
{{#constructors}}
|
||||
'{{name}}': {{className}}(
|
||||
{{#props}}
|
||||
{{key}}{{separator}} this.{{name}},
|
||||
{{/props}}
|
||||
),
|
||||
{{/constructors}}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Map<String, dynamic>> get properties {
|
||||
return {
|
||||
{{#constructors}}
|
||||
'{{name}}': {
|
||||
{{#props}}
|
||||
'{{key}}': this.{{name}},
|
||||
{{/props}}
|
||||
},
|
||||
{{/constructors}}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name': '{{class}}',
|
||||
'props': {
|
||||
{{#fields}}
|
||||
'{{name}}': this.{{name}}Val.toJson(),
|
||||
{{/fields}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, String> toCode() {
|
||||
return {
|
||||
{{#constructors}}
|
||||
'{{name}}': """{{className}}(
|
||||
{{#props}}
|
||||
{{key}}{{separator}} ${this.{{name}}Val.toCode()},
|
||||
{{/props}}
|
||||
)""",
|
||||
{{/constructors}}
|
||||
};
|
||||
}
|
||||
|
||||
final _controller = ValueNotifier<WidgetRect>(null);
|
||||
ValueListenable<WidgetRect> get stats => _controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (isWidget) return TrackedWidget(
|
||||
controller: _controller,
|
||||
child: defaultBase,
|
||||
);
|
||||
return Container();
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isWidget => defaultBase is Widget;
|
||||
|
||||
@override
|
||||
Object get defaultBase => constructors['default'];
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
{{#fields}}
|
||||
properties.add(DiagnosticsProperty('{{name}}', this.{{name}}));
|
||||
{{/fields}}
|
||||
}
|
||||
}
|
||||
|
||||
{{/classes}}
|
||||
@@ -0,0 +1,109 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
@mustCallSuper
|
||||
class TrackedWidget extends SingleChildRenderObjectWidget {
|
||||
TrackedWidget({
|
||||
Key key,
|
||||
@required Widget child,
|
||||
@required this.controller,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
final ValueNotifier<WidgetRect> controller;
|
||||
|
||||
@override
|
||||
RenderObject createRenderObject(BuildContext context) {
|
||||
return WidgetBaseRenderObject(controller);
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class WidgetRect {
|
||||
final double lastDx;
|
||||
final double lastDy;
|
||||
final double lastDw;
|
||||
final double lastDh;
|
||||
|
||||
WidgetRect({
|
||||
this.lastDx = 0.0,
|
||||
this.lastDy = 0.0,
|
||||
this.lastDw = 0.0,
|
||||
this.lastDh = 0.0,
|
||||
});
|
||||
|
||||
WidgetRect copyWith({
|
||||
double lastDx,
|
||||
double lastDy,
|
||||
double lastDw,
|
||||
double lastDh,
|
||||
}) {
|
||||
return WidgetRect(
|
||||
lastDx: lastDx ?? this.lastDx,
|
||||
lastDy: lastDy ?? this.lastDy,
|
||||
lastDw: lastDw ?? this.lastDw,
|
||||
lastDh: lastDh ?? this.lastDh,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetBaseRenderObject extends RenderProxyBox {
|
||||
WidgetBaseRenderObject(this.controller);
|
||||
|
||||
final ValueNotifier<WidgetRect> controller;
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
assert(!debugNeedsLayout);
|
||||
final current = controller.value;
|
||||
controller.value = current.copyWith(lastDx: offset.dx);
|
||||
controller.value = current.copyWith(lastDy: offset.dy);
|
||||
controller.value = current.copyWith(lastDw: size.width);
|
||||
controller.value = current.copyWith(lastDh: size.height);
|
||||
super.paint(context, offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class Core<T> {
|
||||
dynamic get data;
|
||||
ValueChanged<dynamic> get changed;
|
||||
T get fallback;
|
||||
|
||||
T get value;
|
||||
set value(T val);
|
||||
|
||||
String get name => data == null ? null : data['name'];
|
||||
String get type => data == null ? null : data['type'];
|
||||
|
||||
dynamic toCode();
|
||||
dynamic toJson();
|
||||
}
|
||||
|
||||
class BaseCore<T> extends Core<T> {
|
||||
BaseCore(this.data, this.changed);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> data;
|
||||
|
||||
@override
|
||||
final VoidCallback changed;
|
||||
|
||||
@override
|
||||
T get value {
|
||||
if (data == null || data['value'] == null) {
|
||||
return fallback;
|
||||
}
|
||||
return data['value'];
|
||||
}
|
||||
|
||||
@override
|
||||
set value(T val) {
|
||||
if (val == value) {
|
||||
return;
|
||||
}
|
||||
data = {'type': type, 'value': val};
|
||||
changed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
import 'core.dart';
|
||||
|
||||
class EnumObject<T> extends Core<String> {
|
||||
EnumObject(this.data, this.values, this.changed);
|
||||
|
||||
@override
|
||||
final Map<String, dynamic> data;
|
||||
|
||||
@override
|
||||
final ValueChanged<Map<String, dynamic>> changed;
|
||||
|
||||
final List<T> values;
|
||||
|
||||
T get fallback = {{fallback}};
|
||||
|
||||
@override
|
||||
{{type}} get value {
|
||||
if (data == null || data['value'] == null) {
|
||||
return fallback;
|
||||
}
|
||||
final _value = parseValue<{{type}}>(data['value']);
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
set value({{type}} val) {
|
||||
if (val == value) {
|
||||
return;
|
||||
}
|
||||
final _value = serializeValue<{{type}}>(val);
|
||||
changed({'type': type, 'value': _value});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
import 'core.dart';
|
||||
|
||||
class {{name}}Object extends Core<{{type}}> {
|
||||
{{name}}Object(this.data, this.changed);
|
||||
|
||||
@override
|
||||
final Map<String, dynamic> data;
|
||||
|
||||
@override
|
||||
final ValueChanged<Map<String, dynamic>> changed;
|
||||
|
||||
T get fallback = {{fallback}};
|
||||
|
||||
@override
|
||||
{{type}} get value {
|
||||
if (data == null || data['value'] == null) {
|
||||
return fallback;
|
||||
}
|
||||
final _value = parseValue<{{type}}>(data['value']);
|
||||
return _value;
|
||||
}
|
||||
|
||||
@override
|
||||
set value({{type}} val) {
|
||||
if (val == value) {
|
||||
return;
|
||||
}
|
||||
final _value = serializeValue<{{type}}>(val);
|
||||
changed({'type': type, 'value': _value});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
class {{name}}Object<{{type}}> {
|
||||
{{name}}Object(this._data, this._changed);
|
||||
|
||||
final Map<String, dynamic> _data;
|
||||
final VoidCallback _changed;
|
||||
|
||||
{{type}} get value {
|
||||
if (_data == null) {
|
||||
return null;
|
||||
}
|
||||
return _data['value'];
|
||||
}
|
||||
|
||||
set value({{type}} val) {
|
||||
if (val == value) {
|
||||
return;
|
||||
}
|
||||
_data['value'] = val;
|
||||
_changed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
{{#imports}}
|
||||
{{path}}
|
||||
{{/imports}}
|
||||
|
||||
class {{class}}Base extends BaseWidget {
|
||||
{{class}}Base({
|
||||
@required this.constructor,
|
||||
{{#fields}}
|
||||
{{type}} {{name}},
|
||||
{{/fields}}
|
||||
}) {{constructor_divider}}
|
||||
{{#fields}}
|
||||
this._{{name}} = {{name}},
|
||||
{{/fields}}
|
||||
{
|
||||
this.value = this.toJson();
|
||||
}
|
||||
|
||||
@override
|
||||
final String constructor;
|
||||
|
||||
factory {{class}}Base.fromJson(Map<String, dynamic> data, String constructor) {
|
||||
return {{class}}Base(
|
||||
constructor: constructor,
|
||||
{{#fields}}
|
||||
{{name}}: {{value}},
|
||||
{{/fields}}
|
||||
);
|
||||
}
|
||||
|
||||
factory {{class}}Base.readOnly() {
|
||||
return {{class}}Base(
|
||||
constructor: '{{class}}',
|
||||
{{#fields}}
|
||||
{{name}}: null,
|
||||
{{/fields}}
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get description => r"""
|
||||
{{description}}
|
||||
""";
|
||||
|
||||
{{#fields}}
|
||||
{{type}} _{{name}};
|
||||
{{/fields}}
|
||||
|
||||
@override
|
||||
Map<String, String> get properties => {
|
||||
{{#fields}}
|
||||
'{{key}}': '{{type}}',
|
||||
{{/fields}}
|
||||
};
|
||||
|
||||
@override
|
||||
void setProperty(String name, dynamic value) {
|
||||
switch(name) {
|
||||
{{#fields}}
|
||||
case '{{name}}':
|
||||
this._{{name}} = value;
|
||||
break;
|
||||
{{/fields}}
|
||||
default:
|
||||
}
|
||||
this.value = this.toJson();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic getProperty(String name) {
|
||||
switch(name) {
|
||||
{{#fields}}
|
||||
case '{{name}}':
|
||||
return this._{{name}};
|
||||
{{/fields}}
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'name' : '${constructor}',
|
||||
'params': {
|
||||
{{#fields}}
|
||||
'{{key}}': this._{{name}},
|
||||
{{/fields}}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, Object> flavors(BuildContext context) {
|
||||
return {
|
||||
{{#constructors}}
|
||||
'{{name}}': {{widget}},
|
||||
{{/constructors}}
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get constructors => [
|
||||
{{#constructors}}
|
||||
'{{name}}',
|
||||
{{/constructors}}
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user