├── requirements.txt ├── .github ├── contributing.md ├── feature.png ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── workflows │ ├── publish.yml │ └── codeql-analysis.yml ├── code_of_conduct.md └── changelog.md ├── config ├── init │ ├── background.js │ ├── 16x16.png │ ├── 24x24.png │ ├── 32x32.png │ ├── 128x128.png │ ├── test.js │ ├── messages.json │ ├── eslint.json │ ├── manifest.json │ ├── package.json │ ├── intro.md │ └── icon.svg ├── ignore ├── docs.json ├── build.json ├── travis.yml ├── gitlab.yml ├── readme.md └── actions.yml ├── test ├── README.md ├── cli-create.test.js └── cli-utilities.test.js ├── .gitignore ├── renovate.json ├── guide ├── assets │ ├── images │ │ ├── favicon.png │ │ ├── foodoc.jpg │ │ ├── braintree.jpg │ │ ├── tidy-jsdoc.jpg │ │ ├── jsdoc-default.jpg │ │ ├── jsdoc-template.jpg │ │ ├── clean-jsdoc-dark.jpg │ │ ├── clean-jsdoc-light.jpg │ │ ├── guide_icon.svg │ │ ├── icon.svg │ │ └── guide.svg │ ├── custom.js │ └── custom.css ├── overrides │ └── main.html ├── 08-xt-create.md ├── 03-xt-build-assets.md ├── 02-configuration.md ├── 01-getting-started.md ├── 03-xt-build-cmds.md ├── 06-xt-sync.md ├── 04-xt-clean.md ├── 03-xt-build-styles.md ├── 14-user-guide.md ├── 03-xt-build-manifest.md ├── 05-xt-docs.md ├── 13-dev-env.md ├── 03-xt-build-scripts.md ├── 03-xt-build-locales.md ├── 03-xt-build-copy.md ├── 13-cli-development.md ├── 12-helpful.md ├── 07-xt-test.md ├── 09-release-notes-0.md ├── index.md ├── 03-xt-build.md ├── 09-release-notes.md └── 05-xt-docs-templates.md ├── .npmignore ├── .travis.yml ├── LICENSE ├── cli ├── xt-test.js ├── xt-sync.js ├── xt-clean.js ├── xt-build.js ├── rootsuite.js ├── xt-docs.js ├── xt-create.js ├── texts.js ├── gulpfile.js └── utilities.js ├── mkdocs.yml ├── package.json ├── README.md └── .eslintrc /requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | Let's add guidelines here over time 2 | -------------------------------------------------------------------------------- /config/init/background.js: -------------------------------------------------------------------------------- 1 | console.log('This is background service worker - edit me!'); 2 | -------------------------------------------------------------------------------- /.github/feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/.github/feature.png -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # CLI Unit Tests 2 | 3 | All unit tests for extension-CLI are in this directory. 4 | -------------------------------------------------------------------------------- /config/init/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/config/init/16x16.png -------------------------------------------------------------------------------- /config/init/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/config/init/24x24.png -------------------------------------------------------------------------------- /config/init/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/config/init/32x32.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | site/ 4 | tutorial-env/ 5 | .nyc_output 6 | env 7 | icon.png 8 | venv 9 | -------------------------------------------------------------------------------- /config/init/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/config/init/128x128.png -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "labels": ["dependencies"], 3 | "extends": ["config:base", ":disableDependencyDashboard"] 4 | } 5 | -------------------------------------------------------------------------------- /guide/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/favicon.png -------------------------------------------------------------------------------- /guide/assets/images/foodoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/foodoc.jpg -------------------------------------------------------------------------------- /guide/assets/images/braintree.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/braintree.jpg -------------------------------------------------------------------------------- /guide/assets/images/tidy-jsdoc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/tidy-jsdoc.jpg -------------------------------------------------------------------------------- /guide/assets/images/jsdoc-default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/jsdoc-default.jpg -------------------------------------------------------------------------------- /guide/assets/images/jsdoc-template.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/jsdoc-template.jpg -------------------------------------------------------------------------------- /guide/assets/images/clean-jsdoc-dark.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/clean-jsdoc-dark.jpg -------------------------------------------------------------------------------- /guide/assets/images/clean-jsdoc-light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/HEAD/guide/assets/images/clean-jsdoc-light.jpg -------------------------------------------------------------------------------- /config/init/test.js: -------------------------------------------------------------------------------- 1 | describe('Test extension', () => { 2 | 3 | it('This is a dummy test', () => { 4 | expect(true).to.be.true; 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /config/ignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules/ 4 | .nyc_output/ 5 | coverage/ 6 | dist/ 7 | public/documentation/ 8 | release.zip 9 | yarn-error.log 10 | -------------------------------------------------------------------------------- /guide/assets/custom.js: -------------------------------------------------------------------------------- 1 | window.dataLayer = window.dataLayer || []; 2 | function gtag() { 3 | window.dataLayer.push(arguments); 4 | } 5 | gtag('js', new Date()); 6 | gtag('config', 'G-6XB4XDVPX3'); 7 | -------------------------------------------------------------------------------- /config/init/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "${name}" 4 | }, 5 | "appShortName": { 6 | "message": "${name}" 7 | }, 8 | "appDescription": { 9 | "message": "${description}" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .gitignore 3 | .docs.json 4 | .travis.yml 5 | .github/ 6 | requirements.txt 7 | node_modules/ 8 | CHANGELOG.md 9 | *.jpg 10 | assets/ 11 | docs/ 12 | guide/ 13 | site/ 14 | tutorial-env/ 15 | .eslintrc 16 | inch.json 17 | mkdocs.yml 18 | .npmrc 19 | .nyc_output 20 | env/ 21 | test/ 22 | examples/ 23 | venv/ 24 | icon.png 25 | -------------------------------------------------------------------------------- /test/cli-create.test.js: -------------------------------------------------------------------------------- 1 | // TODO: #21: how to require this without actually running the command? 2 | // const BuildScript = require('../cli/xt-build'); 3 | 4 | // const expect = require('chai').expect; 5 | 6 | describe('create command', () => { 7 | 8 | // placeholder; replace this with actual test 9 | // it('...(dummy test)', async () => { 10 | // expect(true).to.equal(true); 11 | // }); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /guide/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block disqus %} 4 | {% if not page.meta.disqus == "False" %} 5 |

{{ lang.t("meta.comments") }}

6 | 14 | 15 | {% endif %} 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /guide/08-xt-create.md: -------------------------------------------------------------------------------- 1 | # extension-cli 2 | 3 | * * * 4 | 5 |

extension-cli command creates a new web extension project.

6 | 7 | * * * 8 | 9 | ## Commands 10 | 11 | ```bash 12 | npx extension-cli 13 | ``` 14 | 15 | This command will prompt with necessary questions and does not take any arguments. 16 | 17 | This command will generate initial files and directories for a new project. 18 | 19 | Run it in the directory where you want to create the extension. 20 | 21 | -------------------------------------------------------------------------------- /config/init/eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended" 9 | ], 10 | "globals": { 11 | "document": false, 12 | "escape": false, 13 | "navigator": false, 14 | "unescape": false, 15 | "window": false, 16 | "describe": true, 17 | "before": true, 18 | "it": true, 19 | "expect": true, 20 | "sinon": true, 21 | "chrome": true 22 | }, 23 | "plugins": [], 24 | "parserOptions": { 25 | "ecmaVersion": 2020, 26 | "sourceType": "module" 27 | }, 28 | "rules": { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | node_js: 4 | - "14.18.3" 5 | 6 | python: 7 | - "3.9" 8 | 9 | jobs: 10 | include: 11 | - language: node_js 12 | node_js: 14.18.3 13 | script: 14 | - npm install 15 | - npm run test:travis || travis_terminate 1 16 | 17 | - language: python 18 | python: "3.9" 19 | script: 20 | - pip install -r requirements.txt 21 | - mkdocs build 22 | deploy: 23 | - provider: pages 24 | skip_cleanup: true 25 | github_token: $GITHUB_TOKEN 26 | keep_history: true 27 | local_dir: site 28 | on: 29 | branch: main 30 | -------------------------------------------------------------------------------- /config/docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": [ 5 | "jsdoc" 6 | ] 7 | }, 8 | "source": { 9 | "include": [ 10 | "src" 11 | ], 12 | "includePattern": ".js$", 13 | "excludePattern": "(node_modules/)" 14 | }, 15 | "plugins": [ 16 | "plugins/markdown" 17 | ], 18 | "templates": { 19 | "default": { 20 | "cleverLinks": true, 21 | "monospaceLinks": false 22 | } 23 | }, 24 | "opts": { 25 | "destination": "./public/documentation", 26 | "encoding": "utf8", 27 | "private": true, 28 | "recurse": true, 29 | "template": "templates/default" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /config/build.json: -------------------------------------------------------------------------------- 1 | { 2 | "dist": "./dist", 3 | "source": "./src", 4 | "releases": "./", 5 | "release_name": "release", 6 | "manifest": "./src/manifest.json", 7 | "js": "./src/**/*.js", 8 | "js_bundles": [{ 9 | "src":"./src/**/*.js", 10 | "name": "script" 11 | }], 12 | "html": "./src/**/*.html", 13 | "scss": "./src/**/*.scss", 14 | "scss_bundles": [{ 15 | "src":"./src/**/*.scss", 16 | "name": "styles" 17 | }], 18 | "assets": [ 19 | "./assets/**/*", 20 | "!./assets/locales", 21 | "!./assets/locales/**/*" 22 | ], 23 | "copyAsIs": [], 24 | "locales_dir": "./assets/locales/", 25 | "locales_list": [ 26 | "en" 27 | ], 28 | "commands": null, 29 | "commands_watch_path": null 30 | } 31 | -------------------------------------------------------------------------------- /config/init/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "short_name": "__MSG_appShortName__", 4 | "description": "__MSG_appDescription__", 5 | "homepage_url": "${homepage}", 6 | "version": "${version}", 7 | "version_name": "${version}", 8 | "manifest_version": 3, 9 | "default_locale": "en", 10 | "minimum_chrome_version": "88", 11 | "permissions": [], 12 | "icons": { 13 | "128": "assets/img/128x128.png" 14 | }, 15 | "background": { 16 | "service_worker": "background.js" 17 | }, 18 | "action": { 19 | "default_icon": { 20 | "16": "assets/img/16x16.png", 21 | "24": "assets/img/24x24.png", 22 | "32": "assets/img/32x32.png" 23 | }, 24 | "default_title": "__MSG_appName__" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config/travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "14.15.4" 5 | 6 | cache: 7 | directories: 8 | - node_modules 9 | 10 | before_script: 11 | - npm run test || travis_terminate 1 12 | 13 | script: 14 | - npm run docs 15 | - npm run build 16 | 17 | deploy: 18 | - provider: pages 19 | skip_cleanup: true 20 | github_token: $github_token 21 | local_dir: public/documentation 22 | on: 23 | branch: master 24 | 25 | - provider: releases 26 | skip_cleanup: true 27 | api_key: $github_token 28 | file: $zip_path 29 | on: 30 | tags: true 31 | 32 | #after_deploy: 33 | ## upload generated zip to chrome web store 34 | ## see: https://github.com/MobileFirstLLC/cws-publish 35 | # - if [ ! -z "$TRAVIS_TAG" ]; then 36 | # npx cws-upload $client_id $secret $token $zip_path $extension_id; 37 | # fi 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | - Node.js Version 31 | 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /config/gitlab.yml: -------------------------------------------------------------------------------- 1 | image: node:latest 2 | 3 | stages: 4 | - install 5 | - pages 6 | - test 7 | - publish 8 | 9 | cache: 10 | key: ${CI_COMMIT_REF_SLUG} 11 | paths: 12 | - node_modules/ 13 | 14 | install_dependencies: 15 | stage: install 16 | script: npm install 17 | artifacts: 18 | paths: 19 | - node_modules/ 20 | 21 | pages: 22 | stage: pages 23 | script: 24 | - npm run docs 25 | artifacts: 26 | paths: 27 | - public/ 28 | only: 29 | - master 30 | 31 | test: 32 | stage: test 33 | script: 34 | - npm run test 35 | 36 | store_publish: 37 | stage: publish 38 | script: 39 | - npm run build 40 | ## see: https://github.com/MobileFirstLLC/cws-publish 41 | # - npx cws-upload $client_id $secret $token $zip_path $extension_id; 42 | artifacts: 43 | paths: 44 | - $zip 45 | only: 46 | - tags 47 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: NPM Publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: '14' 16 | 17 | - name: Tag name 18 | run: echo "TAG=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 19 | - run: npm ci 20 | - run: npm run test 21 | 22 | - name: NPM Publish 23 | uses: JS-DevTools/npm-publish@v1 24 | with: 25 | tag: ${{ fromJSON('["latest", "alpha"]')[contains(env.TAG, '-')] }} 26 | token: ${{ secrets.NPM_TOKEN }} 27 | 28 | - name: Github Release 29 | uses: ncipollo/release-action@v1 30 | if: "!contains(env.TAG, '-')" 31 | with: 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /guide/03-xt-build-assets.md: -------------------------------------------------------------------------------- 1 | # Static assets 2 | 3 | * * * 4 | 5 |

Specify how static assets will be handled during builds.

6 | 7 | * * * 8 | 9 | By default, extension CLI will look for static assets matching this configuration: 10 | 11 | ```json 12 | "assets": [ 13 | "./assets/**/*", 14 | "!./assets/locales", 15 | "!./assets/locales/**/*" 16 | ], 17 | ``` 18 | 19 | You may change this configuration if the project's static assets are located elsewhere or 20 | if you want to include or exclude additional files/directories. 21 | 22 | After the build step, all static assets will be located in the `/dist/assets` directory. 23 | 24 | For example, to refer to images in extension manifest, would be as follows: 25 | 26 | ```json 27 | "browser_action": { 28 | "default_icon": { 29 | "16": "assets/img/16x16.png", 30 | "24": "assets/img/24x24.png", 31 | "32": "assets/img/32x32.png" 32 | } 33 | } 34 | ``` 35 | -------------------------------------------------------------------------------- /guide/02-configuration.md: -------------------------------------------------------------------------------- 1 | # Configuration for Existing Applications 2 | 3 | !!! info 4 | **If you created the extension with Extension CLI, this setup is already done for you, and you may skip this step.** 5 | 6 | 7 | Before using Extension CLI with an existing application, add these configuration options to project's `package.json`: 8 | 9 | ### Babel Presets 10 | 11 | This is needed to compile projects written in modern JavaScript syntax. 12 | 13 | ```json 14 | "babel": { 15 | "presets": [ 16 | "@babel/preset-env" 17 | ] 18 | } 19 | ``` 20 | 21 | ### ESLint Ignore 22 | 23 | Exclude test files from being linted. If your project includes compiled 3rd party libraries, you should exclude them also. 24 | 25 | ```json 26 | "eslintIgnore": [ 27 | "test/**/*" 28 | ] 29 | ``` 30 | 31 | ### Add Scripts 32 | 33 | Add these to `package.json` `scripts` section: 34 | 35 | ```json 36 | "scripts": { 37 | "start": "xt-build -e dev -w", 38 | "build": "xt-build -e prod", 39 | "clean": "xt-clean", 40 | "docs": "xt-docs", 41 | "test": "xt-test", 42 | "coverage": "nyc --reporter=lcov npm run test" 43 | } 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2021 Mobile First LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/init/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "${safeName}", 3 | "description": "${description}", 4 | "version": "${version}", 5 | "homepage": "${homepage}", 6 | "author": "ENTER YOUR NAME HERE", 7 | "repository": { 8 | "type": "git", 9 | "url": "ENTER GIT REPO URL" 10 | }, 11 | "scripts": { 12 | "start": "xt-build -e dev -w", 13 | "start:firefox": "xt-build -e dev -p firefox -w", 14 | "build": "xt-build -e prod", 15 | "build:firefox": "xt-build -e prod -p firefox", 16 | "clean": "xt-clean", 17 | "docs": "xt-docs", 18 | "test": "xt-test", 19 | "coverage": "nyc --reporter=lcov npm run test", 20 | "sync": "xt-sync" 21 | }, 22 | "babel": { 23 | "presets": [ 24 | "@babel/preset-env" 25 | ] 26 | }, 27 | "eslintIgnore": [ 28 | "test/**/*" 29 | ], 30 | "devDependencies": { 31 | "extension-cli": "latest" 32 | }, 33 | "xtdocs": { 34 | "source": { 35 | "include": [ 36 | "README.md", 37 | "src" 38 | ] 39 | } 40 | }, 41 | "xtbuild": { 42 | "js_bundles": [ 43 | { 44 | "name": "background", 45 | "src": "./src/**/*.js" 46 | } 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /guide/assets/custom.css: -------------------------------------------------------------------------------- 1 | /* add custom css here */ 2 | .md-main__inner{padding-bottom: 3em} 3 | .page-intro{font-size:1.3em;line-height:1.7;} 4 | .highlighttable td pre {line-height: 1.9;} 5 | .md-tabs__item{font-weight: bold} 6 | 7 | table {padding: 0;} 8 | table tr {margin: 0;padding: 0; } 9 | table tr th {font-weight: bold;} 10 | table tr th :first-child, table tr td :first-child {margin-top: 0; } 11 | table tr th :last-child, table tr td :last-child {margin-bottom: 0; } 12 | 13 | .md-typeset table:not([class]){ 14 | font-size: .68rem; 15 | box-shadow: none; 16 | background: var(--md-code-bg-color); 17 | } 18 | table tr th,table tr td, 19 | .md-typeset table:not([class]) th, 20 | .md-typeset table:not([class]) td{ 21 | margin: 0; padding: .65em .95em; 22 | } 23 | .md-typeset code{ 24 | padding: 3px .35em; 25 | } 26 | .md-typeset table:not([class]) code { 27 | font-size: .65rem; 28 | white-space: nowrap; 29 | } 30 | .md-typeset table:not([class]) tr:hover{ 31 | transition: none; 32 | background-color: transparent; 33 | box-shadow: none; 34 | } 35 | .admonition > p{ 36 | font-size: 115%; 37 | } 38 | .admonition > p:not(first-child){ 39 | line-height: 1.8; 40 | } 41 | p code{ 42 | white-space: nowrap; 43 | } 44 | -------------------------------------------------------------------------------- /guide/01-getting-started.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ### Prerequisites 4 | 5 | Before using extension CLI, you must have the following: 6 | 7 | - [Node.js](https://nodejs.org/en/download/) 8 | - JavaScript IDE 9 | - Terminal access 10 | - Browser for debugging extensions 11 | 12 | ### Setup 13 | 14 | Create a new extension project: 15 | 16 | ```bash 17 | npx extension-cli 18 | ``` 19 | 20 | Add CLI to an existing project: 21 | 22 | ```bash 23 | npm install extension-cli 24 | ``` 25 | 26 | ### Default Project Organization 27 | 28 | Before you start using the CLI, inspect your project file structure. You can override most of 29 | these paths in configurations, but this organization matches the CLI defaults. 30 | 31 | If you created a new extension using the command above, your file structure already looks like this. 32 | 33 | Path | Description 34 | --- | --- 35 | └ **assets** | static assets 36 |     └─ img | Extension icons 37 |     └─ locales | Localized string resources 38 |         └─ en/messages.json | English dictionary 39 | └ **src** | Source code: put js, scss, html, json files here 40 |     └─ manifest.json | Extension manifest 41 | └ **test** | Unit tests 42 | └ package.json | Application root 43 | 44 | -------------------------------------------------------------------------------- /guide/03-xt-build-cmds.md: -------------------------------------------------------------------------------- 1 | # Custom commands 2 | 3 | * * * 4 | 5 |

Custom commands enables running any custom actions after build and before generating a release.

6 | 7 | * * * 8 | 9 | Custom commands will be executed: 10 | 11 | - _after_ script, styles, HTML and other bundles have been built, and 12 | - _before_ a release `.zip` file is generated 13 | 14 | Custom commands are run for both `dev` and `prod` builds. 15 | 16 | To configure custom commands specify `commands` build configuration key. For example: 17 | 18 | ```json 19 | "xtbuild": { 20 | "commands": "python do_something.py", 21 | } 22 | ``` 23 | 24 | This configuration would first build the extension, then run a custom Python script, 25 | then for a production build, generate the extension zip file. 26 | 27 | 45 | -------------------------------------------------------------------------------- /guide/06-xt-sync.md: -------------------------------------------------------------------------------- 1 | # xt-sync 2 | 3 | 4 | * * * 5 | 6 |

xt-sync command enables copying and updating 7 | configuration files.

8 | 9 | * * * 10 | 11 | When adding more features to an extension project, it is helpful 12 | to \*not\* start from scratch. `xt-sync` command enables extension projects 13 | to pull in starter configuration files for the purposes of linting, 14 | setting up automated CI builds, and for setting up git VCS. 15 | 16 | The configuration files are intended as a starting point. If you 17 | end up modifying them heavily at a project level, you should continue 18 | to maintain them manually instead of using this command. 19 | 20 | If you do not modify these configuration files, you can sync the 21 | latest version periodically, to update to the latest version supplied 22 | by this CLI. 23 | 24 | ## Commands 25 | 26 | **Sync configuration files** 27 | 28 | This command will guide you through the available options. 29 | 30 | ```bash 31 | xt-sync 32 | ``` 33 | 34 | 35 | ## Package.json scripts 36 | 37 | After installing extension-cli, you can run these commands from a terminal by calling 38 | 39 | ```bash 40 | npx xt-sync 41 | ``` 42 | 43 | Alternatively you can add an option to `packages.json` scripts section as shown below 44 | 45 | ```json 46 | "scripts" : { 47 | "sync": "xt-sync" 48 | } 49 | ``` 50 | 51 | and then execute the command by running 52 | 53 | ```bash 54 | npm run sync 55 | ``` 56 | -------------------------------------------------------------------------------- /config/readme.md: -------------------------------------------------------------------------------- 1 | # CLI configuration files 2 | 3 | This directory contains various files that are used by the available CLI commands. Below is a short summary of each to explain their purpose. 4 | 5 | Path | Description 6 | :--- | :--- 7 | **actions.yml** | Github actions starter configuration 8 | **build.json** | default file paths used by the build script 9 | **docs.json** | JSDoc documentation template 10 | **eslint.json** | default eslint configuration 11 | **gitlab.yml** | Gitlab CI starter configuration 12 | **ignore** | gitignore starter 13 | **travis.yml** | Travis CI starter configuration 14 | **init/** | Files for bootstrapping a new extension project 15 |   **└── NNxNN.png** | extension icons 16 |   **└── background.js** | background script starter 17 |   **└── icon.svg** | vector icon 18 |   **└── intro.md** | new extension readme 19 |   **└── manifest.json** | manifest template 20 |   **└── messages.json** | message dictionary template 21 |   **└── package.json** | package.json template 22 |   **└── test.js** | unit test starter 23 | 24 | **Notes** 25 | 26 | - eslint, CI configuration files, (git)ignore can be pulled into a project through `xt-sync` command, 27 | or a project can specify these files independently. 28 | The idea is not having to start from scratch at project level unless it is by choice. 29 | - All files in `init` directory are included in a new extension project 30 | - files that should not be included in a newly generated project go in `/config` directory 31 | - keep `init` directory flat on purpose to keep things simple — create command will generate 32 | the necessary structure 33 | 34 | -------------------------------------------------------------------------------- /config/init/intro.md: -------------------------------------------------------------------------------- 1 | # ${name} 2 | 3 | ${description} 4 | 5 | ## Development 6 | 7 | This extension was created with [Extension CLI](https://oss.mobilefirst.me/extension-cli/)! 8 | 9 | If you find this software helpful [star](https://github.com/MobileFirstLLC/extension-cli/) or [sponsor](https://github.com/sponsors/MobileFirstLLC) this project. 10 | 11 | 12 | ### Available Commands 13 | 14 | | Commands | Description | 15 | | --- | --- | 16 | | `npm run start` | build extension, watch file changes | 17 | | `npm run build` | generate release version | 18 | | `npm run docs` | generate source code docs | 19 | | `npm run clean` | remove temporary files | 20 | | `npm run test` | run unit tests | 21 | | `npm run sync` | update config files | 22 | 23 | For CLI instructions see [User Guide →](https://oss.mobilefirst.me/extension-cli/) 24 | 25 | ### Learn More 26 | 27 | **Extension Developer guides** 28 | 29 | - [Getting started with extension development](https://developer.chrome.com/extensions/getstarted) 30 | - Manifest configuration: [version 2](https://developer.chrome.com/extensions/manifest) - [version 3](https://developer.chrome.com/docs/extensions/mv3/intro/) 31 | - [Permissions reference](https://developer.chrome.com/extensions/declare_permissions) 32 | - [Chrome API reference](https://developer.chrome.com/docs/extensions/reference/) 33 | 34 | **Extension Publishing Guides** 35 | 36 | - [Publishing for Chrome](https://developer.chrome.com/webstore/publish) 37 | - [Publishing for Edge](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/publish-extension) 38 | - [Publishing for Opera addons](https://dev.opera.com/extensions/publishing-guidelines/) 39 | - [Publishing for Firefox](https://extensionworkshop.com/documentation/publish/submitting-an-add-on/) 40 | -------------------------------------------------------------------------------- /guide/04-xt-clean.md: -------------------------------------------------------------------------------- 1 | # xt-clean 2 | 3 | 4 | * * * 5 | 6 |

xt-clean command removes all automatically generated files from the project directories.

7 | 8 | * * * 9 | 10 | Clean operation iterates over files and directories listed in the project `.gitignore` file, and removes all ignored files and directories, except `node_modules/`, `.idea/`, and `.vscode/`. `.idea` is a collection of configuration files used by WebStorm IDE, and `.vscode` is the same for Visual Studio Code. The IDE will generate them automatically if they are absent. To remove these three directories, you must explicitly pass a flag to delete each directory respectively. 11 | 12 | 13 | ## Commands 14 | 15 | Braces `{ }` indicate that the user must choose one (and only one) of the items inside the braces. 16 | 17 | **Remove ignored files (default)** 18 | 19 | ```bash 20 | xt-clean 21 | ``` 22 | 23 | **Clear ignored files, including `node_modules`** 24 | 25 | ```bash 26 | xt-clean {-m|--modules} 27 | ``` 28 | 29 | **Clear ignored files, including `.idea/` directory** 30 | 31 | ```bash 32 | xt-clean {-i|--idea} 33 | ``` 34 | 35 | **Clear ignored files, including `.vscode/` directory** 36 | 37 | ```bash 38 | xt-clean {-v|--vscode} 39 | ``` 40 | 41 | **Clear absolutely all ignored files** 42 | 43 | ```bash 44 | xt-clean -v -i -m 45 | ``` 46 | 47 | **Get help using this command** 48 | 49 | ```bash 50 | xt-clean --help 51 | ``` 52 | 53 | ## Package.json scripts 54 | 55 | After installing extension-cli, you can run these commands from a terminal using syntax `npx xt-clean`. 56 | 57 | Or you can add an option to `packages.json` scripts section and then execute the command as `npm run clean` See example below. 58 | 59 | ```json 60 | "scripts":{ 61 | "clean": "xt-clean" 62 | } 63 | ``` 64 | 65 | 66 | -------------------------------------------------------------------------------- /guide/03-xt-build-styles.md: -------------------------------------------------------------------------------- 1 | # Building Stylesheets 2 | 3 | * * * 4 | 5 |

Instructions for configuring stylesheet build outputs.

6 | 7 | * * * 8 | 9 | `scss_bundles` are used to configure build settings for CSS stylesheets. The expected value is an array with 10 | zero or more objects where. 11 | 12 | - `name` is the output bundle filename with file extension 13 | - `src` specifies which files to include in each bundle; you can use 14 | - a string value for a single file 15 | - array of files, or 16 | - a path with wildcard. 17 | - prefix `!` as a way to negate the inclusion of a file 18 | 19 | See [globs syntax guide](https://gulpjs.com/docs/en/api/src) for more details. 20 | 21 | Dev build does not minify style files. The production build will minify style files. 22 | 23 | By default, the stylesheets are assumed to be written using [Sass](https://sass-lang.com/guide). When naming stylesheet files, use `.scss` file extension because default configuration looks for style files with this file extension. 24 | 25 | If you are not a friend of Sass, you can write style sheets using CSS. In the build configuration override the default configuration: `"scss": "./src/**/*.scss"` to treat other file extensions as style files, and use `"scss_bundles"` key to specify how to generate stylesheets, as shown in the example below. 26 | 27 | **Example** 28 | 29 | Sample project-level configuration with multiple style bundles. This configuration will generate two stylesheets in the output directory: styles.css and display.css. 30 | 31 | ```json 32 | "xtbuild": { 33 | "scss_bundles": [ 34 | { 35 | "src": [ 36 | "./src/**/*.scss", 37 | "!./src/app/styles/ui.scss" 38 | ], 39 | "name": "styles" 40 | }, 41 | { 42 | "src": [ 43 | "./src/app/styles/ui.scss" 44 | ], 45 | "name": "display" 46 | } 47 | ] 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /guide/14-user-guide.md: -------------------------------------------------------------------------------- 1 | # Editing User Guide 2 | 3 | 4 | !!! info 5 | If you are interested in editing the content (and not layout) of this user guide, 6 | simply edit the markdown directly in any markdown editor or on Github. 7 | There is a pencil icon linking to the markdown source on each page of these docs, 8 | which takes you directly to the source document. 9 | 10 | ## Developing User Guide 11 | 12 | When you want to edit the layout, organization and/or theme of these docs, you 13 | will need to run these project docs locally. This user guide is built with Python. 14 | You will need Python 3.x before proceeding. 15 | 16 | 1. If you are not a maintainer, [fork the repo](https://github.com/MobileFirstLLC/extension-cli/fork) 17 | 18 | 2. Clone the forked repo and launch your favorite markdown editor and terminal. 19 | 20 | 3. Setup Python development env as follows: 21 | 22 | - Create virtual env for Python packages: 23 | 24 | ``` 25 | python3 -m venv env 26 | ``` 27 | 28 | - Activate virtual env: 29 | 30 | ``` 31 | source env/bin/activate # macOS/Linux 32 | env\Scripts\activate.bat # Windows 33 | ``` 34 | 35 | - Install requirements: 36 | 37 | ``` 38 | pip install -r requirements.txt 39 | ``` 40 | 41 | - Run and debug the docs: 42 | 43 | ``` 44 | mkdocs serve 45 | ``` 46 | 47 | 4. Relevant files: 48 | 49 | - all written documents are under `guide` directory 50 | - `mkdocs.yml` at project root is a configuration file for Mkdocs 51 | - `guide/assets` includes static assets for these docs 52 | - `guide/overrides` includes customized template files that override default mkdocs-material templates 53 | 54 | 5. After editing the docs, commit your changes and open a PR as 55 | necessary. Travis CI is used to compile and publish the docs automatically 56 | after each merge to master branch. 57 | -------------------------------------------------------------------------------- /guide/assets/images/guide_icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 16 | 18 | image/svg+xml 19 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 34 | 35 | 36 | 40 | 44 | 45 | -------------------------------------------------------------------------------- /config/actions.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | ## to run workflow on pull requests: 8 | #on: 9 | # pull_request: 10 | # branches: [ main ] 11 | 12 | ## to run workflow on tagged commits 13 | #on: 14 | # push: 15 | # tags: 16 | # - '*' 17 | 18 | ## to run workflow on schedule, e.g. nightly build 19 | #on: 20 | # schedule: 21 | # - cron: '0 0 * * *' 22 | 23 | jobs: 24 | build: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v2 28 | 29 | # see https://github.com/marketplace/actions/setup-node-js-environment 30 | - uses: actions/setup-node@v2 31 | with: 32 | node-version: '14' 33 | 34 | # see https://github.com/marketplace/actions/cache 35 | - name: Cache dependecies 36 | uses: actions/cache@v2 37 | with: 38 | path: '**/node_modules' 39 | key: ${{ runner.os }}-modules-${{ hashFiles('**/package-lock.json') }} 40 | 41 | - name: Install dependencies 42 | run: npm install 43 | 44 | - name: Unit test 45 | run: npm run test 46 | 47 | - name: Build docs 48 | run: npm run docs 49 | 50 | - name: Build extension .zip file 51 | run: npm run build 52 | 53 | ## Uncomment to deploy docs to GH pages 54 | ## see: https://github.com/marketplace/actions/deploy-to-github-pages 55 | # - name: Deploy docs 56 | # uses: JamesIves/github-pages-deploy-action@4.1.3 57 | # with: 58 | # branch: gh-pages 59 | # folder: public/documentation 60 | 61 | ## Uncomment to upload generated zip file to web store 62 | ## see: https://github.com/MobileFirstLLC/cws-publish 63 | # - name: Upload to Chrome Web Store 64 | # run: >- 65 | # npx cws-upload 66 | # ${{ secrets.CLIENT }} 67 | # ${{ secrets.SECRET }} 68 | # ${{ secrets.TOKEN }} 69 | # "release.zip" 70 | # ${{ EXTENSION_ID }}; 71 | 72 | ## Uncomment to make a Github release 73 | ## see: https://github.com/marketplace/actions/create-release 74 | # - uses: ncipollo/release-action@v1 75 | # with: 76 | # artifacts: "release.zip" 77 | # token: ${{ secrets.GITHUB_TOKEN }} 78 | -------------------------------------------------------------------------------- /guide/03-xt-build-manifest.md: -------------------------------------------------------------------------------- 1 | # Manifest 2 | 3 | * * * 4 | 5 |

Customize build behavior for extension manifest.

6 | 7 | * * * 8 | 9 | In your build configuration specify path to the manifest file: 10 | 11 | ```json 12 | "xtbuild": { 13 | "manifest": "./src/manifest.json", 14 | } 15 | ``` 16 | 17 | The file will be renamed to `manifest.json` during build regardless of its original name. 18 | 19 | ## Customizing manifests for different target browsers 20 | 21 | There are two strategies for customizing the manifest contents per target browser: 22 | 23 | 1. Specify browser-specific keys in single manifest file 24 | 2. Specify multiple build configurations, each with different manifest file. 25 | 26 | ### Browser specific keys in single manifest 27 | 28 | Using this strategy, the project contains single manifest. In manifest.json: 29 | 30 | ```json 31 | { 32 | "name": "__MSG_appname__", 33 | "description": "__MSG_description__", 34 | "chrome":{ 35 | 36 | ... chrome-specific manifest keys here 37 | 38 | }, 39 | "firefox":{ 40 | 41 | ... firefox-specific manifest keys here 42 | 43 | } 44 | } 45 | ``` 46 | 47 | Then run the build command specifying the target platform: 48 | 49 | ``` 50 | xt-build --platform chrome 51 | xt-build --platform firefox 52 | ``` 53 | 54 | The build will then combine all common manifest elements with those 55 | specified for the target platform. 56 | 57 | When building cross-browser extensions, most browsers can reuse the 58 | same manifest. Therefore, these two targets are typically sufficient to generate the desired 59 | manifests for multiple target browsers. However, if this strategy 60 | is insufficient, see the next option. 61 | 62 | 63 | ### Multiple build configurations 64 | 65 | Create multiple build configuration files: 66 | 67 | chrome-config.json: 68 | 69 | ```json 70 | { 71 | "manifest": "./manifests/chrome.json" 72 | } 73 | ``` 74 | 75 | firefox-config.json: 76 | 77 | ```json 78 | { 79 | "manifest": "./manifests/firefox.json" 80 | } 81 | ``` 82 | 83 | Using this strategy, run the build command specifying path to config file explicitly: 84 | 85 | ``` 86 | xt-build --config chrome-config.json 87 | xt-build --config firefox-config.json 88 | ``` 89 | -------------------------------------------------------------------------------- /guide/05-xt-docs.md: -------------------------------------------------------------------------------- 1 | # xt-docs 2 | 3 | * * * 4 | 5 |

xt-docs command generates source 6 | code documentation for an extension project.

7 | 8 | * * * 9 | 10 | Extension CLI uses [JSDoc](https://jsdoc.app/index.html) specification to 11 | generate documentation for javascript files in an extension project. JSDoc is 12 | a flexible documentation generator that converts javascript code comments to 13 | readable HTML/CSS files which you can be hosted for example with github pages. 14 | 15 | ## Commands 16 | 17 | Braces `{ }` indicate that the user must choose one (and only one) of the 18 | items inside the braces. 19 | 20 | **Default command** 21 | 22 | ```bash 23 | xt-docs 24 | ``` 25 | 26 | **Command using custom configuration file path** 27 | 28 | ```bash 29 | xt-docs {-c|--config} "/path/to/config.json" 30 | ``` 31 | 32 | **Build docs and keep watching changes** 33 | 34 | ```bash 35 | xt-docs {-w|--watch} 36 | ``` 37 | 38 | **Get help using this command** 39 | 40 | ```bash 41 | xt-docs --help 42 | ``` 43 | 44 | ## Package.json scripts 45 | 46 | After installing extension-cli, you can run these commands from a terminal 47 | using syntax `npx xt-docs`. 48 | 49 | Or you can add an option to `packages.json` scripts section and then execute 50 | the command as `npm run docs`. See example below. 51 | 52 | ```json 53 | "scripts":{ 54 | "docs": "xt-docs" 55 | } 56 | ``` 57 | 58 | ## Configuration 59 | 60 | By default the CLI will look for docs configuration in two different 61 | places: 62 | 63 | - in `package.json` using key `xtdocs` 64 | 65 | - in a file named `.xtdocs.json` in project root 66 | 67 | If these two locations cause a conflict, alternatively you can provide a path 68 | to configuration file with `-c` (`--config`) flag, followed by path to file. 69 | [See commands for an example](#commands). 70 | 71 | You can use any compatible template of choice to style your docs. You can find 72 | some [templating options here](05-xt-docs-templates.md). 73 | 74 | ### Default Configuration 75 | 76 | The CLI uses a documentation configuration file shown below. You can override any of these key-value pairs at project level. You can also add key-value pairs that are not defined here so long as they follow to [JSDoc guidelines](https://jsdoc.app/about-configuring-jsdoc.html). 77 | 78 | ```json 79 | --8<-- 80 | ./config/docs.json 81 | --8<-- 82 | ``` 83 | 84 | 85 | -------------------------------------------------------------------------------- /guide/13-dev-env.md: -------------------------------------------------------------------------------- 1 | # Environment Setup 2 | 3 | 4 | To build extension CLI locally you will need [Node.js](https://nodejs.org/en/download/) 5 | and any web IDE of your choice. 6 | 7 | Developing the CLI requires two projects open at the same time: 8 | 9 | 1. the CLI source code, which you are developing 10 | 2. a driver project that is used to execute the CLI commands 11 | 12 | The following instructions explain how to set up this environment. 13 | 14 | ## Instructions 15 | 16 | ### 1. Setup the CLI 17 | 18 | 1. [Fork the extension-CLI repo](https://github.com/MobileFirstLLC/extension-cli/fork) 19 | 20 | 2. Clone the forked repo and then open it in your favorite web IDE 21 | 22 | 3. Run the following command in terminal: 23 | 24 | ```bash 25 | npm install 26 | ``` 27 | 28 | ### 2. Setup driver project 29 | 30 | Next you will need a project to drive the CLI to be able to execute its commands. 31 | You can use any existing extension project that is using extension-cli. 32 | 33 | If you do not have an existing project, create a new project. In the directory where you want to create the driver project run: 34 | 35 | ```bash 36 | npx extension-cli 37 | ``` 38 | 39 | then follow the on-screen instructions. Once you have the project ready, open it in a web IDE. 40 | At this point you should have two IDE windows open. 41 | 42 | ### 3. Link driver and CLI 43 | 44 | 45 | 1. In **CLI project** terminal run this command (use `sudo npm link` if necessary): 46 | 47 | ```bash 48 | npm link 49 | ``` 50 | 51 |
52 | 53 | 2. In the **driver project** terminal run this command: 54 | 55 | ```bash 56 | npm link extension-cli 57 | ``` 58 | 59 | * * * 60 | 61 | **
Your dev environment should now be ready to use.
** 62 | 63 | * * * 64 | 65 | ## Clean up 66 | 67 | Unlink CLI and driver project to remove all local links. 68 | 69 | In the **driver project** terminal run: 70 | 71 | ```bash 72 | npm unlink --no-save extension-cli 73 | ``` 74 | 75 | to unlink project from the local CLI version. Note that this may remove 76 | extension-cli from the project completely, and you may need to run `install extension-cli` 77 | to add back the version from NPM registry. This is relevant only if you used 78 | an existing project as a driver. 79 | 80 | In **CLI project** terminal run: 81 | 82 | ```bash 83 | npm r extension-cli -g 84 | ``` 85 | 86 | to remove the CLI symlink. 87 | -------------------------------------------------------------------------------- /guide/03-xt-build-scripts.md: -------------------------------------------------------------------------------- 1 | # Building Scripts 2 | 3 | * * * 4 | 5 |

Instructions for configuring javascript build outputs.

6 | 7 | * * * 8 | 9 | `js_bundles` key is used to configure build settings for javascript bundles. 10 | 11 | It allows you to specify: 12 | 13 | 1. name of each generated script file 14 | 2. what to included in each script 15 | 3. how many scripts will be generated by build command 16 | 17 | By default `js_bundles` looks for .js files in source directory and generates 18 | a single script.js as output. 19 | 20 | ## Configuration options 21 | 22 | `js_bundles` value is an array of objects, where each object specifies the following keys: 23 | 24 | | Key | Value | 25 | --- | --- 26 | | **`name`** | Output filename without file extension | 27 | | **`src`** | Glob pattern for specifying which files to include in the bundle | 28 | | **`mode`** (optional) | webpack build mode; by default same as `--env` flag | 29 | 30 | !!! info 31 | Internally JavaScript bundles are built using webpack where mode is determined 32 | by build `--env` flag. If you want to override this behavior and always use a 33 | specific webpack mode configuration, explicitly specify `"mode"` in the build 34 | configuration. 35 | 36 | ### Specifying source files 37 | 38 | For specifying value of `src` you can use any valid glob pattern: 39 | 40 | - a string value for a single file, example: `"src/index.js"` 41 | - array of files, example: `["src/index.js", "src/popup.js"]` 42 | - a path with wildcard, for example: `"src/*.js"` 43 | - You may also use `!` as a way to negate inclusion of file(s) 44 | 45 | See ["Explaining Globs"](https://gulpjs.com/docs/en/getting-started/explaining-globs) for detailed reference. 46 | 47 | 48 | ## Example 49 | 50 | Below is a sample build configuration that will generate two JavaScript files: 51 | 52 | - First one contains exactly `src/background.js` 53 | - Second one contains all `.js` files under directories `scr/app/dir1` and `scr/app/dir2` 54 | 55 | After running build command `dist/` will contain `background.js` and `app.js`. 56 | 57 | ```json 58 | "xtbuild": { 59 | "js_bundles": [ 60 | { 61 | "name": "background", 62 | "src": "./src/background.js" 63 | }, 64 | { 65 | "name": "app", 66 | "src": [ 67 | "./src/app/dir1/**/*.js", 68 | "./src/app/dir2/**/*.js" 69 | ] 70 | } 71 | ] 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /cli/xt-test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name xt-test 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | * ```text 11 | * xt-test [--pattern] [--coverage] [--watch] 12 | * ``` 13 | * 14 | * This command will run project unit tests located in `./test` directory. 15 | * 16 | * Command sets up extension unit testing environment with ES6 syntax support that is pre-initialized 17 | * with [mocha](https://mochajs.org/), [chai](https://www.chaijs.com/) and expect. 18 | * [nyc](https://www.npmjs.com/package/nyc) is used for computing code coverage. 19 | * The following browser APIs are also initialized: `window`, `document`, `chrome`. 20 | * Window is setup using [jsdom-global](https://www.npmjs.com/package/jsdom-global) and 21 | * chrome using [sinon-chrome](https://www.npmjs.com/package/sinon-chrome). 22 | * 23 | * You may extend this test environment within a single project. This is simply the base setup 24 | * for running unit tests. Or create your own testing environment at project level if this is 25 | * not suitable. 26 | */ 27 | 28 | const util = require('util'); 29 | const path = require('path'); 30 | const program = require('commander'); 31 | const pkg = require('../package.json'); 32 | const exec = require('child_process').exec; 33 | const texts = require('./texts').xtTest; 34 | 35 | process.chdir(process.cwd()); 36 | 37 | program 38 | .version(pkg.version) 39 | .option('-p --pattern ', texts.argPattern) 40 | .option('-c --coverage', texts.argCoverage) 41 | .option('-w --watch', texts.argWatch) 42 | .parse(process.argv); 43 | 44 | const {pattern, watch} = program.opts(); 45 | const rootSuite = path.resolve(process.cwd(), 46 | 'node_modules', pkg.name, 'cli', 'rootsuite.js'); 47 | 48 | const proc = exec([ 49 | 50 | // use nyc && mocha 51 | 'nyc mocha', 52 | 53 | // where to look for tests 54 | pattern ? pattern : './test/**/*.js', 55 | 56 | // setup test environment 57 | util.format('--file "%s"', rootSuite), 58 | 59 | // enable watch 60 | watch ? '--watch' : '', 61 | 62 | // babel 63 | '--require @babel/register', 64 | 65 | // output colors 66 | '--colors' 67 | 68 | ].join(' ')); 69 | 70 | proc.stdout.on('data', data => { 71 | process.stdout.write(data.toString()); 72 | }); 73 | 74 | proc.stderr.on('data', data => { 75 | process.stdout.write(data.toString()); 76 | }); 77 | 78 | // exit parent process with the unit test result code 79 | proc.on('exit', process.exit); 80 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | # Run every day at 2200 EST 22 | - cron: '0 2 * * *' 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'javascript' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 38 | # Learn more: 39 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 40 | 41 | steps: 42 | - name: Checkout repository 43 | uses: actions/checkout@v2 44 | 45 | # Initializes the CodeQL tools for scanning. 46 | - name: Initialize CodeQL 47 | uses: github/codeql-action/init@v1 48 | with: 49 | languages: ${{ matrix.language }} 50 | # If you wish to specify custom queries, you can do so here or in a config file. 51 | # By default, queries listed here will override any specified in a config file. 52 | # Prefix the list here with "+" to use these queries and those in the config file. 53 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Extension CLI • User Guide 2 | site_description: Command-line build tools for chromium extension development. 3 | site_author: '@mobilefirstllc' 4 | docs_dir: ./guide 5 | site_url: https://oss.mobilefirst.me/extension-cli/ 6 | repo_url: https://github.com/MobileFirstLLC/extension-cli 7 | repo_name: "extension-cli" 8 | edit_uri: blob/master/guide/ 9 | use_directory_urls: true 10 | 11 | nav: 12 | - "Getting Started": 13 | - Intro: index.md 14 | - Installation: 01-getting-started.md 15 | - Configuration: 02-configuration.md 16 | - "Commands": 17 | - 'extension-cli': 08-xt-create.md 18 | - 'xt-build': 19 | - Overview: 03-xt-build.md 20 | - Manifest: 03-xt-build-manifest.md 21 | - Building scripts: 03-xt-build-scripts.md 22 | - Building styles: 03-xt-build-styles.md 23 | - Copying files: 03-xt-build-copy.md 24 | - Localization: 03-xt-build-locales.md 25 | - Static assets: 03-xt-build-assets.md 26 | - Commands: 03-xt-build-cmds.md 27 | - 'xt-clean': 04-xt-clean.md 28 | - 'xt-docs': 29 | - Configuration: 05-xt-docs.md 30 | - Templates: 05-xt-docs-templates.md 31 | - 'xt-sync': 06-xt-sync.md 32 | - 'xt-test': 07-xt-test.md 33 | - "Releases": 34 | - "Version 1.x (latest)": 09-release-notes.md 35 | - "Version 0.x": 09-release-notes-0.md 36 | - "Developer Resources": 37 | - Helpful References: 12-helpful.md 38 | - CLI Development: 39 | - Overview: 13-cli-development.md 40 | - Environment Setup: 13-dev-env.md 41 | - Editing User Guide: 14-user-guide.md 42 | - "Source Code": 43 | - Github: https://github.com/MobileFirstLLC/extension-cli 44 | 45 | extra_css: 46 | - assets/custom.css 47 | extra_javascript: 48 | - "https://buttons.github.io/buttons.js" 49 | - "https://www.googletagmanager.com/gtag/js?id=G-6XB4XDVPX3" 50 | - assets/custom.js 51 | 52 | theme: 53 | name: material 54 | custom_dir: ./guide/overrides 55 | logo: '/extension-cli/assets/images/guide_icon.svg' 56 | favicon: '/extension-cli/assets/images/favicon.png' 57 | features: 58 | - navigation.tabs 59 | - navigation.tabs.sticky 60 | - navigation.expand 61 | palette: 62 | scheme: slate 63 | primary: amber 64 | accent: amber 65 | font: 66 | text: Inter 67 | extra: 68 | disqus: xyz # enable comments 69 | 70 | markdown_extensions: 71 | - admonition 72 | - pymdownx.inlinehilite 73 | - pymdownx.superfences 74 | - pymdownx.snippets 75 | - pymdownx.magiclink 76 | - pymdownx.snippets 77 | - pymdownx.highlight: 78 | use_pygments: true 79 | linenums: true 80 | linenums_style: pymdownx.inline 81 | - meta 82 | -------------------------------------------------------------------------------- /cli/xt-sync.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name xt-sync 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | * ```text 11 | * xt-sync 12 | * ``` 13 | * 14 | * The purpose of this command is to upgrade configuration files of 15 | * a stale project to latest version, where this CLI tool will provide 16 | * updated project configuration files. If the config files have been 17 | * modified heavily for the project, it is not advisable to upgrade them 18 | * in this manner. Instead you should upgrade such configs manually. 19 | */ 20 | 21 | const path = require('path'); 22 | const prompts = require('prompts'); 23 | const program = require('commander'); 24 | const pkg = require('../package.json'); 25 | const texts = require('./texts').xtSync; 26 | const Utilities = require('./utilities').Utilities; 27 | 28 | // list available options 29 | const files = { 30 | actions: {title: texts.argActions, path: '../config/actions.yml', out: 'build.yml', dir: '.github/workflows'}, 31 | gitlab: {title: texts.argGitlab, path: '../config/gitlab.yml', out: '.gitlab-ci.yml'}, 32 | travis: {title: texts.argTravis, path: '../config/travis.yml', out: '.travis.yml'}, 33 | eslint: {title: texts.argLint, path: '../config/init/eslint.json', out: '.eslintrc.json'}, 34 | gitignore: {title: texts.gitignore, path: '../config/ignore', out: '.gitignore'} 35 | }; 36 | 37 | // generate the options to display to user 38 | const options = [{ 39 | type: 'multiselect', 40 | name: 'options', 41 | message: texts.instructions, 42 | choices: Object.entries(files).map( 43 | ([key, {title}]) => ({title, value: key})) 44 | }]; 45 | 46 | program 47 | .name('xt-sync') 48 | .option('-a --all', 'deprecated: call xt-sync without flags') 49 | .version(pkg.version) 50 | .parse(process.argv); 51 | 52 | (async () => { 53 | 54 | const onCancel = () => process.exit(0); 55 | // noinspection JSUnresolvedVariable 56 | const response = (await prompts(options, {onCancel})).options; 57 | 58 | // copy selected options from config -> project 59 | Object.entries(files).map(([key, value]) => { 60 | 61 | if (response.indexOf(key) > -1) { 62 | const relativePath = path.resolve(__dirname, value.path); 63 | const content = Utilities.readFile(relativePath); 64 | const outPath = path.join(process.cwd(), 65 | (value.dir ? path.join(value.dir, value.out) : value.out)); 66 | 67 | if (value.dir) Utilities.createDir(path.join(process.cwd(), value.dir)); 68 | Utilities.writeFile(outPath, content); 69 | console.log(texts.updateSuccess(value.out)); 70 | } 71 | }); 72 | })(); 73 | -------------------------------------------------------------------------------- /cli/xt-clean.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name xt-clean 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | *```text 11 | * xt-clean [--modules] [--idea] [--vscode] 12 | * ``` 13 | * 14 | * Clean operation iterates over files and directories listed in the 15 | * project `.gitignore` file, and removes all ignored files and 16 | * directories, except `node_modules`, `.idea/`, and `.vscode`. To remove these 17 | * directories, you must explicitly pass a flag to delete each one of them. 18 | */ 19 | 20 | const fs = require('fs'); 21 | const del = require('del'); 22 | const path = require('path'); 23 | const readline = require('readline'); 24 | const program = require('commander'); 25 | const pkg = require('../package.json'); 26 | const ignore = path.join(process.cwd(), '.gitignore'); 27 | const Utilities = require('./utilities').Utilities; 28 | const texts = require('./texts').xtClean; 29 | 30 | let counter = 0; 31 | 32 | program 33 | .version(pkg.version) 34 | .option('-m --modules', texts.argModules) 35 | .option('-i --idea', texts.argIdea) 36 | .option('-v --vscode', texts.argVS) 37 | .parse(process.argv); 38 | 39 | if (!Utilities.fileExists(ignore)) { 40 | console.log(texts.onConfigError(ignore)); 41 | process.exit(0); 42 | } 43 | 44 | const {modules, idea, vscode} = program.opts(); 45 | 46 | readline.createInterface({input: fs.createReadStream(ignore)}) 47 | .on('line', function (line) { 48 | 49 | // never clean these 50 | if (line.trim().indexOf('#') === 0 || 51 | line.trim().indexOf('.env') === 0 || 52 | !(line || '').trim().length) { 53 | return false; 54 | } 55 | 56 | // clean these only if flagged 57 | if ((line.indexOf('.idea') > -1 && !idea) || 58 | (line.indexOf('.vscode') > -1 && !vscode) || 59 | (line.indexOf('node_modules') > -1 && !modules)) { 60 | return false; 61 | } 62 | 63 | // otherwise clean if exists 64 | const basePath = path.join(process.cwd(), line); 65 | 66 | if (fs.existsSync(basePath)) { 67 | try { 68 | if (fs.lstatSync(basePath).isDirectory()) { 69 | del.sync(path.join(basePath, '/*')); 70 | } 71 | if (fs.existsSync(basePath)) { 72 | del.sync(basePath); 73 | } 74 | console.log(texts.onCleanFile(line)); 75 | counter++; 76 | } catch (e) { 77 | console.log(texts.onCleanError(e, line)); 78 | } 79 | } 80 | return true; 81 | }) 82 | .on('close', () => { 83 | console.log(texts.result(counter)); 84 | process.exit(0); 85 | }); 86 | -------------------------------------------------------------------------------- /cli/xt-build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name xt-build 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | * ```text 11 | * xt-build --env {prod|dev} --platform {chrome|firefox} [--config filename] [--watch] 12 | * ``` 13 | * 14 | * Build command generates a dist/ directory that can be 15 | * debugged in the browser. When called with production env flag, `-e prod`, 16 | * this command will minify and compile a `release.zip` file that can be 17 | * uploaded to extension marketplace for distribution. 18 | */ 19 | 20 | const util = require('util'); 21 | const path = require('path'); 22 | const program = require('commander'); 23 | const Spinner = require('cli-spinner').Spinner; 24 | const exec = require('child_process').exec; 25 | const pkg = require('../package.json'); 26 | const env = {prod: 'prod', dev: 'dev'}; 27 | const platform = {chrome: 'chrome', firefox: 'firefox'}; 28 | const texts = require('./texts').xtBuild; 29 | const gulpfile = path.resolve(__dirname, './gulpfile.js'); 30 | 31 | program 32 | .version(pkg.version) 33 | .option('-e --env ', texts.envArg, /^(dev|prod)$/i, env.prod) 34 | .option('-p --platform ', texts.platformArg, /^(chrome|firefox)$/i, platform.chrome) 35 | .option('-c --config ', texts.configFileArg, /^(.*)$/i) 36 | .option('-w --watch', texts.watchArg) 37 | .parse(process.argv); 38 | 39 | const {watch, env: programEnv, config, platform: platformEnv} = program.opts(); 40 | const spinner = new Spinner(' %s '); 41 | 42 | spinner.start(); 43 | 44 | const proc = exec([ 45 | 46 | // run either watch or default 47 | watch ? 'gulp watch' : 'gulp', 48 | 49 | // path to gulpfile (in current dir) 50 | util.format('--gulpfile "%s"', gulpfile), 51 | 52 | // path to build configuration file 53 | util.format('--config "%s"', path.resolve(process.cwd(), config || './.xtbuild.json')), 54 | 55 | // path to project's package.json 56 | util.format('--pkg', path.resolve(process.cwd(), './package.json')), 57 | 58 | // explicitly tell gulp to use cwd (necessary) 59 | util.format('--cwd', path.resolve(process.cwd())), 60 | 61 | // ENV is either "--dev" or "--prod" 62 | util.format('--%s', programEnv), 63 | 64 | // target platform is "--chrome" or "--firefox" 65 | util.format('--%s', platformEnv), 66 | 67 | // use colors in terminal output 68 | '--colors' 69 | 70 | ].join(' ')); 71 | 72 | proc.stdout.on('data', (data) => { 73 | if (data && data.indexOf('Using gulpfile') === 0) return; 74 | spinner.stop(true); 75 | process.stdout.write(data.toString()); 76 | }); 77 | 78 | proc.stderr.on('data', (data) => { 79 | spinner.stop(true); 80 | process.stdout.write(data.toString()); 81 | }); 82 | 83 | proc.on('exit', (err) => { 84 | spinner.stop(true); 85 | console.log(!err ? 86 | texts.onBuildSuccess() : 87 | texts.onBuildError()); 88 | }); 89 | -------------------------------------------------------------------------------- /guide/03-xt-build-locales.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | 3 | * * * 4 | 5 |

Localization enables translating extension to different languages.

6 | 7 | * * * 8 | 9 | If the extension supports multiple languages, you can customize 10 | extension localization by specifying two build keys: `locales_dir` and `locales_list`. 11 | 12 | ## Locales directory 13 | 14 | `locales_dir` key specifies where in project directory to look for locales files. 15 | The default `locales_dir` is `./assets/locales/`. 16 | If you prefer a different directory structure, override this default value. 17 | 18 | 19 | ## Locales list 20 | 21 | `locales_list` is an array that lists all supported languages, and such that 22 | the values of this array correspond to subdirectories under `locales_dir`. Only 23 | locales directories specified in this array will be included in the build, which 24 | allows excluding incomplete translations from build until they are ready to be 25 | included. 26 | 27 | The default value of `locales_list` is `["en"]`. 28 | 29 | Refer [to this list of language codes](https://developers.google.com/admin-sdk/directory/v1/languages) 30 | when specifying value for this configuration. 31 | 32 | You may split localization files into multiple `.json` files within the 33 | language-specific directory to improve maintainability. During builds 34 | all files within a language directory will be automatically combined into a single 35 | `messages.json` which is expected from a browser extensions. 36 | 37 | Recommended reading: [learn how to internationalize extensions](https://developer.chrome.com/extensions/i18n). 38 | 39 | ## Example 40 | 41 | This configuration shows build configuration with custom path and multiple language 42 | outputs. 43 | 44 | Build configuration 45 | 46 | ```json 47 | "xtbuild": { 48 | "locales_list": ["en","fr","pl"], 49 | "locales_dir": "./my/custom/locales/path/" 50 | } 51 | ``` 52 | 53 | Corresponding project level file structure: 54 | 55 | File Path | Description 56 | --- | --- 57 | └ **`/my/custom/locales/path/`** | locales directory 58 |         └─ `en`/messages.json | English dictionary 59 |         └─ `fr`/myFile.json | French dictionary 60 |         └─ `pl/` | 61 |                 └─ app.json | Polish dictionary, part 1 62 |                 └─ options.json | Polish dictionary, part 2 63 |         └─ `de`/messages.json | German dictionary 64 | 65 | Build behavior: 66 | 67 | - `myFile.json` will be renamed to `messages.json` 68 | - `app.json` and `options.json` will me concatenated and renamed to `messages.json` 69 | - extension will be available in 3 languages; `dist/` directory will contain: 70 | - `_locales/en/messages.json` 71 | - `_locales/fr/messages.json` 72 | - `_locales/pl/messages.json` 73 | - German dictionary is excluded from build output because it is not included in `locales_list` 74 | -------------------------------------------------------------------------------- /guide/03-xt-build-copy.md: -------------------------------------------------------------------------------- 1 | # Copying Files 2 | 3 | * * * 4 | 5 |

Copying enables including files in the output without modifying them during build. 6 | This includes use case where you want to skip compilation and linting of scripts or stylesheets.

7 | 8 | * * * 9 | 10 | !!! info "Copying static assets" 11 | By default, all static assets under `assets/` directory will be automatically 12 | copied to output directory during builds. 13 | 14 | 15 | `copyAsIs` allows you to specify an array of files and/or directories which should be included in build output 16 | without modification. Files to copy can be located anywhere in your project. The directories to copy are expected to be inside `/src` directory. 17 | 18 | The build command will copy: 19 | 20 | - specified **files** without any modification and add them to the root of the output directory; 21 | directory path for files will be flattened. 22 | 23 | - specified **directories** and their contents without modification and without flattening the path 24 | 25 | If the copy command fails to locate the specified file or directory, it will not 26 | raise an issue, the copy will simply not occur. 27 | 28 | ## Example 1: File copy 29 | 30 | Sample configuration for skipping compilation of pre-compiled files. 31 | 32 | This configuration will copy material theme directly from `node_modules` 33 | and include it in the `dist` directory. It will also copy a project level `special.js` 34 | script into the output directory. No modification will occur to these files during the build step. 35 | 36 | After the build `dist/` directory root will include `material.min.js` and `special.js`. 37 | 38 | ```json 39 | "xtbuild": { 40 | "copyAsIs": [ 41 | "./node_modules/material-design-lite/material.min.js", 42 | "./some/path/special.js" 43 | ] 44 | } 45 | ``` 46 | 47 | 48 | ## Example 2: Directory copy 49 | 50 | When copying directories, directory will maintain its structure. Directory to copy must be 51 | inside `src` directory. When specifying a directory use a match pattern, either `*` or `**/*`: 52 | 53 | This build configuration will perform following copy operations: 54 | 55 | - `/src/directory/*` copies all files under `/src/directory/` to `dist/` root (excludes nested directories). 56 | 57 | - `/src/nested/directory/**/*` recursively copies all files and nested directories to `dist/` root without flattening path. 58 | 59 | 60 | ```json 61 | "xtbuild": { 62 | "copyAsIs": [ 63 | "/src/directory/*", 64 | "/src/nested/directory/**/*" 65 | ] 66 | } 67 | ``` 68 | 69 | ## Disable Linting 70 | 71 | When including precompiled javascript files to an extension project, you should also 72 | disable linting for those files to avoid unnecessary warnings. In `package.json`, 73 | add the file paths to the list of ignored files to prevent them from being linted: 74 | 75 | ```json 76 | { 77 | "eslintIgnore": [ 78 | "test/**/*", 79 | "./some/path/special.js" 80 | ] 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /guide/13-cli-development.md: -------------------------------------------------------------------------------- 1 | --- 2 | disqus: "False" 3 | --- 4 | 5 | # Extension CLI Development 6 | 7 | - This CLI is built with Node.Js, written in JavaScript, and uses numerous packages listed below. 8 | - The source code is available on [Github](https://github.com/MobileFirstLLC/extension-cli). 9 | - Releases are published on [NPM](https://www.npmjs.com/package/extension-cli). 10 | - This user guide is built with [MkDocs](https://www.mkdocs.org/) and [MkDocs material theme](https://squidfunk.github.io/mkdocs-material/). 11 | - CI/CD by [Travis CI](https://travis-ci.org/MobileFirstLLC/extension-cli) and documentation served by [Github Pages](https://pages.github.com/). 12 | 13 | 14 | ## Project Organization 15 | 16 | Path | Description 17 | --- | --- 18 | └ **.github** | Github config files and markdown 19 | └ **cli** | all available commands are defined here 20 | └ **config** | Resources and config files used by the commands in `cli` 21 | └ **guide** | User guide 22 | └ **test** | CLI unit tests 23 | └ `/*` | Application root; various project config files 24 | 25 | * * * 26 | 27 | To setup a local dev environment and develop the CLI application, see 28 | 29 | [Environment Setup →](13-dev-env.md) 30 | 31 | * * * 32 | 33 | ## Dependencies 34 | 35 | Extension CLI is built with the following dependencies: 36 | 37 | | # | Package name | Purpose | 38 | | --- | --- | --- | 39 | | 1. | `@babel/preset-env` | for modern JavaScript syntax | 40 | | 2. | `@babel/register` | for unit testing | 41 | | 3. | `chai` | BDD/TDD assertion library for unit testing | 42 | | 4. | `chalk` | Add color to terminal output | 43 | | 5. | `cli-spinner` | Terminal spinner to indicated progress | 44 | | 6. | `commander` | handle CLI input arguments | 45 | | 7. | `del` | for clearing generated files | 46 | | 8. | `eslint` | for linting JavaScript | 47 | | 9. | `gulp` | for running build script | 48 | | 10. | `gulp-change` | JSON file content manipulations | 49 | | 11. | `gulp-clean-css` | Minify CSS | 50 | | 12. | `gulp-concat` | Concatenates files (used for CSS) | 51 | | 13. | `gulp-htmlmin` | Removes whitespace from HTML | 52 | | 14. | `gulp-jsonminify` | minify JSON files (manifest, locales) | 53 | | 15. | `gulp-load-plugins` | to load various gulp plugins | 54 | | 16. | `gulp-merge-json` | merge locales files | 55 | | 17. | `gulp-rename` | rename files during builds | 56 | | 18. | `gulp-sass` | process SASS files during builds | 57 | | 19. | `gulp-zip` | generate zip files | 58 | | 20. | `jsdoc` | generate docs | 59 | | 21. | `jsdom` | mock DOM in Node.js env | 60 | | 22. | `jsdom-global` | adds window, document to unit testing env | 61 | | 23. | `mocha` | unit testing framework | 62 | | 24. | `nyc` | unit testing code coverage tool | 63 | | 25. | `prompts` | create CLI prompts with interactive selectors | 64 | | 26. | `sass` | compile SASS files during builds | 65 | | 27. | `sinon` | JavaScript test spies, stubs and mocks | 66 | | 28. | `sinon-chrome` | unit testing for extensions | 67 | | 29. | `webpack-stream` | build javascript files | 68 | | 30. | `yargs` | parse keyword args | 69 | -------------------------------------------------------------------------------- /cli/rootsuite.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 3 | * This rootsuite sets up unit testing environment 4 | */ 5 | 6 | const sinon = require('sinon'); 7 | const chrome = require('sinon-chrome'); 8 | const chai = require('chai'); 9 | const argv = require('yargs').argv; 10 | const texts = require('./texts').xtTest; 11 | const enableWatch = argv.watch; 12 | 13 | /** 14 | * Create sinon sandbox 15 | * 16 | * Sandboxes removes the need to keep track of 17 | * every fake created, which greatly simplifies cleanup. 18 | * 19 | * @see {@link https://sinonjs.org/releases/latest/sandbox/} 20 | */ 21 | const sandbox = sinon.createSandbox(); 22 | 23 | /** 24 | * Setup global DOM 25 | */ 26 | global.jsdom = require('jsdom-global')(); 27 | 28 | /** 29 | * Before running any tests - 30 | * setup the test environment 31 | */ 32 | before(function () { 33 | process.env.NODE_ENV = 'test'; 34 | global.sinon = sinon; 35 | global.chai = chai; 36 | global.expect = chai.expect; 37 | global.sandbox = sandbox; 38 | window.sandbox = sandbox; 39 | global.chrome = chrome; 40 | window.chrome = chrome; 41 | 42 | // output list of namespaces that 43 | // are available in test environment 44 | console.log(texts.onRootSetup( 45 | 'window,chrome,chai,expect,sandbox(sinon)' 46 | .split(','))); 47 | }); 48 | 49 | /** 50 | * After each test - 51 | * reset chrome and sandbox 52 | */ 53 | afterEach(function () { 54 | chrome.flush(); 55 | sandbox.restore(); 56 | }); 57 | 58 | /** 59 | * After all tests - 60 | * Clean up everything that was initially set up 61 | */ 62 | after(function () { 63 | // important! do not clean when running in watch mode 64 | if (enableWatch) return; 65 | 66 | delete global.jsdom; 67 | delete global.sinon; 68 | delete global.chrome; 69 | delete global.chai; 70 | delete global.expect; 71 | delete global.sandbox; 72 | delete window.sandbox; 73 | delete window.chrome; 74 | delete global.mouseEvent; 75 | delete global.dispatchEvent; 76 | }); 77 | 78 | /** 79 | * Enable mouse events globally during unit testing 80 | * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent} 81 | * 82 | * @param {String} type - event type 83 | * @param {Object} props - optional properties 84 | * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent} 85 | * @return {Event} - the event 86 | */ 87 | global.mouseEvent = function (type, props) { 88 | return new MouseEvent(type, {...props}); 89 | }; 90 | 91 | /** 92 | * Enable dispatching an event on some DOM element during unit testing 93 | * 94 | * @param {EventTarget} target - element on which to dispatch event 95 | * @param {Event} event - the event to dispatch 96 | * @return {Event} - the event 97 | */ 98 | global.dispatchEvent = function (target, event) { 99 | if (target.dispatchEvent) { 100 | target.dispatchEvent(event); 101 | } else if (target.fireEvent) { 102 | target.fireEvent('on' + event.type, event); 103 | } 104 | return event; 105 | }; 106 | -------------------------------------------------------------------------------- /guide/12-helpful.md: -------------------------------------------------------------------------------- 1 | # Helpful References 2 | 3 | * * * 4 | 5 |

Collection of generally helpful links for extension development.

6 | 7 | * * * 8 | 9 | **Getting started guides**
10 | [Chrome](https://developer.chrome.com/extensions/getstarted) • 11 | [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions#get_started) • 12 | [Safari](https://developer.apple.com/documentation/safariservices/safari_web_extensions) • 13 | [Edge](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/) • 14 | [Opera](https://dev.opera.com/extensions/getting-started/) 15 | 16 | **Extension manifest references**
17 | [Chrome v3](https://developer.chrome.com/extensions/manifest) • 18 | [Chrome v2 ⚠️](https://developer.chrome.com/docs/extensions/mv2/manifest/) • 19 | [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json) • 20 | [Edge](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/getting-started/manifest-format) • 21 | [Opera](https://dev.opera.com/extensions/manifest/) 22 | 23 | **Lists of browser APIs**
24 | [Chrome](https://developer.chrome.com/extensions/api_index) • 25 | [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API#javascript_api_listing) • 26 | [Opera](https://dev.opera.com/extensions/apis/) 27 | 28 | 29 | **Cross-browser compatibility charts**
30 | [Manifest compatibility](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Browser_compatibility_for_manifest.json) • 31 | [JavaScript API support](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Browser_support_for_JavaScript_APIs) 32 | 33 | **Internationalization**
34 | [Supporting multiple languages](https://developer.chrome.com/extensions/i18n) • 35 | [Language locales list](https://developer.chrome.com/docs/webstore/i18n/#choosing-locales-to-support) 36 | 37 | **Extension Publishing Guides**
38 | [Chrome](https://developer.chrome.com/webstore/publish) • 39 | [Edge](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/create-dev-account) • 40 | [Firefox](https://extensionworkshop.com/documentation/publish/submitting-an-add-on/) • 41 | [Opera](https://dev.opera.com/extensions/publishing-guidelines/) • 42 | [Safari](https://developer.apple.com/documentation/safariservices/safari_web_extensions/distributing_your_safari_web_extension) 43 | 44 | **Marketplace Image Guidelines**
45 | [Chrome](https://developer.chrome.com/webstore/images) • 46 | [Edge](https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/publish/publish-extension#step-5-add-store-listing-details-for-your-extension) 47 | 48 | **Marketplace APIs**
49 | [Chrome Web Store](https://developer.chrome.com/webstore/api_index) 50 | 51 | 52 | **Other Resources** 53 | 54 | - [Chrome Extension Samples](https://github.com/GoogleChrome/chrome-extensions-samples) - examples of feature implementations 55 | - [Awesome WebExtensions](https://github.com/fregante/Awesome-WebExtensions) - curated list of resources and tools 56 | -------------------------------------------------------------------------------- /guide/07-xt-test.md: -------------------------------------------------------------------------------- 1 | # xt-test 2 | 3 | 4 | * * * 5 | 6 |

xt-test command runs unit tests.

7 | 8 | * * * 9 | 10 | This command will setup extension testing environment that is pre-initialized 11 | with [mocha](https://mochajs.org/), [chai](https://www.chaijs.com/), 12 | and expect. [nyc](https://www.npmjs.com/package/nyc) is used for computing code coverage. 13 | The following browser features are initialized for convenience: `window`, `chrome`, `document`. 14 | Window is setup using [jsdom-global](https://www.npmjs.com/package/jsdom-global) and 15 | chrome using [sinon-chrome](https://www.npmjs.com/package/sinon-chrome). 16 | 17 | By default this command looks for unit tests in `test/` directory, in any file ending with `.js`. 18 | Mocha will execute with babel, meaning you can use this test environment with modern JavaScript 19 | syntax. 20 | 21 | You may extend this unit testing environment within an extension project. 22 | This is simply a base setup for running unit tests for web extensions. 23 | You may also create your very own test environment if this setup is not suitable for your project. 24 | 25 | ## Commands 26 | 27 | Braces `{ }` indicate that the user must choose one (and only one) of the items inside the braces. 28 | 29 | 30 | **Run unit tests (default)** 31 | 32 | ```bash 33 | xt-test 34 | ``` 35 | 36 | **Configure custom test directory path or match pattern** 37 | 38 | Defaults to `./test/**/*.js` if not specified 39 | 40 | ```bash 41 | xt-test {-p|--pattern} "./test/**/*.js" 42 | ``` 43 | 44 | **Execute tests and keep watching changes** 45 | 46 | ```bash 47 | xt-test {-w|--watch} 48 | ``` 49 | 50 | **Get help using this command** 51 | 52 | ```bash 53 | xt-test --help 54 | ``` 55 | 56 | ## Package.json scripts 57 | 58 | After installing extension-cli, you can run these commands from a terminal using syntax `npx xt-test`. 59 | 60 | You may also add an option to `packages.json` scripts section as shown below, then 61 | 62 | - run unit tests from terminal: `npm run test` 63 | - run unit tests and save coverage to file: `npm run coverage`. 64 | 65 | 66 | ```json 67 | "scripts":{ 68 | "test": "xt-test", 69 | "coverage": "nyc --reporter=lcov npm run test" 70 | } 71 | ``` 72 | 73 | ## Reporting Coverage 74 | 75 | ### Coveralls 76 | 77 | The general setup is: 78 | 79 | 1. Install [coveralls](https://www.npmjs.com/package/coveralls) as a dev dependency at project level: 80 | 81 | ``` 82 | npm install coveralls --save-dev 83 | ``` 84 | 85 | 2. Run unit tests with coverage report during CI/CD workflow, then pipe the result to coveralls: 86 | 87 | ``` 88 | nyc --reporter=lcov npm run test | coveralls 89 | ``` 90 | 91 | 92 | If using Github actions, use [Coveralls Github action](https://github.com/marketplace/actions/coveralls-github-action) to report results. Example: 93 | 94 | ```yaml 95 | - name: Execute unit tests w/ coverage 96 | run: nyc --reporter=lcov npm run test # or: npm run coverage 97 | 98 | - name: Report coverage 99 | uses: coverallsapp/github-action@master 100 | with: 101 | github-token: ${{ secrets.GITHUB_TOKEN }} 102 | ``` 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /.github/code_of_conduct.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@mobilefirst.me. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /guide/09-release-notes-0.md: -------------------------------------------------------------------------------- 1 | --- 2 | disqus: "False" 3 | --- 4 | 5 | 6 | ### 0.11.9 (2021-04-04) 7 | 8 | * **xt-build**: enable customizing release filename [PR #37](https://github.com/MobileFirstLLC/extension-cli/pull/37) 9 | * update dependencies [PR #39](https://github.com/MobileFirstLLC/extension-cli/pull/39) 10 | * improve user guide organization and UI [PR #38](https://github.com/MobileFirstLLC/extension-cli/pull/38), [PR #40](https://github.com/MobileFirstLLC/extension-cli/pull/40) 11 | 12 | ### 0.11.8 (2021-03-12) 13 | 14 | * **xt-build**: support copying directories as-is [#32](https://github.com/MobileFirstLLC/extension-cli/issues/32) 15 | * **xt-build**: append '.css' at the end of name if not specified by the user [PR #36](https://github.com/MobileFirstLLC/extension-cli/pull/36) 16 | 17 | ### 0.11.7 (2021-03-02) 18 | 19 | * **xt-docs**: make watch recursive on watched directories 20 | * **xt-docs**: add tutorials directory to watch list (if exists) 21 | * **xt-docs**: display error when docs command fails 22 | 23 | ### 0.11.6 (2021-02-25) 24 | 25 | * **xt-docs:** add watch mode to docs command, see: [#23](https://github.com/mobilefirstllc/extension-cli/issues/23) 26 | 27 | ### 0.11.5 (2021-02-24) 28 | 29 | * **xt-test:** unit code result reporting fix, see: [#26](https://github.com/mobilefirstllc/extension-cli/issues/26) 30 | 31 | ### 0.11.3 (2021-01-27) 32 | 33 | * **xt-build:** file watch fix 34 | 35 | ### 0.11.2 (2021-01-08) 36 | 37 | * **xt-build:** command path fix 38 | 39 | ### 0.11.1 (2021-01-06) 40 | 41 | * **xt-build:** allow specifying custom build commands 42 | * **xt-create:** fix image generation issue 43 | * update packages 44 | 45 | 46 | ### 0.10.1 (2020-12-15) 47 | 48 | * update test configs 49 | * check if gitignore exists before xt-clean 50 | * **xt-create:** change default icon to high contrast 51 | * update packages 52 | 53 | ### 0.9.4 (2020-11-29) 54 | 55 | * extension-cli: fix typo 56 | * update packages 57 | 58 | ### 0.9.3 (2020-10-31) 59 | 60 | * xt-clean: improve xt-clean command handling of files 61 | * change icon 62 | * update docs 63 | 64 | ### 0.9.1 (2020-10-11) 65 | 66 | - fix: xt-docs config keys replace when value is an array 67 | 68 | ### 0.9.0 (2020-10-05) 69 | 70 | - xt-test: add configurable test path 71 | - xt-create: sanitize package name 72 | - update packages 73 | - xt-clean: refactor command 74 | - xt-docs: refactor docs command 75 | - xt-sync: refactor sync command 76 | 77 | ### 0.8.16 (2020-08-09) 78 | 79 | - update packages 80 | 81 | ### 0.8.15 (2020-08-04) 82 | 83 | - update packages 84 | 85 | ### 0.8.14 (2020-08-01) 86 | 87 | - update xt-create 88 | 89 | ### 0.8.13 (2020-07-26) 90 | 91 | - updated packages 92 | 93 | ### 0.8.12 (2020-05-26) 94 | 95 | - update build command 96 | 97 | ### 0.8.11 (2020-05-25) 98 | 99 | - fix issue with create command docs configs 100 | - add new/missing docs dependency 101 | 102 | ### 0.8.10 (2020-05-25) 103 | 104 | - `xt-build` bug fixes 105 | - Made webpack options configurable, to enable adding loaders etc. 106 | - Upgraded project dependencies 107 | 108 | ### 0.8.9 (2020-04-10) 109 | 110 | - Implemented command to create new extension 111 | - Updated docs to reflect this new command 112 | 113 | ### 0.8.8 (2020-04-08) 114 | 115 | - Upgraded project dependencies 116 | 117 | ### 0.8.7 (2020-01-17) 118 | 119 | - Upgraded project dependencies 120 | - Fixed scripts build step (changed webpack options) 121 | 122 | ### 0.8.6 (2019-12-21) 123 | 124 | - Initial release for this publisher 125 | - Migrated project from older source code 126 | - Upgraded all packages 127 | - Migrated build to use Gulp v4 128 | -------------------------------------------------------------------------------- /guide/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | disqus: "False" 3 | --- 4 | 5 | # Extension CLI 6 | 7 |

is a command-line build tool for developing 8 | chromium browser extensions fast and in a standardized way. It provides a systematic way 9 | to organize, build, test and document extension projects.

10 | 11 | * * * 12 | 13 | ## Features 14 | 15 | 🖥️   **Javascript Bundling** 16 |
       Compiles, bundles and minifies javascript files (supports ES6, ES2021 syntax)
17 | 18 | 🎨   **CSS Bundling** 19 |
       Compiles, bundles, and minifies CSS and [SASS](https://sass-lang.com/guide) files
20 | 21 | 💄   **Linting** 22 |
       lint JavaScript using [ESLint](https://eslint.org/)
23 | 24 | 📦   **ZIP Generation** 25 |
       Generates a .zip file for uploading to extension marketplaces
26 | 27 | 📝   **Document Source Code** 28 |
       Generates source code documentation using [JSDoc](https://jsdoc.app/about-getting-started.html)
29 | 30 | ⚗️   **Unit Testing** 31 |
       Sets up a unit testing environment with [mocha](https://mochajs.org), [chai](https://www.chaijs.com/), [sinon-chrome](https://github.com/acvetkov/sinon-chrome) and [js-dom](https://github.com/rstacruz/jsdom-global)
32 | 33 | ⚔️   **Cross-Browser Compatibility** 34 |
       develop extensions for Chrome, Edge, Firefox, Opera and Brave.
35 | 36 | * * * 37 | 38 | 39 | Extension CLI is made and maintained free and voluntarily by 40 | open source contributors 41 | behind several popular extensions. If you find it helpful, please share, star, or contribute to its development. 42 | 43 |
44 | 45 | Star   Fork   Issue   Watch 46 | 47 | * * * 48 | 49 | ## Getting Started 50 | 51 | **Note:** Using this CLI assumes you have Node.js installed (or [install it here](https://nodejs.org/en/download/)). 52 | 53 | Create a new extension project: 54 | 55 | ```bash 56 | npx extension-cli 57 | ``` 58 | 59 | Add CLI to an existing project: 60 | 61 | ```bash 62 | npm install extension-cli 63 | ``` 64 | 65 | More detailed [getting started guide here →](https://oss.mobilefirst.me/extension-cli/01-getting-started/) 66 | 67 | 68 | ## Command Reference 69 | 70 | Command | Description 71 | --- | --- 72 | **`xt-build`** | Run builds; env flags: `-e prod` or `-e dev` 73 | **`xt-test`**| Run unit tests 74 | **`xt-docs`**| Generate docs 75 | **`xt-clean`** | Remove generated files 76 | **`xt-sync`**| Update project config files to latest versions supplied by this CLI 77 | 78 | More detailed [command instructions and configuration options here →](https://oss.mobilefirst.me/extension-cli/03-xt-build/) 79 | 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extension-cli", 3 | "version": "1.2.5-alpha.0", 4 | "description": "CLI tool for building browser extensions", 5 | "homepage": "https://oss.mobilefirst.me/extension-cli", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/mobilefirstllc/extension-cli.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/mobilefirstllc/extension-cli.git" 12 | }, 13 | "author": { 14 | "name": "Mobile First", 15 | "email": "hello@mobilefirst.me", 16 | "url": "https://mobilefirst.me" 17 | }, 18 | "funding": [ 19 | { 20 | "type": "github", 21 | "url": "https://github.com/sponsors/MobileFirstLLC" 22 | } 23 | ], 24 | "engines": { 25 | "node": ">=12.0.0" 26 | }, 27 | "bin": { 28 | "extension-cli": "cli/xt-create.js", 29 | "xt-build": "cli/xt-build.js", 30 | "xt-docs": "cli/xt-docs.js", 31 | "xt-test": "cli/xt-test.js", 32 | "xt-clean": "cli/xt-clean.js", 33 | "xt-sync": "cli/xt-sync.js" 34 | }, 35 | "keywords": [ 36 | "chrome extensions", 37 | "web extensions", 38 | "browser extensions", 39 | "command line", 40 | "developer tools", 41 | "utility", 42 | "web", 43 | "extensions", 44 | "firefox", 45 | "mozilla", 46 | "add-ons", 47 | "google", 48 | "chrome", 49 | "opera", 50 | "edge", 51 | "brave" 52 | ], 53 | "scripts": { 54 | "test": "nyc mocha ./test/*.test.js --colors", 55 | "test:report": "npm run test -- nyc report", 56 | "test:travis": "npm run test && nyc report --reporter=text-lcov | coveralls", 57 | "alpha:test": "npx standard-version --dry-run --prerelease alpha", 58 | "patch:test": "npx standard-version --dry-run --release-as patch", 59 | "minor:test": "npx standard-version --dry-run --release-as minor", 60 | "minor:beta:test": "npx standard-version --dry-run --release-as minor --prerelease beta", 61 | "minor:alpha:test": "npx standard-version --dry-run --release-as minor --prerelease alpha", 62 | "major:alpha:test": "npx standard-version --dry-run --release-as major --prerelease alpha", 63 | "major:test": "npx standard-version --dry-run --release-as major", 64 | "alpha": "npx standard-version --prerelease alpha", 65 | "patch": "npx standard-version --release-as patch", 66 | "minor": "npx standard-version --release-as minor", 67 | "patch:alpha": "npx standard-version --release-as patch --prerelease alpha", 68 | "minor:alpha": "npx standard-version --release-as minor --prerelease alpha", 69 | "major:alpha": "npx standard-version --release-as major --prerelease alpha", 70 | "major": "npx standard-version --release-as major" 71 | }, 72 | "license": "MIT", 73 | "standard-version": { 74 | "infile": ".github/changelog.md" 75 | }, 76 | "dependencies": { 77 | "@babel/preset-env": "7.16.11", 78 | "@babel/register": "7.17.7", 79 | "chai": "4.3.6", 80 | "chalk": "4.1.2", 81 | "cli-spinner": "0.2.10", 82 | "commander": "9.0.0", 83 | "del": "6.0.0", 84 | "eslint": "8.11.0", 85 | "gulp": "4.0.2", 86 | "gulp-change": "1.0.2", 87 | "gulp-clean-css": "4.3.0", 88 | "gulp-concat": "2.6.1", 89 | "gulp-htmlmin": "5.0.1", 90 | "gulp-jsonminify": "1.1.0", 91 | "gulp-load-plugins": "2.0.7", 92 | "gulp-merge-json": "2.1.1", 93 | "gulp-rename": "2.0.0", 94 | "gulp-sass": "5.1.0", 95 | "gulp-zip": "5.1.0", 96 | "jsdoc": "3.6.10", 97 | "jsdom": "19.0.0", 98 | "jsdom-global": "3.0.2", 99 | "mocha": "9.2.2", 100 | "nyc": "15.1.0", 101 | "prompts": "2.4.2", 102 | "sass": "1.49.9", 103 | "sinon": "13.0.1", 104 | "sinon-chrome": "3.0.1", 105 | "webpack-stream": "7.0.0", 106 | "yargs": "17.3.1" 107 | }, 108 | "devDependencies": { 109 | "coveralls": "3.1.1" 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /cli/xt-docs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name xt-docs 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | * ```text 11 | * xt-docs [--config filename] [--watch] 12 | * ``` 13 | * 14 | * Docs command generates documentation for the project. This command uses 15 | * jsdocs syntax. See {@link https://jsdoc.app/index.html|About JSDoc} for more details, 16 | * including {@link https://jsdoc.app/about-configuring-jsdoc.html|configuration options here}. 17 | * The default template for the guide is JsDoc default template. You can override this template 18 | * in the project by changing `opts.template` in jsdoc config file. 19 | * 20 | * By default, this command will automatically look for configuration in the project `package.json`. 21 | * - use `"xtdocs"` key to define config options in `package.json, 22 | * - -or- add a separate configuration file `.xtdocs.json` in the project root, 23 | * - -or- explicitly provide a path to a config file. 24 | * 25 | * Use `-c` / `--config` flag to provide path and name of the configuration file. 26 | */ 27 | 28 | const fs = require('fs'); 29 | const del = require('del'); 30 | const util = require('util'); 31 | const path = require('path'); 32 | const program = require('commander'); 33 | const pkg = require('../package.json'); 34 | const exec = require('child_process').exec; 35 | const Spinner = require('cli-spinner').Spinner; 36 | const Utilities = require('./utilities').Utilities; 37 | const texts = require('./texts').xtDocs; 38 | const defaultConfig = require('../config/docs.json'); 39 | const spinner = new Spinner(' %s '); 40 | const jsdoc = './node_modules/.bin/jsdoc'; 41 | const tmpFile = path.join(process.cwd(), 42 | './node_modules', pkg.name, 'tmpDocsConfig.json'); 43 | 44 | program 45 | .version(pkg.version) 46 | .option('-c --config ', texts.configArg, /^(.*)$/i) 47 | .option('-w --watch', texts.argWatch) 48 | .parse(process.argv); 49 | 50 | const {config: configArg, watch} = program.opts(); 51 | 52 | const getConfig = (docFileName) => { 53 | const fe = Utilities.fileExists(docFileName); 54 | const temp = Utilities.readJSON(fe ? 55 | docFileName : './package.json'); 56 | 57 | return Utilities.iterateConfigs(defaultConfig, 58 | fe ? temp : temp.xtdocs); 59 | }; 60 | 61 | const buildDocs = (tmpFile, config, callback) => { 62 | spinner.start(); 63 | Utilities.writeFile(tmpFile, config); 64 | 65 | const proc = exec(util.format('"%s" -c %s', jsdoc, tmpFile)); 66 | 67 | proc.stdout.on('data', (data) => { 68 | process.stdout.write(data.toString()); 69 | }); 70 | proc.stderr.on('data', (data) => { 71 | process.stderr.write(data.toString()); 72 | }); 73 | proc.on('exit', err => { 74 | del.sync(tmpFile); 75 | spinner.stop(true); 76 | console.log(err ? texts.failure : texts.success); 77 | if (callback) callback(); 78 | }); 79 | }; 80 | 81 | const startWatch = (tmpFile, watchPaths, configStr) => { 82 | watchPaths.map(fileOrDir => { 83 | fs.watch(path.join(process.cwd(), fileOrDir), { 84 | persistent: true, recursive: true 85 | }, 86 | (curr, prev) => { 87 | // if spinning it is already running 88 | if (!spinner.isSpinning()) { 89 | buildDocs(tmpFile, configStr, false); 90 | } 91 | }); 92 | }); 93 | console.log(texts.watching); 94 | }; 95 | 96 | const config = getConfig(configArg || '.xtdocs.json'); 97 | const configString = JSON.stringify(config); 98 | const watchPaths = config.source.include.concat( 99 | config.opts.tutorials ? [config.opts.tutorials] : []); 100 | 101 | buildDocs(tmpFile, configString, _ => watch ? 102 | startWatch(tmpFile, watchPaths, configString) : 103 | process.exit(0)); 104 | -------------------------------------------------------------------------------- /cli/xt-create.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * @name extension-cli 5 | * @module 6 | * @public 7 | * 8 | * @description 9 | * 10 | *```text 11 | * npx extension-cli 12 | * ``` 13 | * 14 | * This command will create a new extension project and initial code files. 15 | * Command takes no arguments; follow prompts on screen. 16 | */ 17 | 18 | const prompts = require('prompts'); 19 | const path = require('path'); 20 | const exec = require('child_process').exec; 21 | const Spinner = require('cli-spinner').Spinner; 22 | const spinner = new Spinner(' %s '); 23 | const Utilities = require('./utilities').Utilities; 24 | const texts = require('./texts').xtCreate; 25 | const createPrompts = texts.prompts; 26 | const defaultHomepage = 'http://chrome.google.com/webstore'; 27 | const initFilesPath = '../config/init/'; 28 | 29 | /** 30 | * Run the setup script 31 | * @private 32 | */ 33 | (async () => { 34 | 35 | const promptOptions = {onCancel: () => process.exit(0)}; 36 | const response = await prompts(createPrompts.name, promptOptions); 37 | const name = response.name; 38 | const dirname = Utilities.generateDirectoryName(name); 39 | const dir = path.join(process.cwd(), `/${dirname}`); 40 | 41 | // create project directory 42 | const success = Utilities.createDir(dir); 43 | 44 | if (!success) { 45 | console.error(texts.dirError(dirname)); 46 | return process.exit(0); 47 | } 48 | 49 | const {description, homepage} = await prompts(createPrompts.optional, promptOptions); 50 | const vars = { 51 | name, description, safeName: dirname, 52 | version: '0.0.1', homepage: homepage || defaultHomepage 53 | }; 54 | const _file = fileName => path.resolve(__dirname, initFilesPath + fileName); 55 | const _readtext = path => Utilities.readAndReplaceTextFile(path, vars); 56 | const _readjson = path => Utilities.readAndReplaceJSONFile(path, vars); 57 | 58 | console.log(texts.start(dirname, name)); 59 | spinner.start(); 60 | 61 | // SETUP files structure and starter files 62 | // initialize extension image assets 63 | Utilities.createDir(dir + '/assets'); 64 | Utilities.createDir(dir + '/assets/img'); 65 | Utilities.copyFile(_file('icon.svg'), dir + '/assets/icon.svg'); 66 | Utilities.copyFile(_file('16x16.png'), dir + '/assets/img/16x16.png'); 67 | Utilities.copyFile(_file('24x24.png'), dir + '/assets/img/24x24.png'); 68 | Utilities.copyFile(_file('32x32.png'), dir + '/assets/img/32x32.png'); 69 | Utilities.copyFile(_file('128x128.png'), dir + '/assets/img/128x128.png'); 70 | 71 | // setup locales 72 | Utilities.createDir(dir + '/assets/locales'); 73 | Utilities.createDir(dir + '/assets/locales/en'); 74 | Utilities.writeFile(dir + '/assets/locales/en/messages.json', _readjson(_file('messages.json'))); 75 | 76 | // setup source code 77 | Utilities.createDir(dir + '/src'); 78 | Utilities.writeFile(dir + '/src/manifest.json', _readjson(_file('manifest.json'))); 79 | Utilities.copyFile(_file('background.js'), dir + '/src/index.js'); 80 | 81 | // setup test files 82 | Utilities.createDir(dir + '/test'); 83 | Utilities.copyFile(_file('test.js'), dir + '/test/sample.js'); 84 | 85 | // create package.json 86 | Utilities.writeFile(dir + '/package.json', _readjson(_file('package.json'))); 87 | 88 | // create readme 89 | Utilities.writeFile(dir + '/README.md', _readtext(_file('intro.md'))); 90 | 91 | // add eslint config 92 | Utilities.writeFile(dir + '/.eslintrc.json', _readtext(_file('eslint.json'))); 93 | 94 | // INSTALL packages 95 | spinner.stop(true); 96 | console.log(texts.install); 97 | spinner.start(); 98 | 99 | exec('npm install', {cwd: dir}) 100 | .on('exit', code => { 101 | spinner.stop(true); 102 | if (code !== 0) { 103 | console.log(texts.installError); 104 | } 105 | console.log(texts.success(dir)); 106 | process.exit(0); 107 | }); 108 | 109 | // this is just to make eslint happy 110 | return ''; 111 | })(); 112 | -------------------------------------------------------------------------------- /cli/texts.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 3 | * This module specifies terminal/console output for all commands 4 | */ 5 | 6 | const chalk = require('chalk'); 7 | 8 | /** 9 | * xt-create outputs 10 | */ 11 | exports.xtCreate = { 12 | 13 | prompts: { 14 | name: { 15 | type: 'text', 16 | name: 'name', 17 | message: 'What do you want to call the extension?', 18 | validate: value => 19 | // basic null check is sufficient 20 | !value || value.trim().length < 1 ? 21 | 'You must choose a name' : true 22 | }, 23 | optional: [ 24 | { 25 | type: 'text', 26 | name: 'description', 27 | message: 'What does it do?' 28 | }, { 29 | type: 'text', 30 | name: 'homepage', 31 | message: 'Homepage URL (leave blank if you do not have one yet)' 32 | } 33 | ] 34 | }, 35 | 36 | dirError: (dirname) => ( 37 | chalk.bold.red(`Cannot create directory: ${dirname}.\n` + 38 | 'It already exists, is not empty, or is not writable.') 39 | ), 40 | 41 | start: (dirname, name) => ( 42 | `Creating extension ${name} in directory ${chalk.bold.green(dirname)}.` 43 | ), 44 | 45 | install: 'Installing packages - this may take a while...', 46 | 47 | installError: ( 48 | chalk.bold.yellow('ATTN! ') + 49 | 'npm install did not complete successfully\n' + 50 | 'You may have to run npm install again in project directory.' 51 | ), 52 | 53 | success: (dir) => ( 54 | `${chalk.bold.green('DONE! ')}Your extension starter is ready.\n` + 55 | `${chalk.bold.green('What Next: ')} Open ${dir} in your favorite web IDE` 56 | ) 57 | 58 | }; 59 | 60 | /** 61 | * xt-sync outputs 62 | */ 63 | exports.xtSync = { 64 | 65 | argGitlab: 'Gitlab CI config', 66 | 67 | argTravis: 'Travis CI config', 68 | 69 | argLint: 'eslint config', 70 | 71 | argGitIgnore: 'gitignore', 72 | 73 | argActions: 'Github actions workflow config', 74 | 75 | instructions: 'choose the files you want to sync:', 76 | 77 | updateSuccess: (what) => chalk.bold.green(`✓ updated ${what}`) 78 | }; 79 | 80 | /** 81 | * xt-docs outputs 82 | */ 83 | exports.xtDocs = { 84 | 85 | argWatch: 'enable watch', 86 | 87 | watching: 'watching...', 88 | 89 | success: chalk.bold.green('Docs done!'), 90 | 91 | failure: chalk.bold.red('Docs failed'), 92 | 93 | configArg: 'Path to config file; defaults to `.xtdocs.json` in project root, or `xtdocs` in package.json' 94 | }; 95 | 96 | /** 97 | * xt-clean outputs 98 | */ 99 | exports.xtClean = { 100 | 101 | argModules: 'Clean node_modules directory', 102 | 103 | argIdea: 'Clean .idea/ directory', 104 | 105 | argVS: 'Clean .vscode/ directory', 106 | 107 | onConfigError: (path) => chalk.yellow(`File does not exist: ${path}`), 108 | 109 | onCleanFile: (path) => `- ${path}`, 110 | 111 | onCleanError: (e, line) => chalk.bold.red(e) + ' ' + line, 112 | 113 | result: count => chalk.bold[count === 0 ? 'yellow' : 'green'](`Done. Cleaned: ${count}`) 114 | }; 115 | 116 | /** 117 | * xt-test outputs 118 | */ 119 | exports.xtTest = { 120 | 121 | argPattern: 'test file/directory match pattern', 122 | 123 | argCoverage: 'deprecated! see docs on how to report coverage: https://bit.ly/3j5Zrn2', 124 | 125 | argWatch: 'enable watch', 126 | 127 | onRootSetup: (envList) => ( 128 | ['ENV: ', 129 | envList.map(entry => chalk.bold.green(` ${entry} `)) 130 | ].join(' ') + '\n' 131 | ) 132 | }; 133 | 134 | /** 135 | * xt-build outputs 136 | */ 137 | exports.xtBuild = { 138 | 139 | envArg: 'Build environment', 140 | 141 | watchArg: 'Enable watch', 142 | 143 | platformArg: 'Platform', 144 | 145 | configFileArg: 'Path to configuration file, default: .xtbuild.json in root, or xtbuild in package.json)', 146 | 147 | onBuildSuccess: _ => chalk.bold.green('Build done!'), 148 | 149 | onBuildError: _ => chalk.bold.red('Build failed') 150 | }; 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Extension CLI 2 | 3 | [![npm](https://img.shields.io/npm/v/extension-cli?style=flat-square)](https://www.npmjs.com/package/extension-cli) 4 | [![travis](https://img.shields.io/travis/mobilefirstllc/extension-cli?style=flat-square)](https://travis-ci.com/github/MobileFirstLLC/extension-cli) 5 | [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/MobileFirstLLC/extension-cli?style=flat-square)](https://codeclimate.com/github/MobileFirstLLC/extension-cli/maintainability) 6 | [![Last commit](https://img.shields.io/github/last-commit/mobilefirstllc/extension-cli?style=flat-square)](https://github.com/MobileFirstLLC/extension-cli/commits/master) 7 | [![npm](https://img.shields.io/npm/dt/extension-cli?style=flat-square)](https://www.npmjs.com/package/extension-cli) 8 | 9 | 10 | **Extension CLI is a command-line application that facilitates chromium∗-based web extension development by providing 11 | a systematic way to build, test and document extension projects. It handles the project setup and builds and lets you focus 12 | on the extension you are creating.** 13 | 14 | * * * 15 | 16 | ## Features 17 | 18 | - 🖥️   **Javascript Bundling** — Compiles, bundles and minifies javascript files
19 | 20 | - 🎨   **CSS Bundling** — Compiles, bundles, and minifies CSS and [SASS](https://sass-lang.com/guide) files
21 | 22 | - 💄   **Linting** — lint JavaScript using [ESLint](https://eslint.org/)
23 | 24 | - 📦   **ZIP Generation** — Generates a `.zip` file for publishing
25 | 26 | - 📝   **Document Source Code** — Generates code documentation using [JSDoc](https://jsdoc.app/about-getting-started.html)
27 | 28 | - ⚗️   **Unit Testing** — Provides a unit test environment preloaded with [mocha](https://mochajs.org), [chai](https://www.chaijs.com/) and [sinon-chrome](https://github.com/acvetkov/sinon-chrome)
29 | 30 | - ⚔️   **Cross-Browser Compatibility** - develop extensions for Chrome, Edge, Firefox, Opera and Brave.
31 | 32 | ![feature image](https://raw.githubusercontent.com/MobileFirstLLC/extension-cli/master/.github/feature.png) 33 | 34 | ## Getting Started 35 | 36 | **Note:** Using this CLI assumes you have Node.js installed. If you do not, you can [install it here](https://nodejs.org/en/download/). 37 | 38 | ##### Create new extension project 39 | 40 | ```text 41 | npx extension-cli 42 | ``` 43 | 44 | ##### Add to an existing project 45 | 46 | ```text 47 | npm install extension-cli 48 | ``` 49 | 50 | ### Commands Reference 51 | 52 | Command | Description 53 | --- | --- 54 | **xt-build** | Run builds; env flags: `-e prod` and `-e dev` 55 | **xt-test**| Run unit tests 56 | **xt-docs**| Generate docs 57 | **xt-sync**| Update project config files to match the latest defaults supplied by this CLI 58 | **xt-clean** | Remove automatically generated files 59 | 60 | * * * 61 | 62 | ## Read the Docs 63 | 64 |   65 |
   User Guide →

66 | 67 | ### CLI Developer Guide 68 | 69 | If you are interested in extending this project or forking **[see this guide →](https://oss.mobilefirst.me/extension-cli/13-cli-development/)** 70 | 71 | * * * 72 | 73 | ## Motivation 74 | 75 | After developing multiple browser extensions, it became clear that there are several steps in the development process that stay the same between every project. 76 | 77 | Instead of setting up these tasks individually for each project, it made more sense to combine everything in a utility tool that could be shared between projects. 78 | 79 | This approach helps with creating a common, consistent development approach between multiple projects, reduces time to get started, and makes it easier to update build tools and scripts across multiple projects as many npm packages inevitably need to be updated (frequently!). 80 | 81 | * * * 82 | 83 | **Issues & Feature Requests:** [Submit on Github](https://github.com/MobileFirstLLC/extension-cli/issues/new/choose) 84 | 85 | **Maker:** made by developers behind several popular extensions! 86 | 87 | **License:** [MIT](https://github.com/MobileFirstLLC/extension-cli/blob/master/LICENSE) 88 | -------------------------------------------------------------------------------- /guide/03-xt-build.md: -------------------------------------------------------------------------------- 1 | # xt-build 2 | 3 | * * * 4 | 5 |

xt-build command builds an extension project.

6 | 7 | * * * 8 | 9 | Build command can be used to create a debuggable version, or a production-ready .zip file that can be uploaded to an extension/add-on marketplace for distribution. 10 | 11 | Successful build command always generates an extension in build output directory that can be debugged in the browser. The underlying build system uses gulp, babel and webpack (among other plugins) to compile the extension project. 12 | 13 | ### Dev Build Artifacts 14 | 15 | When specifying`dev` build flag, the build will complete using development settings. Successful dev build generates extension source code in the specified build output directory, which can be debugged in a browser. 16 | 17 | ### Prod Build Artifacts 18 | 19 | When specifying `prod` build flag, the build will run a production build. Successful production build generates extension source code in build output directory, which can be debugged in a browser. It also generates a .zip file in the project root. This zip file can be uploaded to extension/add-on marketplace such as Chrome Web Store or Firefox add-ons. When running a production build, all code files (js, css, HTML, json) will be optimized. 20 | 21 | ## Commands 22 | 23 | Braces `{ }` indicate that the user must choose one (and only one) of the items inside the braces. 24 | 25 | 26 | **Run build with default options** 27 | 28 | Default option runs production build targeting Chrome browser. 29 | 30 | ```bash 31 | xt-build 32 | ``` 33 | 34 | **Run build with explicit environment flag `-e` or `--env`** 35 | 36 | ```bash 37 | xt-build {-e|--env} dev 38 | ``` 39 | 40 | ```bash 41 | xt-build {-e|--env} prod 42 | ``` 43 | 44 | **Run build for specific target browser** 45 | 46 | ```bash 47 | xt-build {-p|--platform} chrome 48 | ``` 49 | 50 | ```bash 51 | xt-build {-p|--platform} firefox 52 | ``` 53 | 54 | **Run build using custom configuration file path** 55 | 56 | ```bash 57 | xt-build {-c|--config} "/path/to/config.json" 58 | ``` 59 | 60 | **Run development build and keep watching changes** 61 | 62 | ```bash 63 | xt-build {-e|--env} dev {-w|--watch} 64 | ``` 65 | 66 | **Get help using this command** 67 | 68 | ```bash 69 | xt-build --help 70 | ``` 71 | 72 | ## Package.json scripts 73 | 74 | After adding Extension CLI to your project, you can run these commands from a 75 | terminal using syntax `npx xt-build`, or add the following to `packages.json` scripts section, 76 | then execute the commands as `npm run start` or `npm run build`: 77 | 78 | ```json 79 | "scripts":{ 80 | "start": "xt-build -e dev -w", 81 | "build": "xt-build -e prod", 82 | } 83 | ``` 84 | 85 | ## Default Configuration 86 | 87 | By default the CLI will look for build configuration in two different 88 | places: 89 | 90 | - in `package.json` using key `xtbuild` 91 | 92 | - in a file named `.xtbuild.json` in project root 93 | 94 | Alternatively you can provide a path to configuration file with `-c` or 95 | `--config` flag, followed by a path to configuration file. 96 | 97 | The CLI uses a default build configuration shown below. This tells 98 | extension CLI where to look for input files, how to process them, and where 99 | to output files. You can override any of these key-value pairs at project level. 100 | 101 | Explanations for each of these keys is given below. 102 | 103 | ```json 104 | --8<-- 105 | ./config/build.json 106 | --8<-- 107 | ``` 108 | 109 | ### Configuration Keys 110 | 111 | Key | Description | Guide 112 | --- | --- | --- 113 | `"dist"` | Build output directory || 114 | `"source"` | Source code directory || 115 | `"releases"` | Directory for outputting release zip file || 116 | `"release_name"` | name of release zip file || 117 | `"manifest"` | extension manifest file path | [Guide](03-xt-build-manifest.md) | 118 | `"js"` | Watch pattern for script changes during dev builds || 119 | `"js_bundles"` | Javascript bundles configuration | [Guide](03-xt-build-scripts.md) 120 | `"html"` | location and watch pattern of HTML files || 121 | `"scss"` | Watch pattern for style changes during dev builds || 122 | `"scss_bundles"` | Stylesheets bundles configuration | [Guide](03-xt-build-styles.md) 123 | `"assets"` | Static assets configuration match pattern | [Guide](03-xt-build-assets.md) 124 | `"copyAsIs"` | Files and directories to copy to output directory without modification | [Guide](03-xt-build-copy.md) 125 | `"locales_dir"` | Localizations directory | [Guide](03-xt-build-locales.md) 126 | `"locales_list"` | List of locales | [Guide](03-xt-build-locales.md) 127 | `"commands"` | Custom commands | [Guide](03-xt-build-cmds.md) 128 | 129 | -------------------------------------------------------------------------------- /config/init/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 16 | 18 | image/svg+xml 19 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /guide/09-release-notes.md: -------------------------------------------------------------------------------- 1 | --- 2 | disqus: "False" 3 | --- 4 | 5 | ### 1.2.5 (2021-03-16) (alpha) 6 | 7 | **Dependency upgrades** 8 | 9 | - @babel/preset-env: 7.16.11 ([066f782](https://github.com/MobileFirstLLC/extension-cli/commit/066f7820ce48e877cf5477ff77037c9d3a9d2fdd)) 10 | - @babel/register: 7.17.7 ([2084d42](https://github.com/MobileFirstLLC/extension-cli/commit/2084d42255e811c9f142ca77983b02aaa7dd71f0)) 11 | - chai: 4.3.6 ([2084d42](https://github.com/MobileFirstLLC/extension-cli/commit/2084d42255e811c9f142ca77983b02aaa7dd71f0)) 12 | - commander: 9.0.0 ([2084d42](https://github.com/MobileFirstLLC/extension-cli/commit/2084d42255e811c9f142ca77983b02aaa7dd71f0)) 13 | - eslint: 8.11.0 ([2084d42](https://github.com/MobileFirstLLC/extension-cli/commit/2084d42255e811c9f142ca77983b02aaa7dd71f0)) 14 | - gulp-sass: 5.1.0 ([0ac28b2](https://github.com/MobileFirstLLC/extension-cli/commit/0ac28b2eb26c998e0958ed4f98691419323b1931)) 15 | - jsdoc: 3.6.10 ([d0a09a3](https://github.com/MobileFirstLLC/extension-cli/commit/d0a09a3540580ef1d69edaaf9aa264a4674198b5)) 16 | - jsdom: 19.0.0 ([b0f290f](https://github.com/MobileFirstLLC/extension-cli/commit/b0f290fa43f76fc4e67a95ae35ca4cefb7b9db6c)) 17 | - mocha: 9.2.2 ([2084d42](https://github.com/MobileFirstLLC/extension-cli/commit/2084d42255e811c9f142ca77983b02aaa7dd71f0)) 18 | - sass: 1.49.9 ([aef9fd6](https://github.com/MobileFirstLLC/extension-cli/commit/aef9fd6142879af3bf4610c80eaca1f97aff96a4)) 19 | - sinon: 13.0.1 ([8282c0a](https://github.com/MobileFirstLLC/extension-cli/commit/8282c0a05d2702e6ec92155d8cfaba2de93c9c53)) 20 | - yargs: 17.3.1 ([add18ee](https://github.com/MobileFirstLLC/extension-cli/commit/add18ee15196d4356a74d0f76ded23d1fef1d085)) 21 | 22 | ### 1.2.4 (2021-10-19) 23 | 24 | - Update devtools sourcemap config [PR #119](https://github.com/MobileFirstLLC/extension-cli/pull/119) 25 | - New extension now initialized with MV3 [#86](https://github.com/MobileFirstLLC/extension-cli/pull/111) 26 | 27 | **Dependency updates** 28 | 29 | - update @babel/preset-env to v7.15.8 ([a341965](https://github.com/mobilefirstllc/extension-cli/commit/a3419659b3ac2427f1134f8c6cfb2bb38c29f009)) 30 | - update commander to v8.2.0 ([5226669](https://github.com/mobilefirstllc/extension-cli/commit/52266695f15cace4cc422e229afe5555d30ff0e4)) 31 | - update eslint to v8 ([d5549a8](https://github.com/mobilefirstllc/extension-cli/commit/d5549a8730256f61edbd36ab7cabbac95db5000e)) 32 | - update jsdom to v18 ([681db6b](https://github.com/mobilefirstllc/extension-cli/commit/681db6bafeedda989471235ff6f14ad9edff1885)) 33 | - update mocha to v9.1.2 ([d7cecc6](https://github.com/mobilefirstllc/extension-cli/commit/d7cecc60a2aa918559bea17b2531b3e331500cce)) 34 | - update prompts to v2.4.2 ([f99cb60](https://github.com/mobilefirstllc/extension-cli/commit/f99cb608f43414ecbb8f9309ce2d32453b11b0d5)) 35 | - update sass to v1.43.2 ([32eb148](https://github.com/mobilefirstllc/extension-cli/commit/32eb148d81318f115942df2682270ded3c061652)) 36 | - update webpack-stream to v7 ([#94](https://github.com/mobilefirstllc/extension-cli/issues/94)) ([a19b448](https://github.com/mobilefirstllc/extension-cli/commit/a19b4488cc7f9a31474904e58b2920bf67f0619a)) 37 | - update yargs to v17.2.1 ([9a06f44](https://github.com/mobilefirstllc/extension-cli/commit/9a06f44b878d178dbd15fbae490470082b99221a)) 38 | 39 | ### 1.2.2 (2021-07-28) 40 | 41 | - update dependencies 42 | 43 | ### 1.2.0 (2021-07-28) 44 | 45 | **Changes to build** 46 | 47 | - enable using custom filenames for manifests pre-build [PR #66](https://github.com/MobileFirstLLC/extension-cli/pull/66) 48 | - run build tasks in parallel [PR #70](https://github.com/MobileFirstLLC/extension-cli/pull/70) 49 | - make sourcemap basename match js file name [PR #70](https://github.com/MobileFirstLLC/extension-cli/pull/70) 50 | - dynamically determine project path; remove build config key [PR #71](https://github.com/MobileFirstLLC/extension-cli/pull/71) 51 | 52 | **Other changes** 53 | 54 | - docs: make JsDoc default template the default documentation template for CLI [#62](https://github.com/MobileFirstLLC/extension-cli/issues/62) 55 | - sync: add CI configuration starter for Github actions [#65](https://github.com/MobileFirstLLC/extension-cli/issues/65) 56 | - sync: eslint config file will now have file extension `.json` [PR #78](https://github.com/MobileFirstLLC/extension-cli/pull/78) 57 | - update dependencies 58 | 59 | ### 1.1.0 (2021-06-12) 60 | 61 | - sync: changed command to prompt with options [PR #57](https://github.com/MobileFirstLLC/extension-cli/pull/57), [#59](https://github.com/MobileFirstLLC/extension-cli/pull/59) 62 | - updated dependencies 63 | 64 | ### 1.0.3 (2021-04-27) 65 | 66 | **Changes to build** 67 | 68 | - Make webpack mode configurable [#51](https://github.com/MobileFirstLLC/extension-cli/issues/51), [PR #55](https://github.com/MobileFirstLLC/extension-cli/pull/55) 69 | - use `cheap-source-map` [PR #49](https://github.com/MobileFirstLLC/extension-cli/pull/49) 70 | - remove devtool in prod config [PR #50](https://github.com/MobileFirstLLC/extension-cli/pull/50) 71 | 72 | ### 1.0.2 (2021-04-11) 73 | 74 | **Changes to build** 75 | 76 | - Custom folders for scss bundles and always minify css [PR #47](https://github.com/MobileFirstLLC/extension-cli/pull/47) 77 | - Default style bundle name without extension [PR #48](https://github.com/MobileFirstLLC/extension-cli/pull/48) 78 | 79 | ### 1.0.0 (2021-04-11) 80 | 81 | **Changes to build** 82 | 83 | - automatically copy from `assets/` to output directory `assets/` [PR #43](https://github.com/MobileFirstLLC/extension-cli/pull/43) 84 | - add target platform for manifests: `chrome/firefox` [PR #43](https://github.com/MobileFirstLLC/extension-cli/pull/43) 85 | - improved build outputs [PR #42](https://github.com/MobileFirstLLC/extension-cli/pull/42) 86 | 87 | **Other changes** 88 | 89 | - Updated dependencies [PR #44](https://github.com/MobileFirstLLC/extension-cli/pull/44) 90 | -------------------------------------------------------------------------------- /guide/05-xt-docs-templates.md: -------------------------------------------------------------------------------- 1 | # Documentation Templates 2 | 3 | * * * 4 | 5 |

Use templates to customize the look and feel of 6 | source code documentation.

7 | 8 | * * * 9 | 10 | Extension CLI uses [JsDoc](https://jsdoc.app) to document extension projects. 11 | You can then apply templates to customize the look and feel of these docs. 12 | 13 | ## Customizing Default Template 14 | 15 | If you are using the default template see: [Configuring JSDoc's default template](https://jsdoc.app/about-configuring-default-template.html). 16 | 17 | 19 | 20 | 21 | 22 | 23 | default template 24 | 25 | 26 | ## Alternative Templates 27 | 28 | To use an alternative template: 29 | 30 | 1. Choose a suitable template and use npm to install it at project level 31 | 2. In the [documentation configuration](https://oss.mobilefirst.me/extension-cli/05-xt-docs/#default-configuration): 32 | 1. Specify `"opts.template"` to indicate which template to use 33 | 2. Customize the template options under `"templates"` 34 | 35 | * * * 36 | 37 | ### Braintree JSDoc Template 38 | 39 | [Source and configuration](https://github.com/braintree/jsdoc-template) 40 | 41 | ![GitHub last commit](https://img.shields.io/github/last-commit/braintree/jsdoc-template) 42 | 43 | 45 | 46 | 47 | 48 | 49 | braintree 50 | 51 | 52 | * * * 53 | 54 | ### clean-jsdoc-theme 55 | 56 | [Source and configuration](https://github.com/ankitskvmdam/clean-jsdoc-theme) 57 | 58 | ![GitHub last commit](https://img.shields.io/github/last-commit/ankitskvmdam/clean-jsdoc-theme) 59 | 60 | _Light mode_ 61 | 62 | 64 | 65 | 66 | 67 | 68 | light mode 69 | 70 | 71 | _Dark mode_ 72 | 73 | 75 | 76 | 77 | 78 | 79 | dark mode 80 | 81 | 82 | * * * 83 | 84 | ### Foodoc 85 | 86 | [Source and configuration](https://github.com/steveush/foodoc) 87 | 88 | ![GitHub last commit](https://img.shields.io/github/last-commit/steveush/foodoc) 89 | 90 | 91 | 93 | 94 | 95 | 96 | 97 | foodoc 98 | 99 | 100 | * * * 101 | 102 | ### JsDoc Template 103 | 104 | [Source and configuration](https://github.com/AlexisPuga/jsdoc-template) 105 | 106 | ![GitHub last commit](https://img.shields.io/github/last-commit/AlexisPuga/jsdoc-template) 107 | 108 | 110 | 111 | 112 | 113 | 114 | JsDoc Template 115 | 116 | 117 | * * * 118 | 119 | ### Tidy JsDoc 120 | 121 | [Source and configuration](https://github.com/julie-ng/tidy-jsdoc) 122 | 123 | ![GitHub last commit](https://img.shields.io/github/last-commit/julie-ng/tidy-jsdoc) 124 | 125 | 127 | 128 | 129 | 130 | 131 | Tidy JsDoc 132 | 133 | 134 | * * * 135 | 136 | 137 | 179 | -------------------------------------------------------------------------------- /guide/assets/images/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 16 | 18 | image/svg+xml 19 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 34 | 35 | 36 | 40 | 43 | 47 | 51 | 52 | 56 | 57 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "node": true 6 | }, 7 | "globals": { 8 | "document": false, 9 | "escape": false, 10 | "navigator": false, 11 | "unescape": false, 12 | "window": false, 13 | "describe": true, 14 | "before": true, 15 | "it": true, 16 | "expect": true, 17 | "sinon": true, 18 | "afterEach": true, 19 | "after": true 20 | }, 21 | "plugins": [], 22 | "rules": { 23 | "block-scoped-var": 2, 24 | "brace-style": [ 25 | 2, 26 | "1tbs", 27 | { 28 | "allowSingleLine": true 29 | } 30 | ], 31 | "camelcase": [ 32 | 0, 33 | { 34 | "properties": "always" 35 | } 36 | ], 37 | "comma-dangle": [ 38 | 2, 39 | "never" 40 | ], 41 | "comma-spacing": [ 42 | 2, 43 | { 44 | "before": false, 45 | "after": true 46 | } 47 | ], 48 | "comma-style": [ 49 | 2, 50 | "last" 51 | ], 52 | "complexity": 0, 53 | "consistent-return": 2, 54 | "consistent-this": 0, 55 | "curly": [ 56 | 2, 57 | "multi-line" 58 | ], 59 | "default-case": 0, 60 | "dot-location": [ 61 | 2, 62 | "property" 63 | ], 64 | "dot-notation": 0, 65 | "eol-last": 2, 66 | "eqeqeq": [ 67 | 2, 68 | "allow-null" 69 | ], 70 | "func-names": 0, 71 | "func-style": 0, 72 | "generator-star-spacing": [ 73 | 2, 74 | "both" 75 | ], 76 | "guard-for-in": 0, 77 | "handle-callback-err": [ 78 | 2, 79 | "^(err|error|anySpecificError)$" 80 | ], 81 | "key-spacing": [ 82 | 2, 83 | { 84 | "beforeColon": false, 85 | "afterColon": true 86 | } 87 | ], 88 | "keyword-spacing": [ 89 | 2, 90 | { 91 | "before": true, 92 | "after": true 93 | } 94 | ], 95 | "linebreak-style": 0, 96 | "max-depth": 0, 97 | "max-len": [ 98 | 2, 99 | 120, 100 | 4 101 | ], 102 | "max-nested-callbacks": 0, 103 | "max-params": 0, 104 | "max-statements": 0, 105 | "new-cap": [ 106 | 2, 107 | { 108 | "newIsCap": true, 109 | "capIsNew": false 110 | } 111 | ], 112 | "newline-after-var": [ 113 | 2, 114 | "always" 115 | ], 116 | "new-parens": 2, 117 | "no-alert": 0, 118 | "no-array-constructor": 2, 119 | "no-bitwise": 0, 120 | "no-caller": 2, 121 | "no-catch-shadow": 0, 122 | "no-cond-assign": 2, 123 | "no-console": 0, 124 | "no-constant-condition": 0, 125 | "no-continue": 0, 126 | "no-control-regex": 2, 127 | "no-debugger": 2, 128 | "no-delete-var": 2, 129 | "no-div-regex": 0, 130 | "no-dupe-args": 2, 131 | "no-dupe-keys": 2, 132 | "no-duplicate-case": 2, 133 | "no-else-return": 2, 134 | "no-empty": 0, 135 | "no-empty-character-class": 2, 136 | "no-eq-null": 0, 137 | "no-eval": 2, 138 | "no-ex-assign": 2, 139 | "no-extend-native": 2, 140 | "no-extra-bind": 2, 141 | "no-extra-boolean-cast": 2, 142 | "no-extra-parens": 0, 143 | "no-extra-semi": 0, 144 | "no-extra-strict": 0, 145 | "no-fallthrough": 2, 146 | "no-floating-decimal": 2, 147 | "no-func-assign": 2, 148 | "no-implied-eval": 2, 149 | "no-inline-comments": 0, 150 | "no-inner-declarations": [ 151 | 2, 152 | "functions" 153 | ], 154 | "no-invalid-regexp": 2, 155 | "no-irregular-whitespace": 2, 156 | "no-iterator": 2, 157 | "no-label-var": 2, 158 | "no-labels": 2, 159 | "no-lone-blocks": 0, 160 | "no-lonely-if": 0, 161 | "no-loop-func": 0, 162 | "no-mixed-requires": 0, 163 | "no-mixed-spaces-and-tabs": [ 164 | 2, 165 | false 166 | ], 167 | "no-multi-spaces": 2, 168 | "no-multi-str": 2, 169 | "no-multiple-empty-lines": [ 170 | 2, 171 | { 172 | "max": 1 173 | } 174 | ], 175 | "no-native-reassign": 2, 176 | "no-negated-in-lhs": 2, 177 | "no-nested-ternary": 0, 178 | "no-new": 2, 179 | "no-new-func": 2, 180 | "no-new-object": 2, 181 | "no-new-require": 2, 182 | "no-new-wrappers": 2, 183 | "no-obj-calls": 2, 184 | "no-octal": 2, 185 | "no-octal-escape": 2, 186 | "no-path-concat": 0, 187 | "no-plusplus": 0, 188 | "no-process-env": 0, 189 | "no-process-exit": 0, 190 | "no-proto": 2, 191 | "no-redeclare": 2, 192 | "no-regex-spaces": 2, 193 | "no-reserved-keys": 0, 194 | "no-restricted-modules": 0, 195 | "no-return-assign": 2, 196 | "no-script-url": 0, 197 | "no-self-compare": 2, 198 | "no-sequences": 2, 199 | "no-shadow": 0, 200 | "no-shadow-restricted-names": 2, 201 | "no-spaced-func": 2, 202 | "no-sparse-arrays": 2, 203 | "no-sync": 0, 204 | "no-ternary": 0, 205 | "no-throw-literal": 2, 206 | "no-trailing-spaces": 1, 207 | "no-undef": 2, 208 | "no-undef-init": 2, 209 | "no-undefined": 0, 210 | "no-underscore-dangle": 0, 211 | "no-unneeded-ternary": 2, 212 | "no-unreachable": 2, 213 | "no-unused-expressions": 0, 214 | "no-unused-vars": [ 215 | 2, 216 | { 217 | "vars": "all", 218 | "args": "none" 219 | } 220 | ], 221 | "no-use-before-define": 2, 222 | "no-var": 0, 223 | "no-void": 0, 224 | "no-warning-comments": 0, 225 | "no-with": 2, 226 | "one-var": 0, 227 | "operator-assignment": 0, 228 | "operator-linebreak": [ 229 | 2, 230 | "after" 231 | ], 232 | "padded-blocks": 0, 233 | "quote-props": 0, 234 | "quotes": [ 235 | 2, 236 | "single", 237 | "avoid-escape" 238 | ], 239 | "radix": 2, 240 | "semi": [ 241 | 2, 242 | "always" 243 | ], 244 | "semi-spacing": 0, 245 | "sort-vars": 0, 246 | "space-before-blocks": [ 247 | 2, 248 | "always" 249 | ], 250 | "space-before-function-paren": [ 251 | 2, 252 | { 253 | "anonymous": "always", 254 | "named": "never" 255 | } 256 | ], 257 | "space-in-brackets": 0, 258 | "space-in-parens": [ 259 | 2, 260 | "never" 261 | ], 262 | "space-infix-ops": 2, 263 | "space-unary-ops": [ 264 | 2, 265 | { 266 | "words": true, 267 | "nonwords": false 268 | } 269 | ], 270 | "spaced-comment": [ 271 | 2, 272 | "always" 273 | ], 274 | "strict": 0, 275 | "use-isnan": 2, 276 | "valid-jsdoc": 0, 277 | "valid-typeof": 2, 278 | "vars-on-top": 2, 279 | "wrap-iife": [ 280 | 2, 281 | "any" 282 | ], 283 | "wrap-regex": 0, 284 | "yoda": [ 285 | 2, 286 | "never" 287 | ] 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /cli/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const del = require('del'); 3 | const chalk = require('chalk'); 4 | const gulpChange = require('gulp-change'); 5 | const paths = require('../config/build.json'); 6 | const plugins = require('gulp-load-plugins')(); 7 | const webpack = require('webpack-stream'); 8 | const sass = require('gulp-sass')(require('sass')); 9 | const Utilities = require('./utilities').Utilities; 10 | const argv = require('yargs').argv; 11 | const {prod: isProd, firefox: isFirefox, pkg: pkgPath, config} = argv; 12 | 13 | /** helper method to ensure array type */ 14 | const ensureArray = path => Array.isArray(path) ? path : [path]; 15 | 16 | /** read project package.json **/ 17 | const pkg = Utilities.readJSON(pkgPath); 18 | 19 | /** read project's config file, if specified **/ 20 | let customPaths = null; 21 | 22 | if (Utilities.fileExists(config)) { 23 | // if config is a file 24 | customPaths = Utilities.readJSON(config); 25 | } else if (pkg.xtbuild !== undefined) { 26 | // otherwise config should be specified in package.json 27 | customPaths = pkg.xtbuild; 28 | } 29 | 30 | /** replace default configs with project-level configs **/ 31 | if (customPaths) { 32 | for (let key in customPaths) { 33 | if (customPaths.hasOwnProperty(key)) { 34 | paths[key] = customPaths[key]; 35 | } 36 | } 37 | } 38 | 39 | const clean = () => del([paths.dist + '/*']); 40 | 41 | const script = ({src, name, mode}, done = _ => true) => { 42 | 43 | const webpackOptions = { 44 | // use mode if specified explicitly; otherwise choose by --env 45 | mode: mode || (isProd ? 'production' : 'development'), 46 | // match sourcemap name with configured js file name 47 | output: {filename: `${name}.js`}, 48 | // use source map with dev builds only 49 | devtool: isProd ? undefined : 'cheap-source-map' 50 | }; 51 | 52 | return gulp.src(src) 53 | .pipe(webpack(webpackOptions)) 54 | .on('error', (err) => { 55 | console.log(err.toString()); 56 | this.emit('end'); 57 | }) 58 | .pipe(plugins.rename(path => { 59 | path.dirname = ''; 60 | path.basename = name; 61 | })) 62 | .pipe(gulp.dest(paths.dist)) 63 | .on('end', done); 64 | }; 65 | 66 | const style = ({src, name}, done = _ => true) => { 67 | return gulp.src(src) 68 | // convert to css 69 | .pipe(sass().on('error', sass.logError)) 70 | // concatenate multiple src files 71 | .pipe(plugins.concat(`${name}.css`)) 72 | // minify 73 | .pipe(plugins.cleanCss()) 74 | // rename to user-specified name 75 | .pipe(plugins.rename((path) => { 76 | path.dirname = ''; 77 | path.basename = name; 78 | })) 79 | .pipe(gulp.dest(paths.dist)) 80 | .on('end', done); 81 | }; 82 | 83 | const copy = (src, done = _ => true) => { 84 | // nested copy specified using glob pattern 85 | if (src.endsWith('*')) { 86 | return gulp.src(src, {base: 'src'}) 87 | .pipe(gulp.dest(paths.dist)) 88 | .on('end', done); 89 | } 90 | 91 | // copy single file or directory 92 | return gulp.src(src) 93 | .pipe(plugins.rename(path => { 94 | path.dirname = ''; 95 | })) 96 | .pipe(gulp.dest(paths.dist)) 97 | .on('end', done); 98 | }; 99 | 100 | const locale = (language, done = _ => true) => { 101 | return gulp.src(paths.locales_dir + language + '/**/*.json') 102 | .pipe(plugins.mergeJson({fileName: 'messages.json'})) 103 | .pipe(plugins.jsonminify()) 104 | .pipe(gulp.dest(paths.dist + '/_locales/' + language)) 105 | .on('end', done); 106 | }; 107 | 108 | const copyManifest = done => { 109 | 110 | const {version} = pkg; 111 | 112 | const performChange = (content) => { 113 | let mft = JSON.parse(content); 114 | 115 | mft.version = version; // use version from package 116 | 117 | if (isFirefox && mft.firefox) mft = {...mft, ...mft.firefox}; 118 | else if (!isFirefox && mft.chrome) mft = {...mft, ...mft.chrome}; 119 | delete mft.chrome; 120 | delete mft.firefox; 121 | 122 | return JSON.stringify(mft); 123 | }; 124 | 125 | return gulp.src(paths.manifest) 126 | .pipe(gulpChange(performChange)) 127 | .pipe(plugins.jsonminify()) 128 | .pipe(plugins.rename(path => { 129 | path.dirname = ''; 130 | path.basename = 'manifest'; 131 | path.extname = '.json'; 132 | })) 133 | .pipe(gulp.dest(paths.dist)) 134 | .on('end', done); 135 | }; 136 | 137 | const copyAssets = done => { 138 | return gulp.src(paths.assets) 139 | .pipe(gulp.dest(paths.dist + '/assets')) 140 | .on('end', done); 141 | }; 142 | 143 | const buildHtml = done => { 144 | return gulp.src(paths.html) 145 | .pipe(plugins.htmlmin({collapseWhitespace: true})) 146 | .pipe(plugins.rename(path => { 147 | path.dirname = ''; 148 | })) 149 | .pipe(gulp.dest(paths.dist)) 150 | .on('end', done); 151 | }; 152 | 153 | const customCommands = done => { 154 | if (!paths.commands || !paths.commands.length) { 155 | return done(); 156 | } 157 | 158 | return require('child_process') 159 | .exec(paths.commands, done); 160 | }; 161 | 162 | const release = done => { 163 | if (!isProd) return done(); 164 | 165 | return gulp.src(paths.dist + '/**/*') 166 | .pipe(plugins.zip(`${paths.release_name || 'release'}.zip`)) 167 | .pipe(gulp.dest(paths.releases)) 168 | .on('end', done); 169 | }; 170 | 171 | const dynamicFunc = (action, name) => { 172 | const f = action; 173 | 174 | Object.defineProperty(f, 'name', { 175 | value: name, 176 | writable: false 177 | }); 178 | return f; 179 | }; 180 | 181 | const scripts = paths.js_bundles.map(obj => 182 | dynamicFunc(_ => script(obj), `${obj.name}.js`)); 183 | 184 | const styles = paths.scss_bundles.map(obj => 185 | dynamicFunc(_ => style(obj), `${obj.name}.css`)); 186 | 187 | const locales = paths.locales_list.map(lang => 188 | dynamicFunc(_ => locale(lang), `locale ${lang}`)); 189 | 190 | const copies = ensureArray(paths.copyAsIs).map(obj => 191 | dynamicFunc(_ => copy(obj), `copy ${obj}`)); 192 | 193 | const watch = () => { 194 | console.log(chalk.bold.yellow('watching...')); 195 | if (scripts.length) { 196 | gulp.watch(ensureArray(paths.js), gulp.parallel(...scripts)); 197 | } 198 | if (styles.length) { 199 | gulp.watch(ensureArray(paths.scss), gulp.parallel(...styles)); 200 | } 201 | if (copies.length) { 202 | gulp.watch(ensureArray(paths.copyAsIs), gulp.parallel(...copies)); 203 | } 204 | if (paths.locales_list.length) { 205 | gulp.watch(paths.locales_dir + '**/*.json', gulp.parallel(...locales)); 206 | } 207 | gulp.watch(paths.manifest, copyManifest); 208 | gulp.watch(ensureArray(paths.html), buildHtml); 209 | gulp.watch(ensureArray(paths.assets), copyAssets); 210 | // gulp.watch(paths.commands_watch_path || '', customCommands); 211 | }; 212 | 213 | const build = gulp.series( 214 | clean, 215 | gulp.parallel( 216 | ...scripts, 217 | ...styles, 218 | ...copies, 219 | ...locales, 220 | copyManifest, 221 | copyAssets, 222 | buildHtml 223 | ), 224 | customCommands, 225 | release 226 | ); 227 | 228 | /* 229 | * Define default task that can be called by just running `gulp` from cli 230 | */ 231 | exports.default = build; 232 | 233 | /* 234 | * If watch flag is defined, run build and keep watching 235 | */ 236 | exports.watch = gulp.series(build, watch); 237 | -------------------------------------------------------------------------------- /cli/utilities.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** 5 | * @class 6 | * @classdesc Utility class provides helper methods 7 | * for performing commonly recurring operations such 8 | * as IO; and reading, writing, merging objects. 9 | * 10 | * When performing these operations, it is preferable 11 | * to use these utility methods to ensure same behavior 12 | * for these operations everywhere, and to establish one 13 | * place of change, should these operations change in the 14 | * future. 15 | */ 16 | class Utilities { 17 | 18 | /** 19 | * Given some string value, generate another string 20 | * from it, such that the generated string can be 21 | * used as a directory name. This function will 22 | * normalize the input and remove special characters. 23 | * 24 | * @param name - suggested directory name 25 | * @param defaultName - value to return if 26 | * no characters in name can be used 27 | * @return {string} directory name 28 | */ 29 | generateDirectoryName(name, defaultName = 'extension-1') { 30 | return ((name || '').toLowerCase() 31 | .replace(/[\W_]+/g, ' ') 32 | .replace(/ /g, '-') 33 | .replace(/-$/, '')) || defaultName; 34 | }; 35 | 36 | /** 37 | * Replace string interpolation expressions in 38 | * a content string. 39 | * 40 | * @param content - string with placeholder values, 41 | * @example "sample ${key}" 42 | * @param vars - dictionary of pairs 43 | * @example { key : "value" } 44 | * @return {string} - 45 | * @example "sample value" 46 | */ 47 | replaceVars(content, vars) { 48 | let temp = content.toString(); 49 | 50 | Object.keys(vars).map(key => { 51 | const re = new RegExp('\\${' + key + '}', 'gi'); 52 | 53 | temp = temp.replace(re, vars[key]); 54 | return true; 55 | }); 56 | return temp; 57 | }; 58 | 59 | /** 60 | * a union of two objects, child and parent, 61 | * with child values overriding all shared keys. 62 | * 63 | * This operation happens in place and 64 | * result will be stored in parent object. 65 | * 66 | * @example 67 | * let child = {a:1, b:5, c:{x:1}} 68 | * let parent = {b:8, c:{y:9}} 69 | 70 | * // expected result (parent): 71 | * // {a:1, b:5, c:{x:1, y:9}} 72 | * 73 | * @param child - source object 74 | * @param parent - parent object 75 | */ 76 | keyReplace(child, parent) { 77 | for (let key in child) { 78 | if (!child.hasOwnProperty(key)) continue; 79 | if (Array.isArray(child[key])) { 80 | parent[key] = child[key]; 81 | continue; 82 | } 83 | if (typeof child[key] !== 'object') { 84 | parent[key] = child[key]; 85 | continue; 86 | } 87 | if (!parent[key]) parent[key] = {}; 88 | this.keyReplace(child[key], parent[key]); 89 | } 90 | } 91 | 92 | /** 93 | * Given defaultConfig and project-level config 94 | * replace default configs with project-specific 95 | * configuration. 96 | * 97 | * Any property that is specified at project level 98 | * but not in default config, will be added to 99 | * the result configuration. 100 | * 101 | * Any property that exists in default config that 102 | * is not overwritten at project level, will hold 103 | * default value in the result configuration. 104 | * 105 | * @param defaultConfig 106 | * @param projectConfig 107 | * @return {Object} 108 | */ 109 | iterateConfigs(defaultConfig, projectConfig) { 110 | if (!projectConfig) return defaultConfig; 111 | let temp = Object.assign({}, defaultConfig); 112 | 113 | for (let k in projectConfig) { 114 | if (!projectConfig.hasOwnProperty(k)) continue; 115 | if (typeof projectConfig[k] === 'object') { 116 | if (!temp[k]) temp[k] = {}; 117 | this.keyReplace(projectConfig[k], temp[k]); 118 | } else { 119 | temp[k] = projectConfig[k]; 120 | } 121 | } 122 | return temp; 123 | } 124 | 125 | /** 126 | * Recursively copy a directory and all its files to a new location 127 | * @param from - path to current location 128 | * @param to - target location path 129 | */ 130 | copyFolderSync(from, to) { 131 | try { 132 | fs.mkdirSync(to); 133 | } catch (e) { 134 | } 135 | fs.readdirSync(from).forEach((element) => { 136 | const stat = fs.lstatSync(path.join(from, element)); 137 | 138 | if (stat.isFile()) { 139 | fs.copyFileSync(path.join(from, element), path.join(to, element)); 140 | } else if (stat.isSymbolicLink()) { 141 | fs.symlinkSync(fs.readlinkSync(path.join(from, element)), path.join(to, element)); 142 | } else if (stat.isDirectory()) { 143 | this.copyFolderSync(path.join(from, element), path.join(to, element)); 144 | } 145 | }); 146 | } 147 | 148 | /** 149 | * Copy single file from one location to another (synchronous). 150 | * 151 | * @param from - source file path 152 | * @param to - target file path 153 | */ 154 | copyFile(from, to) { 155 | fs.createReadStream(from).pipe(fs.createWriteStream(to)); 156 | } 157 | 158 | /** 159 | * Read utf-8 encoded file (synchronous) 160 | * @param filePath - path to file 161 | * @return {string} - file contents 162 | */ 163 | readFile(filePath) { 164 | return fs.readFileSync(filePath, 'utf8'); 165 | } 166 | 167 | /** 168 | * Write file to disk (syncronous) 169 | * @param filePath - path to file 170 | * @param content - file contents 171 | */ 172 | writeFile(filePath, content) { 173 | fs.writeFileSync(filePath, content); 174 | } 175 | 176 | /** 177 | * Check if file exists 178 | * @param filePath - path to file 179 | * @return {boolean} - true/false 180 | */ 181 | fileExists(filePath) { 182 | return fs.existsSync(filePath); 183 | } 184 | 185 | /** 186 | * Create empty directory. 187 | * 188 | * @param dirPath - path to directory 189 | * @return {boolean} - true if exists and empty (should be 190 | * writable) and false otherwise 191 | */ 192 | createDir(dirPath) { 193 | // doesn't exist 194 | if (!fs.existsSync(dirPath)) { 195 | fs.mkdirSync(dirPath, { recursive: true }); 196 | return true; 197 | } 198 | // check if empty 199 | return !fs.readdirSync(dirPath).length; 200 | }; 201 | 202 | /** 203 | * Read JSON file 204 | * @param filePath - path to file 205 | * @return {any} - Object 206 | */ 207 | readJSON(filePath) { 208 | return JSON.parse(this.readFile(filePath)); 209 | } 210 | 211 | /** 212 | * Reads text file then replaces all variable placeholders, e.g. ${var1} 213 | * @param path - path to file 214 | * @param vars - variables Object 215 | * @return {string} - file contents with all matched variables replaced 216 | */ 217 | readAndReplaceTextFile(path, vars) { 218 | return this.replaceVars(this.readFile(path), vars); 219 | } 220 | 221 | /** 222 | * Reads JSON file then replaces all variable placeholders, e.g. ${var1} 223 | * @param path - path to file 224 | * @param vars - variables Object 225 | * @return {string} - file contents with all matched variables replaced 226 | */ 227 | readAndReplaceJSONFile(path, vars) { 228 | return JSON.stringify(JSON.parse(this.readAndReplaceTextFile(path, vars)), null, 4); 229 | } 230 | } 231 | 232 | exports.Utilities = new Utilities(); 233 | -------------------------------------------------------------------------------- /guide/assets/images/guide.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 63 | 64 | 67 | 68 | 69 | 70 | 71 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 83 | 84 | 85 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | 105 | 106 | 109 | 110 | 111 | 112 | 113 | 116 | 117 | 118 | 119 | 120 | 123 | 124 | 125 | 126 | 127 | 130 | 131 | 132 | 133 | 134 | 137 | 138 | 139 | 140 | 141 | 144 | 145 | 146 | 147 | 148 | 151 | 152 | 153 | 154 | 155 | 158 | 159 | 160 | 161 | 162 | 165 | 166 | 167 | 168 | 169 | 172 | 173 | 174 | 175 | 176 | 179 | 180 | 181 | 182 | 183 | 186 | 187 | 188 | 189 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /test/cli-utilities.test.js: -------------------------------------------------------------------------------- 1 | const Utilities = require('../cli/utilities').Utilities; 2 | const sinon = require('sinon'); 3 | const expect = require('chai').expect; 4 | const fs = require('fs'); 5 | 6 | describe('Test utility functions', () => { 7 | 8 | /** 9 | * Stub Node.js IO methods 10 | */ 11 | // eslint-disable-next-line no-undef 12 | beforeEach(() => { 13 | sinon.stub(fs, 'readFileSync'); 14 | sinon.stub(fs, 'existsSync'); 15 | sinon.stub(fs, 'mkdirSync'); 16 | sinon.stub(fs, 'readdirSync'); 17 | sinon.stub(fs, 'writeFileSync'); 18 | sinon.stub(fs, 'createReadStream'); 19 | sinon.stub(fs, 'createWriteStream'); 20 | sinon.stub(fs, 'lstatSync'); 21 | sinon.stub(fs, 'copyFileSync'); 22 | sinon.stub(fs, 'symlinkSync'); 23 | sinon.stub(fs, 'readlinkSync'); 24 | 25 | fs.createReadStream.returns({ 26 | pipe: () => true 27 | }); 28 | }); 29 | 30 | /** 31 | * Restore stubbed IO methods 32 | */ 33 | // eslint-disable-next-line no-undef 34 | afterEach(() => { 35 | fs.existsSync.restore(); 36 | fs.mkdirSync.restore(); 37 | fs.readdirSync.restore(); 38 | fs.readFileSync.restore(); 39 | fs.writeFileSync.restore(); 40 | fs.createReadStream.restore(); 41 | fs.createWriteStream.restore(); 42 | fs.lstatSync.restore(); 43 | fs.copyFileSync.restore(); 44 | fs.symlinkSync.restore(); 45 | fs.readlinkSync.restore(); 46 | }); 47 | 48 | describe('generateDirectoryName...', () => { 49 | 50 | it('...returns lowercase name', () => { 51 | expect(Utilities.generateDirectoryName('HELLO WORLD')) 52 | .to.equal('hello-world'); 53 | expect(Utilities.generateDirectoryName('my app name')) 54 | .to.equal('my-app-name'); 55 | expect(Utilities.generateDirectoryName('APP-APP')) 56 | .to.equal('app-app'); 57 | expect(Utilities.generateDirectoryName('test789')) 58 | .to.equal('test789'); 59 | }); 60 | 61 | it('...replaces special characters with hyphen', () => { 62 | expect(Utilities.generateDirectoryName('MyAwesome#@Thing')) 63 | .to.equal('myawesome-thing'); 64 | expect(Utilities.generateDirectoryName('````', 'xyz')) 65 | .to.equal('xyz'); 66 | }); 67 | 68 | it('...removes trailing hyphen', () => { 69 | expect(Utilities.generateDirectoryName('Hello World!!!')) 70 | .to.equal('hello-world'); 71 | expect(Utilities.generateDirectoryName('awesom-o app#$%%')) 72 | .to.equal('awesom-o-app'); 73 | }); 74 | 75 | it('...returns default name instead of empty string', () => { 76 | expect(Utilities.generateDirectoryName('', 'foobar')) 77 | .to.equal('foobar'); 78 | expect(Utilities.generateDirectoryName(null).length) 79 | .to.be.greaterThan(0); 80 | }); 81 | }); 82 | 83 | describe('replaceVars', () => { 84 | it('...replaces one variable', () => { 85 | expect(Utilities.replaceVars( 86 | 'your ${myVar}?', {myVar: 'name'})) 87 | .to.equal('your name?'); 88 | }); 89 | it('...replace two variables', () => { 90 | expect(Utilities.replaceVars( 91 | 'test ${x} ${y}', {x: '1', y: 'z'})) 92 | .to.equal('test 1 z'); 93 | }); 94 | it('...ignores non-matching keys', () => { 95 | expect(Utilities.replaceVars( 96 | 'no ${match} for this', {})) 97 | .to.equal('no ${match} for this'); 98 | }); 99 | it('...returns input if it contains no variables', () => { 100 | expect(Utilities.replaceVars( 101 | 'return me', {me: 'test'})) 102 | .to.equal('return me'); 103 | }); 104 | it('...interpolation syntax must match', () => { 105 | expect(Utilities.replaceVars( 106 | 'return {me}', {me: 'test'})) 107 | .to.equal('return {me}'); 108 | expect(Utilities.replaceVars( 109 | 'return $me2', {me2: 'test'})) 110 | .to.equal('return $me2'); 111 | }); 112 | }); 113 | 114 | describe('keyReplace', () => { 115 | it('...simple override', () => { 116 | let b = {x: 10}; 117 | 118 | Utilities.keyReplace({x: 8}, b); 119 | expect(b.x).to.equal(8); 120 | }); 121 | it('...performs union', () => { 122 | let b = {y: 10}; 123 | 124 | Utilities.keyReplace({x: 8}, b); 125 | expect(b).to.have.keys(['x', 'y']); 126 | }); 127 | it('...replaces array', () => { 128 | let b = {arr: [1, 2, 3]}; 129 | 130 | Utilities.keyReplace({arr: [4, 5]}, b); 131 | expect(b.arr).to.have.length(2) 132 | .and.to.contain(4).and.to.contain(5); 133 | }); 134 | it('...replaces nested properties', () => { 135 | let b = {c: {d: 8, e: 10}}; 136 | 137 | Utilities.keyReplace({c: {e: 11}}, b); 138 | expect(b.c.d).to.equal(8); 139 | expect(b.c.e).to.equal(11); 140 | }); 141 | 142 | it('...nested replace with addition', () => { 143 | let b = {b: 8, c: {y: 9}}; 144 | 145 | Utilities.keyReplace({a: 1, b: 5, c: {x: 1}}, b); 146 | expect(b.a).to.equal(1); 147 | expect(b.b).to.equal(5); 148 | expect(b.c.x).to.equal(1); 149 | expect(b.c.y).to.equal(9); 150 | }); 151 | }); 152 | 153 | describe('iterateConfigs', () => { 154 | const defaultConfig = {name: 'my app', version: '0.0.1'}; 155 | 156 | it('...returns default if project config is undefined', () => { 157 | const result = Utilities.iterateConfigs(defaultConfig, undefined); 158 | 159 | expect(result.name).to.equal(defaultConfig.name); 160 | expect(result.version).to.equal(defaultConfig.version); 161 | expect(Object.keys(result)).to.have.length(2); 162 | }); 163 | 164 | it('...overrides defaults when override is specified', () => { 165 | let projectConfig = {name: 'my awesome app', version: '1.0.0'}; 166 | const result = Utilities.iterateConfigs(defaultConfig, projectConfig); 167 | 168 | expect(result.name).to.equal(projectConfig.name); 169 | expect(result.version).to.equal(projectConfig.version); 170 | expect(Object.keys(result)).to.have.length(2); 171 | }); 172 | 173 | it('...appends new keys when not specified in default', () => { 174 | let projectConfig = {special: {value: 5}}; 175 | const result = Utilities.iterateConfigs(defaultConfig, projectConfig); 176 | 177 | expect(result.special.value).to.equal(projectConfig.special.value); 178 | expect(Object.keys(result)).to.have.length(3); 179 | }); 180 | }); 181 | 182 | describe('copyFolderSync', () => { 183 | 184 | it('...copies directory with files to new location', () => { 185 | fs.readdirSync.returns(['file1.txt', 'file2.txt']); 186 | fs.lstatSync.returns({isFile: () => true}); 187 | Utilities.copyFolderSync('./test_dir', './test_dir_2'); 188 | expect(fs.mkdirSync.calledOnce).to.equal(true); 189 | expect(fs.copyFileSync.calledTwice).to.equal(true); 190 | }); 191 | 192 | it('...iterates nested directories recursively', () => { 193 | fs.readdirSync.returns(['test']); 194 | fs.lstatSync.onCall(0).returns({ 195 | isFile: () => false, 196 | isSymbolicLink: () => false, 197 | isDirectory: () => true 198 | }); 199 | fs.lstatSync.onCall(1).returns({ 200 | isFile: () => false, 201 | isSymbolicLink: () => true 202 | }); 203 | Utilities.copyFolderSync('./test_dir', './test_dir2'); 204 | expect(fs.readdirSync.callCount).to.equal(2); 205 | }); 206 | 207 | it('...does nothing when not file/dir/symlink', () => { 208 | sinon.spy(Utilities, 'copyFolderSync'); 209 | fs.readdirSync.returns(['invalid']); 210 | fs.lstatSync.onCall(0).returns({ 211 | isFile: () => false, 212 | isSymbolicLink: () => false, 213 | isDirectory: () => false 214 | }); 215 | Utilities.copyFolderSync('a', 'b'); 216 | expect(fs.copyFileSync.callCount).to.equal(0); 217 | expect(fs.symlinkSync.callCount).to.equal(0); 218 | expect(Utilities.copyFolderSync.callCount).to.equal(1); 219 | }); 220 | 221 | }); 222 | 223 | describe('copyFile', () => { 224 | 225 | it('...copies file from old location to new location', () => { 226 | Utilities.copyFile('./test1', './test2'); 227 | expect(fs.createReadStream.calledOnce).to.equal(true); 228 | expect(fs.createWriteStream.calledOnce).to.equal(true); 229 | }); 230 | 231 | }); 232 | 233 | describe('readFile', () => { 234 | 235 | it('...calls read file', () => { 236 | Utilities.readFile('xyz'); 237 | expect(fs.readFileSync.calledOnce).to.equal(true); 238 | }); 239 | }); 240 | 241 | describe('writeFile', () => { 242 | 243 | it('...calls write file', () => { 244 | Utilities.writeFile('xyz', 'text content...'); 245 | expect(fs.writeFileSync.calledOnce).to.equal(true); 246 | }); 247 | 248 | }); 249 | 250 | describe('fileExists', () => { 251 | 252 | it('...returns true for existing file', () => { 253 | fs.existsSync.returns(true); 254 | expect(Utilities.fileExists('im_here')).to.equal(true); 255 | }); 256 | 257 | it('...returns false when file does not exist', () => { 258 | fs.existsSync.returns(false); 259 | expect(Utilities.fileExists('nope')).to.equal(false); 260 | }); 261 | 262 | }); 263 | 264 | describe('createDir', () => { 265 | 266 | it('...will create a directory when it doesn\'t exist', () => { 267 | fs.existsSync.returns(false); 268 | const result = Utilities.createDir('my_dir'); 269 | 270 | expect(fs.mkdirSync.calledOnce).to.equal(true); 271 | expect(result).to.equal(true); 272 | }); 273 | 274 | it('...returns true for empty folder', () => { 275 | fs.existsSync.returns(true); 276 | fs.readdirSync.returns({length: 0}); 277 | const result = Utilities.createDir('empty_dir'); 278 | 279 | expect(fs.mkdirSync.notCalled).to.equal(true); 280 | expect(result).to.equal(true); 281 | }); 282 | 283 | it('...returns false for non-empty folder', () => { 284 | fs.existsSync.returns(true); 285 | fs.readdirSync.returns({length: 1}); 286 | expect(Utilities.createDir('non_empty_dir')).to.equal(false); 287 | }); 288 | 289 | }); 290 | 291 | describe('readJSON', () => { 292 | 293 | it('...returns a parsed JSON object', () => { 294 | fs.readFileSync.returns('{ "title" : "test" }'); 295 | const obj = Utilities.readJSON('xyz'); 296 | 297 | expect(obj.title).to.equal('test'); 298 | }); 299 | 300 | it('...throws error for non-JSON format file content', () => { 301 | fs.readFileSync.returns('this is some plain text'); 302 | expect(() => Utilities.readJSON('xyz')).to 303 | .throw('Unexpected token'); 304 | }); 305 | 306 | }); 307 | 308 | describe('readAndReplaceTextFile', () => { 309 | 310 | it('...replaces single variable', () => { 311 | fs.readFileSync.returns('Text with ${variable} in the middle!'); 312 | const variables = {variable: 'find me'}; 313 | const result = Utilities.readAndReplaceTextFile('some_file', variables); 314 | 315 | expect(result).to.contain(variables.variable); 316 | }); 317 | 318 | it('...replaces multiple variables', () => { 319 | fs.readFileSync.returns('Some math ${a} + ${b} = ${c}'); 320 | const math = {a: 1, b: 2, c: 3}; 321 | const result = Utilities.readAndReplaceTextFile('my_file', math); 322 | 323 | expect(result).to.contain('1 + 2 = 3'); 324 | }); 325 | 326 | }); 327 | 328 | describe('readAndReplaceJSONFile', () => { 329 | 330 | it('...replaces variables in an object', () => { 331 | fs.readFileSync.returns('{ "name":"${name}", "version" : "v-${version}" }'); 332 | const values = {name: 'my_app', version: '1.0.0'}; 333 | const jsonString = Utilities.readAndReplaceJSONFile('manifest', values); 334 | const obj = JSON.parse(jsonString); 335 | 336 | expect(obj.name).to.equal(values.name); 337 | expect(obj.version).to.equal('v-1.0.0'); 338 | }); 339 | 340 | it('...replaces nested variables', () => { 341 | fs.readFileSync.returns('{ "config": { "count" : "${n}" }}'); 342 | const values = {n: 10}; 343 | const result = Utilities.readAndReplaceJSONFile('manifest', values); 344 | 345 | expect(result).to.contain('10'); 346 | }); 347 | 348 | }); 349 | }); 350 | -------------------------------------------------------------------------------- /.github/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.2.5-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.4...v1.2.5-alpha.0) (2022-03-17) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * **deps:** downgraded chalk to v4 ([fee1634](https://github.com/mobilefirstllc/extension-cli/commit/fee16346a9b30b6dc61d3c76d0f1deaf868d3988)), closes [#155](https://github.com/mobilefirstllc/extension-cli/issues/155) 11 | * **deps:** update babel monorepo ([#157](https://github.com/mobilefirstllc/extension-cli/issues/157)) ([066f782](https://github.com/mobilefirstllc/extension-cli/commit/066f7820ce48e877cf5477ff77037c9d3a9d2fdd)) 12 | * **deps:** update babel monorepo to v7.16.0 ([945f8a0](https://github.com/mobilefirstllc/extension-cli/commit/945f8a0d9041e0c424f87351e2ecbd56b2cf4ce2)) 13 | * **deps:** update babel monorepo to v7.16.5 ([1145058](https://github.com/mobilefirstllc/extension-cli/commit/11450586884e27324b63b35d4c99209b623217bf)) 14 | * **deps:** update babel monorepo to v7.16.8 ([c4a44aa](https://github.com/mobilefirstllc/extension-cli/commit/c4a44aaf06abedeb4fcda8ec3abac55bef34f147)) 15 | * **deps:** update dependency @babel/preset-env to v7.16.4 ([502269b](https://github.com/mobilefirstllc/extension-cli/commit/502269bfa4b9ec02c0ca9a4a0292be2b1f62fb1e)) 16 | * **deps:** update dependency @babel/register to v7.16.9 ([206eafd](https://github.com/mobilefirstllc/extension-cli/commit/206eafdb7a1a3109f8da56691ebf36b291c0414b)) 17 | * **deps:** update dependency chalk to v5 ([#134](https://github.com/mobilefirstllc/extension-cli/issues/134)) ([e7c7af0](https://github.com/mobilefirstllc/extension-cli/commit/e7c7af0091bbab8db9be87a1d5ae36982a410d67)) 18 | * **deps:** update dependency eslint to v8.0.1 ([204a9c0](https://github.com/mobilefirstllc/extension-cli/commit/204a9c025abd57336dda507fe14c28f66afd5145)) 19 | * **deps:** update dependency eslint to v8.2.0 ([f208e0f](https://github.com/mobilefirstllc/extension-cli/commit/f208e0fe1d96ba95af10ab3ad5278024a05f26c4)) 20 | * **deps:** update dependency eslint to v8.3.0 ([d7bec9f](https://github.com/mobilefirstllc/extension-cli/commit/d7bec9f05496e8c73f4144885f2481583578b161)) 21 | * **deps:** update dependency eslint to v8.4.0 ([2489220](https://github.com/mobilefirstllc/extension-cli/commit/248922085384faa5fd8b568c51601d3f0ab23720)) 22 | * **deps:** update dependency eslint to v8.4.1 ([91d64e0](https://github.com/mobilefirstllc/extension-cli/commit/91d64e0c20c84b1c4759f8409bb26370affeb87d)) 23 | * **deps:** update dependency eslint to v8.5.0 ([c612311](https://github.com/mobilefirstllc/extension-cli/commit/c6123110367028cac4342f637fd345b37c2efa70)) 24 | * **deps:** update dependency eslint to v8.7.0 ([#149](https://github.com/mobilefirstllc/extension-cli/issues/149)) ([4684574](https://github.com/mobilefirstllc/extension-cli/commit/4684574c16afa04af4bc6b797d4837b5bfaae84d)) 25 | * **deps:** update dependency eslint to v8.8.0 ([#161](https://github.com/mobilefirstllc/extension-cli/issues/161)) ([87fb840](https://github.com/mobilefirstllc/extension-cli/commit/87fb8403339033106d10b13a50429841a8dd20ac)) 26 | * **deps:** update dependency jsdoc to v3.6.10 ([#158](https://github.com/mobilefirstllc/extension-cli/issues/158)) ([d0a09a3](https://github.com/mobilefirstllc/extension-cli/commit/d0a09a3540580ef1d69edaaf9aa264a4674198b5)) 27 | * **deps:** update dependency jsdom to v18.0.1 ([9daee71](https://github.com/mobilefirstllc/extension-cli/commit/9daee71d218cec2cc5cac39022ef7483792a600d)) 28 | * **deps:** update dependency jsdom to v18.1.0 ([db6641b](https://github.com/mobilefirstllc/extension-cli/commit/db6641bfaf6ab9bbb7ee66ababff1db5742747d7)) 29 | * **deps:** update dependency jsdom to v18.1.1 ([7e1e222](https://github.com/mobilefirstllc/extension-cli/commit/7e1e22282b95329a7c99d4069a36b0501475e6bb)) 30 | * **deps:** update dependency jsdom to v19 ([b0f290f](https://github.com/mobilefirstllc/extension-cli/commit/b0f290fa43f76fc4e67a95ae35ca4cefb7b9db6c)) 31 | * **deps:** update dependency mocha to v9.1.3 ([c5186f9](https://github.com/mobilefirstllc/extension-cli/commit/c5186f9cb3b0cf1c27998d519c9d470863074358)) 32 | * **deps:** update dependency mocha to v9.1.4 ([f90c528](https://github.com/mobilefirstllc/extension-cli/commit/f90c528f16182d652c305144fb247b5b375f38e6)) 33 | * **deps:** update dependency mocha to v9.2.1 ([534eef9](https://github.com/mobilefirstllc/extension-cli/commit/534eef9c897f36e999fecf475a4a8effb9e326c0)) 34 | * **deps:** update dependency sass to v1.43.4 ([e9674a8](https://github.com/mobilefirstllc/extension-cli/commit/e9674a8de69848da4a6557f7beef6387f2bf283e)) 35 | * **deps:** update dependency sass to v1.43.5 ([ce6e9ab](https://github.com/mobilefirstllc/extension-cli/commit/ce6e9abf46188690762dd45a15b6659b620a122f)) 36 | * **deps:** update dependency sass to v1.44.0 ([60842ce](https://github.com/mobilefirstllc/extension-cli/commit/60842cef73de48d2bdbfa2fd93955701981ce9d3)) 37 | * **deps:** update dependency sass to v1.45.0 ([524a5d7](https://github.com/mobilefirstllc/extension-cli/commit/524a5d770c49365f449fc4bda8cf293f9c80f969)) 38 | * **deps:** update dependency sass to v1.45.1 ([6539428](https://github.com/mobilefirstllc/extension-cli/commit/6539428590a26e61fcbc9edc02e7ecd6b7e2bd7f)) 39 | * **deps:** update dependency sass to v1.46.0 ([bb70958](https://github.com/mobilefirstllc/extension-cli/commit/bb70958be4edb8ed6faef497fb5ba6a6bfbe0694)) 40 | * **deps:** update dependency sass to v1.48.0 ([1cfb841](https://github.com/mobilefirstllc/extension-cli/commit/1cfb841a92df7d99bf29852ea6110897690f7334)) 41 | * **deps:** update dependency sass to v1.49.8 ([#156](https://github.com/mobilefirstllc/extension-cli/issues/156)) ([5a658c0](https://github.com/mobilefirstllc/extension-cli/commit/5a658c09db5e06934b6f53345d212ab72edeecb8)) 42 | * **deps:** update dependency sass to v1.49.9 ([aef9fd6](https://github.com/mobilefirstllc/extension-cli/commit/aef9fd6142879af3bf4610c80eaca1f97aff96a4)) 43 | * **deps:** update dependency sinon to v12 ([1c9807d](https://github.com/mobilefirstllc/extension-cli/commit/1c9807d4ab682a8d4e24ebcf591fdd693828c0a2)) 44 | * **deps:** update dependency sinon to v13 ([8282c0a](https://github.com/mobilefirstllc/extension-cli/commit/8282c0a05d2702e6ec92155d8cfaba2de93c9c53)) 45 | * **deps:** update dependency yargs to v17.3.0 ([9f86f05](https://github.com/mobilefirstllc/extension-cli/commit/9f86f05b858551c9cff0b237830e19223627264a)) 46 | * **deps:** update dependency yargs to v17.3.1 ([#145](https://github.com/mobilefirstllc/extension-cli/issues/145)) ([add18ee](https://github.com/mobilefirstllc/extension-cli/commit/add18ee15196d4356a74d0f76ded23d1fef1d085)) 47 | 48 | ### [1.2.4](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.4-beta.0...v1.2.4) (2021-10-20) 49 | 50 | ### [1.2.4-beta.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.2...v1.2.4-beta.0) (2021-10-14) 51 | 52 | * Update devtools sourcemap config [PR #119](https://github.com/MobileFirstLLC/extension-cli/pull/119) 53 | * New extension now initialized with MV3 [#86](https://github.com/MobileFirstLLC/extension-cli/pull/111) 54 | 55 | **Dependency updates** 56 | 57 | * **deps:** update dependency sass to v1.43.2 ([32eb148](https://github.com/mobilefirstllc/extension-cli/commit/32eb148d81318f115942df2682270ded3c061652)) 58 | * **deps:** update dependency @babel/preset-env to v7.15.8 ([a341965](https://github.com/mobilefirstllc/extension-cli/commit/a3419659b3ac2427f1134f8c6cfb2bb38c29f009)) 59 | * **deps:** update dependency commander to v8.2.0 ([5226669](https://github.com/mobilefirstllc/extension-cli/commit/52266695f15cace4cc422e229afe5555d30ff0e4)) 60 | * **deps:** update dependency eslint to v8 ([d5549a8](https://github.com/mobilefirstllc/extension-cli/commit/d5549a8730256f61edbd36ab7cabbac95db5000e)) 61 | * **deps:** update dependency jsdom to v18 ([681db6b](https://github.com/mobilefirstllc/extension-cli/commit/681db6bafeedda989471235ff6f14ad9edff1885)) 62 | * **deps:** update dependency mocha to v9.1.2 ([d7cecc6](https://github.com/mobilefirstllc/extension-cli/commit/d7cecc60a2aa918559bea17b2531b3e331500cce)) 63 | * **deps:** update dependency prompts to v2.4.2 ([f99cb60](https://github.com/mobilefirstllc/extension-cli/commit/f99cb608f43414ecbb8f9309ce2d32453b11b0d5)) 64 | * **deps:** update dependency webpack-stream to v7 ([#94](https://github.com/mobilefirstllc/extension-cli/issues/94)) ([a19b448](https://github.com/mobilefirstllc/extension-cli/commit/a19b4488cc7f9a31474904e58b2920bf67f0619a)) 65 | * **deps:** update dependency yargs to v17.2.1 ([9a06f44](https://github.com/mobilefirstllc/extension-cli/commit/9a06f44b878d178dbd15fbae490470082b99221a)) 66 | 67 | ### [1.2.2](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.0...v1.2.2) (2021-08-28) 68 | 69 | ## [1.2.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.0-beta.1...v1.2.0) (2021-07-28) 70 | 71 | ## [1.2.0-beta.1](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.0-beta.0...v1.2.0-beta.1) (2021-07-25) 72 | 73 | ## [1.2.0-beta.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.0-alpha.1...v1.2.0-beta.0) (2021-07-24) 74 | 75 | ## [1.2.0-alpha.1](https://github.com/mobilefirstllc/extension-cli/compare/v1.2.0-alpha.0...v1.2.0-alpha.1) (2021-07-19) 76 | 77 | ## [1.2.0-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.1.0...v1.2.0-alpha.0) (2021-07-19) 78 | 79 | * Useless regular-expression character escape ([935d0b3](https://github.com/mobilefirstllc/extension-cli/commit/935d0b3df52255c3c18526b4a1a3212c2c6b1b20)) 80 | * Useless regular-expression character escape ([f2ebae5](https://github.com/mobilefirstllc/extension-cli/commit/f2ebae55ca8bc5df2fe8872a58131219b6f81a88)) 81 | 82 | ## [1.1.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.1.0-alpha.1...v1.1.0) (2021-06-12) 83 | 84 | ## [1.1.0-alpha.1](https://github.com/mobilefirstllc/extension-cli/compare/v1.1.0-alpha.0...v1.1.0-alpha.1) (2021-06-12) 85 | 86 | ## [1.1.0-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.3...v1.1.0-alpha.0) (2021-06-12) 87 | 88 | * **xt-sync:** change command to prompt with options; update relevant docs ([7a65245](https://github.com/mobilefirstllc/extension-cli/commit/7a652455e834929c1d5e78d8dc5648a64f079aca)) 89 | 90 | ### [1.0.3](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.3-alpha.2...v1.0.3) (2021-04-27) 91 | 92 | ### [1.0.3-alpha.2](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.3-alpha.1...v1.0.3-alpha.2) (2021-04-27) 93 | 94 | ### [1.0.3-alpha.1](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.3-alpha.0...v1.0.3-alpha.1) (2021-04-16) 95 | 96 | ### [1.0.3-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.2...v1.0.3-alpha.0) (2021-04-15) 97 | 98 | ### [1.0.2](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.1...v1.0.2) (2021-04-12) 99 | 100 | * default style bundle without file extension ([70c4aa6](https://github.com/mobilefirstllc/extension-cli/commit/70c4aa69f7c4bdaa440cd3fe61eabe5a4ff7327c)) 101 | 102 | ### [1.0.1](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.0...v1.0.1) (2021-04-12) 103 | 104 | ## [1.0.0](https://github.com/mobilefirstllc/extension-cli/compare/v1.0.0-alpha.0...v1.0.0) (2021-04-11) 105 | 106 | ## [1.0.0-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.9...v1.0.0-alpha.0) (2021-04-09) 107 | 108 | ### [0.11.9](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.9-alpha.0...v0.11.9) (2021-04-05) 109 | 110 | ### [0.11.9-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.8...v0.11.9-alpha.0) (2021-04-04) 111 | 112 | ### [0.11.8](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.8-alpha.2...v0.11.8) (2021-03-12) 113 | 114 | ### [0.11.8-alpha.2](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.8-alpha.1...v0.11.8-alpha.2) (2021-03-11) 115 | 116 | ### [0.11.8-alpha.1](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.8-alpha.0...v0.11.8-alpha.1) (2021-03-10) 117 | 118 | ### [0.11.8-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.7...v0.11.8-alpha.0) (2021-03-09) 119 | 120 | ### [0.11.7](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.7-alpha.0...v0.11.7) (2021-03-02) 121 | 122 | * **xt-docs**: make watch recursive, display more output on errors 123 | 124 | ### [0.11.7-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.6...v0.11.7-alpha.0) (2021-03-02) 125 | 126 | ### [0.11.6](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.6-alpha.0...v0.11.6) (2021-02-26) 127 | 128 | * **xt-docs** [#23](https://github.com/MobileFirstLLC/extension-cli/issues/23) add watch mode for docs 129 | 130 | ### [0.11.5](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.5-alpha.1...v0.11.5) (2021-02-24) 131 | 132 | * **xt-test** [#26](https://github.com/mobilefirstllc/extension-cli/issues/26) unit test coverage reporting and reporting error code on test failure ([d3bba9d](https://github.com/mobilefirstllc/extension-cli/commit/d3bba9d08e04d574ab26468b522f33db3567fd9a)) 133 | 134 | ### [0.11.3](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.3-alpha.0...v0.11.3) (2021-01-27) 135 | 136 | ### [0.11.3-alpha.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.2...v0.11.3-alpha.0) (2021-01-27) 137 | 138 | ### [0.11.2](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.1...v0.11.2) (2021-01-08) 139 | 140 | * **xt-build:** command path fix ([c0c2e08](https://github.com/mobilefirstllc/extension-cli/commit/c0c2e08bcfbb348ce8bb8d9980d4db22e2713d37)) 141 | 142 | ### [0.11.1](https://github.com/mobilefirstllc/extension-cli/compare/v0.11.0...v0.11.1) (2021-01-06) 143 | 144 | * **custom commands:** [#18](https://github.com/mobilefirstllc/extension-cli/issues/18) enable custom commands watch ([3cfe52b](https://github.com/mobilefirstllc/extension-cli/commit/3cfe52ba38779d1570943a74a0a2e45203159d54)) 145 | * [#18](https://github.com/mobilefirstllc/extension-cli/issues/18) add configuration option for custom commands before building release zip file ([f8126e6](https://github.com/mobilefirstllc/extension-cli/commit/f8126e6838faa043fe38844f8941cc465ec2425a)) 146 | 147 | * xt-create images fix (again) 148 | 149 | ### [0.10.1](https://github.com/mobilefirstllc/extension-cli/compare/v0.9.4...v0.10.1) (2020-12-15) 150 | 151 | * xt-create images ([b6ad50f](https://github.com/mobilefirstllc/extension-cli/commit/b6ad50f0367a20d45d2ea677591e3869c3f4fff3)) 152 | * [#16](https://github.com/mobilefirstllc/extension-cli/issues/16) update test configs ([7f57bf5](https://github.com/mobilefirstllc/extension-cli/commit/7f57bf527ab6fcccec53fd4544e2fc134b5083ee)) 153 | * [#17](https://github.com/mobilefirstllc/extension-cli/issues/17) check if gitignore exists ([b96edc9](https://github.com/mobilefirstllc/extension-cli/commit/b96edc9a01e0077b0d68f994782c2b5c840199c1)) 154 | * **xt-create:** change default icon to high contrast ([4895f43](https://github.com/mobilefirstllc/extension-cli/commit/4895f43bf16b63309bc9e3d14248843050bc164c)) 155 | 156 | ### [0.9.4](https://github.com/mobilefirstllc/extension-cli/compare/v0.9.3...v0.9.4) (2020-11-29) 157 | 158 | ### [0.9.3](https://github.com/mobilefirstllc/extension-cli/compare/v0.9.2...v0.9.3) (2020-10-31) 159 | 160 | ### [0.9.2](https://github.com/mobilefirstllc/extension-cli/compare/v0.9.1...v0.9.2) (2020-10-31) 161 | 162 | * [#10](https://github.com/mobilefirstllc/extension-cli/issues/10) improve xt-clean command handling of files ([b14f311](https://github.com/mobilefirstllc/extension-cli/commit/b14f311077b713f749e352a889a5c7b843e5f89a)) 163 | 164 | ### [0.9.1](https://github.com/mobilefirstllc/extension-cli/compare/v0.9.0...v0.9.1) (2020-10-11) 165 | 166 | * [#8](https://github.com/mobilefirstllc/extension-cli/issues/8) xt-docs fix config keys replace when value is an array ([98d72ca](https://github.com/mobilefirstllc/extension-cli/commit/98d72ca8f3df251e36bd1b2da3ecea9e2832496d)) 167 | 168 | ## [0.9.0](https://github.com/mobilefirstllc/extension-cli/compare/v0.8.16...v0.9.0) (2020-10-05) 169 | 170 | * **xt-clean:** refactor command ([8acf9c8](https://github.com/mobilefirstllc/extension-cli/commit/8acf9c80757c44265c9bf605882d8a06fd97d7bb)) 171 | * **xt-create:** refactor command ([1c64f93](https://github.com/mobilefirstllc/extension-cli/commit/1c64f932966c405b3958ce6069323661ac5079d9)) 172 | * **xt-create:** refactor create command ([6ac7f95](https://github.com/mobilefirstllc/extension-cli/commit/6ac7f95f1e08ea278498293c4f4d3048b5bc8190)) 173 | * **xt-docs:** refactor docs command ([432fa79](https://github.com/mobilefirstllc/extension-cli/commit/432fa7979ca7dc686c31cdd6d92fa5911ad467f1)) 174 | * **xt-sync:** refactor sync ([c938eec](https://github.com/mobilefirstllc/extension-cli/commit/c938eec37a6ce3a4ad8bba1c5a22519a01242761)) 175 | * **xt-test:** add configurable test path ([876fb8a](https://github.com/mobilefirstllc/extension-cli/commit/876fb8a0afbaeab196df6aebd09fc92a977150de)) 176 | 177 | ### [0.8.16](https://github.com/mobilefirstllc/extension-cli/compare/v0.8.15...v0.8.16) (2020-08-09) 178 | 179 | ### [0.8.15](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.14...v0.8.15) (2020-08-04) 180 | 181 | * fix packages ([0cad024](https://github.com/MobileFirstLLC/extension-cli/commit/0cad024ba53ec61ae96db0bcf6a616f476acebcf)) 182 | 183 | ### [0.8.14](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.13...v0.8.14) (2020-08-01) 184 | 185 | * **xt-create:** update generated docs ([a7ebb95](https://github.com/MobileFirstLLC/extension-cli/commit/a7ebb952cfeb0beac89b71a680a24eaad7325d95)) 186 | 187 | ### [0.8.13](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.12...v0.8.13) (2020-07-11) 188 | 189 | ### [0.8.12](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.11...v0.8.12) (2020-05-26) 190 | 191 | * **xt-build:** undo webpack config change ([19a19ba](https://github.com/MobileFirstLLC/extension-cli/commit/19a19baf7a99787bf34c295fe580d646a70b8b2f)) 192 | 193 | ### [0.8.11](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.10...v0.8.11) (2020-05-26) 194 | 195 | * **xt-docs:** fix docs command related issue with init configs ([88c5f58](https://github.com/MobileFirstLLC/extension-cli/commit/88c5f58986b9b2c33c8b86d774e466c7a9f24e03)) 196 | 197 | ### [0.8.10](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.9...v0.8.10) (2020-05-25) 198 | 199 | * **xt-build:** [#2](https://github.com/MobileFirstLLC/extension-cli/issues/2) Updating manifest while watching causes looping behavior ([935377a](https://github.com/MobileFirstLLC/extension-cli/commit/935377a2ee4ebbf68e1bd2165de6a6dd641397b9)) 200 | * **xt-build:** [#3](https://github.com/MobileFirstLLC/extension-cli/issues/3) make webpack options configurable to user ([74af14f](https://github.com/MobileFirstLLC/extension-cli/commit/74af14f3ed6eacf26f7534636412b20151b76909)) 201 | * **xt-build:** JS build on watch doesn't rebuild js files correctly [#4](https://github.com/MobileFirstLLC/extension-cli/issues/4) ([e80c2d6](https://github.com/MobileFirstLLC/extension-cli/commit/e80c2d635e15a27c6c5f0c53fa63e25ba50a6233)) 202 | * **xt-build:** make webpack options configurable to user [#3](https://github.com/MobileFirstLLC/extension-cli/issues/3) ([4148890](https://github.com/MobileFirstLLC/extension-cli/commit/4148890e1c7d925214974710c515746009033f91)) 203 | 204 | ### [0.8.9](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.9-alpha.2...v0.8.9) (2020-04-10) 205 | 206 | ### [0.8.9-alpha.2](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.9-alpha.1...v0.8.9-alpha.2) (2020-04-10) 207 | 208 | * **xt-create:** update docs regarding xt-create and use ([27b39bb](https://github.com/MobileFirstLLC/extension-cli/commit/27b39bb46c5f57336a0083fa35d395936605936a)) 209 | 210 | ### [0.8.9-alpha.1](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.9-alpha.0...v0.8.9-alpha.1) (2020-04-10) 211 | 212 | * **xt-create:** minor fixes ([c3c7b31](https://github.com/MobileFirstLLC/extension-cli/commit/c3c7b31fc8ca8c92be0e546287f474e3744439e3)) 213 | 214 | ### [0.8.9-alpha.0](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.8...v0.8.9-alpha.0) (2020-04-10) 215 | 216 | * **xt-create:** implement create command ([c28b22b](https://github.com/MobileFirstLLC/extension-cli/commit/c28b22b8ae41b786e49222c3d7c8830f5a2e92b4)) 217 | 218 | ### [0.8.8](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.7...v0.8.8) (2020-04-08) 219 | 220 | * Upgraded NPM packages 221 | 222 | ### [0.8.7](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.6...v0.8.7) (2020-01-17) 223 | 224 | * build scripts and update packages ([74f51d2](https://github.com/MobileFirstLLC/extension-cli/commit/74f51d2a1dfe1b4d1611ca4ef848f20dfc92b81e)) 225 | 226 | ### [0.8.6](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.5...v0.8.6) (2019-12-21) 227 | 228 | ### [0.8.5](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.4...v0.8.5) (2019-12-21) 229 | 230 | ### [0.8.4](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.3...v0.8.4) (2019-12-20) 231 | 232 | ### [0.8.3](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.2...v0.8.3) (2019-12-20) 233 | 234 | ### [0.8.2](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.1...v0.8.2) (2019-12-20) 235 | 236 | * cognitive complexity ([de4d2fb](https://github.com/MobileFirstLLC/extension-cli/commit/de4d2fbbb475fbc43787925a7b25ad28fbb13330)) 237 | * reduce complexity ([78bbb16](https://github.com/MobileFirstLLC/extension-cli/commit/78bbb163e6f6ec9396a4146147a52488ab1637c9)) 238 | * Similar blocks of code found in 2 locations. Consider refactoring. ([b3c4e3d](https://github.com/MobileFirstLLC/extension-cli/commit/b3c4e3d014a3f2ac7db301ee3f80e16dbd8b7b00)) 239 | * Similar blocks of code found in 3 locations. Consider refactoring. ([c6a7e30](https://github.com/MobileFirstLLC/extension-cli/commit/c6a7e3074d8c1305246b0b69fb03475a3d27ec9f)) 240 | 241 | ### [0.8.1](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.0...v0.8.1) (2019-12-20) 242 | 243 | ## [0.8.0](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.0-alpha.3...v0.8.0) (2019-12-19) 244 | 245 | ## [0.8.0-alpha.3](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.0-alpha.2...v0.8.0-alpha.3) (2019-12-18) 246 | 247 | ## [0.8.0-alpha.2](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.0-alpha.1...v0.8.0-alpha.2) (2019-12-18) 248 | 249 | * try fix ci publish ([23ae7b3](https://github.com/MobileFirstLLC/extension-cli/commit/23ae7b33e2fc361fffafec2e9ec79693d212d292)) 250 | 251 | ## [0.8.0-alpha.1](https://github.com/MobileFirstLLC/extension-cli/compare/v0.8.0-alpha.0...v0.8.0-alpha.1) (2019-12-18) 252 | 253 | * xt-build -> use webpack with scripts ([063cecc](https://github.com/MobileFirstLLC/extension-cli/commit/063cecc585d5657c005af7342eeb04466930ea09)) 254 | * xt-build locales and manifest tasks ([f98872e](https://github.com/MobileFirstLLC/extension-cli/commit/f98872e0f39cd7095076e4f97897de6857fe3306)) 255 | * xt-build webpack scripts ([13e2297](https://github.com/MobileFirstLLC/extension-cli/commit/13e2297b912b067eaf1ad44e449e83585b3a22ab)) 256 | * xt-test command ([130986f](https://github.com/MobileFirstLLC/extension-cli/commit/130986f3bf58c01d46e14308d2bf080b1b88c6fc)) 257 | 258 | ## 0.8.0-alpha.0 (2019-12-17) 259 | 260 | * jsdoc config ([b021a0e](https://github.com/MobileFirstLLC/extension-cli/commit/b021a0e1424d4f5e226dea1d1940dee9af3fdbb8)) 261 | * migrate build script to gulp 4 ([237c44f](https://github.com/MobileFirstLLC/extension-cli/commit/237c44f4107a66c7ffc7fd9917e6343d28f90021)) 262 | * migrate xt-test; update docs ([2ae3e78](https://github.com/MobileFirstLLC/extension-cli/commit/2ae3e78128b5db0dad81b1f589fb3e6add732fe8)) 263 | * xt-clean, xt-docs; update packages; add build configuration, image assets, and source docs ([85eead2](https://github.com/MobileFirstLLC/extension-cli/commit/85eead28704cf684b520409f192fe7073623f2fa)) 264 | * xt-sync; update readme ([ddcc8aa](https://github.com/MobileFirstLLC/extension-cli/commit/ddcc8aaab252e7e184061576fb6683f3ed095343)) 265 | 266 | 267 | ## 0.7.11 (2018-06-25) 268 | 269 | 270 | ## 0.7.10 (2018-06-25) 271 | 272 | 273 | ## 0.7.9 (2018-06-10) 274 | 275 | 276 | ## 0.7.8 (2018-06-06) 277 | 278 | * **xt-docs:** update docs output path 279 | * **xt-sync:** update ci configs 280 | 281 | 282 | ## 0.7.7 (2018-06-04) 283 | 284 | * **xt-test:** revert 285 | 286 | 287 | ## 0.7.5 (2018-05-29) 288 | 289 | 290 | ## 0.7.6 (2018-06-04) 291 | 292 | * **xt-test:** expose jsdom 293 | 294 | 295 | ## 0.7.5 (2018-05-29) 296 | 297 | 298 | ## 0.7.4 (2018-05-25) 299 | 300 | * **xt-test:** added chai-as-promised 301 | 302 | 303 | ## 0.7.3 (2018-05-24) 304 | 305 | 306 | ## 0.7.2 (2018-05-22) 307 | 308 | * **xt-test:** added move event simulators 309 | 310 | 311 | ## 0.7.1 (2018-05-22) 312 | 313 | 314 | # 0.7.0 (2018-05-21) 315 | 316 | * **xt-test:** add test runner and env setup 317 | 318 | 319 | ## 0.6.3 (2018-05-21) 320 | 321 | * **xt-build:** added spinner and logging on watch 322 | 323 | 324 | ## 0.6.2 (2018-05-21) 325 | 326 | * **xt-sync:** fix path 327 | 328 | 329 | ## 0.6.1 (2018-05-21) 330 | 331 | * **xt-sync:** update commands 332 | 333 | 334 | # 0.6.0 (2018-05-21) 335 | 336 | * **xt-sync:** consolidate file updates into one command; remove xt-ci, xt-ignore, and xt-lint 337 | 338 | 339 | # 0.5.0 (2018-05-21) 340 | 341 | * **ci:** fix ci command 342 | * **xt-clean:** added clean option 343 | 344 | 345 | # 0.4.0 (2018-05-20) 346 | 347 | * **build:** remove initial build script console output 348 | * **build:** add option to have configs in package.json 349 | * **docs:** simplyfy docs command and enable config in package.json 350 | * **xt-cli:** update ci settings 351 | 352 | 353 | ## 0.3.2 (2018-05-19) 354 | 355 | * **build:** update build to pipe output, change default configs && path 356 | 357 | 358 | ## 0.3.1 (2018-05-18) 359 | 360 | * **build:** fix done callback 361 | 362 | 363 | # 0.3.0 (2018-05-14) 364 | 365 | * **xt-build:** added copyAsIs to build options 366 | 367 | 368 | ## 0.2.1 (2018-05-14) 369 | 370 | * **config:** update docs default config 371 | 372 | 373 | # 0.2.0 (2018-05-14) 374 | 375 | * **xt-lint:** create or update eslint settings 376 | 377 | 378 | # 0.1.0 (2018-05-14) 379 | 380 | 381 | #### 0.0.7 (2018-05-14) 382 | 383 | 384 | #### 0.0.6 (2018-05-14) 385 | 386 | 387 | #### 0.0.5 (2018-05-14) 388 | 389 | 390 | #### 0.0.4 (2018-05-14) 391 | 392 | 393 | #### 0.0.3 (2018-05-14) 394 | 395 | 396 | #### 0.0.2 (2018-05-14) 397 | 398 | 399 | #### 0.0.1 (2018-05-14) 400 | --------------------------------------------------------------------------------