init commit

This commit is contained in:
Thomas Osmonson
2019-09-03 16:47:10 -05:00
parent f3003f6990
commit 17d80339e8
44 changed files with 7898 additions and 0 deletions

675
packages/app/.gitignore vendored Executable file
View File

@@ -0,0 +1,675 @@
### Project specifics ###
dist/
node_modules/
# Created by https://www.gitignore.io/api/node,macos,linux,eclipse,windows,intellij,visualstudio,microsoftoffice,visualstudiocode
# Edit at https://www.gitignore.io/?templates=node,macos,linux,eclipse,windows,intellij,visualstudio,microsoftoffice,visualstudiocode
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# CDT- autotools
.autotools
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Annotation Processing
.apt_generated/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Eclipse Patch ###
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Annotation Processing
.apt_generated
.sts4-cache/
### Intellij ###
# 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
.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
# 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
# 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
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# 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
### MicrosoftOffice ###
# Word temporary
~$*.doc*
# Word Auto Backup File
Backup of *.doc*
# Excel temporary
~$*.xls*
# Excel Backup File
*.xlk
# PowerPoint temporary
~$*.ppt*
# Visio autosave temporary files
*.~vsd*
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like jest
coverage
# 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/
# 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
# 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/
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
### Windows ###
# Windows thumbnail cache files
Thumbs.db
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
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp_proj
*_wpftmp.csproj
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
**/wwwroot/lib/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# End of https://www.gitignore.io/api/node,macos,linux,eclipse,windows,intellij,visualstudio,microsoftoffice,visualstudiocode

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings" wasCopied="true">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="1" emojiImages="0">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="HTML_DEEP_PARSER" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="false" />
</PegdownExtensions>
<ParserOptions>
<option name="ADMONITION_EXT" value="false" />
<option name="ATTRIBUTES_EXT" value="false" />
<option name="COMMONMARK_LISTS" value="true" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_LISTS" value="false" />
<option name="GITHUB_WIKI_LINKS" value="false" />
<option name="GITLAB_EXT" value="false" />
<option name="GITLAB_MATH_EXT" value="false" />
<option name="GITLAB_MERMAID_EXT" value="false" />
<option name="HEADER_ID_NON_ASCII_TO_LOWERCASE" value="false" />
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="MACROS_EXT" value="false" />
<option name="NO_TEXT_ATTRIBUTES" value="false" />
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false" addDocTypeHtml="true" noParaTags="false">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
<cssUriHistory />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="" targetDir="" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetPathType="2" targetExt="" useTargetExt="false" noCssNoScripts="false" useElementStyleAttribute="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

6
packages/app/.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

6
packages/app/.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

21
packages/app/LICENSE Executable file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 ymdevs
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.

51
packages/app/README.md Executable file
View File

@@ -0,0 +1,51 @@
<div align="center">
# Web Extension Starter
</div>
This project containes a minimal setup for developing a performant and well structured web extension. The extension contains a demonstration counter that is displayed on the popup and options pages as well as any open tab.
## Good to know before using:
* [Typescript](https://www.typescriptlang.org/)
* [React](https://reactjs.org/)
* [redux](https://redux.js.org/)
* [styled-components](https://www.styled-components.com/)
## Requirements:
* [NodeJS](https://nodejs.org/en/) - Javascript runtime
* [VSCode](https://code.visualstudio.com/) - Recomended editor
* [Chrome](https://www.google.com/chrome/) or [Firefox](https://www.mozilla.org/en-US/firefox/) - Web browser
## How to run:
### In terminal or command prompt
```
install dependencies
- npm install
Transpile the code
- npm run dev (only transpiles the code)
- npm run watch (transpiles and watches for code changes)
yarn commands will work too if yarn is installed.
```
### In Chrome web browser
1. Go to: [**chrome://extensions**](chrome://extensions)
2. Toggle: "**developer mode**" on.
3. Click on: "**Load unpacked**"
4. Select the newly created folder "**dist**" from the project folder.
5. Thats it.
### In Firefox web browser
1. Go to: [**about:debugging**](about:debugging)
2. Select: "**Enable add-on debugging**"
3. Click on: "**Load Temporary Add-on…**"
4. Open the newly created folder "**dist**" from the project folder, and choose the "**manifest.json**" file.
5. Thats it.
## License
MIT

186
packages/app/jest.config.js Executable file
View File

@@ -0,0 +1,186 @@
// For a detailed explanation regarding each configuration property, visit:
// https://jestjs.io/docs/en/configuration.html
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// Respect "browser" field in package.json when resolving modules
// browser: false,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\Boobalay\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
// clearMocks: false,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
// The directory where Jest should output its coverage files
// coverageDirectory: null,
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
// A path to a custom dependency extractor
// dependencyExtractor: null,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files usin a array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: null,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: null,
// A set of global variables that need to be available in all test environments
// globals: {},
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
moduleFileExtensions: [
"js",
"json",
"jsx",
"ts",
"tsx",
"node"
],
// A map from regular expressions to module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: null,
// Run tests from one or more projects
// projects: null,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: null,
// Automatically restore mock state between every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: null,
// A list of paths to directories that Jest should use to search for files in
roots: [
"<rootDir>/tests",
"<rootDir>/src"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "jsdom",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
// testMatch: [
// "**/tests/**/*.[jt]s?(x)",
// "**/?(*.)+(spec|test).[tj]s?(x)"
// ],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
testRegex: "/tests/.*\.test\.tsx?$",
// This option allows the use of a custom results processor
// testResultsProcessor: null,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
transform: { "^.+\\.tsx?$": "ts-jest" },
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: null,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};

53
packages/app/package.json Executable file
View File

@@ -0,0 +1,53 @@
{
"name": "web-extension-starter",
"version": "1.0.0",
"description": "A web browser extension starter - based on Typescript, React, Redux, Styled-Component, Webpack, and more. Runs on Chrome and Firefox.",
"main": "index.js",
"scripts": {
"dev": "cross-env NODE_ENV=development webpack -d",
"prod": "cross-env NODE_ENV=production webpack -p",
"watch": "cross-env NODE_ENV=watch webpack -d",
"clean": "rm -rf ./dist",
"clean:all": "rm -rf ./dist && rm -rf ./coverage && rm -rf ./node_modules",
"test": "jest",
"test:coverage": "jest --collect-coverage"
},
"license": "MIT",
"dependencies": {
"react": "^16.8.6",
"react-chrome-redux": "^2.0.0-alpha.5",
"react-dom": "^16.8.6",
"react-redux": "^7.1.0",
"redux": "4.0.1",
"styled-components": "^4.3.2"
},
"devDependencies": {
"@types/chrome": "^0.0.86",
"@types/jest": "^24.0.15",
"@types/node": "^12.6.2",
"@types/react": "^16.8.23",
"@types/react-dom": "^16.8.4",
"@types/react-redux": "^7.1.1",
"@types/react-test-renderer": "^16.8.2",
"@types/redux": "^3.6.0",
"@types/styled-components": "4.1.8",
"@types/webpack": "^4.4.35",
"awesome-typescript-loader": "^5.2.1",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^5.0.3",
"cross-env": "^5.2.0",
"html-webpack-plugin": "^3.2.0",
"jest": "^24.8.0",
"react-test-renderer": "^16.8.6",
"ts-jest": "^24.0.2",
"tslint": "^5.18.0",
"typescript": "^3.5.3",
"webpack": "^4.35.3",
"webpack-chrome-extension-reloader": "^1.3.0",
"webpack-cli": "^3.3.5"
},
"repository": {
"type": "git",
"url": "git://github.com/ymdevs/Web-Extension-Starter.git"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@@ -0,0 +1,12 @@
<!DOCTYPE svg >
<svg id="icon" width="500px" height="500px" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<g id="logo">
<circle fill="rgb(190, 190, 190)" cx="50" cy="50" r="50" />
<circle fill="rgb(135, 153, 217)" cx="47" cy="50" r="44" />
<path fill="rgb(240, 240, 240)"
d=" M 81, 45
A 50, 50 0 0,0 58, 17
A 30, 30 0 0,1 42, 18
A 50, 50 0 0,1 81, 45" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 423 B

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="options-root"></div>
</body>
</html>

View File

@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="popup-root"></div>
</body>
</html>

43
packages/app/src/manifest.json Executable file
View File

@@ -0,0 +1,43 @@
{
"name": "Web Extension Starter",
"author": "ymdevs",
"version": "1.0.0",
"description": "A web browser extension starter - based on Typescript, React, Redux, Styled-Component, Webpack, and more. Runs on Chrome and Firefox.",
"icons": {
"128": "assets/icon-128.png"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"permissions": [
"activeTab",
"alarms",
"bookmarks",
"cookies",
"storage",
"tabs",
"webRequest",
"webRequestBlocking",
"*://*/*"
],
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Web Extension Starter",
"default_icon": "assets/icon-48.png",
"default_popup": "popup.html"
},
"options_ui": {
"page": "options.html",
"open_in_tab": true
},
"content_scripts": [
{
"js": ["counter.js"],
"matches": ["*://*/*"]
}
]
}

View File

@@ -0,0 +1,14 @@
import { Store } from 'redux';
import { IAppState, saveState } from './store';
const autoSaveAppState = (store: Store<IAppState>) => {
chrome.tabs.onRemoved.addListener(() => saveState(store.getState()));
chrome.windows.onRemoved.addListener(() => saveState(store.getState()));
const saveFrequency = 30000; // 30seconds * 1000milliseconds / 1second
setInterval(() => (saveState(store.getState())), saveFrequency);
};
export const configureApp = (store: Store<IAppState>) => {
autoSaveAppState(store);
};

View File

@@ -0,0 +1,13 @@
import { Store, wrapStore } from 'react-chrome-redux';
import { createStore } from 'redux';
import { configureApp } from './AppConfig';
import reducers, { IAppState, loadState } from './store';
const preloadedState = loadState();
const store: Store<IAppState> = createStore(reducers, preloadedState);
configureApp(store);
wrapStore(store, {
portName: 'ExPort' // Communication port between the background component and views such as browser tabs.
});

View File

@@ -0,0 +1,9 @@
import { Action } from 'redux';
export type CounterActionTypes = 'INCREMENT' | 'DECREMENT';
export type CounterPayload = number;
export type CounterActions = Action<CounterActionTypes, CounterPayload>;
export const increment = (payload: CounterPayload = 1) => ({ type: 'INCREMENT', payload });
export const decrement = (payload: CounterPayload = 1) => ({ type: 'DECREMENT', payload });

View File

@@ -0,0 +1,2 @@
export * from './actions';
export * from './reducer';

View File

@@ -0,0 +1,26 @@
import { Reducer} from 'redux';
import { CounterActions } from './actions';
export interface ICounter {
clicksMade: number;
}
const initialState: ICounter = {
clicksMade: 0
};
const counter: Reducer<ICounter, CounterActions> = (state = initialState, action) => {
const { payload } = action;
switch (action.type) {
case 'INCREMENT':
return { ...state, clicksMade: state.clicksMade + (payload || 1) };
case 'DECREMENT':
return { ...state, clicksMade: state.clicksMade - (payload || 1) };
default:
return state;
}
};
export default counter;

View File

@@ -0,0 +1,50 @@
import { combineReducers } from 'redux';
import counter, { ICounter } from './counter/reducer';
import settings, { IAppSettings } from './settings/reducer';
import 'redux';
// Enhance the Action interface with the option of a payload.
// While still importing the Action interface from redux.
declare module 'redux' {
export interface Action<T = any, P = any> {
type: T;
payload?: P;
}
}
type OnSuccess = () => void;
type OnError = (e: Error) => void;
export interface IAppState {
counter: ICounter;
settings: IAppSettings;
}
export const loadState = (): IAppState | undefined => {
try {
const serializedState = localStorage.getItem('appstate');
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
export const saveState = (appstate: IAppState, success: OnSuccess = () => {}, error: OnError = () => {}) => {
try {
const serializedState = JSON.stringify(appstate);
localStorage.setItem('appstate', serializedState);
success();
} catch (e) {
error(e);
}
};
const reducers = combineReducers<IAppState>({
counter,
settings
});
export default reducers;

View File

@@ -0,0 +1,8 @@
import { Action } from 'redux';
export type ThemeActionTypes = 'DARK_THEME' | 'LIGHT_THEME';
export type SettingsActions = Action<ThemeActionTypes>;
export const setDarkTheme = () => ({ type: 'DARK_THEME' });
export const setLightTheme = () => ({ type: 'LIGHT_THEME' });

View File

@@ -0,0 +1,2 @@
export * from './actions';
export * from './reducer';

View File

@@ -0,0 +1,26 @@
import { Reducer } from 'redux';
import { ThemeTypes } from './../../../components/styles/themes';
import { SettingsActions } from './actions';
export interface IAppSettings {
theme: ThemeTypes;
}
const initialState: IAppSettings = {
theme: 'light'
};
const settings: Reducer<IAppSettings, SettingsActions> = (state = initialState, action) => {
switch (action.type) {
case 'DARK_THEME':
return { ...state, theme: 'dark' };
case 'LIGHT_THEME':
return { ...state, theme: 'light' };
default:
return state;
}
};
export default settings;

View File

@@ -0,0 +1,11 @@
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
body {
box-sizing: border-box;
display: block;
margin: 0;
}
`;
export default GlobalStyle;

View File

@@ -0,0 +1,8 @@
import 'styled-components';
// Enhance the DefaultTheme interface with new attributes.
// While still importing the DefaultTheme interface from styled-components.
declare module 'styled-components' {
export interface DefaultTheme {
backgroundColor: string;
}
}

View File

@@ -0,0 +1,16 @@
import { DefaultTheme } from 'styled-components';
export type ThemeTypes = 'light' | 'dark';
export const lightTheme: DefaultTheme = {
backgroundColor: 'lightblue'
};
export const darkTheme: DefaultTheme = {
backgroundColor: '#181818'
};
export const themes = {
light: lightTheme,
dark: darkTheme,
};

View File

@@ -0,0 +1,90 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import styled from 'styled-components';
import { IAppState } from '../background/store';
import { decrement, increment } from '../background/store/counter/actions';
import { ICounter } from '../background/store/counter/reducer';
interface ICounterProps {
counter: ICounter;
dispatch: Dispatch;
}
class Counter extends React.Component<ICounterProps> {
increment = () => {
this.props.dispatch(increment());
}
decrement = () => {
this.props.dispatch(decrement());
}
render() {
return (
<CounterContainer >
<Display>
{this.props.counter.clicksMade}
</Display>
<Controls>
<Button onClick={this.increment}>+</Button>
<Button onClick={this.decrement}>-</Button>
</Controls>
</CounterContainer>
);
}
}
const mapStateToProps = (state: IAppState) => {
return {
counter: state.counter,
};
};
export default connect(mapStateToProps)(Counter);
const CounterContainer = styled('div')`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
min-width: 100px;
padding: 5px;
margin: 5px;
background-color: ${p => p.theme.backgroundColor};
`;
const Display = styled('div')`
font-size: 48px;
justify-self: center;
`;
const Controls = styled('div')`
display: flex;
flex-direction: row;
justify-content: space-around;
min-width: 200px;
`;
// Thanks to: https://codepen.io/FelipeMarcos/pen/tfhEg?editors=1100
const Button = styled('button')`
display: inline-block;
position: relative;
padding: 10px 30px;
border: 1px solid transparent;
border-bottom: 4px solid rgba(0,0,0,0.21);
border-radius: 4px;
background: linear-gradient(rgba(27,188,194,1) 0%, rgba(24,163,168,1) 100%);
color: white;
font-size: 22px;
text-shadow: 0 1px 0 rgba(0,0,0,0.15);
text-decoration: none;
cursor: pointer;
outline: none;
user-select: none;
&:active {
background: #169499;
}
`;

View File

@@ -0,0 +1,44 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import styled, { ThemeProvider } from 'styled-components';
import { IAppState } from '../../../background/store';
import { themes, ThemeTypes } from '../../../components/styles/themes';
import Counter from '../../../containers/Counter';
interface ICounterApp {
theme: ThemeTypes;
dispatch: Dispatch;
}
class CounterApp extends React.Component<ICounterApp> {
render() {
return (
<ThemeProvider theme={themes[this.props.theme]}>
<React.Fragment>
<CounterAppContainer >
<Counter />
</CounterAppContainer>
</React.Fragment>
</ThemeProvider>
);
}
}
const mapStateToProps = (state: IAppState) => {
return {
theme: state.settings.theme
};
};
export default connect(mapStateToProps)(CounterApp);
const CounterAppContainer = styled('div')`
position: fixed;
z-index: 9;
bottom: 0;
right: 0;
background-color: ${p => p.theme.backgroundColor};
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
`;

View File

@@ -0,0 +1,21 @@
import * as React from 'react';
import { Store } from 'react-chrome-redux';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import CounterApp from './containers/CounterApp';
import { createDomAnchor } from '../../scripts/dom';
createDomAnchor('counter-root');
const store = new Store({
portName: 'ExPort' // Communication port between the background component and views such as browser tabs.
});
store.ready().then(() => {
ReactDOM.render(
<Provider store={store}>
<CounterApp />
</Provider>
, document.getElementById('counter-root'));
});

View File

@@ -0,0 +1,52 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import styled, { ThemeProvider } from 'styled-components';
import { IAppState } from '../../background/store';
import GlobalStyle from '../../components/styles/GlobalStyle';
import { themes, ThemeTypes } from '../../components/styles/themes';
import Counter from '../../containers/Counter';
interface IOptionsApp {
theme: ThemeTypes;
dispatch: Dispatch;
}
class OptionsApp extends React.Component<IOptionsApp> {
render() {
return (
<ThemeProvider theme={themes[this.props.theme]}>
<React.Fragment>
<GlobalStyle />
<OptionsAppContainer>
<Counter/>
</OptionsAppContainer>
</React.Fragment>
</ThemeProvider>
);
}
}
const mapStateToProps = (state: IAppState) => {
return {
theme: state.settings.theme
};
};
export default connect(mapStateToProps)(OptionsApp);
const OptionsAppContainer = styled('div')`
position: absolute;
display: flex;
flex-direction: row;
justify-content: center;
justify-items: center;
align-items: center;
height: 90vh;
width: 90vw;
left: 5vw;
top: 5vh;
background-color: ${p => p.theme.backgroundColor};
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
`;

View File

@@ -0,0 +1,17 @@
import * as React from 'react';
import { Store } from 'react-chrome-redux';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import OptionsApp from './containers/OptionsApp';
const store = new Store({
portName: 'ExPort' // Communication port between the background component and views such as browser tabs.
});
store.ready().then(() => {
ReactDOM.render(
<Provider store={store}>
<OptionsApp />
</Provider>
, document.getElementById('options-root'));
});

View File

@@ -0,0 +1,50 @@
import * as React from 'react';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import styled, { ThemeProvider } from 'styled-components';
import { IAppState } from '../../background/store';
import GlobalStyle from '../../components/styles/GlobalStyle';
import { themes, ThemeTypes } from '../../components/styles/themes';
import Counter from '../../containers/Counter';
interface IPopupApp {
theme: ThemeTypes;
dispatch: Dispatch;
}
class PopupApp extends React.Component<IPopupApp> {
render() {
return (
<ThemeProvider theme={themes[this.props.theme]}>
<React.Fragment>
<GlobalStyle />
<PopupAppContainer>
<Counter />
</PopupAppContainer>
</React.Fragment>
</ThemeProvider>
);
}
}
const mapStateToProps = (state: IAppState) => {
return {
theme: state.settings.theme
};
};
export default connect(mapStateToProps)(PopupApp);
const PopupAppContainer = styled('div')`
display: flex;
flex-direction: row;
justify-content: center;
justify-items: center;
align-items: center;
height: 200px;
width: 300px;
margin: 10px;
background-color: ${p => p.theme.backgroundColor};
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
`;

View File

@@ -0,0 +1,17 @@
import * as React from 'react';
import { Store } from 'react-chrome-redux';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import PopupApp from './containers/PopupApp';
const store = new Store({
portName: 'ExPort' // Communication port between the background component and views such as browser tabs.
});
store.ready().then(() => {
ReactDOM.render(
<Provider store={store}>
<PopupApp />
</Provider>
, document.getElementById('popup-root'));
});

View File

@@ -0,0 +1,5 @@
export const createDomAnchor = (anchorId: string) => {
const anchor = document.createElement('div');
anchor.id = anchorId;
document.body.insertBefore(anchor, document.body.childNodes[0]);
};

View File

@@ -0,0 +1,11 @@
import renderer from 'react-test-renderer';
import GlobalStyle from '../../../../src/ts/components/styles/GlobalStyle';
describe('Testing Global Style', () => {
test('GlobalStyle Snapshot.', () => {
// @ts-ignore
const wow = renderer.create(GlobalStyle.globalStyle.rules).toJSON();
expect(wow).toMatchSnapshot();
});
});

View File

@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Testing Global Style GlobalStyle Snapshot. 1`] = `
"
body {
box-sizing: border-box;
display: block;
margin: 0;
}
"
`;

View File

@@ -0,0 +1,8 @@
import { themes } from '../../../../../src/ts/components/styles/themes';
describe('Testing themes configuration', () => {
test('Theme object containes a dark and a light property', () => {
expect(themes).toHaveProperty('light');
expect(themes).toHaveProperty('dark');
});
});

View File

@@ -0,0 +1,9 @@
import { createDomAnchor } from '../../../src/ts/scripts/dom';
describe('Testing DOM scripts', () => {
const ANCHOR_ID = 'anchorId';
test('createDomAnchor - creates the first body element with an id', () => {
createDomAnchor(ANCHOR_ID);
expect(document.body.children[0].id).toBe(ANCHOR_ID);
});
});

60
packages/app/tsconfig.json Executable file
View File

@@ -0,0 +1,60 @@
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "es2016", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es2016", "dom"], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
"removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
"strictFunctionTypes": true, /* Enable strict checking of function types. */
"strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
"strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}

18
packages/app/tslint.json Executable file
View File

@@ -0,0 +1,18 @@
{
"defaultSeverity": "error",
"extends": [ "tslint:recommended" ],
"jsRules": {},
"rules": {
"arrow-parens": false,
"indent": [ true, "tabs", 4 ],
"interface-name": false,
"jsx-wrap-multiline": false,
"max-line-length": [true, {"limit": 120, "ignore-pattern": "^import |^export {(.*?)}|class [a-zA-Z]+ implements |//"}],
"member-access": [ true, "no-public" ],
"no-empty": [ true, "allow-empty-functions" ],
"object-literal-sort-keys": false,
"quotemark": [ true, "single", "jsx-double" ],
"trailing-comma": [ true, { "multiline": "ignore", "singleline": "never" }]
},
"rulesDirectory": []
}

View File

@@ -0,0 +1,30 @@
const consoleColors = {
reset: '\x1b[0m',
bold: '\x1b[1m',
dim: '\x1b[2m',
underscore: '\x1b[4m',
blink: '\x1b[5m',
reverse: '\x1b[7m',
hidden: '\x1b[8m',
black: '\x1b[30m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
gray: '\x1b[90m',
bgBlack: '\x1b[40m',
bgRed: '\x1b[41m',
bgGreen: '\x1b[42m',
bgYellow: '\x1b[43m',
bgBlue: '\x1b[44m',
bgMagenta: '\x1b[45m',
bgCyan: '\x1b[46m',
bgWhite: '\x9B[47m',
};
module.exports = consoleColors;

View File

@@ -0,0 +1,58 @@
const path = require('path');
const { readdirSync, lstatSync } = require('fs');
const { red, yellow, reset, bold, blink, dim} = require('./consoleColors');
const isDirectory = (source) => {
return lstatSync(source).isDirectory();
}
/**
* findContentEntryFile - Will return the index file in a path. Supports files ending with js, ts and tsx.
* Throws error if more than one index file is present in the folder with the supported extensions.
* returns undefined if no file exists.
* @param contentPath - String representaion of the content path.
* @returns - The index file name if exists in the given path.
*/
const findContentEntryFile = (contentPath) => {
const files = readdirSync(contentPath).filter((name) => /index.(js|ts|tsx)/.test(name));
if (!files || files.length < 1) {
console.log(`${yellow}Could not find any index files in path:${reset}\n${contentPath}\n`);
return undefined;
}
else if (files.length == 1) {
return files.pop();
}
else {
throw `${red}Found ${files.length} index files, ${files}, in path:${reset}\n${contentPath}\nOnly one should be set.\n`;
}
}
/**
* getDirectories - scans a folder and returns a list directories within.
* If a directory was found,
* an object containing the full path and folder name will be returned in the list.
* @param rootPath - String representaion of the root path.
* @returns - A list of objects containing path and folder name
*/
const getDirectories = (rootPath) => {
return readdirSync(rootPath)
.map(name => {
return {
path: path.join(rootPath, name),
folderName: name
}
})
.filter((dir) => isDirectory(dir.path));
}
const locateContentScripts = (rootPath) => {
const entries = {};
const directories = getDirectories(rootPath);
directories.forEach((dir) => {
const entryFile = findContentEntryFile(dir.path);
if (entryFile) { entries[dir.folderName] = path.join(dir.path, entryFile) }
})
return entries;
}
module.exports = locateContentScripts;

88
packages/app/webpack.config.js Executable file
View File

@@ -0,0 +1,88 @@
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const { CheckerPlugin } = require('awesome-typescript-loader');
const ChromeExtensionReloader = require('webpack-chrome-extension-reloader');
const locateContentScripts = require('./utils/locateContentScripts');
const sourceRootPath = path.join(__dirname, 'src');
const contentScriptsPath = path.join(sourceRootPath, 'ts', 'contentScripts');
const distRootPath = path.join(__dirname, 'dist');
const nodeEnv = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
const webBrowser = process.env.WEB_BROWSER ? process.env.WEB_BROWSER : 'chrome';
module.exports = {
entry: {
background: path.join(sourceRootPath, 'ts', 'background', 'index.ts'),
options: path.join(sourceRootPath, 'ts', 'options', 'index.tsx'),
popup: path.join(sourceRootPath, 'ts', 'popup', 'index.tsx'),
...locateContentScripts(contentScriptsPath)
},
output: {
path: distRootPath,
filename: '[name].js',
},
resolve: {
extensions: ['.js', '.ts', '.tsx', '.json'],
},
module: {
rules: [
{ test: /\.(js|ts|tsx)?$/, loader: "awesome-typescript-loader", exclude: /node_modules/ },
]
},
plugins: [
new CheckerPlugin(),
new HtmlWebpackPlugin({
template: path.join(sourceRootPath, 'html', 'options.html'),
inject: 'body',
filename: 'options.html',
title: 'Web Extension Starter - Options Page',
chunks: ['options'],
}),
new HtmlWebpackPlugin({
template: path.join(sourceRootPath, 'html', 'popup.html'),
inject: 'body',
filename: 'popup.html',
title: 'Web Extension Starter - Popup Page',
chunks: ['popup'],
}),
new CopyWebpackPlugin([
{
from: path.join(sourceRootPath, 'assets'),
to: path.join(distRootPath, 'assets'),
test: /\.(jpg|jpeg|png|gif|svg)?$/,
},
{
from: path.join(sourceRootPath, 'manifest.json'),
to: path.join(distRootPath, 'manifest.json'),
toType: 'file',
}
]),
new webpack.DefinePlugin({
'NODE_ENV': JSON.stringify(nodeEnv),
'WEB_BROWSER': JSON.stringify(webBrowser),
}),
],
}
if (nodeEnv === 'watch') {
module.exports.watch = true;
module.exports.plugins.push(
new ChromeExtensionReloader({
port: 9128,
reloadPage: true,
entries: {
background: 'background',
options: 'options',
popup: 'popup',
contentScript: ['counter'],
}
})
);
}
if (nodeEnv === 'production') {
module.exports.plugins.push(new CleanWebpackPlugin(distRootPath, { verbose: true, dry: false }));
}

5968
packages/app/yarn.lock Normal file

File diff suppressed because it is too large Load Diff