Repository: Silind-Software/direflow
Branch: master
Commit: dd1278dcbbfa
Files: 144
Total size: 154.1 KB
Directory structure:
gitextract_rnz70h0d/
├── .eslintrc
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── build.yml
│ └── test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── bin/
│ └── direflow
├── cli/
│ ├── checkForUpdate.ts
│ ├── cli.ts
│ ├── create.ts
│ ├── headline.ts
│ ├── helpers/
│ │ ├── copyTemplate.ts
│ │ ├── detectDireflowSetup.ts
│ │ ├── nameFormat.ts
│ │ └── writeNames.ts
│ ├── messages.ts
│ ├── questions.ts
│ └── types/
│ ├── Command.ts
│ ├── LangageOption.ts
│ ├── Names.ts
│ ├── QuestionOption.ts
│ └── TemplateOption.ts
├── cypress/
│ ├── integration/
│ │ ├── basic_tests.ts
│ │ ├── event_tests.ts
│ │ ├── external_loader_test.ts
│ │ ├── material_ui_test.ts
│ │ ├── props_tests.ts
│ │ ├── slot_tests.ts
│ │ └── styled_components_test.ts
│ ├── support/
│ │ ├── commands.js
│ │ └── index.js
│ ├── test-setup/
│ │ ├── direflow-config.json
│ │ ├── direflow-webpack.js
│ │ ├── jsconfig.json
│ │ ├── jsconfig.paths.json
│ │ ├── package.json
│ │ ├── public/
│ │ │ ├── index.css
│ │ │ ├── index.html
│ │ │ └── index_prod.html
│ │ └── src/
│ │ ├── component-exports.js
│ │ ├── direflow-components/
│ │ │ └── test-setup/
│ │ │ ├── App.css
│ │ │ ├── App.js
│ │ │ ├── MaterialUI.js
│ │ │ ├── StyledComponent.js
│ │ │ ├── index.js
│ │ │ └── test/
│ │ │ └── App.test.js
│ │ └── index.js
│ └── tsconfig.json
├── cypress.json
├── declarations.d.ts
├── package.json
├── packages/
│ ├── direflow-component/
│ │ ├── README.md
│ │ ├── declarations.d.ts
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── DireflowComponent.tsx
│ │ │ ├── WebComponentFactory.tsx
│ │ │ ├── components/
│ │ │ │ ├── EventContext.tsx
│ │ │ │ └── Styled.tsx
│ │ │ ├── decorators/
│ │ │ │ └── DireflowConfiguration.ts
│ │ │ ├── helpers/
│ │ │ │ ├── asyncScriptLoader.ts
│ │ │ │ ├── domControllers.ts
│ │ │ │ ├── getSerialized.ts
│ │ │ │ ├── polyfillHandler.ts
│ │ │ │ ├── proxyRoot.tsx
│ │ │ │ ├── registerPlugin.ts
│ │ │ │ └── styleInjector.tsx
│ │ │ ├── hooks/
│ │ │ │ └── useExternalSource.ts
│ │ │ ├── index.ts
│ │ │ ├── plugins/
│ │ │ │ ├── externalLoaderPlugin.ts
│ │ │ │ ├── fontLoaderPlugin.ts
│ │ │ │ ├── iconLoaderPlugin.ts
│ │ │ │ ├── materialUiPlugin.tsx
│ │ │ │ ├── plugins.ts
│ │ │ │ └── styledComponentsPlugin.tsx
│ │ │ └── types/
│ │ │ ├── DireflowConfig.ts
│ │ │ ├── DireflowElement.ts
│ │ │ ├── DireflowPromiseAlike.ts
│ │ │ └── PluginRegistrator.ts
│ │ └── tsconfig.json
│ └── direflow-scripts/
│ ├── README.md
│ ├── bin/
│ │ └── direflow-scripts
│ ├── declarations.d.ts
│ ├── direflow-jest.config.js
│ ├── package.json
│ ├── src/
│ │ ├── cli.ts
│ │ ├── config/
│ │ │ └── config-overrides.ts
│ │ ├── helpers/
│ │ │ ├── asyncScriptLoader.ts
│ │ │ ├── entryResolver.ts
│ │ │ ├── getDireflowConfig.ts
│ │ │ ├── messages.ts
│ │ │ └── writeTsConfig.ts
│ │ ├── index.ts
│ │ ├── template-scripts/
│ │ │ ├── entryLoader.ts
│ │ │ └── welcome.ts
│ │ └── types/
│ │ ├── ConfigOverrides.ts
│ │ └── DireflowConfig.ts
│ ├── tsconfig.json
│ └── webpack.config.js
├── scripts/
│ ├── bash/
│ │ ├── setupLocal.sh
│ │ └── startIntegrationTest.sh
│ └── node/
│ ├── buildAll.js
│ ├── cleanupAll.js
│ ├── installAll.js
│ └── updateVersion.js
├── templates/
│ ├── js/
│ │ ├── .eslintrc
│ │ ├── README.md
│ │ ├── direflow-config.json
│ │ ├── direflow-webpack.js
│ │ ├── package.json
│ │ ├── public/
│ │ │ ├── index.css
│ │ │ └── index.html
│ │ └── src/
│ │ ├── component-exports.js
│ │ ├── direflow-components/
│ │ │ └── direflow-component/
│ │ │ ├── App.css
│ │ │ ├── App.js
│ │ │ ├── index.js
│ │ │ └── test/
│ │ │ └── App.test.js
│ │ └── index.js
│ └── ts/
│ ├── .eslintrc
│ ├── README.md
│ ├── direflow-config.json
│ ├── direflow-webpack.js
│ ├── package.json
│ ├── public/
│ │ ├── index.css
│ │ └── index.html
│ ├── src/
│ │ ├── component-exports.ts
│ │ ├── direflow-components/
│ │ │ └── direflow-component/
│ │ │ ├── App.css
│ │ │ ├── App.tsx
│ │ │ ├── index.tsx
│ │ │ └── test/
│ │ │ └── App.test.tsx
│ │ ├── index.tsx
│ │ └── react-app-env.d.ts
│ ├── tsconfig.json
│ └── tslint.json
├── test/
│ ├── detectDireflowSetup.test.ts
│ ├── domController.test.ts
│ ├── nameformats.test.ts
│ └── writeNames.test.ts
├── tsconfig.eslint.json
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintrc
================================================
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"settings": {
"pragma": "React",
"version": "detect"
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"airbnb-typescript"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module",
"project": "./tsconfig.eslint.json"
},
"plugins": ["react", "@typescript-eslint"],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/prop-types": "off",
"react/jsx-props-no-spreading": "off",
"react/destructuring-assignment": "off",
"react/display-name": "off",
"import/no-extraneous-dependencies": "off",
"import/no-unresolved": "off",
"no-async-promise-executor": "off",
"no-console": "off",
"no-param-reassign": "off",
"no-underscore-dangle": "off",
"global-require": "off",
"arrow-body-style": "off",
"implicit-arrow-linebreak": "off",
"object-curly-newline": "off",
"lines-between-class-members": "off",
"function-paren-newline": "off",
"linebreak-style": "off",
"operator-linebreak": "off",
"jsx-quotes": "off",
"no-prototype-builtins": "off",
"consistent-return": "off",
"max-len": ["warn", { "code": 120 }]
}
}
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: If you found a bug in Direflow, please file a bug report
title: ''
labels: 'Bug'
assignees: 'Silind'
---
**Describe the bug**
A clear and concise description of what the bug is.
**To reproduce**
Steps to reproduce the behavior:
1. Install '...'
2. Open '....'
3. Build '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Package Manager:**
To install Direflow, I used... (npm / yarn / something else)
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for Direflow
title: ''
labels: 'Enhancement'
assignees: 'Silind'
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/pull_request_template.md
================================================
**What does this pull request introduce? Please describe**
A clear and concise description of what this pull requests includes.
Please add a reference to the related issue, if relevant.
**How did you verify that your changes work as expected? Please describe**
Please describe your methods of testing your changes.
**Example**
Please describe how we can try out your changes
1. Create a new '...'
2. Build with '...'
3. See '...'
**Screenshots**
If applicable, add screenshots to demonstrate your changes.
**Version**
Which version is your changes included in?
Did you remember to update the version of Direflow as explained here:
https://github.com/Silind-Software/direflow/blob/master/CONTRIBUTING.md#version
================================================
FILE: .github/workflows/build.yml
================================================
name: build
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 12.x
registry-url: 'https://registry.npmjs.org'
- name: Prepare
run: |
sudo apt-get install lsof
npm install codecov -g
- name: Install
run: |
npm run clean:all
npm run install:all
- name: Codecov
run: codecov -t ${{ secrets.CODECOV_TOKEN }}
- name: Build
run: |
npm run build:all
- name: Test
run: |
npm run test
- name: Integration Test
run: |
npm run cypress:test
- name: Create version patch
run: npm run update-version patch
- name: Publish direflow-cli to NPM
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
- name: Publish direflow-component to NPM
run: |
cd packages/direflow-component
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
- name: Publish direflow-scripts to NPM
run: |
cd packages/direflow-scripts
npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
================================================
FILE: .github/workflows/test.yml
================================================
name: test
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 12.x
browser: chrome
- name: Prepare
run: |
sudo apt-get install lsof
- name: Install
run: |
npm run clean:all
npm run install:all
- name: Build
run: |
npm run build:all
- name: Test
run: |
npm run test
- name: Integration Test
run: |
npm run cypress:test
================================================
FILE: .gitignore
================================================
# Project setup specifics
dist
build
yarn.lock
config-overrides.js
config-overrides.d.ts
tsconfig.lib.json
# Cypress
cypress/fixtures
cypress/plugins
cypress/screenshots
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
.idea/
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
*.iml
modules.xml
.idea/misc.xml
*.ipr
# Sonarlint plugin
.idea/sonarlint
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
### OSX ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### VisualStudioCode ###
.vscode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### WebStorm+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
# Generated files
# Sensitive or high-churn files
# Gradle
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
# Mongo Explorer plugin
# File-based project format
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
# Editor-based Rest Client
# Android studio 3.1+ serialized cache file
### WebStorm+all Patch ###
# Ignores the whole .idea folder and all .iml files
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
# Sonarlint plugin
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.gitignore.io/api/osx,node,windows,webstorm+all,intellij+all,visualstudiocode
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing or otherwise, unacceptable behavior may be
reported by contacting the project team at contact@silind.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality concerning the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [https://contributor-covenant.org/version/1/4][version]
[homepage]: https://contributor-covenant.org
[version]: https://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
## Issues
In the case of a bug report, a suggestions, or if you just need help, please feel very free to open an issue.
For general issues, please use the following labels:
- Something is not working as intended: `Bug`
- Need help with something: `Help wanted`
- Have a question: `Question`
- Have a suggestion or want to request a feature: `Enhancement`
- Does the question regard direflow-component: `Direflow Component`
- Does the question regard direflow-cli: `Direflow CLI`
## Pull request
Pull requests are really welcome!
### Version
When doing a pull request, please make sure to include an new version in your PR.
There are multiple packages that needs to be in sync, so in order to update the version of Direflow, please use the script:
```console
npm run update-version <new-version>
```
In order to create a version patch, use the command:
```console
npm run update-version patch
```
## Developing on Direflow
Start by making a fork of the direflow repository, and clone it down.
### Install
Now cd into the project folder and run the command:
```console
npm run install:all
```
This command will install the project, the packages and all peer dependencies.
### Link
In order to test your changes locally, you want to link the project using the command:
```console
npm link
```
Now, in order to make sure all version-references are pointed to your local version of the project, use the command:
```console
npm run update-version link
```
_NB: To do all of this in one command, you can use:_
```console
npm run setup-local
```
### Build
After applying your changes, build the project using the command:
```console
npm run build:full
```
Now, test that the project is working by using the command:
```console
direflow -v
```
This should give you the latest version with a '-link' appended:
```console
Current version of direflow-cli:
3.4.3-link
```
In this way you know that the command `direflow` is using your local setup.
Now, test your new functionality.
> Note: After you have build the project using build:full, you may want to run install:all again before continuing to develop.
### Commit the changes
Before committing your new changes, remember to change the version using the command:
```console
npm run update-version <new-version>
```
### Create the PR
Create a branch for your changes called '_feature/name-of-the-changes_'.
Make a PR into the **development** branch on the direflow repository.
## Updating the docs
If you introduced user-facing changes, please update the [direflow-docs](https://github.com/Silind-Software/direflow-docs) accordingly.
## Additional resources
Check out the [Direflow Wiki](https://github.com/Silind-Software/direflow/wiki). Here you can find guides and know-how that will help you developing on Direflow.
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2020 Silind Ltd
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.
================================================
FILE: README.md
================================================
<span align="center">
[](https://direflow.io/)
</span>
<span align="right">
[](https://www.npmjs.com/package/direflow-cli)
[](https://github.com/Silind-Software/direflow/blob/master/LICENSE)


</span>
# [direflow.io](https://direflow.io/)
#### Set up a React App and build it as a Web Component
> This setup is based on [*react-scripts*](https://www.npmjs.com/package/react-scripts) from [*create-react-app*](https://create-react-app.dev/docs/getting-started)
> A walkthrough of the principles used in this setup, can be read [in this article](https://itnext.io/react-and-web-components-3e0fca98a593)
## Get started
Start by downloading the cli:
```console
npm i -g direflow-cli
```
### Create a new Direflow Component
```console
direflow create
```
This will bootstrap a new Direflow Component for you.
Now use the following commands:
```console
cd <project-folder>
npm install
npm start
```
Your Direflow Component will start running on `localhost:3000` and your browser opens a new window
<p align="center">
<img src="https://silind-s3.s3.eu-west-2.amazonaws.com/direflow/direflow-component-new-base.png" />
</p>
#### See full documentation on [direflow.io](https://direflow.io)
================================================
FILE: bin/direflow
================================================
#!/usr/bin/env node
require = require('esm')(module);
const cli = require('../dist/cli');
cli.default();
================================================
FILE: cli/checkForUpdate.ts
================================================
import chalk from 'chalk';
import { execSync } from 'child_process';
import { updateAvailable } from './messages';
const checkForUpdates = () => {
const rootPackage = require('../package.json');
const buffer = execSync('npm view direflow-cli version');
const currentVersion = buffer.toString('utf8');
if (rootPackage.version.trim() !== currentVersion.trim()) {
return chalk.white(updateAvailable(rootPackage.version.trim(), currentVersion.trim()));
}
return '';
};
export default checkForUpdates;
================================================
FILE: cli/cli.ts
================================================
import { program, Command } from 'commander';
import chalk from 'chalk';
import headline from './headline';
import { createDireflowSetup } from './create';
import checkForUpdates from './checkForUpdate';
import { showVersion } from './messages';
type TOptions =
| 'small'
| 'js'
| 'ts'
| 'tslint'
| 'eslint'
| 'npm';
type TParsed = Command & { [key in TOptions]?: true } & { desc: string };
export default function cli() {
program
.command('create [project-name]')
.alias('c')
.description('Create a new Direflow Setup')
.option('-d, --desc <description>', 'Choose description for your project')
.option('--js', 'Choose JavaScript Direflow template')
.option('--ts', 'Choose TypeScript Direflow template')
.option('--tslint', 'Use TSLint for TypeScript template')
.option('--eslint', 'Use ESLint for TypeScript template')
.option('--npm', 'Make the project an NPM module')
.action(handleAction);
program
.description(chalk.magenta(headline))
.version(showVersion())
.helpOption('-h, --help', 'Show how to use direflow-cli')
.option('-v, --version', 'Show the current version');
const [, , simpleArg] = process.argv;
if (!simpleArg) {
return program.help();
}
if (['-v', '--version'].includes(simpleArg)) {
console.log(checkForUpdates());
}
program.parse(process.argv);
}
async function handleAction(name: string | undefined, parsed: TParsed) {
const { js, ts, tslint, eslint, npm, desc: description } = parsed;
let language: 'js' | 'ts' | undefined;
let linter: 'eslint' | 'tslint' | undefined;
if (js) {
language = 'js';
} else if (ts) {
language = 'ts';
}
if (eslint) {
linter = 'eslint';
} else if (tslint) {
linter = 'tslint';
}
await createDireflowSetup({
name,
linter,
language,
description,
npmModule: !!npm,
}).catch((err) => {
console.log('');
console.log(chalk.red('Unfortunately, something went wrong creating your Direflow Component'));
console.log(err);
console.log('');
});
}
================================================
FILE: cli/create.ts
================================================
import fs from 'fs';
import chalk from 'chalk';
import { chooseName, chooseDescription, chooseLanguage, chooseLinter, isNpmModule } from './questions';
import copyTemplate from './helpers/copyTemplate';
import { getNameFormats, createDefaultName } from './helpers/nameFormat';
import isDireflowSetup from './helpers/detectDireflowSetup';
import { writeProjectNames } from './helpers/writeNames';
import { moreInfoMessage, componentFinishedMessage } from './messages';
interface ISetupPresets {
name?: string;
description?: string;
language?: 'js' | 'ts';
linter?: 'eslint' | 'tslint';
npmModule?: boolean;
}
export async function createDireflowSetup(preset: ISetupPresets = {}): Promise<void> {
if (isDireflowSetup()) {
console.log(
chalk.red('You are trying to create a new Direflow Setup inside an existing Direflow Setup.'),
);
return;
}
if (!preset.name) {
const { name } = await chooseName();
preset.name = name;
}
if (!preset.description) {
const { description } = await chooseDescription();
preset.description = description;
}
if (!preset.language) {
const { language } = await chooseLanguage();
preset.language = language;
}
if (!preset.linter) {
const { linter } = await chooseLinter(preset.language);
preset.linter = linter;
}
if (!preset.npmModule) {
const { npmModule } = await isNpmModule();
preset.npmModule = npmModule;
}
const { name, description, language, linter, npmModule } = preset;
const componentName = createDefaultName(name);
const projectName = componentName;
if (fs.existsSync(projectName)) {
console.log(chalk.red(`The directory '${projectName}' already exists at the current location`));
return;
}
const projectDirectoryPath = await copyTemplate({
language,
projectName,
});
await writeProjectNames({
linter,
projectDirectoryPath,
description,
npmModule,
names: getNameFormats(componentName),
type: 'direflow-component',
});
console.log(chalk.greenBright(componentFinishedMessage(projectName)));
console.log(chalk.blueBright(moreInfoMessage));
}
export default createDireflowSetup;
================================================
FILE: cli/headline.ts
================================================
const headline = `
██████╗ ██╗██████╗ ███████╗███████╗██╗ ██████╗ ██╗ ██╗
██╔══██╗██║██╔══██╗██╔════╝██╔════╝██║ ██╔═══██╗██║ ██║
██║ ██║██║██████╔╝█████╗ █████╗ ██║ ██║ ██║██║ █╗ ██║
██║ ██║██║██╔══██╗██╔══╝ ██╔══╝ ██║ ██║ ██║██║███╗██║
██████╔╝██║██║ ██║███████╗██║ ███████╗╚██████╔╝╚███╔███╔╝
╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝`;
export default headline;
================================================
FILE: cli/helpers/copyTemplate.ts
================================================
import { resolve } from 'path';
import fs from 'fs';
import ncp from 'ncp';
import { ITemplateOption } from '../types/TemplateOption';
const copyTemplate = async (options: ITemplateOption): Promise<string> => {
const currentDirectory = process.cwd();
const templateDirectory = resolve(__dirname, `../../templates/${options.language}`);
const projectDirectory: string = await new Promise((projectResolve, reject) => {
const projectDir = `${currentDirectory}/${options.projectName}`;
fs.mkdir(projectDir, (err: any) => {
if (err) {
console.log(err);
reject(new Error(`Could not create directory: ${projectDir}`));
}
projectResolve(projectDir);
});
});
await new Promise((ncpResolve, reject) => {
ncp.ncp(templateDirectory, projectDirectory, (err) => {
if (err) {
console.log(err);
reject(new Error('Could not copy template files'));
}
ncpResolve(true);
});
});
await new Promise((renameResolve, reject) => {
fs.rename(
`${projectDirectory}/src/direflow-components/direflow-component`,
`${projectDirectory}/src/direflow-components/${options.projectName}`,
(err) => {
if (err) {
console.log(err);
reject(new Error('Could not rename component folder'));
}
renameResolve(true);
},
);
});
return projectDirectory;
};
export default copyTemplate;
================================================
FILE: cli/helpers/detectDireflowSetup.ts
================================================
import fs from 'fs';
const isDireflowSetup = (currentDirectory = process.cwd()): boolean => {
return fs.existsSync(`${currentDirectory}/direflow-webpack.js`);
};
export default isDireflowSetup;
================================================
FILE: cli/helpers/nameFormat.ts
================================================
import to from 'to-case';
import { INames } from '../types/Names';
export const getNameFormats = (name: string): INames => {
return {
title: to.title(name),
pascal: to.pascal(name),
snake: to.slug(name),
};
};
export const createDefaultName = (name: string) => {
const snakeName = to.slug(name);
if (!snakeName.includes('-')) {
return `${snakeName}-component`;
}
return snakeName;
};
================================================
FILE: cli/helpers/writeNames.ts
================================================
import fs from 'fs';
import handelbars from 'handlebars';
import path from 'path';
import { INames } from '../types/Names';
const packageJson = require('../../package.json');
const { version } = packageJson;
interface IWriteNameOptions {
projectDirectoryPath: string;
linter: 'eslint' | 'tslint';
packageVersion?: string;
description: string;
npmModule: boolean;
names: INames;
type: string;
}
type TWriteNameExtendable = Required<Pick<IWriteNameOptions,
| 'names'
| 'type'
| 'packageVersion'
| 'npmModule'
>>;
interface IHandelbarData extends TWriteNameExtendable {
defaultDescription: string;
eslint: boolean;
tslint: boolean;
}
export async function writeProjectNames({
type, names, description, linter, npmModule,
projectDirectoryPath,
packageVersion = version,
}: IWriteNameOptions): Promise<void> {
const projectDirectory = fs.readdirSync(projectDirectoryPath);
const defaultDescription = description || 'This project is created using Direflow';
const writeNames = projectDirectory.map(async (dirElement: string) => {
const filePath = path.join(projectDirectoryPath, dirElement);
if (fs.statSync(filePath).isDirectory()) {
return writeProjectNames({
names, description, type, linter, npmModule, projectDirectoryPath: filePath,
});
}
if (linter !== 'tslint') {
if (filePath.endsWith('tslint.json')) {
return fs.unlinkSync(filePath);
}
}
if (linter !== 'eslint') {
if (filePath.endsWith('.eslintrc')) {
return fs.unlinkSync(filePath);
}
}
return changeNameInfile(filePath, {
names,
defaultDescription,
type,
packageVersion,
npmModule,
eslint: linter === 'eslint',
tslint: linter === 'tslint',
});
});
await Promise.all(writeNames).catch(() => console.log('Failed to write files'));
}
async function changeNameInfile(filePath: string, data: IHandelbarData): Promise<void> {
const changedFile = await new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf-8', (err, content) => {
if (err) {
reject(false);
}
const template = handelbars.compile(content);
const changed = template(data);
resolve(changed);
});
});
await new Promise((resolve, reject) => {
if ( typeof changedFile == 'string' ) {
fs.writeFile(filePath, changedFile, 'utf-8', (err) => {
if (err) {
reject();
}
resolve(true);
});
}
});
}
export default writeProjectNames;
================================================
FILE: cli/messages.ts
================================================
import chalk from 'chalk';
import boxen from 'boxen';
export const componentFinishedMessage = (componentName: string) => `
Your Direflow Component is ready!
To get started:
cd ${componentName}
npm install
npm start
The Direflow Component will be running at: ${chalk.magenta('localhost:3000')}
`;
export const moreInfoMessage = `
To learn more about Direflow, visit:
https://direflow.io
`;
export const updateAvailable = (currentVersion: string, newVersion: string) => {
const content = `There is a new version of direflow-cli available: ${chalk.greenBright(newVersion)}.
You are currently running direflow-cli version: ${chalk.blueBright(currentVersion)}.
Run '${chalk.magenta('npm i -g direflow-cli')}' to get the latest version.`;
return boxen(content, { padding: 1, align: 'center', margin: 1 });
};
export const showVersion = () => {
const packageJson = require('../package.json');
return `Current version of direflow-cli:
${packageJson.version}
`;
};
================================================
FILE: cli/questions.ts
================================================
import inquirer from 'inquirer';
import { IQuestionOption } from './types/QuestionOption';
import { ILanguageOption } from './types/LangageOption';
export async function chooseLanguage(): Promise<ILanguageOption> {
console.log('');
return inquirer.prompt([
{
type: 'list',
name: 'language',
message: 'Which language do you want to use?',
choices: [
{
value: 'js',
name: 'JavaScript',
},
{
value: 'ts',
name: 'TypeScript',
},
],
},
]);
}
export async function chooseLinter(language: 'js' | 'ts'): Promise<{ linter: 'eslint' | 'tslint' }> {
if (language === 'js') {
return {
linter: 'eslint',
};
}
console.log('');
return inquirer.prompt([
{
type: 'list',
name: 'linter',
message: 'Which linter do you want to use?',
choices: [
{
value: 'eslint',
name: 'ESLint',
},
{
value: 'tslint',
name: 'TSLint',
},
],
},
]);
}
export async function isNpmModule(): Promise<{ npmModule: boolean }> {
console.log('');
return inquirer.prompt([
{
type: 'list',
name: 'npmModule',
message: 'Do you want this to be an NPM module?',
choices: [
{
value: true,
name: 'Yes',
},
{
value: false,
name: 'No',
},
],
},
]);
}
export async function chooseName(): Promise<IQuestionOption> {
console.log('');
return inquirer.prompt([
{
type: 'input',
name: 'name',
message: 'Choose a name for your Direflow Setup:',
validate: (value: string) => {
const pass = /^[a-zA-Z0-9-_]+$/.test(value);
if (pass) {
return true;
}
return 'Please enter a valid name';
},
},
]);
}
export async function chooseDescription(): Promise<IQuestionOption> {
console.log('');
return inquirer.prompt([
{
type: 'input',
name: 'description',
message: 'Give your Direflow Setup a description (optional)',
},
]);
}
================================================
FILE: cli/types/Command.ts
================================================
export interface ICommand {
[arg: string]: string;
}
================================================
FILE: cli/types/LangageOption.ts
================================================
export interface ILanguageOption {
language: 'js' | 'ts';
}
================================================
FILE: cli/types/Names.ts
================================================
export interface INames {
title: string;
pascal: string;
snake: string;
}
================================================
FILE: cli/types/QuestionOption.ts
================================================
export interface IQuestionOption {
name: string;
description: string;
}
================================================
FILE: cli/types/TemplateOption.ts
================================================
export interface ITemplateOption {
projectName: string;
language: 'ts' | 'js';
}
================================================
FILE: cypress/integration/basic_tests.ts
================================================
describe('Running basic component', () => {
before(() => {
cy.visit('/');
});
it('should contain a custom element', () => {
cy.get('basic-test').should('exist');
});
it('should have default componentTitle', () => {
cy.shadowGet('basic-test')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Test Setup');
});
it('should have default sampleList items', () => {
cy.shadowGet('basic-test')
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(0)
.shadowContains('Item 1');
cy.shadowGet('basic-test')
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(1)
.shadowContains('Item 2');
cy.shadowGet('basic-test')
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(2)
.shadowContains('Item 3');
});
});
================================================
FILE: cypress/integration/event_tests.ts
================================================
describe('Delegating events', () => {
let eventHasFired = false;
const fireEvent = () => {
eventHasFired = true;
};
before(() => {
cy.visit('/');
cy.shadowGet('props-test').then((element) => {
const [component] = element;
component.addEventListener('test-click-event', fireEvent);
});
});
it('should contain a custom element', () => {
cy.get('event-test').should('exist');
});
it('should not have fired event', () => {
expect(eventHasFired).to.equal(false);
});
it('should fire event', () => {
cy.shadowGet('props-test')
.shadowFind('.app')
.shadowFind('.button')
.shadowClick().then(() => {
expect(eventHasFired).to.equal(true);
});
});
});
================================================
FILE: cypress/integration/external_loader_test.ts
================================================
describe('Applying external resources', () => {
before(() => {
cy.visit('/');
});
it('should contain a custom element', () => {
cy.get('external-loader-test').should('exist');
});
it('should have script tag in head', () => {
cy.get('head script[src="https://code.jquery.com/jquery-3.3.1.slim.min.js"]').should('exist');
});
it('should have async script tag in head', () => {
cy.get(
'head script[src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"]',
).should('have.attr', 'async');
});
it('should contain external styles', () => {
cy.shadowGet('external-loader-test')
.shadowFind('#direflow_external-sources')
.then((elem) => {
const [element] = elem;
const [link] = element.children;
expect(link.href).to.equal(
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css',
);
});
});
});
================================================
FILE: cypress/integration/material_ui_test.ts
================================================
describe('Applying external resources', () => {
before(() => {
cy.visit('/');
});
it('should contain a custom element', () => {
cy.get('material-ui-test').should('exist');
});
it('should contain styles', () => {
cy.shadowGet('material-ui-test')
.shadowFind('#direflow_material-ui-styles')
.then((elem) => {
const [element] = elem;
expect(element).not.to.be.undefined;
});
});
it('should style button correctly', () => {
cy.shadowGet('material-ui-test')
.shadowFind('#material-ui-button')
.then((elem) => {
const [element] = elem;
const bgColor = window.getComputedStyle(element, null).getPropertyValue('background-color');
expect(bgColor).to.equal('rgb(63, 81, 181)');
});
});
});
================================================
FILE: cypress/integration/props_tests.ts
================================================
describe('Using properties and attributes', () => {
before(() => {
cy.visit('/');
});
const assertSampleList = (id, assertions) => {
cy.shadowGet(id)
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(0)
.shadowContains(assertions[0]);
cy.shadowGet(id)
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(1)
.shadowContains(assertions[1]);
cy.shadowGet(id)
.shadowFind('.app')
.shadowFind('div')
.shadowEq(1)
.shadowFind('.sample-text')
.shadowEq(2)
.shadowContains(assertions[2]);
};
it('should contain a custom element', () => {
cy.get('#props-test-1').should('exist');
});
it('should have default componentTitle', () => {
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Props Title');
});
it('should contain a custom element', () => {
cy.get('#props-test-1').should('exist');
});
it('setting componentTitle property should update componentTitle', () => {
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
component.componentTitle = 'Update Title';
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Update Title');
});
});
it('setting componenttitle attribute should update componentTitle', () => {
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
component.setAttribute('componenttitle', 'Any');
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Any');
});
});
it('should update componentTitle with delay', () => {
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Any');
cy.wait(500);
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
component.componentTitle = 'Delay Title';
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Delay Title');
});
});
it('should update sampleList items', () => {
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
const samples = ['New Item 1', 'New Item 2', 'New Item 3'];
component.sampleList = samples;
assertSampleList('#props-test-1', samples);
});
});
it('should update sampleList items with delay', () => {
const currentSamples = ['New Item 1', 'New Item 2', 'New Item 3'];
assertSampleList('#props-test-1', currentSamples);
cy.wait(500);
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
const newSamples = ['Delayed Item 1', 'Delayed Item 2', 'Delayed Item 3'];
component.sampleList = newSamples;
assertSampleList('#props-test-1', newSamples);
});
});
it('should update based on falsy value', () => {
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('Delay Title');
cy.shadowGet('#props-test-1').then((element) => {
const [component] = element;
component.showTitle = false;
cy.shadowGet('#props-test-1')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('no-title');
});
});
it('should update based on attribute without value', () => {
cy.shadowGet('#props-test-2')
.shadowFind('.app')
.shadowFind('.hidden')
.shadowContains('SHOW HIDDEN');
});
it('should treat attribute value "false" as boolean', () => {
cy.shadowGet('#props-test-3')
.shadowFind('.app')
.shadowFind('.header-title')
.shadowContains('no-title');
});
it('should parse attribute with JSON content', () => {
assertSampleList('#props-test-4', ['test-1', 'test-2', 'test-3']);
});
});
================================================
FILE: cypress/integration/slot_tests.ts
================================================
describe('Running basic component without Shadow DOM', () => {
before(() => {
cy.visit('/');
});
it('should contain a custom element', () => {
cy.get('slot-test').should('exist');
});
it('should contain a slotted element', () => {
cy.shadowGet('slot-test')
.shadowFind('.app')
.shadowFind('.slotted-elements')
.shadowFind('slot')
.shadowEq(0)
.then((element) => {
const [slotted] = element[0].assignedNodes();
expect(slotted).contain('Slot Item 1');
});
});
it('should dynamically inject a slotted element', () => {
cy.shadowGet('slot-test').then((element) => {
const [component] = element;
const newSlotted = document.createElement('div');
newSlotted.innerHTML = 'Slot Item 2';
newSlotted.slot = 'slotted-item-2';
component.appendChild(newSlotted);
cy.shadowGet('slot-test')
.shadowFind('.app')
.shadowFind('.slotted-elements')
.shadowFind('slot')
.shadowEq(1)
.then((element) => {
const [slotted] = element[0].assignedNodes();
expect(slotted).contain('Slot Item 2');
});
});
});
});
================================================
FILE: cypress/integration/styled_components_test.ts
================================================
describe('Applying external resources', () => {
before(() => {
cy.visit('/');
});
it('should contain a custom element', () => {
cy.get('styled-components-test').should('exist');
});
it('should contain styles', () => {
cy.shadowGet('styled-components-test')
.shadowFind('#direflow_styled-components-styles')
.then((elem) => {
const [element] = elem;
const [style] = element.children;
expect(style.getAttribute('data-styled')).to.equal('active');
});
});
it('should style button correctly', () => {
cy.shadowGet('styled-components-test')
.shadowFind('#styled-component-button')
.then((elem) => {
const [element] = elem;
const bgColor = window.getComputedStyle(element, null).getPropertyValue('background-color');
expect(bgColor).to.equal('rgb(255, 0, 0)');
});
});
});
================================================
FILE: cypress/support/commands.js
================================================
import 'cypress-shadow-dom';
================================================
FILE: cypress/support/index.js
================================================
import './commands';
================================================
FILE: cypress/test-setup/direflow-config.json
================================================
{
"build": {
"filename": "direflowBundle.js"
}
}
================================================
FILE: cypress/test-setup/direflow-webpack.js
================================================
const { webpackConfig } = require('direflow-scripts');
const { aliasWebpack } = require("react-app-alias");
/**
* Webpack configuration for Direflow Component
* Additional webpack plugins / overrides can be provided here
*/
module.exports = (config, env) => {
let useWebpackConfig = {
...webpackConfig(config, env),
// Add your own webpack config here (optional)
};
useWebpackConfig = aliasWebpack({})(useWebpackConfig);
return useWebpackConfig;
};
================================================
FILE: cypress/test-setup/jsconfig.json
================================================
{
"extends": "./jsconfig.paths.json",
"compilerOptions": {
"strict": true,
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "cypress-shadow-dom", "node"],
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
"noEmit": false
},
"include": ["**/*.js"]
}
================================================
FILE: cypress/test-setup/jsconfig.paths.json
================================================
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"react": ["./node_modules/react"],
"react-dom": ["./node_modules/react-dom"],
"styled-components": ["./node_modules/styled-components"],
"@material-ui": ["./node_modules/@material-ui"]
}
}
}
================================================
FILE: cypress/test-setup/package.json
================================================
{
"name": "test-setup",
"description": "This project is created using Direflow",
"version": "1.0.0",
"private": true,
"scripts": {
"start": "PORT=5000 direflow-scripts start",
"build": "direflow-scripts build && cp ./public/index.css ./build && cp ./public/index_prod.html ./build/index.html",
"serve": "serve ./build -l 5000"
},
"dependencies": {
"@material-ui/core": "^4.9.7",
"direflow-component": "../../packages/direflow-component",
"direflow-scripts": "../../packages/direflow-scripts",
"eslint-plugin-react-hooks": "^4.5.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-lib-adler32": "^1.0.3",
"react-scripts": "^4.0.3",
"serve": "^13.0.2",
"styled-components": "^5.0.1",
"webfontloader": "^1.6.28"
},
"devDependencies": {
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.30.0",
"jest-environment-jsdom-fourteen": "^1.0.1",
"react-app-alias": "^2.2.2",
"react-app-rewired": "^2.2.1",
"react-test-renderer": "17.0.2",
"to-string-loader": "^1.2.0",
"webpack-cli": "^4.9.2"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"setupFilesAfterEnv": [
"direflow-scripts/direflow-jest.config.js"
]
},
"config-overrides-path": "direflow-webpack.js"
}
================================================
FILE: cypress/test-setup/public/index.css
================================================
body {
padding: 0;
margin: 0;
width: 100vw;
padding-top: 150px;
display: flex;
justify-content: center;
align-items: center;
background-color: #F6FAFA;
}
================================================
FILE: cypress/test-setup/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test Setup</title>
</head>
<body>
<basic-test></basic-test>
<props-test id="props-test-1" componentTitle="Props Title"></props-test>
<props-test id="props-test-2" componentTitle="Props Title" showHidden ></props-test>
<props-test id="props-test-3" componentTitle="Props Title" showTitle="false" ></props-test>
<props-test id="props-test-4" componentTitle="Props Title" sampleList="['test-1', 'test-2', 'test-3']"></props-test>
<event-test></event-test>
<slot-test componentTitle="Props Title">
<div slot="slotted-item-1">Slot Item 1</div>
</slot-test>
<external-loader-test></external-loader-test>
<styled-components-test></styled-components-test>
<material-ui-test></material-ui-test>
</body>
</html>
================================================
FILE: cypress/test-setup/public/index_prod.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test Setup</title>
<script src="./direflowBundle.js"></script>
</head>
<body>
<basic-test></basic-test>
<props-test id="props-test-1" componentTitle="Props Title"></props-test>
<props-test id="props-test-2" componentTitle="Props Title" showHidden ></props-test>
<props-test id="props-test-3" componentTitle="Props Title" showTitle="false" ></props-test>
<props-test id="props-test-4" componentTitle="Props Title" sampleList="['test-1', 'test-2', 'test-3']"></props-test>
<event-test></event-test>
<slot-test componentTitle="Props Title">
<div slot="slotted-item-1">Slot Item 1</div>
</slot-test>
<external-loader-test></external-loader-test>
<styled-components-test></styled-components-test>
<material-ui-test></material-ui-test>
</body>
</html>
================================================
FILE: cypress/test-setup/src/component-exports.js
================================================
/**
* In this file you can export components that will
* be build as a pure React component library.
*
* Using the command `npm run build:lib` will
* produce a folder `lib` with your React components.
*
* If you're not using a React component library,
* this file can be safely deleted.
*/
import App from './direflow-components/test-setup/App';
export {
App
};
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/App.css
================================================
.app {
width: 400px;
height: 575px;
padding: 30px 60px;
box-sizing: border-box;
background-color: white;
box-shadow: 0 4px 14px 4px #375c821c;
font-family: 'Noto Sans JP', sans-serif;
border-bottom: 5px solid #cad5e6;
}
.top {
width: 100%;
height: 50%;
border-bottom: 2px solid #7998c7;
display: flex;
justify-content: center;
align-items: center;
}
.bottom {
width: 100%;
height: 50%;
border-top: 2px solid #7998c7;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.header-image {
width: 165px;
height: 165px;
background: url('https://silind-s3.s3.eu-west-2.amazonaws.com/direflow/logo.svg');
background-size: contain;
}
.header-title {
font-size: 34px;
color: #5781C2;
font-family: 'Advent Pro', sans-serif;
}
.sample-text {
font-family: 'Noto Sans JP', sans-serif;
font-size: 16px;
color: #666;
text-align: center;
}
.button {
width: 150px;
height: 45px;
font-family: 'Noto Sans JP', sans-serif;
font-size: 20px;
font-weight: bold;
background-color: #5781C2;
color: white;
box-shadow: 2px 2px 5px #16314d98;
outline: none;
border: 0;
cursor: pointer;
transition: 0.3s;
}
.button:hover {
box-shadow: 4px 4px 8px #16314d63;
background-color: #40558f;
}
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/App.js
================================================
import React from 'react';
import { Styled, EventConsumer } from 'direflow-component';
import styles from './App.css';
const App = (props) => {
const handleClick = (dispatch) => {
const event = new Event('test-click-event');
dispatch(event);
};
const renderSampleList = props.sampleList.map((sample) => (
<div key={sample} className='sample-text'>
{sample}
</div>
));
const title = props.showTitle ? props.componentTitle : 'no-title';
const hidden = props.showHidden ? 'SHOW HIDDEN' : null;
return (
<Styled styles={styles}>
<div className='app'>
<div className='header-title'>{title}</div>
<div>{renderSampleList}</div>
<div className='slotted-elements'>
<slot name='slotted-item-1' />
<slot name='slotted-item-2' />
</div>
<div className='hidden'>{hidden}</div>
<EventConsumer>
{(dispatch) => (
<button className='button' onClick={() => handleClick(dispatch)}>
Click
</button>
)}
</EventConsumer>
</div>
</Styled>
);
};
App.defaultProps = {
componentTitle: 'Test Setup',
showTitle: true,
showHidden: false,
sampleList: ['Item 1', 'Item 2', 'Item 3'],
};
export default App;
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/MaterialUI.js
================================================
import React from 'react';
import Button from '@material-ui/core/Button';
const MaterialUI = () => {
return (
<Button id='material-ui-button' variant='contained' color='primary'>
My Material UI Button
</Button>
);
};
export default MaterialUI;
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/StyledComponent.js
================================================
import React from 'react';
import styled from 'styled-components';
const RedButton = styled.div`
width: 100px;
height: 50px;
background-color: red;
`;
const StyledComponent = () => {
return <RedButton id='styled-component-button'>Styled Component Button</RedButton>;
};
export default StyledComponent;
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/index.js
================================================
import { DireflowComponent } from 'direflow-component';
import App from './App';
import StyledComponent from './StyledComponent';
import MaterialUI from './MaterialUI';
DireflowComponent.createAll([
{
component: App,
configuration: {
tagname: 'basic-test',
},
},
{
component: App,
configuration: {
tagname: 'props-test',
},
},
{
component: App,
configuration: {
tagname: 'event-test',
},
},
{
component: App,
configuration: {
tagname: 'slot-test',
},
},
{
component: App,
configuration: {
tagname: 'external-loader-test',
},
plugins: [
{
name: 'external-loader',
options: {
paths: [
'https://code.jquery.com/jquery-3.3.1.slim.min.js',
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css',
{
src: 'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js',
async: true,
},
],
},
},
],
},
{
component: StyledComponent,
configuration: {
tagname: 'styled-components-test',
},
plugins: [
{
name: 'styled-components',
},
],
},
{
component: MaterialUI,
configuration: {
tagname: 'material-ui-test',
},
plugins: [
{
name: 'material-ui',
},
],
},
]);
================================================
FILE: cypress/test-setup/src/direflow-components/test-setup/test/App.test.js
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import App from '../App';
const reactProps = {
componentTitle: 'Component Test',
sampleList: ['Mock', 'Test', 'Data'],
};
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App {...reactProps} />, div);
ReactDOM.unmountComponentAtNode(div);
});
it('matches snapshot as expected', () => {
const renderTree = renderer.create(<App {...reactProps} />).toJSON();
expect(renderTree).toMatchSnapshot();
});
================================================
FILE: cypress/test-setup/src/index.js
================================================
/**
* This is the entry file of the Direflow setup.
*
* You can add any additional functionality here.
* For example, this is a good place to hook into your
* Web Component once it's mounted on the DOM.
*
* !This file cannot be removed.
* It can be left blank if not needed.
*/
================================================
FILE: cypress/tsconfig.json
================================================
{
"compilerOptions": {
"strict": true,
"baseUrl": "../node_modules",
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress", "cypress-shadow-dom", "node"],
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
"noEmit": false
},
"include": ["**/*.ts"]
}
================================================
FILE: cypress.json
================================================
{
"baseUrl": "http://localhost:5000",
"video": false
}
================================================
FILE: declarations.d.ts
================================================
declare module 'to-case';
================================================
FILE: package.json
================================================
{
"name": "direflow-cli",
"version": "4.0.0",
"description": "Official CLI for Direflow",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "jest",
"update-version": "node scripts/node/updateVersion.js",
"setup-local": "./scripts/bash/setupLocal.sh",
"clean:all": "node ./scripts/node/cleanupAll.js",
"install:all": "node ./scripts/node/installAll.js",
"build:all": "node ./scripts/node/buildAll.js && npm run install:all -- --no-deps",
"build:full": "npm run clean:all && npm run install:all && npm run build:all && npm run clean:all -- --modules",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"cypress:test": "./scripts/bash/startIntegrationTest.sh"
},
"bin": {
"direflow": "bin/direflow"
},
"files": [
"bin/*",
"dist/*",
"templates/*"
],
"repository": {
"type": "git",
"url": "git@github.com:Silind-Software/direflow.git"
},
"keywords": [
"cli",
"widget",
"web component",
"react",
"typescript"
],
"author": "Silind Software",
"license": "MIT",
"homepage": "https://direflow.io",
"dependencies": {
"boxen": "^6.2.1",
"chalk": "4.1.2",
"commander": "^9.3.0",
"deepmerge": "^4.2.2",
"esm": "^3.2.25",
"handlebars": "^4.7.7",
"inquirer": "^8.2.4",
"mkdirp": "^1.0.4",
"ncp": "^2.0.0",
"rimraf": "^3.0.2",
"to-case": "^2.0.0"
},
"devDependencies": {
"@types/inquirer": "^8.2.1",
"@types/jest": "^28.1.0",
"@types/jsdom": "^16.2.14",
"@types/mkdirp": "^1.0.2",
"@types/mock-fs": "^4.13.1",
"@types/ncp": "^2.0.5",
"@types/node": "^17.0.39",
"@types/rimraf": "^3.0.2",
"@types/webpack": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"cypress": "9.5.0",
"cypress-shadow-dom": "^1.3.0",
"eslint": "^8.17.0",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.5.0",
"jest": "^28.1.0",
"jsdom": "^19.0.0",
"memfs": "^3.4.7",
"prettier": "^2.6.2",
"start-server-and-test": "^1.14.0",
"ts-jest": "^28.0.4",
"typescript": "^4.7.3"
},
"eslintConfig": {
"extends": "react-app"
},
"jest": {
"roots": [
"<rootDir>/"
],
"moduleFileExtensions": [
"ts",
"js",
"tsx"
],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"testMatch": [
"**/**/*.test.(ts|tsx)"
],
"testPathIgnorePatterns": [
"<rootDir>/templates",
"<rootDir>/packages",
"<rootDir>/cypress"
],
"collectCoverage": true
}
}
================================================
FILE: packages/direflow-component/README.md
================================================
# direflow-component
### This package includes configurations and scripts used by [Direflow](https://direflow.io)
:warning: This package is not meant to be used on its own, but as a part of [Direflow](https://direflow.io).
Please refer to the official webpage in order to get started.
================================================
FILE: packages/direflow-component/declarations.d.ts
================================================
declare module 'react-lib-adler32';
declare module '*.css' {
const classes: { readonly [key: string]: string };
export default classes;
}
declare module '*.svg' {
const content: any;
export default content;
}
================================================
FILE: packages/direflow-component/package.json
================================================
{
"name": "direflow-component",
"version": "4.0.0",
"description": "Create Web Components using React",
"main": "dist/index.js",
"author": "Silind Software",
"license": "MIT",
"scripts": {
"build": "tsc"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "git@github.com:Silind-Software/direflow.git"
},
"homepage": "https://direflow.io",
"dependencies": {
"chalk": "4.1.2",
"lodash": "^4.17.21",
"react-lib-adler32": "^1.0.3",
"sass": "^1.52.2",
"webfontloader": "^1.6.28"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@types/react": "17.0.2",
"@types/react-dom": "^17.0.2",
"@types/webfontloader": "^1.6.34",
"typescript": "^4.7.3"
},
"peerDependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
}
}
================================================
FILE: packages/direflow-component/src/DireflowComponent.tsx
================================================
import WebComponentFactory from './WebComponentFactory';
import { IDireflowComponent } from './types/DireflowConfig';
import { DireflowElement } from './types/DireflowElement';
import includePolyfills from './helpers/polyfillHandler';
import DireflowPromiseAlike from './types/DireflowPromiseAlike';
let _resolve: Function;
const callback = (element: HTMLElement) => {
_resolve?.(element as DireflowElement);
};
class DireflowComponent {
/**
* Create muliple Direflow Components
* @param App React Component
*/
public static createAll(componentConfigs: IDireflowComponent[]): Array<DireflowPromiseAlike> {
return componentConfigs.map(DireflowComponent.create);
}
/**
* Create Direflow Component
* @param App React Component
*/
public static create(componentConfig: IDireflowComponent): DireflowPromiseAlike {
const { component } = componentConfig;
const plugins = component.plugins || componentConfig.plugins;
const configuration = component.configuration || componentConfig.configuration;
if (!component) {
throw Error('Root component has not been set');
}
if (!configuration) {
throw Error('No configuration found');
}
const componentProperties = {
...componentConfig?.properties,
...component.properties,
...component.defaultProps,
};
const tagName = configuration.tagname || 'direflow-component';
const shadow = configuration.useShadow !== undefined ? configuration.useShadow : true;
const anonymousSlot = configuration.useAnonymousSlot !== undefined ? configuration.useAnonymousSlot : false;
(async () => {
/**
* TODO: This part should be removed in next minor version
*/
await Promise.all([includePolyfills({ usesShadow: !!shadow }, plugins)]);
const WebComponent = new WebComponentFactory(
componentProperties,
component,
shadow,
anonymousSlot,
plugins,
callback,
).create();
customElements.define(tagName, WebComponent);
})();
return {
then: async (resolve?: (element: HTMLElement) => void) => {
if (resolve) {
_resolve = resolve;
}
},
};
}
}
export default DireflowComponent;
================================================
FILE: packages/direflow-component/src/WebComponentFactory.tsx
================================================
/* eslint-disable class-methods-use-this */
/* eslint-disable max-classes-per-file */
import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import createProxyRoot from './helpers/proxyRoot';
import { IDireflowPlugin } from './types/DireflowConfig';
import { EventProvider } from './components/EventContext';
import { PluginRegistrator } from './types/PluginRegistrator';
import registeredPlugins from './plugins/plugins';
import getSerialized from './helpers/getSerialized';
class WebComponentFactory {
constructor(
private componentProperties: { [key: string]: unknown },
private rootComponent: React.FC<any> | React.ComponentClass<any, any>,
private shadow?: boolean,
private anonymousSlot?: boolean,
private plugins?: IDireflowPlugin[],
private connectCallback?: (element: HTMLElement) => void,
) {
this.reflectPropertiesToAttributes();
}
private componentAttributes: { [key: string]: {
property: string;
value: unknown;
}; } = {};
/**
* All properties with primitive values are added to attributes.
*/
private reflectPropertiesToAttributes() {
Object.entries(this.componentProperties).forEach(([key, value]) => {
if (typeof value !== 'number' && typeof value !== 'string' && typeof value !== 'boolean') {
return;
}
this.componentAttributes[key.toLowerCase()] = {
property: key,
value,
};
});
}
/**
* Create new class that will serve as the Web Component.
*/
public create() {
const factory = this;
return class WebComponent extends HTMLElement {
public initialProperties = _.cloneDeep(factory.componentProperties);
public properties: { [key: string]: unknown } = {};
public hasConnected = false;
constructor() {
super();
this.transferInitialProperties();
this.subscribeToProperties();
}
/**
* Observe attributes for changes.
* Part of the Web Component Standard.
*/
public static get observedAttributes() {
return Object.keys(factory.componentAttributes);
}
/**
* Web Component gets mounted on the DOM.
*/
public connectedCallback() {
this.mountReactApp({ initial: true });
this.hasConnected = true;
factory.connectCallback?.(this);
}
/**
* When an attribute is changed, this callback function is called.
* @param name name of the attribute
* @param oldValue value before change
* @param newValue value after change
*/
public attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (!this.hasConnected) {
return;
}
if (oldValue === newValue) {
return;
}
if (!factory.componentAttributes.hasOwnProperty(name)) {
return;
}
const propertyName = factory.componentAttributes[name].property;
this.properties[propertyName] = getSerialized(newValue);
this.mountReactApp();
}
/**
* When a property is changed, this callback function is called.
* @param name name of the property
* @param oldValue value before change
* @param newValue value after change
*/
public propertyChangedCallback(name: string, oldValue: unknown, newValue: unknown) {
if (!this.hasConnected) {
return;
}
if (oldValue === newValue) {
return;
}
this.properties[name] = newValue;
this.mountReactApp();
}
/**
* Web Component gets unmounted from the DOM.
*/
public disconnectedCallback() {
ReactDOM.unmountComponentAtNode(this);
}
/**
* Setup getters and setters for all properties.
* Here we ensure that the 'propertyChangedCallback' will get invoked
* when a property changes.
*/
public subscribeToProperties() {
const propertyMap = {} as PropertyDescriptorMap;
Object.keys(this.initialProperties).forEach((key: string) => {
propertyMap[key] = {
configurable: true,
enumerable: true,
get: (): unknown => {
const currentValue = this.properties.hasOwnProperty(key)
? this.properties[key]
: _.get(this.initialProperties, key);
return currentValue;
},
set: (newValue: unknown) => {
const oldValue = this.properties.hasOwnProperty(key)
? this.properties[key]
: _.get(this.initialProperties, key);
this.propertyChangedCallback(key, oldValue, newValue);
},
};
});
Object.defineProperties(this, propertyMap);
}
/**
* Syncronize all properties and attributes
*/
public syncronizePropertiesAndAttributes() {
Object.keys(this.initialProperties).forEach((key: string) => {
if (this.properties.hasOwnProperty(key)) {
return;
}
if (this.getAttribute(key) !== null) {
this.properties[key] = getSerialized(this.getAttribute(key) as string);
return;
}
this.properties[key] = _.get(this.initialProperties, key);
});
}
/**
* Transfer initial properties from the custom element.
*/
public transferInitialProperties() {
Object.keys(this.initialProperties).forEach((key: string) => {
if (this.hasOwnProperty(key)) {
this.properties[key] = this[key as keyof WebComponent];
}
});
}
/**
* Apply plugins
*/
public applyPlugins(application: JSX.Element): [JSX.Element, Element[]] {
const shadowChildren: Element[] = [];
const applicationWithPlugins = registeredPlugins.reduce(
(app: JSX.Element, currentPlugin: PluginRegistrator) => {
const pluginResult = currentPlugin(this, factory.plugins, app);
if (!pluginResult) {
return app;
}
const [wrapper, shadowChild] = pluginResult;
if (shadowChild) {
shadowChildren.push(shadowChild);
}
return wrapper;
},
application,
);
return [applicationWithPlugins, shadowChildren];
}
/**
* Generate react props based on properties and attributes.
*/
public reactProps(): { [key: string]: unknown } {
this.syncronizePropertiesAndAttributes();
return this.properties;
}
/**
* Mount React App onto the Web Component
*/
public mountReactApp(options?: { initial: boolean }) {
const anonymousSlot = factory.anonymousSlot ? React.createElement('slot') : undefined;
const application = (
<EventProvider value={this.eventDispatcher}>
{React.createElement(factory.rootComponent, this.reactProps(), anonymousSlot)}
</EventProvider>
);
const [applicationWithPlugins, shadowChildren] = this.applyPlugins(application);
if (!factory.shadow) {
ReactDOM.render(applicationWithPlugins, this);
return;
}
let currentChildren: Node[] | undefined;
if (options?.initial) {
currentChildren = Array.from(this.children).map((child: Node) => child.cloneNode(true));
}
const root = createProxyRoot(this, shadowChildren);
ReactDOM.render(<root.open>{applicationWithPlugins}</root.open>, this);
if (currentChildren) {
currentChildren.forEach((child: Node) => this.append(child));
}
}
/**
* Dispatch an event from the Web Component
*/
public eventDispatcher = (event: Event) => {
this.dispatchEvent(event);
};
};
}
}
export default WebComponentFactory;
================================================
FILE: packages/direflow-component/src/components/EventContext.tsx
================================================
import { createContext } from 'react';
const EventContext = createContext<Function>(() => { /* Initially return nothing */ });
export const EventProvider = EventContext.Provider;
export const EventConsumer = EventContext.Consumer;
export { EventContext };
================================================
FILE: packages/direflow-component/src/components/Styled.tsx
================================================
import React, { FC, Component, ReactNode, ComponentClass, CSSProperties } from 'react';
import Style from '../helpers/styleInjector';
type TStyles = string | string[] | CSSProperties | CSSProperties[];
interface IStyled {
styles: TStyles;
scoped?: boolean;
children: ReactNode | ReactNode[];
}
const Styled: FC<IStyled> = (props): JSX.Element => {
let styles;
if (typeof props.styles === 'string') {
styles = (props.styles as CSSProperties).toString();
} else {
styles = (props.styles as CSSProperties[]).reduce(
(acc: CSSProperties, current: CSSProperties) => `${acc} ${current}` as CSSProperties,
);
}
return (
<Style scoped={props.scoped}>
{styles}
{props.children}
</Style>
);
};
const withStyles = (styles: TStyles) => <P, S>(WrappedComponent: ComponentClass<P, S> | FC<P>) => {
// eslint-disable-next-line react/prefer-stateless-function
return class extends Component<P, S> {
public render(): JSX.Element {
return (
<Styled styles={styles}>
<div>
<WrappedComponent {...(this.props as P)} />
</div>
</Styled>
);
}
};
};
export { withStyles, Styled };
================================================
FILE: packages/direflow-component/src/decorators/DireflowConfiguration.ts
================================================
import { IDireflowComponent } from '../types/DireflowConfig';
function DireflowConfiguration(config: Partial<IDireflowComponent>) {
return <T extends React.ComponentClass<any, any>>(
constructor: T & Partial<IDireflowComponent>,
) => {
const decoratedConstructor = constructor;
decoratedConstructor.configuration = config.configuration;
decoratedConstructor.properties = config.properties;
decoratedConstructor.plugins = config.plugins;
return decoratedConstructor;
};
}
export default DireflowConfiguration;
================================================
FILE: packages/direflow-component/src/helpers/asyncScriptLoader.ts
================================================
type TBundle = { script: Element; hasLoaded: boolean };
declare global {
interface Window {
wcPolyfillsLoaded: TBundle[];
reactBundleLoaded: TBundle[];
}
}
const asyncScriptLoader = (src: string, bundleListKey: 'wcPolyfillsLoaded' | 'reactBundleLoaded'): Promise<void> => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.src = src;
if (!window[bundleListKey]) {
window[bundleListKey] = [];
}
const existingPolyfill = window[bundleListKey].find((loadedScript) => {
return loadedScript.script.isEqualNode(script);
});
if (existingPolyfill) {
if (existingPolyfill.hasLoaded) {
resolve();
}
existingPolyfill.script.addEventListener('load', () => resolve());
return;
}
const scriptEntry = {
script,
hasLoaded: false,
};
window[bundleListKey].push(scriptEntry);
script.addEventListener('load', () => {
scriptEntry.hasLoaded = true;
resolve();
});
script.addEventListener('error', () => reject(new Error('Polyfill failed to load')));
document.head.appendChild(script);
});
};
export default asyncScriptLoader;
================================================
FILE: packages/direflow-component/src/helpers/domControllers.ts
================================================
export const injectIntoShadowRoot = (webComponent: HTMLElement, element: Element): void => {
const elementToPrepend = webComponent.shadowRoot || webComponent;
if (existsIdenticalElement(element, elementToPrepend)) {
return;
}
elementToPrepend.prepend(element);
};
export const injectIntoHead = (element: Element) => {
if (existsIdenticalElement(element, document.head)) {
return;
}
document.head.append(element);
};
export const stripStyleFromHead = (styleId: string) => {
const allChildren = document.head.children;
const style = Array.from(allChildren).find((child) => child.id === styleId);
if (style) {
document.head.removeChild(style);
}
};
export const existsIdenticalElement = (element: Element, host: Element | ShadowRoot): boolean => {
const allChildren = host.children;
const exists = Array.from(allChildren).some((child) => element.isEqualNode(child));
return exists;
};
================================================
FILE: packages/direflow-component/src/helpers/getSerialized.ts
================================================
const getSerialized = (data: string) => {
if (data === '') {
return true;
}
if (data === 'true' || data === 'false') {
return data === 'true';
}
try {
const parsed = JSON.parse(data.replace(/'/g, '"'));
return parsed;
} catch (error) {
return data;
}
};
export default getSerialized;
================================================
FILE: packages/direflow-component/src/helpers/polyfillHandler.ts
================================================
import { IDireflowPlugin } from '../types/DireflowConfig';
import asyncScriptLoader from './asyncScriptLoader';
type TWcPolyfillsLoaded = Array<{ script: Element; hasLoaded: boolean }>;
declare global {
interface Window {
wcPolyfillsLoaded: TWcPolyfillsLoaded;
}
}
let didIncludeOnce = false;
const DEFAULT_SD = 'https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.4.1/bundles/webcomponents-sd.js';
const DEFAULT_CE = 'https://cdn.jsdelivr.net/npm/@webcomponents/webcomponentsjs@2.4.1/bundles/webcomponents-ce.js';
const DEFAULT_AD = 'https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.4.1/custom-elements-es5-adapter.js';
const includePolyfills = async (
options: { usesShadow: boolean },
plugins: IDireflowPlugin[] | undefined,
) => {
if (didIncludeOnce) {
return;
}
const scriptsList = [];
let useSD = '';
let useCE = '';
let useAD = '';
const polyfillLoaderPlugin = plugins?.find((plugin) => plugin.name === 'polyfill-loader');
if (polyfillLoaderPlugin) {
console.warn(
'polyfill-loader plugin is deprecated. Use direflow-config.json instead.' + '\n' +
'See more: https://direflow.io/configuration',
);
}
const polyfillSD = process.env.DIREFLOW_SD ?? polyfillLoaderPlugin?.options?.use.sd;
const polyfillCE = process.env.DIREFLOW_CE ?? polyfillLoaderPlugin?.options?.use.ce;
const polyfillAdapter = process.env.DIREFLOW_ADAPTER ?? polyfillLoaderPlugin?.options?.use.adapter;
const disableSD = polyfillSD === false;
const disableCE = polyfillCE === false;
const disableAD = polyfillAdapter === false;
if (polyfillSD) {
useSD = typeof polyfillSD === 'string'
? polyfillSD
: DEFAULT_SD;
}
if (polyfillCE) {
useCE = typeof polyfillCE === 'string'
? polyfillCE
: DEFAULT_CE;
}
if (polyfillAdapter) {
useAD = typeof polyfillAdapter === 'string'
? polyfillAdapter
: DEFAULT_AD;
}
if (options.usesShadow && !disableSD) {
scriptsList.push(asyncScriptLoader(useSD || DEFAULT_SD, 'wcPolyfillsLoaded'));
}
if (!disableCE) {
scriptsList.push(asyncScriptLoader(useCE || DEFAULT_CE, 'wcPolyfillsLoaded'));
}
if (!disableAD) {
scriptsList.push(asyncScriptLoader(useAD || DEFAULT_AD, 'wcPolyfillsLoaded'));
}
try {
await Promise.all(scriptsList);
didIncludeOnce = true;
} catch (error) {
console.error(error);
}
};
export default includePolyfills;
================================================
FILE: packages/direflow-component/src/helpers/proxyRoot.tsx
================================================
import React, { FC } from 'react';
import { createPortal } from 'react-dom';
interface IPortal {
targetElement: ShadowRoot;
children: React.ReactNode;
}
interface IShadowComponent {
children: React.ReactNode | React.ReactNode[];
}
interface IComponentOptions {
webComponent: Element;
mode: 'open' | 'closed';
shadowChildren: Element[];
}
const Portal: FC<IPortal> = (props) => {
const targetElement = (props.targetElement as unknown) as Element;
return createPortal(props.children, targetElement);
};
const createProxyComponent = (options: IComponentOptions) => {
const ShadowRoot: FC<IShadowComponent> = (props) => {
const shadowedRoot = options.webComponent.shadowRoot
|| options.webComponent.attachShadow({ mode: options.mode });
options.shadowChildren.forEach((child) => {
shadowedRoot.appendChild(child);
});
return <Portal targetElement={shadowedRoot}>{props.children}</Portal>;
};
return ShadowRoot;
};
const componentMap = new WeakMap<Element, React.FC<IShadowComponent>>();
const createProxyRoot = (
webComponent: Element,
shadowChildren: Element[],
): { [key in 'open' | 'closed']: React.FC<IShadowComponent> } => {
return new Proxy<any>(
{ open: null, closed: null },
{
get(_: unknown, mode: 'open' | 'closed') {
if (componentMap.get(webComponent)) {
return componentMap.get(webComponent);
}
const proxyComponent = createProxyComponent({ webComponent, mode, shadowChildren });
componentMap.set(webComponent, proxyComponent);
return proxyComponent;
},
},
);
};
export default createProxyRoot;
================================================
FILE: packages/direflow-component/src/helpers/registerPlugin.ts
================================================
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
const registerPlugin = (registrator: PluginRegistrator) => {
return (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => registrator(element, plugins, app);
};
export default registerPlugin;
================================================
FILE: packages/direflow-component/src/helpers/styleInjector.tsx
================================================
import React, { Component, cloneElement, isValidElement } from 'react';
import adler32 from 'react-lib-adler32';
const isDevEnv = process.env.NODE_ENV !== 'production';
interface IProps {
scoped?: boolean;
}
class Style extends Component<IProps> {
private scopeClassNameCache: { [key: string]: string } = {};
private scopedCSSTextCache: { [key: string]: string } = {};
private scoped = this.props.scoped !== undefined ? this.props.scoped : true;
private pepper = '';
getStyleString = () => {
if (this.props.children instanceof Array) {
const styleString = this.props.children.filter(
(child) => !isValidElement(child) && typeof child === 'string',
);
if (styleString.length > 1) {
throw new Error(`Multiple style objects as direct descedents of a
Style component are not supported (${styleString.length} style objects detected):
${styleString[0]}
`);
}
return styleString[0];
}
if (typeof this.props.children === 'string' && !isValidElement(this.props.children)) {
return this.props.children;
}
return null;
};
getRootElement = () => {
if (this.props.children instanceof Array) {
const rootElement = this.props.children.filter((child) => isValidElement(child));
if (isDevEnv) {
if (rootElement.length > 1) {
console.log(rootElement);
throw new Error(`Adjacent JSX elements must be wrapped in an enclosing tag
(${rootElement.length} root elements detected)`);
}
if (
typeof rootElement[0] !== 'undefined' &&
this.isVoidElement((rootElement[0] as any).type)
) {
throw new Error(`Self-closing void elements like ${(rootElement as any).type} must be
wrapped in an enclosing tag. Reactive Style must be able to nest a style element inside of the
root element and void element content models never
allow it to have contents under any circumstances.`);
}
}
return rootElement[0];
}
if (isValidElement(this.props.children)) {
return this.props.children;
}
return null;
};
getRootSelectors = (rootElement: any) => {
const rootSelectors = [];
if (rootElement.props.id) {
rootSelectors.push(`#${rootElement.props.id}`);
}
if (rootElement.props.className) {
rootElement.props.className
.trim()
.split(/\s+/g)
.forEach((className: string) => rootSelectors.push(className));
}
if (!rootSelectors.length && typeof rootElement.type !== 'function') {
rootSelectors.push(rootElement.type);
}
return rootSelectors;
};
processCSSText = (styleString: any, scopeClassName?: string, rootSelectors?: any[]) => {
return styleString
.replace(/\s*\/\/(?![^(]*\)).*|\s*\/\*.*\*\//g, '')
.replace(/\s\s+/g, ' ')
.split('}')
.map((fragment: any) => {
const isDeclarationBodyPattern = /.*:.*;/g;
const isLastItemDeclarationBodyPattern = /.*:.*(;|$|\s+)/g;
const isAtRulePattern = /\s*@/g;
const isKeyframeOffsetPattern = /\s*(([0-9][0-9]?|100)\s*%)|\s*(to|from)\s*$/g;
return fragment
.split('{')
.map((statement: any, i: number, arr: any[]) => {
if (!statement.trim().length) {
return '';
}
const isDeclarationBodyItemWithOptionalSemicolon =
arr.length - 1 === i && statement.match(isLastItemDeclarationBodyPattern);
if (
statement.match(isDeclarationBodyPattern) ||
isDeclarationBodyItemWithOptionalSemicolon
) {
return this.escapeTextContentForBrowser(statement);
}
const selector = statement;
if (scopeClassName && !/:target/gi.test(selector)) {
if (!selector.match(isAtRulePattern) && !selector.match(isKeyframeOffsetPattern)) {
return this.scopeSelector(scopeClassName, selector, rootSelectors);
}
return selector;
}
return selector;
})
.join('{\n');
})
.join('}\n');
};
escaper = (match: any) => {
const ESCAPE_LOOKUP: { [key: string]: string } = {
'>': '>',
'<': '<',
};
return ESCAPE_LOOKUP[match];
};
escapeTextContentForBrowser = (text: string) => {
const ESCAPE_REGEX = /[><]/g;
return `${text}`.replace(ESCAPE_REGEX, this.escaper);
};
scopeSelector = (scopeClassName: string, selector: string, rootSelectors?: any[]) => {
const scopedSelector: string[] = [];
const groupOfSelectorsPattern = /,(?![^(|[]*\)|\])/g;
const selectors = selector.split(groupOfSelectorsPattern);
selectors.forEach((selectorElement) => {
let containsSelector;
let unionSelector;
if (
rootSelectors?.length &&
rootSelectors.some((rootSelector) => selectorElement.match(rootSelector))
) {
unionSelector = selectorElement;
const escapedRootSelectors = rootSelectors?.map((rootSelector) =>
rootSelector.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'),
);
unionSelector = unionSelector.replace(
new RegExp(`(${escapedRootSelectors?.join('|')})`),
`$1${scopeClassName}`,
);
containsSelector = this.scoped ? `${scopeClassName} ${selectorElement}` : selectorElement;
scopedSelector.push(unionSelector, containsSelector);
} else {
containsSelector = this.scoped ? `${scopeClassName} ${selectorElement}` : selectorElement;
scopedSelector.push(containsSelector);
}
});
if (!this.scoped && scopedSelector.length > 1) {
return scopedSelector[1];
}
return scopedSelector.join(', ');
};
getScopeClassName = (styleString: any, rootElement: any) => {
let hash = styleString;
if (rootElement) {
this.pepper = '';
this.traverseObjectToGeneratePepper(rootElement);
hash += this.pepper;
}
return (isDevEnv ? 'scope-' : 's') + adler32(hash);
};
traverseObjectToGeneratePepper = (obj: any, depth = 0) => {
if (depth > 32 || this.pepper.length > 10000) return;
Object.keys(obj).forEach((prop) => {
const isPropReactInternal = /^[_$]|type|ref|^value$/.test(prop);
if (!!obj[prop] && typeof obj[prop] === 'object' && !isPropReactInternal) {
this.traverseObjectToGeneratePepper(obj[prop], depth + 1);
} else if (!!obj[prop] && !isPropReactInternal && typeof obj[prop] !== 'function') {
this.pepper += obj[prop];
}
});
};
isVoidElement = (type: string) =>
[
'area',
'base',
'br',
'col',
'command',
'embed',
'hr',
'img',
'input',
'keygen',
'link',
'meta',
'param',
'source',
'track',
'wbr',
].some((voidType) => type === voidType);
createStyleElement = (cssText: string, scopeClassName: string) => {
return (
<style
id='direflow_styles'
type='text/css'
key={scopeClassName}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: cssText || '' }}
/>
);
};
getNewChildrenForCloneElement = (
cssText: string,
rootElement: JSX.Element,
scopeClassName: string,
) => {
return [this.createStyleElement(cssText, scopeClassName)].concat(rootElement.props.children);
};
render() {
const styleString = this.getStyleString();
const rootElement: any = this.getRootElement();
if (!styleString && rootElement) {
return rootElement.props.children;
}
if (styleString && !rootElement) {
return this.createStyleElement(
this.processCSSText(styleString),
this.getScopeClassName(styleString, rootElement),
);
}
const rootElementId = rootElement.props.id ? rootElement.props.id : '';
const rootElementClassNames = rootElement.props.className
? `${rootElement.props.className} `
: '';
let scopeClassName;
let scopedCSSText;
const scopeClassNameAddress = rootElementClassNames + rootElementId + styleString;
if (this.scopeClassNameCache[scopeClassNameAddress]) {
scopeClassName = this.scopeClassNameCache[scopeClassNameAddress];
scopedCSSText = this.scopedCSSTextCache[scopeClassName];
} else {
scopeClassName = this.getScopeClassName(styleString, rootElement);
scopedCSSText = this.processCSSText(
styleString,
`.${scopeClassName}`,
this.getRootSelectors(rootElement),
);
this.scopeClassNameCache[scopeClassNameAddress] = scopeClassName;
this.scopedCSSTextCache[scopeClassName] = scopedCSSText;
}
const className = this.scoped
? `${rootElementClassNames}${scopeClassName}`
: rootElementClassNames;
return cloneElement(
rootElement,
{
...rootElement.props,
className: className.trim(),
},
this.getNewChildrenForCloneElement(scopedCSSText, rootElement, scopeClassName),
);
}
}
export default Style;
================================================
FILE: packages/direflow-component/src/hooks/useExternalSource.ts
================================================
import { useState, useEffect } from 'react';
type TSource = {
[key: string]: {
state: 'loading' | 'completed';
callback?: Function | null;
};
};
declare global {
interface Window {
externalSourcesLoaded: TSource;
}
}
/**
* Hook into an external source given a path
* Returns whether the source is loaded or not
* @param source
*/
const useExternalSource = (source: string) => {
const [hasLoaded, setHasLoaded] = useState(false);
useEffect(() => {
if (window.externalSourcesLoaded[source].state === 'completed') {
setHasLoaded(true);
return;
}
window.externalSourcesLoaded[source].callback = () => {
setHasLoaded(true);
};
}, []);
return hasLoaded;
};
export default useExternalSource;
================================================
FILE: packages/direflow-component/src/index.ts
================================================
import DireflowComponent from './DireflowComponent';
import DireflowConfiguration from './decorators/DireflowConfiguration';
import useExternalSource from './hooks/useExternalSource';
export { Styled, withStyles } from './components/Styled';
export { EventProvider, EventConsumer, EventContext } from './components/EventContext';
export { DireflowComponent, DireflowConfiguration, useExternalSource };
================================================
FILE: packages/direflow-component/src/plugins/externalLoaderPlugin.ts
================================================
import { injectIntoHead } from '../helpers/domControllers';
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
type TSource = {
[key: string]: {
state: 'loading' | 'completed';
callback?: Function | null;
};
};
declare global {
interface Window {
externalSourcesLoaded: TSource;
}
}
const externalLoaderPlugin: PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => {
const plugin = plugins?.find((p) => p.name === 'external-loader');
const paths = plugin?.options?.paths;
if (!paths || !paths.length || !app) {
return;
}
const scriptTags: HTMLScriptElement[] = [];
const styleTags: HTMLLinkElement[] = [];
paths.forEach((path: string | { src: string; async?: boolean; useHead?: boolean }) => {
const actualPath = typeof path === 'string' ? path : path.src;
const async = typeof path === 'string' ? false : path.async;
const useHead = typeof path === 'string' ? undefined : path.useHead;
if (actualPath.endsWith('.js')) {
const script = document.createElement('script');
script.src = actualPath;
script.async = !!async;
if (useHead !== undefined && !useHead) {
script.setAttribute('use-head', 'false');
} else {
script.setAttribute('use-head', 'true');
}
scriptTags.push(script);
}
if (actualPath.endsWith('.css')) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = actualPath;
if (useHead) {
link.setAttribute('use-head', 'true');
} else {
link.setAttribute('use-head', 'false');
}
styleTags.push(link);
}
});
const insertionPoint = document.createElement('span');
insertionPoint.id = 'direflow_external-sources';
if (!window.externalSourcesLoaded) {
window.externalSourcesLoaded = {};
}
scriptTags.forEach((script) => {
if (script.getAttribute('use-head') === 'true') {
injectIntoHead(script);
} else {
insertionPoint.appendChild(script);
}
window.externalSourcesLoaded[script.src] = {
state: 'loading',
};
script.addEventListener('load', () => {
window.externalSourcesLoaded[script.src].state = 'completed';
window.externalSourcesLoaded[script.src].callback?.();
});
});
styleTags.forEach((link) => {
if (link.getAttribute('use-head') === 'true') {
injectIntoHead(link);
} else {
insertionPoint.appendChild(link);
}
window.externalSourcesLoaded[link.href] = {
state: 'loading',
};
link.addEventListener('load', () => {
window.externalSourcesLoaded[link.href].state = 'completed';
window.externalSourcesLoaded[link.href].callback?.();
});
});
return [app, insertionPoint];
};
export default externalLoaderPlugin;
================================================
FILE: packages/direflow-component/src/plugins/fontLoaderPlugin.ts
================================================
import WebFont from 'webfontloader';
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
let didInclude = false;
const fontLoaderPlugin: PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
) => {
if (didInclude) {
return;
}
const plugin = plugins?.find((p) => p.name === 'font-loader');
if (plugin?.options) {
WebFont.load(plugin.options);
didInclude = true;
}
};
export default fontLoaderPlugin;
================================================
FILE: packages/direflow-component/src/plugins/iconLoaderPlugin.ts
================================================
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
const iconLoaderPlugin: PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => {
const plugin = plugins?.find((p) => p.name === 'icon-loader');
if (!app) {
return;
}
if (plugin?.options?.packs.includes('material-icons')) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://fonts.googleapis.com/icon?family=Material+Icons';
const insertionPoint = document.createElement('span');
insertionPoint.id = 'direflow_material-icons';
insertionPoint.appendChild(link);
return [app, insertionPoint];
}
};
export default iconLoaderPlugin;
================================================
FILE: packages/direflow-component/src/plugins/materialUiPlugin.tsx
================================================
import React from 'react';
import uniqueid from 'lodash';
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
const jssCache = new WeakMap<Element, any>();
const materialUiPlugin: PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => {
if (plugins?.find((plugin) => plugin.name === 'material-ui')) {
try {
const { create } = require('jss');
const { jssPreset, StylesProvider, createGenerateClassName } = require('@material-ui/core/styles');
const seed = uniqueid(`${element.tagName.toLowerCase()}-`);
const insertionPoint = document.createElement('span');
insertionPoint.id = 'direflow_material-ui-styles';
let jss: any;
if (jssCache.has(element)) {
jss = jssCache.get(element);
} else {
jss = create({
...jssPreset(),
insertionPoint,
});
jssCache.set(element, jss);
}
return [
<StylesProvider
jss={jss}
sheetsManager={new Map()}
generateClassName={createGenerateClassName({ seed })}
>
{app}
</StylesProvider>,
insertionPoint,
];
} catch (err) {
console.error('Could not load Material-UI. Did you remember to install @material-ui/core?');
}
}
};
export default materialUiPlugin;
================================================
FILE: packages/direflow-component/src/plugins/plugins.ts
================================================
import registerPlugin from '../helpers/registerPlugin';
import styledComponentsPlugin from './styledComponentsPlugin';
import externalLoaderPlugin from './externalLoaderPlugin';
import fontLoaderPlugin from './fontLoaderPlugin';
import iconLoaderPlugin from './iconLoaderPlugin';
import materialUiPlugin from './materialUiPlugin';
const plugins = [
registerPlugin(fontLoaderPlugin),
registerPlugin(iconLoaderPlugin),
registerPlugin(externalLoaderPlugin),
registerPlugin(styledComponentsPlugin),
registerPlugin(materialUiPlugin)
];
export default plugins;
================================================
FILE: packages/direflow-component/src/plugins/styledComponentsPlugin.tsx
================================================
import React from 'react';
import { IDireflowPlugin } from '../types/DireflowConfig';
import { PluginRegistrator } from '../types/PluginRegistrator';
const styledComponentsPlugin: PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => {
if (plugins?.find((plugin) => plugin.name === 'styled-components')) {
try {
const { StyleSheetManager } = require('styled-components');
const insertionPoint = document.createElement('span');
insertionPoint.id = 'direflow_styled-components-styles';
return [<StyleSheetManager target={insertionPoint}>{app}</StyleSheetManager>, insertionPoint];
} catch (error) {
console.error(
'Could not load styled-components. Did you remember to install styled-components?',
);
}
}
};
export default styledComponentsPlugin;
================================================
FILE: packages/direflow-component/src/types/DireflowConfig.ts
================================================
export interface IDireflowComponent {
component: (React.FC<any> | React.ComponentClass<any, any>) & { [key: string]: any };
configuration: IDireflowConfig;
properties?: any;
plugins?: IDireflowPlugin[];
}
export interface IDireflowConfig {
tagname: string;
useShadow?: boolean;
useAnonymousSlot?: boolean;
}
export interface IDireflowPlugin {
name: string;
options?: any;
}
================================================
FILE: packages/direflow-component/src/types/DireflowElement.ts
================================================
export type DireflowElement = { [key: string]: unknown } & HTMLElement;
================================================
FILE: packages/direflow-component/src/types/DireflowPromiseAlike.ts
================================================
import { type } from 'os';
type DireflowPromiseAlike = { then: (resolve: (element: HTMLElement) => void) => void };
export default DireflowPromiseAlike;
================================================
FILE: packages/direflow-component/src/types/PluginRegistrator.ts
================================================
import { IDireflowPlugin } from './DireflowConfig';
export type PluginRegistrator = (
element: HTMLElement,
plugins: IDireflowPlugin[] | undefined,
app?: JSX.Element,
) => [JSX.Element, Element?] | void;
================================================
FILE: packages/direflow-component/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es2015",
"module": "esNext",
"lib": [
"es2017",
"es7",
"es6",
"dom"
],
"declaration": true,
"outDir": "./dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
"jsx": "react",
"types": [
"node"
]
},
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: packages/direflow-scripts/README.md
================================================
# direflow-scripts
### This package includes configurations and scripts used by [Direflow](https://direflow.io)
:warning: This package is not meant to be used on its own, but as a part of [Direflow](https://direflow.io).
Please refer to the official webpage in order to get started.
================================================
FILE: packages/direflow-scripts/bin/direflow-scripts
================================================
#!/usr/bin/env node
require = require('esm')(module);
const cli = require('../dist/cli');
const [,, ...args] = process.argv;
cli.default(args);
================================================
FILE: packages/direflow-scripts/declarations.d.ts
================================================
declare module 'config-overrides';
declare module 'event-hooks-webpack-plugin';
declare module 'html-webpack-externals-plugin';
declare module 'event-hooks-webpack-plugin/lib/tasks';
declare module 'webpack-filter-warnings-plugin';
declare interface Window {
React?: any;
ReactDOM?: any;
}
================================================
FILE: packages/direflow-scripts/direflow-jest.config.js
================================================
const { createContext } = require('react');
// eslint-disable-next-line no-undef
jest.mock('../direflow-component', () => ({
Styled: (props) => {
return props.children;
},
EventContext: createContext(() => {}),
}));
================================================
FILE: packages/direflow-scripts/package.json
================================================
{
"name": "direflow-scripts",
"version": "4.0.0",
"description": "Create Web Components using React",
"main": "dist/index.js",
"author": "Silind Software",
"license": "MIT",
"scripts": {
"build": "tsc"
},
"bin": {
"direflow-scripts": "bin/direflow-scripts"
},
"files": [
"dist",
"bin",
"webpack.config.js",
"direflow-jest.config.js"
],
"repository": {
"type": "git",
"url": "git@github.com:Silind-Software/direflow.git"
},
"homepage": "https://direflow.io",
"dependencies": {
"@svgr/webpack": "^6.2.1",
"esm": "^3.2.25",
"event-hooks-webpack-plugin": "^2.2.0",
"handlebars": "^4.7.7",
"rimraf": "^3.0.2",
"ts-loader": "^9.3.0",
"webfontloader": "^1.6.28",
"webpack": "^5.73.0"
},
"devDependencies": {
"@babel/core": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
"@types/webfontloader": "^1.6.34",
"babel-loader": "^8.2.5",
"terser-webpack-plugin": "^5.3.3",
"typescript": "^4.7.3"
}
}
================================================
FILE: packages/direflow-scripts/src/cli.ts
================================================
import chalk from 'chalk';
import { ChildProcess, spawn } from 'child_process';
import { resolve } from 'path';
import { interupted, succeeded } from './helpers/messages';
type TCommand = 'start' | 'test' | 'build' | 'build:lib';
const env = { ...process.env };
env.SKIP_PREFLIGHT_CHECK = 'true';
export default function cli(args: Array<TCommand | string>) {
const [command, ...restArgs] = args;
switch (command as TCommand) {
case 'start':
start();
break;
case 'test':
test(restArgs);
break;
case 'build':
build(restArgs);
break;
case 'build:lib':
buildLib(restArgs);
break;
default:
console.log('No arguments provided.');
}
}
function spawner(command: string, args: ReadonlyArray<string>, options?: any) {
return spawn(command, args, options).on('exit', (code: number) => {
if (code !== 0) {
process.exit(code as number);
}
});
}
function start() {
spawner('react-app-rewired', ['start'], {
shell: true,
stdio: 'inherit',
env,
});
}
function test(args: string[]) {
spawner('react-app-rewired', ['test', '--env=jest-environment-jsdom-fourteen', ...args], {
shell: true,
stdio: 'inherit',
env,
});
}
function build(args: string[]) {
spawner('react-app-rewired', ['build', ...args], {
shell: true,
stdio: 'inherit',
env,
});
}
function buildLib(args: string[]) {
console.log('Building React component library...');
let webpack: ChildProcess | undefined;
if (args[0] === '--verbose') {
webpack = spawner('webpack', ['--config', resolve(__dirname, '../webpack.config.js')], {
shell: true,
stdio: 'inherit',
env,
});
} else {
webpack = spawner('webpack', ['--config', resolve(__dirname, '../webpack.config.js')]);
}
webpack.stdout?.on('data', (data) => {
if (data.toString().includes('ERROR')) {
console.log(chalk.red('An error occurred during the build!'));
console.log(chalk.red(data.toString()));
}
});
webpack.on('exit', (code: number) => {
if (code !== 0) {
console.log(interupted());
return;
}
console.log(succeeded());
});
}
================================================
FILE: packages/direflow-scripts/src/config/config-overrides.ts
================================================
import EventHooksPlugin from 'event-hooks-webpack-plugin';
import { EnvironmentPlugin } from 'webpack';
import rimraf from 'rimraf';
import fs from 'fs';
import { resolve } from 'path';
import { PromiseTask } from 'event-hooks-webpack-plugin/lib/tasks';
import entryResolver from '../helpers/entryResolver';
import {
TConfig,
IOptions,
IModule,
IOptimization,
IResolve,
TEntry,
IPlugin,
} from '../types/ConfigOverrides';
import getDireflowConfig from '../helpers/getDireflowConfig';
import IDireflowConfig from '../types/DireflowConfig';
export = function override(config: TConfig, env: string, options?: IOptions) {
const originalEntry = [config.entry].flat() as string[];
const [pathIndex] = originalEntry.splice(0, 1);
/**
* TODO: Remove deprecated options
*/
const direflowConfig = setDeprecatedOptions(
// Set deprecated options on config
env,
getDireflowConfig(pathIndex),
options,
);
const entries = addEntries(config.entry, pathIndex, env, direflowConfig);
const overridenConfig = {
...config,
entry: entries,
module: overrideModule(config.module),
output: overrideOutput(config.output, direflowConfig),
optimization: overrideOptimization(config.optimization, env, direflowConfig),
resolve: overrideResolve(config.resolve),
plugins: overridePlugins(config.plugins, entries, env, direflowConfig),
externals: overrideExternals(config.externals, env, direflowConfig),
};
return overridenConfig;
};
function addEntries(entry: TEntry, pathIndex: string, env: string, config?: IDireflowConfig) {
const originalEntry = [entry].flat() as string[];
const react = config?.modules?.react;
const reactDOM = config?.modules?.reactDOM;
const useSplit = !!config?.build?.split;
const componentPath = config?.build?.componentPath || 'direflow-components';
const resolvedEntries = entryResolver(pathIndex, componentPath, { react, reactDOM });
const newEntry: { [key: string]: string } = { main: pathIndex };
originalEntry.forEach((path, index) => {
newEntry[`path-${index}`] = path;
});
resolvedEntries.forEach((entries: { [key: string]: string }) => {
Object.keys(entries).forEach((key) => {
newEntry[key] = entries[key];
});
});
const flatList = Object.values(newEntry);
if (env === 'development') {
return [...flatList, resolve(__dirname, '../template-scripts/welcome.js')];
}
if (useSplit) {
return newEntry;
}
return flatList;
}
function overrideModule(module: IModule) {
const nestedRulesIndex = module.rules.findIndex((rule) => 'oneOf' in rule);
const cssRuleIndex = module.rules[nestedRulesIndex].oneOf.findIndex((rule) => '.css'.match(rule.test));
const scssRuleIndex = module.rules[nestedRulesIndex].oneOf.findIndex((rule) => '.scss'.match(rule.test));
if (cssRuleIndex !== -1) {
module.rules[nestedRulesIndex].oneOf[cssRuleIndex].use = ['to-string-loader', 'css-loader'];
}
if (scssRuleIndex !== -1) {
module.rules[nestedRulesIndex].oneOf[scssRuleIndex].use = ['to-string-loader', 'css-loader', 'sass-loader'];
}
module.rules[nestedRulesIndex].oneOf.unshift({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return module;
}
function overrideOutput(output: IOptions, config?: IDireflowConfig) {
const useSplit = config?.build?.split;
const filename = config?.build?.filename || 'direflowBundle.js';
const chunkFilename = config?.build?.chunkFilename || 'vendor.js';
const outputFilename = useSplit ? '[name].js' : filename;
return {
...output,
filename: outputFilename,
chunkFilename,
};
}
function overrideOptimization(optimization: IOptimization, env: string, config?: IDireflowConfig) {
optimization.minimizer[0].options.sourceMap = env === 'development';
const useVendor = config?.build?.vendor;
const vendorSplitChunks = {
cacheGroups: {
vendor: {
test: /node_modules/,
chunks: 'initial',
name: 'vendor',
enforce: true,
},
},
};
return {
...optimization,
splitChunks: useVendor ? vendorSplitChunks : false,
runtimeChunk: false,
};
}
function overridePlugins(plugins: IPlugin[], entry: TEntry, env: string, config?: IDireflowConfig) {
if (plugins[0].options) {
plugins[0].options.inject = 'head';
}
plugins.push(
new EventHooksPlugin({
done: new PromiseTask(() => copyBundleScript(env, entry, config)),
}),
);
if (config?.polyfills) {
plugins.push(
new EnvironmentPlugin(
Object.fromEntries(
Object.entries(config.polyfills).map(([key, value]) => {
const envKey = `DIREFLOW_${key.toUpperCase()}`;
if (value === 'true' || value === 'false') {
return [envKey, value === 'true'];
}
return [envKey, value];
}),
),
),
);
}
return plugins;
}
function overrideResolve(currentResolve: IResolve) {
try {
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
currentResolve.plugins = currentResolve.plugins.filter(
(plugin) => !(plugin instanceof ModuleScopePlugin),
);
} catch (error) {
// supress error
}
return currentResolve;
}
function overrideExternals(
externals: { [key: string]: any },
env: string,
config?: IDireflowConfig,
) {
if (env === 'development') {
return externals;
}
const extraExternals: any = { ...externals };
const react = config?.modules?.react;
const reactDOM = config?.modules?.reactDOM;
if (react) {
extraExternals.react = 'React';
}
if (reactDOM) {
extraExternals['react-dom'] = 'ReactDOM';
}
return extraExternals;
}
async function copyBundleScript(env: string, entry: TEntry, config?: IDireflowConfig) {
if (env !== 'production') {
return;
}
if (!fs.existsSync('build')) {
return;
}
const filename = config?.build?.filename || 'direflowBundle.js';
const chunkFilename = config?.build?.chunkFilename || 'vendor.js';
const emitAll = config?.build?.emitAll;
const emitSourceMaps = config?.build?.emitSourceMap;
const emitIndexHTML = config?.build?.emitIndexHTML;
if (emitAll) {
return;
}
fs.readdirSync('build').forEach((file: string) => {
if (file === filename) {
return;
}
if (file === chunkFilename) {
return;
}
if (!Array.isArray(entry) && Object.keys(entry).some((path) => `${path}.js` === file)) {
return;
}
if (emitSourceMaps && file.endsWith('.map')) {
return;
}
if (emitIndexHTML && file.endsWith('.html')) {
return;
}
rimraf.sync(`build/${file}`);
});
}
/**
* TODO: This function should be removed in next minor version
* @deprecated
* @param flag
* @param env
*/
function hasOptions(flag: string, env: string) {
if (env !== 'production') {
return false;
}
if (process.argv.length < 3) {
return false;
}
if (!process.argv.some((arg: string) => arg === `--${flag}` || arg === `-${flag[0]}`)) {
return false;
}
return true;
}
/**
* TODO: This function should be removed in next minor version
* @deprecated
* @param config
* @param options
*/
function setDeprecatedOptions(env: string, config?: IDireflowConfig, options?: IOptions) {
if (!options) {
return config;
}
const newObj = config ? (JSON.parse(JSON.stringify(config)) as IDireflowConfig) : {};
const { filename, chunkFilename, react, reactDOM } = options;
const useSplit = hasOptions('split', env);
const useVendor = hasOptions('vendor', env);
if (filename && !newObj.build?.filename) {
if (!newObj.build) {
newObj.build = { filename };
} else {
newObj.build.filename = filename;
}
}
if (chunkFilename && !newObj.build?.chunkFilename) {
if (!newObj.build) {
newObj.build = { chunkFilename };
} else {
newObj.build.chunkFilename = chunkFilename;
}
}
if (useSplit && !newObj.build?.split) {
if (!newObj.build) {
newObj.build = { split: useSplit };
} else {
newObj.build.split = useSplit;
}
}
if (useVendor && !newObj.build?.vendor) {
if (!newObj.build) {
newObj.build = { vendor: useVendor };
} else {
newObj.build.vendor = useVendor;
}
}
if (react && !newObj.modules?.react) {
if (!newObj.modules) {
newObj.modules = { react } as { react: string };
} else {
newObj.modules.react = react as string;
}
}
if (reactDOM && !newObj.modules?.reactDOM) {
if (!newObj.modules) {
newObj.modules = { reactDOM } as { reactDOM: string };
} else {
newObj.modules.reactDOM = reactDOM as string;
}
}
return newObj;
}
================================================
FILE: packages/direflow-scripts/src/helpers/asyncScriptLoader.ts
================================================
type TBundle = { script: Element; hasLoaded: boolean };
declare global {
interface Window {
wcPolyfillsLoaded: TBundle[];
reactBundleLoaded: TBundle[];
}
}
const asyncScriptLoader = (src: string, bundleListKey: 'wcPolyfillsLoaded' | 'reactBundleLoaded'): Promise<void> => {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.async = true;
script.src = src;
if (!window[bundleListKey]) {
window[bundleListKey] = [];
}
const existingPolyfill = window[bundleListKey].find((loadedScript) => {
return loadedScript.script.isEqualNode(script);
});
if (existingPolyfill) {
if (existingPolyfill.hasLoaded) {
resolve();
}
existingPolyfill.script.addEventListener('load', () => resolve());
return;
}
const scriptEntry = {
script,
hasLoaded: false,
};
window[bundleListKey].push(scriptEntry);
script.addEventListener('load', () => {
scriptEntry.hasLoaded = true;
resolve();
});
script.addEventListener('error', () => reject(new Error('Polyfill failed to load')));
document.head.appendChild(script);
});
};
export default asyncScriptLoader;
================================================
FILE: packages/direflow-scripts/src/helpers/entryResolver.ts
================================================
import fs from 'fs';
import { resolve, join, sep } from 'path';
import handlebars from 'handlebars';
import { IOptions } from '../types/ConfigOverrides';
const DEFAULT_REACT = 'https://unpkg.com/react@17/umd/react.production.min.js';
const DEFAULT_REACT_DOM = 'https://unpkg.com/react-dom@17/umd/react-dom.production.min.js';
function entryResolver(indexPath: string, componentPath: string, { react, reactDOM }: IOptions) {
const paths = indexPath.split(sep);
const srcPath = [...paths].slice(0, paths.length - 1).join(sep);
let reactResource: any = 'none';
let reactDOMResource: any = 'none';
if (react !== false) {
reactResource = react || DEFAULT_REACT;
}
if (reactDOM !== false) {
reactDOMResource = reactDOM || DEFAULT_REACT_DOM;
}
const entryLoaderFile = fs.readFileSync(
resolve(__dirname, '../template-scripts/entryLoader.js'),
'utf8',
);
const componentFolders = fs.readdirSync(join(srcPath, componentPath));
const entryLoaderTemplate = handlebars.compile(entryLoaderFile);
const mainEntryFile = entryLoaderTemplate({
pathIndex: join(srcPath, paths[paths.length - 1]).replace(/\\/g, '\\\\'),
reactResource,
reactDOMResource,
});
const mainEntryLoaderPath = resolve(__dirname, '../main.js');
fs.writeFileSync(mainEntryLoaderPath, mainEntryFile);
const entryList = componentFolders
.map((folder) => {
if (!fs.statSync(join(srcPath, componentPath, folder)).isDirectory()) {
return;
}
const pathIndex = join(srcPath, componentPath, folder, paths[paths.length - 1]);
if (!fs.existsSync(pathIndex)) {
return;
}
const escapedPathIndex = pathIndex.replace(/\\/g, '\\\\');
const entryFile = entryLoaderTemplate({ pathIndex: escapedPathIndex, reactResource, reactDOMResource });
const entryLoaderPath = resolve(__dirname, `../${folder}.js`);
fs.writeFileSync(entryLoaderPath, entryFile);
return { [folder]: entryLoaderPath };
})
.filter(Boolean);
entryList.unshift({ main: mainEntryLoaderPath });
return entryList as Array<{ [key: string]: string }>;
}
export default entryResolver;
================================================
FILE: packages/direflow-scripts/src/helpers/getDireflowConfig.ts
================================================
import fs from 'fs';
import { sep } from 'path';
import IDireflowConfig from '../types/DireflowConfig';
const getDireflowConfig = (indexPath: string) => {
try {
const paths = indexPath.split(sep);
const rootPath = [...paths].slice(0, paths.length - 2).join(sep);
const config = fs.readFileSync(`${rootPath}/direflow-config.json`, 'utf8');
if (!config) {
throw Error();
}
return JSON.parse(config) as IDireflowConfig;
} catch (error) { /* Suppress error */ }
};
export default getDireflowConfig;
================================================
FILE: packages/direflow-scripts/src/helpers/messages.ts
================================================
import chalk from 'chalk';
export const interupted = () => `
${chalk.red('Build got interrupted.')}
Did you remove the ${chalk.blue('src/component-exports')} file?
Try building with the command ${chalk.blue('build:lib --verbose')}
`;
export const succeeded = () => `
${chalk.greenBright('Succesfully created React component library')}
The ${chalk.blue('library')} folder can be found at ${chalk.green('/lib')}
Alter your ${chalk.blue('package.json')} file by adding the field:
${chalk.green('{ "main": "lib/component-exports.js" }')}
${chalk.blueBright('NB:')} If you are using hooks in your React components,
you may need to move ${chalk.blue('react')} and ${chalk.blue('react-dom')}
to ${chalk.blue('peerDependencies')}:
${chalk.green(
`
"peerDependencies": {
"react": "17.0.2",
"react-dom": "17.0.2"
}`,
)}
You may publish the React component library with: ${chalk.blue('npm publish')}
`;
================================================
FILE: packages/direflow-scripts/src/helpers/writeTsConfig.ts
================================================
import { existsSync, writeFileSync } from 'fs';
import { resolve } from 'path';
async function writeTsConfig(srcPath: string) {
if (existsSync(resolve(__dirname, '../../tsconfig.lib.json'))) {
return;
}
const tsConfig = {
extends: `${srcPath}/tsconfig.json`,
compilerOptions: {
module: 'esnext',
noEmit: false,
outDir: `${srcPath}/lib`,
declaration: true,
lib: ['es6', 'dom', 'es2016', 'es2017'],
},
};
writeFileSync(resolve(__dirname, '../../tsconfig.lib.json'), JSON.stringify(tsConfig, null, 2));
}
export default writeTsConfig;
================================================
FILE: packages/direflow-scripts/src/index.ts
================================================
import webpackConfig from './config/config-overrides';
export { webpackConfig };
================================================
FILE: packages/direflow-scripts/src/template-scripts/entryLoader.ts
================================================
const asyncScriptLoader = require('./helpers/asyncScriptLoader.js').default;
const reactResource: any = '{{reactResource}}';
const reactDOMResource: any = '{{reactDOMResource}}';
const includeReact = async () => {
try {
if (reactResource !== 'none') {
await asyncScriptLoader(reactResource, 'reactBundleLoaded');
}
if (reactDOMResource !== 'none') {
await asyncScriptLoader(reactDOMResource, 'reactBundleLoaded');
}
} catch (error) {
console.error(error);
}
};
const includeIndex = () => {
try {
require('{{pathIndex}}');
} catch (error) {
console.warn('File is not found: {{pathIndex}}');
}
};
setTimeout(async () => {
if (process.env.NODE_ENV === 'development'
|| (window.React && window.ReactDOM)
|| (reactResource === 'none' && reactDOMResource === 'none')) {
includeIndex();
return;
}
await includeReact();
try {
await new Promise((resolve, reject) => {
let intervalCounts = 0;
const interval = setInterval(() => {
if (intervalCounts >= 2500) {
reject(new Error('Direflow Error: React & ReactDOM was unable to load'));
}
if (window.React && window.ReactDOM) {
clearInterval(interval);
resolve(true);
}
intervalCounts += 1;
});
});
includeIndex();
} catch (error) {
console.error(error);
}
});
================================================
FILE: packages/direflow-scripts/src/template-scripts/welcome.ts
================================================
console.log(
`
%cDireflow Components running in development.
%cTo build your components
%c- Run 'npm run build'
%c- Your build will be found in %cbuild/direflowBundle.js
%cNeed help?
%c- Visit: https://direflow.io
`,
'color: white; font-size: 16px',
'color: #a0abd6; font-size: 16px; font-weight: bold',
'color: #ccc; font-size: 16px',
'color: #ccc; font-size: 16px',
'color: #ccc; font-size: 16px; font-weight: bold',
'color: #a0abd6; font-size: 16px; font-weight: bold',
'color: #ccc; font-size: 16px',
);
================================================
FILE: packages/direflow-scripts/src/types/ConfigOverrides.ts
================================================
export interface IOptions {
filename?: string;
chunkFilename?: string;
react?: string | boolean;
reactDOM?: string | boolean;
}
export interface IModule {
rules: {
oneOf: {
test: RegExp;
use: string[];
}[];
}[];
}
export interface IOutput {
filename: string;
chunkFilename: string;
}
export interface IOptimization {
minimizer: {
options: {
sourceMap: boolean;
};
}[];
runtimeChunk: boolean;
splitChunks: boolean | {
cacheGroups: {
vendor: {
test: RegExp;
chunks: string;
name: string;
enforce: boolean;
};
};
};
}
export interface IPlugin {
options?: {
inject: string;
};
[key: string]: any;
}
export interface IResolve {
plugins: unknown[];
}
export type TEntry = string[] | { [key: string]: string };
export type TConfig = {
[key: string]: unknown;
entry: TEntry;
module: IModule;
output: IOutput;
optimization: IOptimization;
plugins: IPlugin[];
resolve: IResolve;
externals: { [key: string]: any };
};
================================================
FILE: packages/direflow-scripts/src/types/DireflowConfig.ts
================================================
export default interface IDireflowConfig {
build?: {
componentPath?: string;
filename?: string;
chunkFilename?: string;
emitSourceMap?: boolean;
emitIndexHTML?: boolean;
emitAll?: boolean;
split?: boolean;
vendor?: boolean;
};
modules?: {
react?: string;
reactDOM?: string;
};
polyfills?: {
sd?: string | boolean;
ce?: string | boolean;
adapter: string | boolean;
};
}
================================================
FILE: packages/direflow-scripts/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2019", "es2017", "es7", "es6", "dom"],
"declaration": true,
"outDir": "./dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true
},
"exclude": [
"node_modules",
"direflow-jest.config.js",
"dist"
]
}
================================================
FILE: packages/direflow-scripts/webpack.config.js
================================================
/* eslint-disable @typescript-eslint/camelcase */
/* eslint-disable @typescript-eslint/no-var-requires */
const { resolve } = require('path');
const { existsSync } = require('fs');
const { EnvironmentPlugin } = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
const srcPath = process.cwd();
const indexJsPath = `${srcPath}/src/component-exports.js`;
const indexTsPath = `${srcPath}/src/component-exports.ts`;
const existsIndexJs = existsSync(indexJsPath);
const existsIndexTs = existsSync(indexTsPath);
if (!existsIndexJs && !existsIndexTs) {
throw Error('No component-exports.js or component-exports.ts file found');
}
const entryPath = existsIndexJs ? indexJsPath : indexTsPath;
const jsLoader = () => {
if (!existsIndexJs) {
return {};
}
return {
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env', '@babel/preset-react'],
},
},
};
};
const tsLoder = () => {
if (!existsIndexTs) {
return {};
}
const writeTsConfig = require('./dist/helpers/writeTsConfig').default;
writeTsConfig(srcPath);
return {
test: /\.tsx?$/,
loader: 'ts-loader',
options: {
configFile: resolve(__dirname, './tsconfig.lib.json'),
},
};
};
module.exports = {
mode: 'production',
devtool: 'none',
entry: entryPath,
resolve: {
extensions: ['.ts', '.tsx', '.js', '.css', '.scss'],
alias: {
react: resolve('../react'),
reactDOM: resolve('../reactDOM'),
},
},
output: {
path: `${srcPath}/lib`,
filename: 'component-exports.js',
library: 'direflow-library',
libraryTarget: 'commonjs2',
hashFunction: 'xxhash64',
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
ecma: 5,
warnings: false,
parse: {},
compress: {
toplevel: true,
unused: true,
dead_code: true,
drop_console: true,
},
mangle: true,
module: true,
output: null,
toplevel: false,
nameCache: null,
ie8: false,
keep_classnames: undefined,
keep_fnames: false,
safari10: false,
},
}),
],
moduleIds: 'hashed',
runtimeChunk: false,
},
module: {
rules: [
{
...jsLoader(),
},
{
...tsLoder(),
},
{
test: /\.css$/,
use: [
{
loader: 'to-string-loader',
},
{
loader: 'css-loader',
},
],
},
{
test: /\.scss$/,
use: [
{
loader: 'to-string-loader',
},
{
loader: 'css-loader',
},
{
loader: 'sass-loader',
},
],
},
{
test: /\.svg$/,
use: ['@svgr/webpack'],
},
],
},
plugins: [new EnvironmentPlugin({ NODE_ENV: 'production' })],
externals: {
'react': 'commonjs react',
'react-dom': 'commonjs react-dom',
},
};
================================================
FILE: scripts/bash/setupLocal.sh
================================================
npm -g remove direflow-cli
npm run update-version link
npm link
npm run build:full
================================================
FILE: scripts/bash/startIntegrationTest.sh
================================================
# Install
install() {
node ./scripts/node/installAll.js --test-setup
cd cypress/test-setup
}
# Build
build() {
npm run build
# Replace with pm2 and kill by id, not by port
npm run serve &
wait-on http://localhost:5000
}
# CleanUp
cleanup() {
echo "Cypress has finished testing"
echo "Closing dev server ..."
kill $(lsof -t -i:5000)
}
install
build
cd .. && cd ..
if npm run cypress:run; then
cleanup
exit 0
else
cleanup
exit 1
fi
================================================
FILE: scripts/node/buildAll.js
================================================
const fs = require('fs');
const { exec } = require('child_process');
build('.');
if (!fs.existsSync('packages')) {
return;
}
const widgetsDirectory = fs.readdirSync('packages');
// eslint-disable-next-line no-restricted-syntax
for (const directory of widgetsDirectory) {
if (fs.statSync(`packages/${directory}`).isDirectory()) {
build(`packages/${directory}`);
}
}
function build(dir) {
console.log('Beginning to build:', dir);
exec(`cd ${dir} && npm run build`, (err) => {
if (err) {
console.log(`✗ ${dir} could not build`);
console.log(err);
return;
}
console.log(`✓ ${dir} build succesfully`);
});
}
================================================
FILE: scripts/node/cleanupAll.js
================================================
const fs = require('fs');
const { exec } = require('child_process');
if (process.argv[2] === '--modules') {
if (fs.existsSync('packages/direflow-component/node_modules')) {
exec('rm -rf packages/direflow-component/node_modules', (err) => {
if (err) {
console.log('✗ packages/direflow-component/node_modules FAILED to remove');
console.log(err);
return;
}
console.log('✓ packages/direflow-component/node_modules is REMOVED');
});
}
return;
}
cleanDeps('.');
if (!fs.existsSync('packages')) {
return;
}
const widgetsDirectory = fs.readdirSync('packages');
// eslint-disable-next-line no-restricted-syntax
for (const directory of widgetsDirectory) {
if (fs.statSync(`packages/${directory}`).isDirectory()) {
cleanDeps(`packages/${directory}`);
}
}
if (!fs.existsSync('cypress/test-setup')) {
return;
}
if (!fs.statSync('cypress/test-setup').isDirectory()) {
return;
}
cleanDeps('cypress/test-setup');
function cleanDeps(dir) {
console.log('Beginning to clean:', dir);
if (fs.existsSync(`${dir}/node_modules`)) {
exec(`rm -rf ${dir}/node_modules`, (err) => {
if (err) {
console.log(`✗ ${dir}/node_modules FAILED to remove`);
console.log(err);
return;
}
console.log(`✓ ${dir}/node_modules is REMOVED`);
});
}
if (fs.existsSync(`${dir}/npm yarn.lock`)) {
exec(`rm ${dir}/npm yarn.lock`, (err) => {
if (err) {
console.log(`✗ ${dir}/npm yarn.lock FAILED to remove`);
console.log(err);
return;
}
console.log(`✓ ${dir}/npm yarn.lock is REMOVED`);
});
}
if (fs.existsSync(`${dir}/tsconfig.lib.json`)) {
exec(`rm ${dir}/tsconfig.lib.json`, (err) => {
if (err) {
console.log(`✗ ${dir}/tsconfig.lib.json FAILED to remove`);
console.log(err);
return;
}
console.log(`✓ ${dir}/tsconfig.lib.json is REMOVED`);
});
}
if (fs.existsSync(`${dir}/dist`)) {
exec(`rm -rf ${dir}/dist`, (err) => {
if (err) {
console.log(`✗ ${dir}/dist FAILED to remove`);
console.log(err);
return;
}
console.log(`✓ ${dir}/dist is REMOVED`);
});
}
}
================================================
FILE: scripts/node/installAll.js
================================================
const fs = require('fs');
const { exec, execSync } = require('child_process');
async function installAll() {
await install('.');
if (!fs.existsSync('packages')) {
return;
}
const widgetsDirectory = fs.readdirSync('packages');
// eslint-disable-next-line no-restricted-syntax
for (const directory of widgetsDirectory) {
if (fs.statSync(`packages/${directory}`).isDirectory()) {
// eslint-disable-next-line no-await-in-loop
await install(`packages/${directory}`);
}
}
}
function installTestSetup() {
if (!fs.existsSync('cypress/test-setup')) {
return;
}
if (!fs.statSync('cypress/test-setup').isDirectory()) {
return;
}
install('cypress/test-setup');
}
function install(dir) {
return new Promise(async (resolve, reject) => {
console.log('Beginning to install: ', dir);
await new Promise((subResolve) => {
exec(`cd ${dir} && npm install`, (err) => {
if (err) {
console.log(`✗ ${dir} could not install`);
console.log(err);
reject();
}
console.log(`✓ ${dir} installed succesfully`);
subResolve();
});
});
if (process.argv[2] === '--no-deps') {
resolve();
return;
}
const packageJson = JSON.parse(fs.readFileSync(`${dir}/package.json`));
const peerDeps = packageJson.peerDependencies;
if (peerDeps) {
// eslint-disable-next-line no-restricted-syntax
for (const [package, version] of Object.entries(peerDeps)) {
execSync(`cd ${dir} && npm install ${package}@${version}`);
console.log(`✓ ${package}@${version} peer dependency installed succesfully`);
}
fs.writeFileSync(`${dir}/package.json`, JSON.stringify(packageJson, null, 2), 'utf-8');
}
resolve();
});
}
if (process.argv[2] === '--test-setup') {
installTestSetup();
} else {
installAll();
}
================================================
FILE: scripts/node/updateVersion.js
================================================
const fs = require('fs');
const { execSync } = require('child_process');
const path = require('path');
const arg = process.argv[2];
if (!arg) {
console.log('Provide a version number');
return;
}
const rootPackage = require('../../package.json');
const componentPackage = require('../../packages/direflow-component/package.json');
const scriptPackage = require('../../packages/direflow-scripts/package.json');
let componentPackageJs = fs.readFileSync('templates/js/package.json').toString();
let componentPackageTs = fs.readFileSync('templates/ts/package.json').toString();
const componentRegex = /"direflow-component": "(.*)"/g;
const componentReplace = (r) => `"direflow-component": ${JSON.stringify(r)}`;
const scriptsRegex = /"direflow-scripts": "(.*)"/g;
const scriptsReplace = (r) => `"direflow-scripts": ${JSON.stringify(r)}`;
const updateLink = () => {
const currentDirectory = process.cwd();
if (!rootPackage.version.includes('-link')) {
rootPackage.version = `${rootPackage.version}-link`;
}
if (!componentPackage.version.includes('-link')) {
componentPackage.version = `${componentPackage.version}-link`;
}
if (!scriptPackage.version.includes('-link')) {
scriptPackage.version = `${scriptPackage.version}-link`;
}
const componentPath = path.join(currentDirectory, 'packages', 'direflow-component');
const scriptsPath = path.join(currentDirectory, 'packages', 'direflow-scripts');
componentPackageJs = componentPackageJs
.replace(componentRegex, componentReplace(componentPath))
.replace(scriptsRegex, scriptsReplace(scriptsPath));
componentPackageTs = componentPackageTs
.replace(componentRegex, componentReplace(componentPath))
.replace(scriptsRegex, scriptsReplace(scriptsPath));
console.log('');
console.log('Version have been set to use LINK.');
console.log(`New version: ${rootPackage.version}`);
console.log('');
};
const updateVersion = (version) => {
rootPackage.version = version;
componentPackage.version = version;
scriptPackage.version = version;
componentPackageJs = componentPackageJs
.replace(componentRegex, componentReplace(version))
.replace(scriptsRegex, scriptsReplace(version));
componentPackageTs = componentPackageTs
.replace(componentRegex, componentReplace(version))
.replace(scriptsRegex, scriptsReplace(version));
console.log('');
console.log('Version have updated.');
console.log(`New version: ${version}`);
console.log('');
};
const writeToFiles = () => {
fs.writeFileSync('package.json', JSON.stringify(rootPackage, null, 2), 'utf-8');
fs.writeFileSync('packages/direflow-component/package.json', JSON.stringify(componentPackage, null, 2), 'utf-8');
fs.writeFileSync('packages/direflow-scripts/package.json', JSON.stringify(scriptPackage, null, 2), 'utf-8');
fs.writeFileSync('templates/js/package.json', componentPackageJs, 'utf-8');
fs.writeFileSync('templates/ts/package.json', componentPackageTs, 'utf-8');
};
if (arg === 'patch') {
const buffer = execSync('npm view direflow-cli version');
const currentVersion = buffer.toString('utf8');
if (
currentVersion.trim() === rootPackage.version.trim()
|| rootPackage.version.includes('link')
) {
const versionNumbers = currentVersion.split('.');
const patch = Number(versionNumbers[2]);
const patchVersion = `${versionNumbers[0]}.${versionNumbers[1]}.${patch + 1}`;
updateVersion(patchVersion);
writeToFiles();
}
return;
}
if (arg === 'link') {
updateLink();
writeToFiles();
return;
}
updateVersion(arg);
writeToFiles();
================================================
FILE: templates/js/.eslintrc
================================================
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"settings": {
"pragma": "React",
"version": "detect"
},
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
}
}
================================================
FILE: templates/js/README.md
================================================
This component was bootstrapped with [Direflow](https://direflow.io).
# {{names.title}}
> {{defaultDescription}}
```html
<{{names.snake}}></{{names.snake}}>
```
Use this README to describe your Direflow Component
================================================
FILE: templates/js/direflow-config.json
================================================
{
"build": {
"componentPath": "direflow-components",
"filename": "direflowBundle.js"
}
}
================================================
FILE: templates/js/direflow-webpack.js
================================================
const { webpackConfig } = require('direflow-scripts');
/**
* Webpack configuration for Direflow Component
* Additional webpack plugins / overrides can be provided here
*/
module.exports = (config, env) => ({
...webpackConfig(config, env),
// Add your own webpack config here (optional)
});
================================================
FILE: templates/js/package.json
================================================
{
"name": "{{names.snake}}",
"description": "{{defaultDescription}}",
"version": "1.0.0",
{{#if npmModule}}
"author": "",
"license": "MIT",
"keywords": [
"direflow",
"react",
"webcomponent"
],
"homepage": "https://direflow.io/",
"repository": {
"type": "git",
"url": "https://github.com/Silind-Software/direflow"
},
"bugs": {
"email": "direflow@silind.com",
"url": "https://github.com/Silind-Software/direflow/issues/new"
},
"main": "build/direflowBundle.js",
"files": [
"build"
],
{{else}}
"private": true,
{{/if}}
"scripts": {
"start": "direflow-scripts start",
"build": "direflow-scripts build",
"build:lib": "direflow-scripts build:lib",
"test": "direflow-scripts test"
},
"dependencies": {
"react": "17.0.2",
"react-dom": "17.0.2",
"react-scripts": "^4.0.3",
"direflow-component": "4.0.0",
"direflow-scripts": "4.0.0"
},
"devDependencies": {
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.18.0",
"to-string-loader": "^1.1.6",
"jest-environment-jsdom-fourteen": "0.1.0",
"react-app-rewired": "2.1.3",
"react-test-renderer": "16.9.0",
"webpack-cli": "^3.3.11"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"setupFilesAfterEnv": [
"direflow-scripts/direflow-jest.config.js"
]
},
"config-overrides-path": "direflow-webpack.js"
}
================================================
FILE: templates/js/public/index.css
================================================
body {
padding: 0;
margin: 0;
width: 100vw;
padding-top: 150px;
display: flex;
justify-content: center;
align-items: center;
background-color: #F6FAFA;
}
================================================
FILE: templates/js/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="index.css">
<title>{{names.title}}</title>
</head>
<body>
<{{names.snake}}></{{names.snake}}>
</body>
</html>
================================================
FILE: templates/js/src/component-exports.js
================================================
/**
* In this file you can export components that will
* be build as a pure React component library.
*
* Using the command `npm run build:lib` will
* produce a folder `lib` with your React components.
*
* If you're not using a React component library,
* this file can be safely deleted.
*/
import App from './direflow-components/{{names.snake}}/App';
export {
App
};
================================================
FILE: templates/js/src/direflow-components/direflow-component/App.css
================================================
.app {
width: 400px;
height: 575px;
padding: 30px 60px;
box-sizing: border-box;
background-color: white;
box-shadow: 0 4px 14px 4px #375c821c;
font-family: 'Noto Sans JP', sans-serif;
border-bottom: 5px solid #cad5e6;
}
.top {
width: 100%;
height: 50%;
border-bottom: 2px solid #7998c7;
display: flex;
justify-content: center;
align-items: center;
}
.bottom {
width: 100%;
height: 50%;
border-top: 2px solid #7998c7;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.header-image {
width: 165px;
height: 165px;
background: url('https://silind-s3.s3.eu-west-2.amazonaws.com/direflow/logo.svg');
background-size: contain;
}
.header-title {
font-size: 34px;
color: #5781C2;
font-family: 'Advent Pro', sans-serif;
}
.sample-text {
font-family: 'Noto Sans JP', sans-serif;
font-size: 16px;
color: #666;
text-align: center;
}
.button {
width: 150px;
height: 45px;
font-family: 'Noto Sans JP', sans-serif;
font-size: 20px;
font-weight: bold;
background-color: #5781C2;
color: white;
box-shadow: 2px 2px 5px #16314d98;
outline: none;
border: 0;
cursor: pointer;
transition: 0.3s;
}
.button:hover {
box-shadow: 4px 4px 8px #16314d63;
background-color: #40558f;
}
================================================
FILE: templates/js/src/direflow-components/direflow-component/App.js
================================================
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { EventContext, Styled } from 'direflow-component';
import styles from './App.css';
const App = (props) => {
const dispatch = useContext(EventContext);
const handleClick = () => {
const event = new Event('my-event');
dispatch(event);
};
const renderSampleList = props.sampleList.map((sample) => (
<div key={sample} className='sample-text'>
→ {sample}
</div>
));
return (
<Styled styles={styles}>
<div className='app'>
<div className='top'>
<div className='header-image' />
</div>
<div className='bottom'>
<div className='header-title'>{props.componentTitle}</div>
<div>{renderSampleList}</div>
<button className='button' onClick={handleClick}>
Click me!
</button>
</div>
</div>
</Styled>
);
};
App.defaultProps = {
componentTitle: '{{names.title}}',
sampleList: [
'Create with React',
'Build as Web Component',
'Use it anywhere!',
],
}
App.propTypes = {
componentTitle: PropTypes.string,
sampleList: PropTypes.array,
};
export default App;
================================================
FILE: templates/js/src/direflow-components/direflow-component/index.js
================================================
import { DireflowComponent } from 'direflow-component';
import App from './App';
export default DireflowComponent.create({
component: App,
configuration: {
tagname: '{{names.snake}}',
},
plugins: [
{
name: 'font-loader',
options: {
google: {
families: ['Advent Pro', 'Noto Sans JP'],
},
},
},
],
});
================================================
FILE: templates/js/src/direflow-components/direflow-component/test/App.test.js
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import App from '../App';
const reactProps = {
componentTitle: 'Component Test',
sampleList: ['Mock', 'Test', 'Data'],
};
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App {...reactProps} />, div);
ReactDOM.unmountComponentAtNode(div);
});
it('matches snapshot as expected', () => {
const renderTree = renderer.create(<App {...reactProps} />).toJSON();
expect(renderTree).toMatchSnapshot();
});
================================================
FILE: templates/js/src/index.js
================================================
/**
* This is the entry file of the Direflow setup.
*
* You can add any additional functionality here.
* For example, this is a good place to hook into your
* Web Component once it's mounted on the DOM.
*
* !This file cannot be removed.
* It can be left blank if not needed.
*/
import {{names.pascal}} from './direflow-components/{{names.snake}}';
{{names.pascal}}.then((element) => {
/**
* Access DOM node when it's mounted
*/
console.log('{{names.snake}} is mounted on the DOM', element);
});
================================================
FILE: templates/ts/.eslintrc
================================================
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"settings": {
"pragma": "React",
"version": "detect"
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": ["react", "@typescript-eslint"],
"rules": {
"react/prop-types": "off",
"interface-name-prefix": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-var-requires": "off"
}
}
================================================
FILE: templates/ts/README.md
================================================
This component was bootstrapped with [Direflow](https://direflow.io).
# {{names.title}}
> {{defaultDescription}}
```html
<{{names.snake}}></{{names.snake}}>
```
Use this README to describe your Direflow Component
================================================
FILE: templates/ts/direflow-config.json
================================================
{
"build": {
"componentPath": "direflow-components",
"filename": "direflowBundle.js"
}
}
================================================
FILE: templates/ts/direflow-webpack.js
================================================
const { webpackConfig } = require('direflow-scripts');
/**
* Webpack configuration for Direflow Component
* Additional webpack plugins / overrides can be provided here
*/
module.exports = (config, env) => ({
...webpackConfig(config, env),
// Add your own webpack config here (optional)
});
================================================
FILE: templates/ts/package.json
================================================
{
"name": "{{names.snake}}",
"description": "{{defaultDescription}}",
"version": "1.0.0",
{{#if npmModule}}
"author": "",
"license": "MIT",
"keywords": [
"direflow",
"react",
"webcomponent"
],
"homepage": "https://direflow.io/",
"repository": {
"type": "git",
"url": "https://github.com/Silind-Software/direflow"
},
"bugs": {
"email": "direflow@silind.com",
"url": "https://github.com/Silind-Software/direflow/issues/new"
},
"main": "build/direflowBundle.js",
"files": [
"build"
],
{{else}}
"private": true,
{{/if}}
"scripts": {
"start": "direflow-scripts start",
"build": "direflow-scripts build",
"build:lib": "direflow-scripts build:lib",
"test": "direflow-scripts test"
},
"dependencies": {
"@types/node": "^16.11.7",
"@types/react": "17.0.2",
"@types/react-dom": "17.0.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-lib-adler32": "^1.0.3",
"react-scripts": "^4.0.3",
"direflow-component": "4.0.0",
"direflow-scripts": "4.0.0",
"webfontloader": "^1.6.28"
},
"devDependencies": {
{{#if eslint}}
"@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.30.0",
{{/if}}
"@types/jest": "24.0.18",
"@types/react-test-renderer": "16.9.1",
"jest-environment-jsdom": "28.1.0",
"react-app-rewired": "2.2.1",
"react-test-renderer": "17.0.2",
"to-string-loader": "^1.2.0",
{{#if tslint}}
"tslint-react": "^5.0.0",
"tslint": "6.1.3",
{{/if}}
"typescript": "^4.7.3",
"webpack-cli": "^4.9.2",
"ts-loader": "^9.3.0"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"setupFilesAfterEnv": [
"direflow-scripts/direflow-jest.config.js"
]
},
"config-overrides-path": "direflow-webpack.js"
}
================================================
FILE: templates/ts/public/index.css
================================================
body {
padding: 0;
margin: 0;
width: 100vw;
padding-top: 150px;
display: flex;
justify-content: center;
align-items: center;
background-color: #F6FAFA;
}
================================================
FILE: templates/ts/public/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" type="text/css" href="index.css">
<title>{{names.title}}</title>
</head>
<body>
<{{names.snake}}></{{names.snake}}>
</body>
</html>
================================================
FILE: templates/ts/src/component-exports.ts
================================================
/**
* In this file you can export components that will
* be built as a pure React component library.
*
* Using the command `npm run build:lib` will
* produce a folder `lib` with your React components.
*
* If you're not using a React component library,
* this file can be safely deleted.
*/
import App from './direflow-components/{{names.snake}}/App';
export {
App
};
================================================
FILE: templates/ts/src/direflow-components/direflow-component/App.css
================================================
.app {
width: 400px;
height: 575px;
padding: 30px 60px;
box-sizing: border-box;
background-color: white;
box-shadow: 0 4px 14px 4px #375c821c;
font-family: 'Noto Sans JP', sans-serif;
border-bottom: 5px solid #cad5e6;
}
.top {
width: 100%;
height: 50%;
border-bottom: 2px solid #7998c7;
display: flex;
justify-content: center;
align-items: center;
}
.bottom {
width: 100%;
height: 50%;
border-top: 2px solid #7998c7;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: center;
}
.header-image {
width: 165px;
height: 165px;
background: url('https://silind-s3.s3.eu-west-2.amazonaws.com/direflow/logo.svg');
background-size: contain;
}
.header-title {
font-size: 34px;
color: #5781C2;
font-family: 'Advent Pro', sans-serif;
}
.sample-text {
font-family: 'Noto Sans JP', sans-serif;
font-size: 16px;
color: #666;
text-align: center;
}
.button {
width: 150px;
height: 45px;
font-family: 'Noto Sans JP', sans-serif;
font-size: 20px;
font-weight: bold;
background-color: #5781C2;
color: white;
box-shadow: 2px 2px 5px #16314d98;
outline: none;
border: 0;
cursor: pointer;
transition: 0.3s;
}
.button:hover {
box-shadow: 4px 4px 8px #16314d63;
background-color: #40558f;
}
================================================
FILE: templates/ts/src/direflow-components/direflow-component/App.tsx
================================================
import React, { FC, useContext } from 'react';
import { EventContext, Styled } from 'direflow-component';
import styles from './App.css';
interface IProps {
componentTitle: string;
sampleList: string[];
}
const App: FC<IProps> = (props) => {
const dispatch = useContext(EventContext);
const handleClick = () => {
const event = new Event('my-event');
dispatch(event);
};
const renderSampleList = props.sampleList.map((sample: string) => (
<div key={sample} className='sample-text'>
→ {sample}
</div>
));
return (
<Styled styles={styles}>
<div className='app'>
<div className='top'>
<div className='header-image' />
</div>
<div className='bottom'>
<div className='header-title'>{props.componentTitle}</div>
<div>{renderSampleList}</div>
<button className='button' onClick={handleClick}>
Click me!
</button>
</div>
</div>
</Styled>
);
};
App.defaultProps = {
componentTitle: '{{names.title}}',
sampleList: [
'Create with React',
'Build as Web Component',
'Use it anywhere!',
],
}
export default App;
================================================
FILE: templates/ts/src/direflow-components/direflow-component/index.tsx
================================================
import { DireflowComponent } from 'direflow-component';
import App from './App';
export default DireflowComponent.create({
component: App,
configuration: {
tagname: '{{names.snake}}',
},
plugins: [
{
name: 'font-loader',
options: {
google: {
families: ['Advent Pro', 'Noto Sans JP'],
},
},
},
],
});
================================================
FILE: templates/ts/src/direflow-components/direflow-component/test/App.test.tsx
================================================
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import App from '../App';
const reactProps = {
componentTitle: 'Component Test',
sampleList: ['Mock', 'Test', 'Data'],
};
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App {...reactProps} />, div);
ReactDOM.unmountComponentAtNode(div);
});
it('matches snapshot as expected', () => {
const renderTree = renderer.create(<App {...reactProps} />).toJSON();
expect(renderTree).toMatchSnapshot();
});
================================================
FILE: templates/ts/src/index.tsx
================================================
/**
* This is the entry file of the Direflow setup.
*
* You can add any additional functionality here.
* For example, this is a good place to hook into your
* Web Component once it's mounted on the DOM.
*
* !This file cannot be removed.
* It can be left blank if not needed.
*/
import {{names.pascal}} from './direflow-components/{{names.snake}}';
{{names.pascal}}.then((element) => {
/**
* Access DOM node when it's mounted
*/
console.log('{{names.snake}} is mounted on the DOM', element);
});
================================================
FILE: templates/ts/src/react-app-env.d.ts
================================================
/// <reference types="react-scripts" />
declare module '*.css' {
export default style as string
}
declare module '*.scss' {
export default style as string
}
declare module '*.sass' {
export default style as string
}
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
declare namespace JSX {
interface IntrinsicElements {
'slot';
}
}
================================================
FILE: templates/ts/tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"noEmit": true,
"jsx": "react",
"isolatedModules": true
},
"include": [
"src"
],
"exclude": [
"node_modules",
"dist"
]
}
================================================
FILE: templates/ts/tslint.json
================================================
{
"rules": {
"max-line-length": { "options": [120], "severity": "warning" },
"no-lowlevel-commenting": { "severity": "warning" },
"discreet-ternary": { "severity": "warning" },
"curly": { "severity": "warning" },
"jsx-wrap-multiline": false,
"typedef": false
},
"linterOptions": {
"exclude": [
"config/**/*.js",
"node_modules/**/*.ts",
"coverage/lcov-report/*.js",
"webpack.config.js"
]
},
"extends": ["tslint:recommended", "tslint-react", "tslint-config-airbnb"]
}
================================================
FILE: test/detectDireflowSetup.test.ts
================================================
import { fs, vol } from 'memfs';
import isDireflowSetup from '../cli/helpers/detectDireflowSetup';
jest.mock('fs', () => fs);
const isSetupFilePath = '/path/to/mock/setup';
const isNotSetupFilePath = '/path/to/mock/non-setup';
const mockFsJson = {
[`${isSetupFilePath}/direflow-webpack.js`]: '',
[`${isNotSetupFilePath}/foo`]: '',
};
vol.fromJSON(mockFsJson);
describe('Detect Direflow Setup', () => {
afterAll(() => {
vol.reset();
});
it('should return true if Direflow Setup', () => {
const isSetup = isDireflowSetup(isSetupFilePath);
expect(isSetup).toBeTruthy();
});
it('should return false if not Direflow Setup', () => {
const isSetup = isDireflowSetup(isNotSetupFilePath);
expect(isSetup).toBeFalsy();
});
});
================================================
FILE: test/domController.test.ts
================================================
import { JSDOM } from 'jsdom';
import {
injectIntoShadowRoot,
injectIntoHead,
stripStyleFromHead,
existsIdenticalElement,
} from '../packages/direflow-component/src/helpers/domControllers';
const dom = new JSDOM();
(global as any).document = dom.window.document;
(global as any).window = dom.window;
const webComponent = document.createElement('shadow-web-component');
const webComponentNoShadow = document.createElement('no-shadow-web-component');
const appElement = document.createElement('div');
webComponent.attachShadow({ mode: 'open' });
webComponent.shadowRoot?.append(appElement);
const linkElement = document.createElement('link');
linkElement.rel = 'shortcut icon';
linkElement.type = 'image/x-icon';
linkElement.href = 'https://some-test-url.jest';
appElement.id = 'app';
appElement.append(document.createElement('style'));
appElement.append(document.createElement('script'));
appElement.append(linkElement);
describe('Inject into Shadow Root', () => {
it('should correctly inject into Shadow Root', () => {
const element = document.createElement('div');
element.id = 'injected_shadow';
injectIntoShadowRoot(webComponent, element);
expect(webComponent.shadowRoot?.children.length).toBe(2);
expect(webComponent.shadowRoot?.children[0]?.id).toBe('injected_shadow');
});
it('should not inject if already exists', () => {
const element = document.createElement('div');
element.id = 'injected_shadow';
injectIntoShadowRoot(webComponent, element);
expect(webComponent.shadowRoot?.children.length).toBe(2);
});
});
describe('Inject into Web Component', () => {
it('should correctly inject into Web Component', () => {
const element = document.createElement('div');
element.id = 'injected_no_shadow';
injectIntoShadowRoot(webComponentNoShadow, element);
expect(webComponentNoShadow.children.length).toBe(1);
expect(webComponentNoShadow.children[0]?.id).toBe('injected_no_shadow');
});
it('should not inject if already exists', () => {
const element = document.createElement('div');
element.id = 'injected_no_shadow';
injectIntoShadowRoot(webComponentNoShadow, element);
expect(webComponentNoShadow.children.length).toBe(1);
});
});
describe('Inject into head', () => {
it('should correctly inject into head', () => {
const element = document.createElement('style');
element.id = 'direflow-style';
injectIntoHead(element);
expect(document.head.children.length).toBe(1);
expect(document.head.children[0]?.id).toBe('direflow-style');
});
it('should not inject if already exists', () => {
const element = document.createElement('style');
element.id = 'direflow-style';
injectIntoHead(element);
expect(document.head.children.length).toBe(1);
expect(document.head.children[0]?.id).toBe('direflow-style');
});
});
describe('Strip style from head', () => {
it('should correctly strip style from head', () => {
stripStyleFromHead('direflow-style');
expect(document.head.children.length).toBe(0);
expect(document.head.children[0]).toBeUndefined();
});
});
describe('Exists identical element', () => {
it('should return true if identical element exists', () => {
const identicalLinkElement = document.createElement('link');
identicalLinkElement.rel = 'shortcut icon';
identicalLinkElement.type = 'image/x-icon';
identicalLinkElement.href = 'https://some-test-url.jest';
const exists = existsIdenticalElement(identicalLinkElement, appElement);
expect(exists).toBeTruthy();
});
it('should return true if identical element exists', () => {
const identicalLinkElement = document.createElement('link');
identicalLinkElement.rel = 'shortcut icon';
identicalLinkElement.type = 'image/x-icon';
identicalLinkElement.href = 'https://some-different-url.jest';
const exists = existsIdenticalElement(identicalLinkElement, appElement);
expect(exists).toBeFalsy();
});
});
================================================
FILE: test/nameformats.test.ts
================================================
import { getNameFormats, createDefaultName } from '../cli/helpers/nameFormat';
describe('Get correct name formats', () => {
it('should create name formats from slug', () => {
const slug = 'test-component-name';
const formats = getNameFormats(slug);
expect(formats.title).toBe('Test Component Name');
expect(formats.pascal).toBe('TestComponentName');
expect(formats.snake).toBe('test-component-name');
});
it('should create name formats from pascal', () => {
const slug = 'TestComponentName';
const formats = getNameFormats(slug);
expect(formats.title).toBe('Test Component Name');
expect(formats.pascal).toBe('TestComponentName');
expect(formats.snake).toBe('test-component-name');
});
it('should create name formats from title', () => {
const slug = 'Test Component Name';
const formats = getNameFormats(slug);
expect(formats.title).toBe('Test Component Name');
expect(formats.pascal).toBe('TestComponentName');
expect(formats.snake).toBe('test-component-name');
});
});
describe('Get defualt name', () => {
it('should create a default name', () => {
const defaultName = createDefaultName('awesome');
expect(defaultName).toBe('awesome-component');
});
it('should not create a default name', () => {
const defaultName = createDefaultName('nice-component');
expect(defaultName).toBe('nice-component');
});
});
================================================
FILE: test/writeNames.test.ts
================================================
import { promisify } from 'util';
import { fs, vol } from 'memfs';
import writeProjectNames from '../cli/helpers/writeNames';
jest.mock('fs', () => fs);
const mockDirPath = '/path/to/mock/dir';
const mockFsJson = {
'./package.json': `
{
"name": "{{names.snake}}",
"description": "{{defaultDescription}}"
}
`,
'./README.md': `
# {{names.title}}
> {{defaultDescription}}
`,
'./tslint.json': 'this-content-includes-tslint-rules',
'./.eslintrc': 'this-content-includes-eslint-rules',
'./nested/index.tsx': `
direflowComponent.configure({
name: '{{names.snake}}',
useShadow: true,
});
direflowComponent.create(App);
`,
};
const readFile = promisify(fs.readFile);
const createMockFileSystem = async (options?: { noDescription?: boolean; useTslint?: boolean }) => {
vol.fromJSON(mockFsJson, mockDirPath);
await writeProjectNames({
names: {
title: 'Cool Component',
pascal: 'CoolComponent',
snake: 'cool-component',
},
projectDirectoryPath: mockDirPath,
description: options?.noDescription ? '' : 'This component is cool',
linter: options?.useTslint ? 'tslint' : 'eslint',
packageVersion: '0.0.0',
type: 'direflow-component',
npmModule: false,
});
};
describe('Write names to file #1', () => {
beforeAll(async () => {
await createMockFileSystem();
});
afterAll(() => {
vol.reset();
});
it('should change package.json correctly', async () => {
const changedFile = await readFile(`${mockDirPath}/package.json`) as any;
expect(changedFile.toString()).toBe(`
{
"name": "cool-component",
"description": "This component is cool"
}
`);
});
it('should change index.tsx correctly', async () => {
const changedFile = await readFile(`${mockDirPath}/nested/index.tsx`) as any;
expect(changedFile.toString()).toBe(`
direflowComponent.configure({
name: 'cool-component',
useShadow: true,
});
direflowComponent.create(App);
`);
});
it('should change README.md correctly', async () => {
const changedFile = await readFile(`${mockDirPath}/README.md`) as any;
expect(changedFile.toString()).toBe(`
# Cool Component
> This component is cool
`);
});
});
describe('Write names to file #1', () => {
beforeAll(async () => {
await createMockFileSystem({ noDescription: true });
});
afterAll(() => {
vol.reset();
});
it('should use fallback description in package.json', async () => {
const changedFile = await readFile(`${mockDirPath}/package.json`) as any;
expect(changedFile.toString()).toBe(`
{
"name": "cool-component",
"description": "This project is created using Direflow"
}
`);
});
it('should use fallback description in README.md', async () => {
const changedFile = await readFile(`${mockDirPath}/README.md`) as any;
expect(changedFile.toString()).toBe(`
# Cool Component
> This project is created using Direflow
`);
});
});
describe('Remove tslint file', () => {
beforeAll(async () => {
await createMockFileSystem();
});
afterAll(() => {
vol.reset();
});
it('should remove tslint file given eslint option', async () => {
const getFile = () => {
return readFile(`${mockDirPath}/tslint.json`);
};
await expect(getFile).rejects.toThrow(
Error("ENOENT: no such file or directory, open '/path/to/mock/dir/tslint.json'"),
);
});
});
describe('Remove eslint file', () => {
beforeAll(async () => {
await createMockFileSystem({ useTslint: true });
});
afterAll(() => {
vol.reset();
});
it('should remove eslint file given tslint option', async () => {
const getFile = () => {
return readFile(`${mockDirPath}/.eslintrc`) as any;
};
await expect(getFile).rejects.toThrow(
Error("ENOENT: no such file or directory, open '/path/to/mock/dir/.eslintrc'"),
);
});
});
================================================
FILE: tsconfig.eslint.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "dist", "templates"]
}
================================================
FILE: tsconfig.json
================================================
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2017", "es7", "es6", "dom"],
"declaration": true,
"outDir": "./dist",
"rootDir": "cli",
"strict": true,
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true
},
"exclude": [
"node_modules",
"dist",
"templates",
"test",
"packages",
"cypress"
]
}
gitextract_rnz70h0d/ ├── .eslintrc ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── pull_request_template.md │ └── workflows/ │ ├── build.yml │ └── test.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bin/ │ └── direflow ├── cli/ │ ├── checkForUpdate.ts │ ├── cli.ts │ ├── create.ts │ ├── headline.ts │ ├── helpers/ │ │ ├── copyTemplate.ts │ │ ├── detectDireflowSetup.ts │ │ ├── nameFormat.ts │ │ └── writeNames.ts │ ├── messages.ts │ ├── questions.ts │ └── types/ │ ├── Command.ts │ ├── LangageOption.ts │ ├── Names.ts │ ├── QuestionOption.ts │ └── TemplateOption.ts ├── cypress/ │ ├── integration/ │ │ ├── basic_tests.ts │ │ ├── event_tests.ts │ │ ├── external_loader_test.ts │ │ ├── material_ui_test.ts │ │ ├── props_tests.ts │ │ ├── slot_tests.ts │ │ └── styled_components_test.ts │ ├── support/ │ │ ├── commands.js │ │ └── index.js │ ├── test-setup/ │ │ ├── direflow-config.json │ │ ├── direflow-webpack.js │ │ ├── jsconfig.json │ │ ├── jsconfig.paths.json │ │ ├── package.json │ │ ├── public/ │ │ │ ├── index.css │ │ │ ├── index.html │ │ │ └── index_prod.html │ │ └── src/ │ │ ├── component-exports.js │ │ ├── direflow-components/ │ │ │ └── test-setup/ │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── MaterialUI.js │ │ │ ├── StyledComponent.js │ │ │ ├── index.js │ │ │ └── test/ │ │ │ └── App.test.js │ │ └── index.js │ └── tsconfig.json ├── cypress.json ├── declarations.d.ts ├── package.json ├── packages/ │ ├── direflow-component/ │ │ ├── README.md │ │ ├── declarations.d.ts │ │ ├── package.json │ │ ├── src/ │ │ │ ├── DireflowComponent.tsx │ │ │ ├── WebComponentFactory.tsx │ │ │ ├── components/ │ │ │ │ ├── EventContext.tsx │ │ │ │ └── Styled.tsx │ │ │ ├── decorators/ │ │ │ │ └── DireflowConfiguration.ts │ │ │ ├── helpers/ │ │ │ │ ├── asyncScriptLoader.ts │ │ │ │ ├── domControllers.ts │ │ │ │ ├── getSerialized.ts │ │ │ │ ├── polyfillHandler.ts │ │ │ │ ├── proxyRoot.tsx │ │ │ │ ├── registerPlugin.ts │ │ │ │ └── styleInjector.tsx │ │ │ ├── hooks/ │ │ │ │ └── useExternalSource.ts │ │ │ ├── index.ts │ │ │ ├── plugins/ │ │ │ │ ├── externalLoaderPlugin.ts │ │ │ │ ├── fontLoaderPlugin.ts │ │ │ │ ├── iconLoaderPlugin.ts │ │ │ │ ├── materialUiPlugin.tsx │ │ │ │ ├── plugins.ts │ │ │ │ └── styledComponentsPlugin.tsx │ │ │ └── types/ │ │ │ ├── DireflowConfig.ts │ │ │ ├── DireflowElement.ts │ │ │ ├── DireflowPromiseAlike.ts │ │ │ └── PluginRegistrator.ts │ │ └── tsconfig.json │ └── direflow-scripts/ │ ├── README.md │ ├── bin/ │ │ └── direflow-scripts │ ├── declarations.d.ts │ ├── direflow-jest.config.js │ ├── package.json │ ├── src/ │ │ ├── cli.ts │ │ ├── config/ │ │ │ └── config-overrides.ts │ │ ├── helpers/ │ │ │ ├── asyncScriptLoader.ts │ │ │ ├── entryResolver.ts │ │ │ ├── getDireflowConfig.ts │ │ │ ├── messages.ts │ │ │ └── writeTsConfig.ts │ │ ├── index.ts │ │ ├── template-scripts/ │ │ │ ├── entryLoader.ts │ │ │ └── welcome.ts │ │ └── types/ │ │ ├── ConfigOverrides.ts │ │ └── DireflowConfig.ts │ ├── tsconfig.json │ └── webpack.config.js ├── scripts/ │ ├── bash/ │ │ ├── setupLocal.sh │ │ └── startIntegrationTest.sh │ └── node/ │ ├── buildAll.js │ ├── cleanupAll.js │ ├── installAll.js │ └── updateVersion.js ├── templates/ │ ├── js/ │ │ ├── .eslintrc │ │ ├── README.md │ │ ├── direflow-config.json │ │ ├── direflow-webpack.js │ │ ├── package.json │ │ ├── public/ │ │ │ ├── index.css │ │ │ └── index.html │ │ └── src/ │ │ ├── component-exports.js │ │ ├── direflow-components/ │ │ │ └── direflow-component/ │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── index.js │ │ │ └── test/ │ │ │ └── App.test.js │ │ └── index.js │ └── ts/ │ ├── .eslintrc │ ├── README.md │ ├── direflow-config.json │ ├── direflow-webpack.js │ ├── package.json │ ├── public/ │ │ ├── index.css │ │ └── index.html │ ├── src/ │ │ ├── component-exports.ts │ │ ├── direflow-components/ │ │ │ └── direflow-component/ │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── index.tsx │ │ │ └── test/ │ │ │ └── App.test.tsx │ │ ├── index.tsx │ │ └── react-app-env.d.ts │ ├── tsconfig.json │ └── tslint.json ├── test/ │ ├── detectDireflowSetup.test.ts │ ├── domController.test.ts │ ├── nameformats.test.ts │ └── writeNames.test.ts ├── tsconfig.eslint.json └── tsconfig.json
SYMBOL INDEX (96 symbols across 36 files)
FILE: cli/cli.ts
type TOptions (line 8) | type TOptions =
type TParsed (line 16) | type TParsed = Command & { [key in TOptions]?: true } & { desc: string };
function cli (line 18) | function cli() {
function handleAction (line 50) | async function handleAction(name: string | undefined, parsed: TParsed) {
FILE: cli/create.ts
type ISetupPresets (line 10) | interface ISetupPresets {
function createDireflowSetup (line 18) | async function createDireflowSetup(preset: ISetupPresets = {}): Promise<...
FILE: cli/helpers/writeNames.ts
type IWriteNameOptions (line 10) | interface IWriteNameOptions {
type TWriteNameExtendable (line 20) | type TWriteNameExtendable = Required<Pick<IWriteNameOptions,
type IHandelbarData (line 27) | interface IHandelbarData extends TWriteNameExtendable {
function writeProjectNames (line 33) | async function writeProjectNames({
function changeNameInfile (line 76) | async function changeNameInfile(filePath: string, data: IHandelbarData):...
FILE: cli/questions.ts
function chooseLanguage (line 5) | async function chooseLanguage(): Promise<ILanguageOption> {
function chooseLinter (line 26) | async function chooseLinter(language: 'js' | 'ts'): Promise<{ linter: 'e...
function isNpmModule (line 53) | async function isNpmModule(): Promise<{ npmModule: boolean }> {
function chooseName (line 74) | async function chooseName(): Promise<IQuestionOption> {
function chooseDescription (line 94) | async function chooseDescription(): Promise<IQuestionOption> {
FILE: cli/types/Command.ts
type ICommand (line 1) | interface ICommand {
FILE: cli/types/LangageOption.ts
type ILanguageOption (line 1) | interface ILanguageOption {
FILE: cli/types/Names.ts
type INames (line 1) | interface INames {
FILE: cli/types/QuestionOption.ts
type IQuestionOption (line 1) | interface IQuestionOption {
FILE: cli/types/TemplateOption.ts
type ITemplateOption (line 1) | interface ITemplateOption {
FILE: packages/direflow-component/src/DireflowComponent.tsx
class DireflowComponent (line 13) | class DireflowComponent {
method createAll (line 18) | public static createAll(componentConfigs: IDireflowComponent[]): Array...
method create (line 26) | public static create(componentConfig: IDireflowComponent): DireflowPro...
FILE: packages/direflow-component/src/WebComponentFactory.tsx
class WebComponentFactory (line 13) | class WebComponentFactory {
method constructor (line 14) | constructor(
method reflectPropertiesToAttributes (line 33) | private reflectPropertiesToAttributes() {
method create (line 49) | public create() {
FILE: packages/direflow-component/src/components/Styled.tsx
type TStyles (line 4) | type TStyles = string | string[] | CSSProperties | CSSProperties[];
type IStyled (line 6) | interface IStyled {
method render (line 34) | public render(): JSX.Element {
FILE: packages/direflow-component/src/decorators/DireflowConfiguration.ts
function DireflowConfiguration (line 3) | function DireflowConfiguration(config: Partial<IDireflowComponent>) {
FILE: packages/direflow-component/src/helpers/asyncScriptLoader.ts
type TBundle (line 1) | type TBundle = { script: Element; hasLoaded: boolean };
type Window (line 3) | interface Window {
FILE: packages/direflow-component/src/helpers/polyfillHandler.ts
type TWcPolyfillsLoaded (line 4) | type TWcPolyfillsLoaded = Array<{ script: Element; hasLoaded: boolean }>;
type Window (line 6) | interface Window {
constant DEFAULT_SD (line 13) | const DEFAULT_SD = 'https://cdn.jsdelivr.net/npm/@webcomponents/webcompo...
constant DEFAULT_CE (line 14) | const DEFAULT_CE = 'https://cdn.jsdelivr.net/npm/@webcomponents/webcompo...
constant DEFAULT_AD (line 15) | const DEFAULT_AD = 'https://cdnjs.cloudflare.com/ajax/libs/webcomponents...
FILE: packages/direflow-component/src/helpers/proxyRoot.tsx
type IPortal (line 4) | interface IPortal {
type IShadowComponent (line 9) | interface IShadowComponent {
type IComponentOptions (line 13) | interface IComponentOptions {
method get (line 48) | get(_: unknown, mode: 'open' | 'closed') {
FILE: packages/direflow-component/src/helpers/styleInjector.tsx
type IProps (line 6) | interface IProps {
class Style (line 10) | class Style extends Component<IProps> {
method render (line 258) | render() {
FILE: packages/direflow-component/src/hooks/useExternalSource.ts
type TSource (line 3) | type TSource = {
type Window (line 11) | interface Window {
FILE: packages/direflow-component/src/plugins/externalLoaderPlugin.ts
type TSource (line 5) | type TSource = {
type Window (line 13) | interface Window {
FILE: packages/direflow-component/src/types/DireflowConfig.ts
type IDireflowComponent (line 1) | interface IDireflowComponent {
type IDireflowConfig (line 8) | interface IDireflowConfig {
type IDireflowPlugin (line 14) | interface IDireflowPlugin {
FILE: packages/direflow-component/src/types/DireflowElement.ts
type DireflowElement (line 1) | type DireflowElement = { [key: string]: unknown } & HTMLElement;
FILE: packages/direflow-component/src/types/DireflowPromiseAlike.ts
type DireflowPromiseAlike (line 3) | type DireflowPromiseAlike = { then: (resolve: (element: HTMLElement) => ...
FILE: packages/direflow-component/src/types/PluginRegistrator.ts
type PluginRegistrator (line 3) | type PluginRegistrator = (
FILE: packages/direflow-scripts/declarations.d.ts
type Window (line 11) | interface Window {
FILE: packages/direflow-scripts/src/cli.ts
type TCommand (line 6) | type TCommand = 'start' | 'test' | 'build' | 'build:lib';
function cli (line 11) | function cli(args: Array<TCommand | string>) {
function spawner (line 32) | function spawner(command: string, args: ReadonlyArray<string>, options?:...
function start (line 40) | function start() {
function test (line 48) | function test(args: string[]) {
function build (line 56) | function build(args: string[]) {
function buildLib (line 64) | function buildLib(args: string[]) {
FILE: packages/direflow-scripts/src/config/config-overrides.ts
function addEntries (line 50) | function addEntries(entry: TEntry, pathIndex: string, env: string, confi...
function overrideModule (line 85) | function overrideModule(module: IModule) {
function overrideOutput (line 106) | function overrideOutput(output: IOptions, config?: IDireflowConfig) {
function overrideOptimization (line 120) | function overrideOptimization(optimization: IOptimization, env: string, ...
function overridePlugins (line 142) | function overridePlugins(plugins: IPlugin[], entry: TEntry, env: string,...
function overrideResolve (line 174) | function overrideResolve(currentResolve: IResolve) {
function overrideExternals (line 188) | function overrideExternals(
function copyBundleScript (line 212) | async function copyBundleScript(env: string, entry: TEntry, config?: IDi...
function hasOptions (line 262) | function hasOptions(flag: string, env: string) {
function setDeprecatedOptions (line 284) | function setDeprecatedOptions(env: string, config?: IDireflowConfig, opt...
FILE: packages/direflow-scripts/src/helpers/asyncScriptLoader.ts
type TBundle (line 1) | type TBundle = { script: Element; hasLoaded: boolean };
type Window (line 3) | interface Window {
FILE: packages/direflow-scripts/src/helpers/entryResolver.ts
constant DEFAULT_REACT (line 6) | const DEFAULT_REACT = 'https://unpkg.com/react@17/umd/react.production.m...
constant DEFAULT_REACT_DOM (line 7) | const DEFAULT_REACT_DOM = 'https://unpkg.com/react-dom@17/umd/react-dom....
function entryResolver (line 9) | function entryResolver(indexPath: string, componentPath: string, { react...
FILE: packages/direflow-scripts/src/helpers/writeTsConfig.ts
function writeTsConfig (line 4) | async function writeTsConfig(srcPath: string) {
FILE: packages/direflow-scripts/src/types/ConfigOverrides.ts
type IOptions (line 1) | interface IOptions {
type IModule (line 8) | interface IModule {
type IOutput (line 17) | interface IOutput {
type IOptimization (line 22) | interface IOptimization {
type IPlugin (line 41) | interface IPlugin {
type IResolve (line 48) | interface IResolve {
type TEntry (line 52) | type TEntry = string[] | { [key: string]: string };
type TConfig (line 54) | type TConfig = {
FILE: packages/direflow-scripts/src/types/DireflowConfig.ts
type IDireflowConfig (line 1) | interface IDireflowConfig {
FILE: scripts/node/buildAll.js
function build (line 19) | function build(dir) {
FILE: scripts/node/cleanupAll.js
function cleanDeps (line 45) | function cleanDeps(dir) {
FILE: scripts/node/installAll.js
function installAll (line 4) | async function installAll() {
function installTestSetup (line 22) | function installTestSetup() {
function install (line 34) | function install(dir) {
FILE: templates/ts/src/direflow-components/direflow-component/App.tsx
type IProps (line 5) | interface IProps {
FILE: templates/ts/src/react-app-env.d.ts
type IntrinsicElements (line 25) | interface IntrinsicElements {
Condensed preview — 144 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (176K chars).
[
{
"path": ".eslintrc",
"chars": 1789,
"preview": "{\n \"env\": {\n \"browser\": true,\n \"es6\": true,\n \"node\": true\n },\n \"settings\": {\n \"pragma\": \"React\",\n \"ver"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 650,
"preview": "---\nname: Bug report\nabout: If you found a bug in Direflow, please file a bug report\ntitle: ''\nlabels: 'Bug'\nassignees: "
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 579,
"preview": "---\nname: Feature request\nabout: Suggest an idea for Direflow\ntitle: ''\nlabels: 'Enhancement'\nassignees: 'Silind'\n\n---\n\n"
},
{
"path": ".github/pull_request_template.md",
"chars": 733,
"preview": "**What does this pull request introduce? Please describe** \nA clear and concise description of what this pull requests "
},
{
"path": ".github/workflows/build.yml",
"chars": 1281,
"preview": "name: build\n\non:\n push:\n branches:\n - master\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - uses: ac"
},
{
"path": ".github/workflows/test.yml",
"chars": 563,
"preview": "name: test\n\non: [pull_request]\n\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v1\n "
},
{
"path": ".gitignore",
"chars": 4808,
"preview": "# Project setup specifics\n\ndist\nbuild\nyarn.lock\nconfig-overrides.js\nconfig-overrides.d.ts\ntsconfig.lib.json\n\n# Cypress\nc"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3222,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.md",
"chars": 2798,
"preview": "# Contributing\n\n## Issues\nIn the case of a bug report, a suggestions, or if you just need help, please feel very free to"
},
{
"path": "LICENSE",
"chars": 1077,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2020 Silind Ltd\n\nPermission is hereby granted, free of charge, to any person obtain"
},
{
"path": "README.md",
"chars": 1588,
"preview": "<span align=\"center\">\n\n [](https://direflow.io/"
},
{
"path": "bin/direflow",
"chars": 106,
"preview": "#!/usr/bin/env node\n\nrequire = require('esm')(module);\nconst cli = require('../dist/cli');\ncli.default();\n"
},
{
"path": "cli/checkForUpdate.ts",
"chars": 517,
"preview": "import chalk from 'chalk';\nimport { execSync } from 'child_process';\nimport { updateAvailable } from './messages';\n\ncons"
},
{
"path": "cli/cli.ts",
"chars": 2071,
"preview": "import { program, Command } from 'commander';\nimport chalk from 'chalk';\nimport headline from './headline';\nimport { cre"
},
{
"path": "cli/create.ts",
"chars": 2179,
"preview": "import fs from 'fs';\nimport chalk from 'chalk';\nimport { chooseName, chooseDescription, chooseLanguage, chooseLinter, is"
},
{
"path": "cli/headline.ts",
"chars": 436,
"preview": "const headline = `\n ██████╗ ██╗██████╗ ███████╗███████╗██╗ ██████╗ ██╗ ██╗\n ██╔══██╗██║██╔══██╗██╔════╝██╔════"
},
{
"path": "cli/helpers/copyTemplate.ts",
"chars": 1430,
"preview": "import { resolve } from 'path';\nimport fs from 'fs';\nimport ncp from 'ncp';\nimport { ITemplateOption } from '../types/Te"
},
{
"path": "cli/helpers/detectDireflowSetup.ts",
"chars": 198,
"preview": "import fs from 'fs';\n\nconst isDireflowSetup = (currentDirectory = process.cwd()): boolean => {\n return fs.existsSync(`$"
},
{
"path": "cli/helpers/nameFormat.ts",
"chars": 416,
"preview": "import to from 'to-case';\nimport { INames } from '../types/Names';\n\nexport const getNameFormats = (name: string): INames"
},
{
"path": "cli/helpers/writeNames.ts",
"chars": 2542,
"preview": "import fs from 'fs';\nimport handelbars from 'handlebars';\nimport path from 'path';\nimport { INames } from '../types/Name"
},
{
"path": "cli/messages.ts",
"chars": 998,
"preview": "import chalk from 'chalk';\nimport boxen from 'boxen';\n\nexport const componentFinishedMessage = (componentName: string) ="
},
{
"path": "cli/questions.ts",
"chars": 2148,
"preview": "import inquirer from 'inquirer';\nimport { IQuestionOption } from './types/QuestionOption';\nimport { ILanguageOption } fr"
},
{
"path": "cli/types/Command.ts",
"chars": 55,
"preview": "export interface ICommand {\n [arg: string]: string;\n}\n"
},
{
"path": "cli/types/LangageOption.ts",
"chars": 62,
"preview": "export interface ILanguageOption {\n language: 'js' | 'ts';\n}\n"
},
{
"path": "cli/types/Names.ts",
"chars": 80,
"preview": "export interface INames {\n title: string;\n pascal: string;\n snake: string;\n}\n"
},
{
"path": "cli/types/QuestionOption.ts",
"chars": 76,
"preview": "export interface IQuestionOption {\n name: string;\n description: string;\n}\n"
},
{
"path": "cli/types/TemplateOption.ts",
"chars": 85,
"preview": "export interface ITemplateOption {\n projectName: string;\n language: 'ts' | 'js';\n}\n"
},
{
"path": "cypress/integration/basic_tests.ts",
"chars": 997,
"preview": "describe('Running basic component', () => {\n before(() => {\n cy.visit('/');\n });\n\n it('should contain a custom ele"
},
{
"path": "cypress/integration/event_tests.ts",
"chars": 741,
"preview": "describe('Delegating events', () => {\n let eventHasFired = false;\n\n const fireEvent = () => {\n eventHasFired = true"
},
{
"path": "cypress/integration/external_loader_test.ts",
"chars": 942,
"preview": "describe('Applying external resources', () => {\n before(() => {\n cy.visit('/');\n });\n\n it('should contain a custom"
},
{
"path": "cypress/integration/material_ui_test.ts",
"chars": 792,
"preview": "describe('Applying external resources', () => {\n before(() => {\n cy.visit('/');\n });\n\n it('should contain a custom"
},
{
"path": "cypress/integration/props_tests.ts",
"chars": 4117,
"preview": "describe('Using properties and attributes', () => {\n before(() => {\n cy.visit('/');\n });\n\n const assertSampleList "
},
{
"path": "cypress/integration/slot_tests.ts",
"chars": 1183,
"preview": "describe('Running basic component without Shadow DOM', () => {\n before(() => {\n cy.visit('/');\n });\n\n it('should c"
},
{
"path": "cypress/integration/styled_components_test.ts",
"chars": 886,
"preview": "describe('Applying external resources', () => {\n before(() => {\n cy.visit('/');\n });\n\n it('should contain a custom"
},
{
"path": "cypress/support/commands.js",
"chars": 29,
"preview": "import 'cypress-shadow-dom';\n"
},
{
"path": "cypress/support/index.js",
"chars": 21,
"preview": "import './commands';\n"
},
{
"path": "cypress/test-setup/direflow-config.json",
"chars": 57,
"preview": "{\n \"build\": {\n \"filename\": \"direflowBundle.js\"\n }\n}\n"
},
{
"path": "cypress/test-setup/direflow-webpack.js",
"chars": 469,
"preview": "const { webpackConfig } = require('direflow-scripts');\nconst { aliasWebpack } = require(\"react-app-alias\");\n\n/**\n * Webp"
},
{
"path": "cypress/test-setup/jsconfig.json",
"chars": 326,
"preview": "{\n \"extends\": \"./jsconfig.paths.json\",\n \"compilerOptions\": {\n \"strict\": true,\n \"target\": \"es5\",\n \"lib\": [\"es5"
},
{
"path": "cypress/test-setup/jsconfig.paths.json",
"chars": 283,
"preview": "{\n \"compilerOptions\": {\n \"baseUrl\": \"src\",\n \"paths\": {\n \"react\": [\"./node_modules/react\"],\n \"react-dom\""
},
{
"path": "cypress/test-setup/package.json",
"chars": 1575,
"preview": "{\n \"name\": \"test-setup\",\n \"description\": \"This project is created using Direflow\",\n \"version\": \"1.0.0\",\n \"private\": "
},
{
"path": "cypress/test-setup/public/index.css",
"chars": 169,
"preview": "body {\n padding: 0;\n margin: 0;\n width: 100vw;\n padding-top: 150px;\n display: flex;\n justify-content: center;\n al"
},
{
"path": "cypress/test-setup/public/index.html",
"chars": 811,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Test Setup</title>\n </head>\n <body>\n <basic-test></basic-test>"
},
{
"path": "cypress/test-setup/public/index_prod.html",
"chars": 859,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>Test Setup</title>\n <script src=\"./direflowBundle.js\"></script>\n"
},
{
"path": "cypress/test-setup/src/component-exports.js",
"chars": 378,
"preview": "/**\n * In this file you can export components that will\n * be build as a pure React component library.\n * \n * Using the "
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/App.css",
"chars": 1297,
"preview": ".app {\n width: 400px;\n height: 575px;\n padding: 30px 60px;\n box-sizing: border-box;\n background-color: white;\n box"
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/App.js",
"chars": 1282,
"preview": "import React from 'react';\nimport { Styled, EventConsumer } from 'direflow-component';\nimport styles from './App.css';\n\n"
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/MaterialUI.js",
"chars": 264,
"preview": "import React from 'react';\nimport Button from '@material-ui/core/Button';\n\nconst MaterialUI = () => {\n return (\n <Bu"
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/StyledComponent.js",
"chars": 313,
"preview": "import React from 'react';\nimport styled from 'styled-components';\n\nconst RedButton = styled.div`\n width: 100px;\n heig"
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/index.js",
"chars": 1427,
"preview": "import { DireflowComponent } from 'direflow-component';\nimport App from './App';\nimport StyledComponent from './StyledCo"
},
{
"path": "cypress/test-setup/src/direflow-components/test-setup/test/App.test.js",
"chars": 570,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport renderer from 'react-test-renderer';\nimport App from"
},
{
"path": "cypress/test-setup/src/index.js",
"chars": 287,
"preview": "/**\n * This is the entry file of the Direflow setup.\n *\n * You can add any additional functionality here.\n * For example"
},
{
"path": "cypress/tsconfig.json",
"chars": 322,
"preview": "{\n \"compilerOptions\": {\n \"strict\": true,\n \"baseUrl\": \"../node_modules\",\n \"target\": \"es5\",\n \"lib\": [\"es5\", \""
},
{
"path": "cypress.json",
"chars": 59,
"preview": "{\n \"baseUrl\": \"http://localhost:5000\",\n \"video\": false\n}\n"
},
{
"path": "declarations.d.ts",
"chars": 26,
"preview": "declare module 'to-case';\n"
},
{
"path": "package.json",
"chars": 2843,
"preview": "{\n \"name\": \"direflow-cli\",\n \"version\": \"4.0.0\",\n \"description\": \"Official CLI for Direflow\",\n \"main\": \"dist/index.js"
},
{
"path": "packages/direflow-component/README.md",
"chars": 288,
"preview": "# direflow-component\n### This package includes configurations and scripts used by [Direflow](https://direflow.io)\n\n:warn"
},
{
"path": "packages/direflow-component/declarations.d.ts",
"chars": 219,
"preview": "declare module 'react-lib-adler32';\n\ndeclare module '*.css' {\n const classes: { readonly [key: string]: string };\n exp"
},
{
"path": "packages/direflow-component/package.json",
"chars": 825,
"preview": "{\n \"name\": \"direflow-component\",\n \"version\": \"4.0.0\",\n \"description\": \"Create Web Components using React\",\n \"main\": "
},
{
"path": "packages/direflow-component/src/DireflowComponent.tsx",
"chars": 2258,
"preview": "import WebComponentFactory from './WebComponentFactory';\nimport { IDireflowComponent } from './types/DireflowConfig';\nim"
},
{
"path": "packages/direflow-component/src/WebComponentFactory.tsx",
"chars": 8013,
"preview": "/* eslint-disable class-methods-use-this */\n/* eslint-disable max-classes-per-file */\nimport React from 'react';\nimport "
},
{
"path": "packages/direflow-component/src/components/EventContext.tsx",
"chars": 257,
"preview": "import { createContext } from 'react';\n\nconst EventContext = createContext<Function>(() => { /* Initially return nothing"
},
{
"path": "packages/direflow-component/src/components/Styled.tsx",
"chars": 1194,
"preview": "import React, { FC, Component, ReactNode, ComponentClass, CSSProperties } from 'react';\nimport Style from '../helpers/st"
},
{
"path": "packages/direflow-component/src/decorators/DireflowConfiguration.ts",
"chars": 543,
"preview": "import { IDireflowComponent } from '../types/DireflowConfig';\n\nfunction DireflowConfiguration(config: Partial<IDireflowC"
},
{
"path": "packages/direflow-component/src/helpers/asyncScriptLoader.ts",
"chars": 1234,
"preview": "type TBundle = { script: Element; hasLoaded: boolean };\ndeclare global {\n interface Window {\n wcPolyfillsLoaded: TBu"
},
{
"path": "packages/direflow-component/src/helpers/domControllers.ts",
"chars": 929,
"preview": "export const injectIntoShadowRoot = (webComponent: HTMLElement, element: Element): void => {\n const elementToPrepend = "
},
{
"path": "packages/direflow-component/src/helpers/getSerialized.ts",
"chars": 321,
"preview": "const getSerialized = (data: string) => {\n if (data === '') {\n return true;\n }\n\n if (data === 'true' || data === '"
},
{
"path": "packages/direflow-component/src/helpers/polyfillHandler.ts",
"chars": 2436,
"preview": "import { IDireflowPlugin } from '../types/DireflowConfig';\nimport asyncScriptLoader from './asyncScriptLoader';\n\ntype TW"
},
{
"path": "packages/direflow-component/src/helpers/proxyRoot.tsx",
"chars": 1647,
"preview": "import React, { FC } from 'react';\nimport { createPortal } from 'react-dom';\n\ninterface IPortal {\n targetElement: Shado"
},
{
"path": "packages/direflow-component/src/helpers/registerPlugin.ts",
"chars": 367,
"preview": "import { IDireflowPlugin } from '../types/DireflowConfig';\nimport { PluginRegistrator } from '../types/PluginRegistrator"
},
{
"path": "packages/direflow-component/src/helpers/styleInjector.tsx",
"chars": 9190,
"preview": "import React, { Component, cloneElement, isValidElement } from 'react';\nimport adler32 from 'react-lib-adler32';\n\nconst "
},
{
"path": "packages/direflow-component/src/hooks/useExternalSource.ts",
"chars": 758,
"preview": "import { useState, useEffect } from 'react';\n\ntype TSource = {\n [key: string]: {\n state: 'loading' | 'completed';\n "
},
{
"path": "packages/direflow-component/src/index.ts",
"chars": 404,
"preview": "import DireflowComponent from './DireflowComponent';\nimport DireflowConfiguration from './decorators/DireflowConfigurati"
},
{
"path": "packages/direflow-component/src/plugins/externalLoaderPlugin.ts",
"chars": 2923,
"preview": "import { injectIntoHead } from '../helpers/domControllers';\nimport { IDireflowPlugin } from '../types/DireflowConfig';\ni"
},
{
"path": "packages/direflow-component/src/plugins/fontLoaderPlugin.ts",
"chars": 531,
"preview": "import WebFont from 'webfontloader';\nimport { IDireflowPlugin } from '../types/DireflowConfig';\nimport { PluginRegistrat"
},
{
"path": "packages/direflow-component/src/plugins/iconLoaderPlugin.ts",
"chars": 798,
"preview": "import { IDireflowPlugin } from '../types/DireflowConfig';\nimport { PluginRegistrator } from '../types/PluginRegistrator"
},
{
"path": "packages/direflow-component/src/plugins/materialUiPlugin.tsx",
"chars": 1427,
"preview": "import React from 'react';\nimport uniqueid from 'lodash';\nimport { IDireflowPlugin } from '../types/DireflowConfig';\nimp"
},
{
"path": "packages/direflow-component/src/plugins/plugins.ts",
"chars": 567,
"preview": "import registerPlugin from '../helpers/registerPlugin';\nimport styledComponentsPlugin from './styledComponentsPlugin';\ni"
},
{
"path": "packages/direflow-component/src/plugins/styledComponentsPlugin.tsx",
"chars": 869,
"preview": "import React from 'react';\nimport { IDireflowPlugin } from '../types/DireflowConfig';\nimport { PluginRegistrator } from "
},
{
"path": "packages/direflow-component/src/types/DireflowConfig.ts",
"chars": 394,
"preview": "export interface IDireflowComponent {\n component: (React.FC<any> | React.ComponentClass<any, any>) & { [key: string]: a"
},
{
"path": "packages/direflow-component/src/types/DireflowElement.ts",
"chars": 72,
"preview": "export type DireflowElement = { [key: string]: unknown } & HTMLElement;\n"
},
{
"path": "packages/direflow-component/src/types/DireflowPromiseAlike.ts",
"chars": 155,
"preview": "import { type } from 'os';\n\ntype DireflowPromiseAlike = { then: (resolve: (element: HTMLElement) => void) => void };\n\nex"
},
{
"path": "packages/direflow-component/src/types/PluginRegistrator.ts",
"chars": 211,
"preview": "import { IDireflowPlugin } from './DireflowConfig';\n\nexport type PluginRegistrator = (\n element: HTMLElement,\n plugins"
},
{
"path": "packages/direflow-component/tsconfig.json",
"chars": 435,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es2015\",\n \"module\": \"esNext\",\n \"lib\": [\n \"es2017\",\n \"es7\",\n "
},
{
"path": "packages/direflow-scripts/README.md",
"chars": 286,
"preview": "# direflow-scripts\n### This package includes configurations and scripts used by [Direflow](https://direflow.io)\n\n:warnin"
},
{
"path": "packages/direflow-scripts/bin/direflow-scripts",
"chars": 145,
"preview": "#!/usr/bin/env node\n\nrequire = require('esm')(module);\nconst cli = require('../dist/cli');\nconst [,, ...args] = process."
},
{
"path": "packages/direflow-scripts/declarations.d.ts",
"chars": 299,
"preview": "declare module 'config-overrides';\n\ndeclare module 'event-hooks-webpack-plugin';\n\ndeclare module 'html-webpack-externals"
},
{
"path": "packages/direflow-scripts/direflow-jest.config.js",
"chars": 227,
"preview": "const { createContext } = require('react');\n\n// eslint-disable-next-line no-undef\njest.mock('../direflow-component', () "
},
{
"path": "packages/direflow-scripts/package.json",
"chars": 1111,
"preview": "{\n \"name\": \"direflow-scripts\",\n \"version\": \"4.0.0\",\n \"description\": \"Create Web Components using React\",\n \"main\": \"d"
},
{
"path": "packages/direflow-scripts/src/cli.ts",
"chars": 2179,
"preview": "import chalk from 'chalk';\nimport { ChildProcess, spawn } from 'child_process';\nimport { resolve } from 'path';\nimport {"
},
{
"path": "packages/direflow-scripts/src/config/config-overrides.ts",
"chars": 8703,
"preview": "import EventHooksPlugin from 'event-hooks-webpack-plugin';\nimport { EnvironmentPlugin } from 'webpack';\nimport rimraf fr"
},
{
"path": "packages/direflow-scripts/src/helpers/asyncScriptLoader.ts",
"chars": 1234,
"preview": "type TBundle = { script: Element; hasLoaded: boolean };\ndeclare global {\n interface Window {\n wcPolyfillsLoaded: TBu"
},
{
"path": "packages/direflow-scripts/src/helpers/entryResolver.ts",
"chars": 2153,
"preview": "import fs from 'fs';\nimport { resolve, join, sep } from 'path';\nimport handlebars from 'handlebars';\nimport { IOptions }"
},
{
"path": "packages/direflow-scripts/src/helpers/getDireflowConfig.ts",
"chars": 534,
"preview": "import fs from 'fs';\nimport { sep } from 'path';\nimport IDireflowConfig from '../types/DireflowConfig';\n\nconst getDirefl"
},
{
"path": "packages/direflow-scripts/src/helpers/messages.ts",
"chars": 942,
"preview": "import chalk from 'chalk';\n\nexport const interupted = () => `\n ${chalk.red('Build got interrupted.')}\n Did you remove "
},
{
"path": "packages/direflow-scripts/src/helpers/writeTsConfig.ts",
"chars": 592,
"preview": "import { existsSync, writeFileSync } from 'fs';\nimport { resolve } from 'path';\n\nasync function writeTsConfig(srcPath: s"
},
{
"path": "packages/direflow-scripts/src/index.ts",
"chars": 82,
"preview": "import webpackConfig from './config/config-overrides';\n\nexport { webpackConfig };\n"
},
{
"path": "packages/direflow-scripts/src/template-scripts/entryLoader.ts",
"chars": 1397,
"preview": "const asyncScriptLoader = require('./helpers/asyncScriptLoader.js').default;\n\nconst reactResource: any = '{{reactResourc"
},
{
"path": "packages/direflow-scripts/src/template-scripts/welcome.ts",
"chars": 527,
"preview": "console.log(\n `\n%cDireflow Components running in development.\n\n%cTo build your components\n%c- Run 'npm run build'\n%c- Y"
},
{
"path": "packages/direflow-scripts/src/types/ConfigOverrides.ts",
"chars": 1054,
"preview": "export interface IOptions {\n filename?: string;\n chunkFilename?: string;\n react?: string | boolean;\n reactDOM?: stri"
},
{
"path": "packages/direflow-scripts/src/types/DireflowConfig.ts",
"chars": 433,
"preview": "export default interface IDireflowConfig {\n build?: {\n componentPath?: string;\n filename?: string;\n chunkFilen"
},
{
"path": "packages/direflow-scripts/tsconfig.json",
"chars": 391,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"module\": \"commonjs\",\n \"lib\": [\"es2019\", \"es2017\", \"es7\", \"es6\", \"d"
},
{
"path": "packages/direflow-scripts/webpack.config.js",
"chars": 3157,
"preview": "/* eslint-disable @typescript-eslint/camelcase */\n/* eslint-disable @typescript-eslint/no-var-requires */\nconst { resolv"
},
{
"path": "scripts/bash/setupLocal.sh",
"chars": 83,
"preview": "npm -g remove direflow-cli\nnpm run update-version link\nnpm link\nnpm run build:full\n"
},
{
"path": "scripts/bash/startIntegrationTest.sh",
"chars": 464,
"preview": "# Install\ninstall() {\n node ./scripts/node/installAll.js --test-setup\n\n cd cypress/test-setup\n}\n\n# Build\nbuild() {\n n"
},
{
"path": "scripts/node/buildAll.js",
"chars": 656,
"preview": "const fs = require('fs');\nconst { exec } = require('child_process');\n\nbuild('.');\n\nif (!fs.existsSync('packages')) {\n r"
},
{
"path": "scripts/node/cleanupAll.js",
"chars": 2214,
"preview": "const fs = require('fs');\nconst { exec } = require('child_process');\n\nif (process.argv[2] === '--modules') {\n if (fs.ex"
},
{
"path": "scripts/node/installAll.js",
"chars": 1885,
"preview": "const fs = require('fs');\nconst { exec, execSync } = require('child_process');\n\nasync function installAll() {\n await in"
},
{
"path": "scripts/node/updateVersion.js",
"chars": 3588,
"preview": "const fs = require('fs');\nconst { execSync } = require('child_process');\nconst path = require('path');\n\nconst arg = proc"
},
{
"path": "templates/js/.eslintrc",
"chars": 508,
"preview": "{\n \"env\": {\n \"browser\": true,\n \"es6\": true,\n \"node\": true\n },\n \"settings\": {\n \"pragma\": \"React\",\n"
},
{
"path": "templates/js/README.md",
"chars": 215,
"preview": "This component was bootstrapped with [Direflow](https://direflow.io).\n\n# {{names.title}}\n> {{defaultDescription}}\n\n```ht"
},
{
"path": "templates/js/direflow-config.json",
"chars": 101,
"preview": "{\n \"build\": {\n \"componentPath\": \"direflow-components\",\n \"filename\": \"direflowBundle.js\"\n }\n}\n"
},
{
"path": "templates/js/direflow-webpack.js",
"chars": 298,
"preview": "const { webpackConfig } = require('direflow-scripts');\n\n/**\n * Webpack configuration for Direflow Component\n * Additiona"
},
{
"path": "templates/js/package.json",
"chars": 1696,
"preview": "{\n \"name\": \"{{names.snake}}\",\n \"description\": \"{{defaultDescription}}\",\n \"version\": \"1.0.0\",\n {{#if npmModule}}\n \"a"
},
{
"path": "templates/js/public/index.css",
"chars": 169,
"preview": "body {\n padding: 0;\n margin: 0;\n width: 100vw;\n padding-top: 150px;\n display: flex;\n justify-content: center;\n al"
},
{
"path": "templates/js/public/index.html",
"chars": 374,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-wid"
},
{
"path": "templates/js/src/component-exports.js",
"chars": 383,
"preview": "/**\n * In this file you can export components that will\n * be build as a pure React component library.\n * \n * Using the "
},
{
"path": "templates/js/src/direflow-components/direflow-component/App.css",
"chars": 1297,
"preview": ".app {\n width: 400px;\n height: 575px;\n padding: 30px 60px;\n box-sizing: border-box;\n background-color: white;\n box"
},
{
"path": "templates/js/src/direflow-components/direflow-component/App.js",
"chars": 1205,
"preview": "import React, { useContext } from 'react';\nimport PropTypes from 'prop-types';\nimport { EventContext, Styled } from 'dir"
},
{
"path": "templates/js/src/direflow-components/direflow-component/index.js",
"chars": 367,
"preview": "import { DireflowComponent } from 'direflow-component';\nimport App from './App';\n\nexport default DireflowComponent.creat"
},
{
"path": "templates/js/src/direflow-components/direflow-component/test/App.test.js",
"chars": 570,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport renderer from 'react-test-renderer';\nimport App from"
},
{
"path": "templates/js/src/index.js",
"chars": 517,
"preview": "/**\n * This is the entry file of the Direflow setup.\n *\n * You can add any additional functionality here.\n * For example"
},
{
"path": "templates/ts/.eslintrc",
"chars": 862,
"preview": "{\n \"env\": {\n \"browser\": true,\n \"es6\": true,\n \"node\": true\n },\n \"settings\": {\n \"pragma\": \"React\",\n \"ver"
},
{
"path": "templates/ts/README.md",
"chars": 215,
"preview": "This component was bootstrapped with [Direflow](https://direflow.io).\n\n# {{names.title}}\n> {{defaultDescription}}\n\n```ht"
},
{
"path": "templates/ts/direflow-config.json",
"chars": 101,
"preview": "{\n \"build\": {\n \"componentPath\": \"direflow-components\",\n \"filename\": \"direflowBundle.js\"\n }\n}\n"
},
{
"path": "templates/ts/direflow-webpack.js",
"chars": 298,
"preview": "const { webpackConfig } = require('direflow-scripts');\n\n/**\n * Webpack configuration for Direflow Component\n * Additiona"
},
{
"path": "templates/ts/package.json",
"chars": 2188,
"preview": "{\n \"name\": \"{{names.snake}}\",\n \"description\": \"{{defaultDescription}}\",\n \"version\": \"1.0.0\",\n {{#if npmModule}}\n \"a"
},
{
"path": "templates/ts/public/index.css",
"chars": 169,
"preview": "body {\n padding: 0;\n margin: 0;\n width: 100vw;\n padding-top: 150px;\n display: flex;\n justify-content: center;\n al"
},
{
"path": "templates/ts/public/index.html",
"chars": 374,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-wid"
},
{
"path": "templates/ts/src/component-exports.ts",
"chars": 383,
"preview": "/**\n * In this file you can export components that will\n * be built as a pure React component library.\n * \n * Using the "
},
{
"path": "templates/ts/src/direflow-components/direflow-component/App.css",
"chars": 1297,
"preview": ".app {\n width: 400px;\n height: 575px;\n padding: 30px 60px;\n box-sizing: border-box;\n background-color: white;\n box"
},
{
"path": "templates/ts/src/direflow-components/direflow-component/App.tsx",
"chars": 1176,
"preview": "import React, { FC, useContext } from 'react';\nimport { EventContext, Styled } from 'direflow-component';\nimport styles "
},
{
"path": "templates/ts/src/direflow-components/direflow-component/index.tsx",
"chars": 367,
"preview": "import { DireflowComponent } from 'direflow-component';\nimport App from './App';\n\nexport default DireflowComponent.creat"
},
{
"path": "templates/ts/src/direflow-components/direflow-component/test/App.test.tsx",
"chars": 570,
"preview": "import React from 'react';\nimport ReactDOM from 'react-dom';\nimport renderer from 'react-test-renderer';\nimport App from"
},
{
"path": "templates/ts/src/index.tsx",
"chars": 517,
"preview": "/**\n * This is the entry file of the Direflow setup.\n *\n * You can add any additional functionality here.\n * For example"
},
{
"path": "templates/ts/src/react-app-env.d.ts",
"chars": 494,
"preview": "/// <reference types=\"react-scripts\" />\n\ndeclare module '*.css' {\n export default style as string\n}\n\ndeclare module '*."
},
{
"path": "templates/ts/tsconfig.json",
"chars": 542,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\n \"dom\",\n \"dom.iterable\",\n \"esnext\"\n ],\n "
},
{
"path": "templates/ts/tslint.json",
"chars": 532,
"preview": "{\n \"rules\": {\n \"max-line-length\": { \"options\": [120], \"severity\": \"warning\" },\n \"no-lowlevel-commenting\": { \"seve"
},
{
"path": "test/detectDireflowSetup.test.ts",
"chars": 759,
"preview": "import { fs, vol } from 'memfs';\nimport isDireflowSetup from '../cli/helpers/detectDireflowSetup';\n\njest.mock('fs', () ="
},
{
"path": "test/domController.test.ts",
"chars": 3992,
"preview": "import { JSDOM } from 'jsdom';\nimport {\n injectIntoShadowRoot,\n injectIntoHead,\n stripStyleFromHead,\n existsIdentica"
},
{
"path": "test/nameformats.test.ts",
"chars": 1413,
"preview": "import { getNameFormats, createDefaultName } from '../cli/helpers/nameFormat';\n\ndescribe('Get correct name formats', () "
},
{
"path": "test/writeNames.test.ts",
"chars": 3970,
"preview": "import { promisify } from 'util';\nimport { fs, vol } from 'memfs';\nimport writeProjectNames from '../cli/helpers/writeNa"
},
{
"path": "tsconfig.eslint.json",
"chars": 87,
"preview": "{\n \"extends\": \"./tsconfig.json\",\n \"exclude\": [\"node_modules\", \"dist\", \"templates\"]\n}\n"
},
{
"path": "tsconfig.json",
"chars": 410,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"module\": \"commonjs\",\n \"lib\": [\"es2017\", \"es7\", \"es6\", \"dom\"],\n "
}
]
About this extraction
This page contains the full source code of the Silind-Software/direflow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 144 files (154.1 KB), approximately 44.5k tokens, and a symbol index with 96 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.