adding packages

This commit is contained in:
2026-01-15 14:38:46 -08:00
parent ef86e3ab6a
commit 6869cf47e5
5253 changed files with 726695 additions and 34 deletions
@@ -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!"
+42
View File
@@ -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
+3
View File
@@ -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
+7
View File
@@ -0,0 +1,7 @@
README.md
flutter_gen
definition-manifest.json
.devcontainer/library-scripts/README.md
.vscode
.npmignore
+20
View File
@@ -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"
}
}
]
}
+21
View File
@@ -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.
+693
View File
@@ -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';
+38
View File
@@ -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;
}
}
+49
View File
@@ -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(),
);
}
}
+15
View File
@@ -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);
}
}
+113
View File
@@ -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,
);
}
+56
View File
@@ -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();
}
}
+10
View File
@@ -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';
+225
View File
@@ -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);
}
}
+21
View File
@@ -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}}
];
}