├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── cookiecutter.json ├── pytest.ini ├── tests ├── test_instantiate.py └── testconfig.yaml └── {{cookiecutter.github_project_name}} ├── .coveragerc ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── .yarnrc.yml ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── babel.config.js ├── codecov.yml ├── css └── widget.css ├── docs ├── Makefile ├── environment.yml ├── make.bat └── source │ ├── _static │ └── helper.js │ ├── conf.py │ ├── develop-install.rst │ ├── examples │ ├── index.rst │ └── introduction.nblink │ ├── index.rst │ ├── installing.rst │ └── introduction.rst ├── examples └── introduction.ipynb ├── install.json ├── jest.config.js ├── package.json ├── pyproject.toml ├── pytest.ini ├── readthedocs.yml ├── setup.py ├── src ├── __tests__ │ ├── index.spec.ts │ └── utils.ts ├── extension.ts ├── index.ts ├── plugin.ts ├── version.ts └── widget.ts ├── tsconfig.eslint.json ├── tsconfig.json ├── webpack.config.js ├── {{cookiecutter.python_package_name}}.json └── {{cookiecutter.python_package_name}} ├── __init__.py ├── _frontend.py ├── _version.py ├── example.py ├── nbextension └── extension.js └── tests ├── __init__.py ├── conftest.py ├── test_example.py └── test_nbextension_path.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | 8 | concurrency: 9 | group: ci-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | defaults: 13 | run: 14 | shell: bash -eux {0} 15 | 16 | jobs: 17 | build: 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [ubuntu-latest, windows-latest, macos-latest] 23 | python-version: ["3.7", "3.10"] 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v2 27 | 28 | - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 29 | 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install -U cookiecutter jupyterlab~=3.0 ipywidgets 33 | 34 | - name: Install the extension 35 | run: | 36 | cookiecutter . --config-file tests/testconfig.yaml --no-input 37 | cd jupyter-widget-testwidgets 38 | python -m pip install --upgrade -v -e ".[test, examples, docs]" 39 | 40 | - name: Test the extension 41 | working-directory: jupyter-widget-testwidgets 42 | run: | 43 | python -m pytest 44 | yarn run test 45 | 46 | - name: Linting 47 | if: ${{ matrix.os == 'ubuntu-latest' }} 48 | working-directory: jupyter-widget-testwidgets 49 | run: | 50 | yarn run lint:check 51 | 52 | - name: Validate extension usage 53 | working-directory: jupyter-widget-testwidgets 54 | run: | 55 | # Validate nbextension 56 | jupyter nbextension list 2>&1 | grep -ie "jupyter-js-widgets/extension.*enabled" 57 | 58 | # Validate labextension 59 | jupyter labextension list 60 | jupyter labextension list 2>&1 | grep -ie "jupyter-widget-testwidgets.*OK" 61 | # Make sure our lab extension can be develop installed. 62 | jupyter labextension develop . --overwrite 63 | 64 | - name: Check docs can be build + links 65 | if: ${{ matrix.os == 'ubuntu-latest' }} 66 | working-directory: jupyter-widget-testwidgets/docs 67 | run: | 68 | sudo apt install -y pandoc 69 | make html SPHINXOPTS="-W" 70 | python -m pytest --check-links 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | *.pyc 4 | .cache 5 | .vscode 6 | 7 | myproject 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Project Jupyter Contributors 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # widget-ts-cookiecutter 2 | 3 | ![Github Actions Status](https://github.com/jupyter-widgets/widget-ts-cookiecutter/workflows/Build/badge.svg) 4 | 5 | A [cookiecutter](https://github.com/cookiecutter/cookiecutter) template for a custom 6 | Jupyter widget project. 7 | 8 | ## What is widget-ts-cookiecutter? 9 | 10 | With **widget-ts-cookiecutter** you can create a custom Jupyter interactive 11 | widget project with sensible defaults. widget-ts-cookiecutter helps custom widget 12 | authors get started with best practices for the packaging and distribution 13 | of a custom Jupyter interactive widget library. 14 | 15 | ## Usage 16 | 17 | Install [cookiecutter](https://github.com/audreyr/cookiecutter): 18 | 19 | pip install cookiecutter 20 | 21 | After installing cookiecutter, use widget-ts-cookiecutter: 22 | 23 | cookiecutter https://github.com/jupyter-widgets/widget-ts-cookiecutter.git 24 | 25 | As widget-ts-cookiecutter runs, you will be asked for basic information about 26 | your custom Jupyter widget project. You will be prompted for the following 27 | information: 28 | 29 | - `author_name`: your name or the name of your organization, 30 | - `author_email`: your project's contact email, 31 | - `github_project_name`: name of your custom Jupyter widget's GitHub repository, 32 | - `github_organization_name`: name of your custom Jupyter widget's GitHub user or organization, 33 | - `python_package_name`: name of the Python "back-end" package used in your custom widget. 34 | - `npm_package_name`: name for the npm "front-end" package holding the JavaScript 35 | implementation used in your custom widget. 36 | - `npm_package_version`: initial version of the npm package. 37 | - `project_short_description` : a short description for your project that will 38 | be used for both the "back-end" and "front-end" packages. 39 | 40 | After this, you will have a directory containing files used for creating a 41 | custom Jupyter widget. To check that eveything is set up as it should be, 42 | you should run the tests: 43 | 44 | Create a dev environment: 45 | ```bash 46 | conda create -n {{ cookiecutter.python_package_name }}-dev -c conda-forge nodejs yarn python jupyterlab 47 | conda activate {{ cookiecutter.python_package_name }}-dev 48 | ``` 49 | 50 | Install the python. This will also build the TS package. 51 | 52 | ```bash 53 | # First install the python package. This will also build the JS packages. 54 | pip install -e ".[test, examples]" 55 | 56 | # Run the python tests. This should not give you a few sucessful example tests 57 | py.test 58 | 59 | # Run the JS tests. This should again, only give TODO errors (Expected 'Value' to equal 'Expected value'): 60 | yarn test 61 | ``` 62 | 63 | When developing your extensions, you need to manually enable your extensions with the 64 | notebook / lab frontend. For lab, this is done by the command: 65 | 66 | ``` 67 | jupyter labextension develop --overwrite . 68 | ``` 69 | 70 | For classic notebook, you can run: 71 | 72 | ``` 73 | jupyter nbextension install --sys-prefix --symlink --overwrite --py 74 | jupyter nbextension enable --sys-prefix --py 75 | ``` 76 | 77 | Note that the `--symlink` flag doesn't work on Windows, so you will here have to run 78 | the `install` command every time that you rebuild your extension. For certain installations 79 | you might also need another flag instead of `--sys-prefix`, but we won't cover the meaning 80 | of those flags here. 81 | 82 | Every time you make a change in the TypeScript code, you will need to rebuild it then refresh the browser page: 83 | 84 | ``` 85 | yarn run build 86 | ``` 87 | 88 | ### How to see your changes 89 | #### Typescript: 90 | If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different 91 | terminals to watch for changes in the extension's source and automatically rebuild the widget. 92 | 93 | ```bash 94 | # Watch the source directory in one terminal, automatically rebuilding when needed 95 | yarn run watch 96 | # Run JupyterLab in another terminal 97 | jupyter lab 98 | ``` 99 | 100 | After a change wait for the build to finish and then refresh your browser and the changes should take effect. 101 | 102 | #### Python: 103 | If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. 104 | 105 | ## Releasing your initial packages: 106 | 107 | - Add tests 108 | - Ensure tests pass locally and on CI. Check that the coverage is reasonable. 109 | - Make a release commit, where you remove the `, 'dev'` entry in `_version.py`. 110 | - Update the version in `package.json` 111 | - Relase the npm packages: 112 | ```bash 113 | npm login 114 | npm publish 115 | ``` 116 | - Install publish dependencies: 117 | ```bash 118 | pip install build twine 119 | ``` 120 | - Build the assets and publish 121 | ```bash 122 | python -m build . 123 | twine check dist/* 124 | twine upload dist/* 125 | ``` 126 | - Tag the release commit (`git tag `) 127 | - Update the version in `_version.py`, and put it back to dev (e.g. 0.1.0 -> 0.2.0.dev). 128 | Update the versions of the npm packages (without publishing). 129 | - Commit the changes. 130 | - `git push` and `git push --tags`. 131 | -------------------------------------------------------------------------------- /cookiecutter.json: -------------------------------------------------------------------------------- 1 | { 2 | "author_name": "me", 3 | "author_email": "me@me.com", 4 | "github_project_name": "myproject", 5 | "github_organization_name": "myorg", 6 | "python_package_name": "{{ cookiecutter.github_project_name | replace('-', '_') }}", 7 | "npm_package_name": "{{ cookiecutter.github_project_name }}", 8 | "npm_package_version": "0.1.0", 9 | "project_short_description": "A Custom Jupyter Widget Library", 10 | "_copy_without_render": [ 11 | ".github/workflows/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths=tests 3 | norecursedirs=node_modules 4 | -------------------------------------------------------------------------------- /tests/test_instantiate.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import sys 4 | 5 | import pytest 6 | 7 | HERE = os.path.abspath(os.path.dirname(__file__)) 8 | PROJECT_ROOT = os.path.dirname(HERE) 9 | 10 | pytest_plugins = "pytester" 11 | 12 | use_shell = os.name == 'nt' 13 | 14 | 15 | @pytest.fixture(scope='session') 16 | def example_instance(tmpdir_factory): 17 | from cookiecutter.main import cookiecutter 18 | import pip 19 | 20 | tmpdir = tmpdir_factory.mktemp('example_instance') 21 | 22 | with tmpdir.as_cwd(): 23 | cookiecutter(PROJECT_ROOT, no_input=True, config_file=os.path.join(HERE, 'testconfig.yaml')) 24 | instance_path = tmpdir.join('jupyter-widget-testwidgets') 25 | with instance_path.as_cwd(): 26 | print(str(instance_path)) 27 | try: 28 | pip.main(['install', '-v', '-e', '.[test]']) 29 | yield instance_path 30 | finally: 31 | try: 32 | pip.main(['uninstall', 'jupyter_widget_testwidgets', '-y']) 33 | except Exception: 34 | pass 35 | 36 | 37 | def test_python_tests(example_instance, testdir): 38 | with example_instance.as_cwd(): 39 | testdir.runpytest() 40 | 41 | 42 | def test_js_tests(example_instance): 43 | from subprocess import check_call 44 | 45 | cmd = ['npm', 'test'] 46 | with example_instance.as_cwd(): 47 | check_call(cmd, stdout=sys.stdout, stderr=sys.stderr, shell=use_shell) 48 | -------------------------------------------------------------------------------- /tests/testconfig.yaml: -------------------------------------------------------------------------------- 1 | default_context: 2 | author_name: "Test Widget" 3 | author_email: "testwidget@example.com" 4 | github_project_name: "jupyter-widget-testwidgets" 5 | github_organization_name: "jupyter" 6 | npm_package_name: "@jupyter-widgets/jupyter-widget-testwidgets" 7 | npm_package_version: "1.1.0" 8 | project_short_description: "A Test Jupyter Widget Library" 9 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = {{ cookiecutter.python_package_name }}/tests/* 3 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | coverage 4 | **/*.d.ts 5 | tests -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | 'plugin:@typescript-eslint/eslint-recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:prettier/recommended' 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { 10 | project: 'tsconfig.eslint.json', 11 | sourceType: 'module' 12 | }, 13 | plugins: ['@typescript-eslint'], 14 | rules: { 15 | '@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }], 16 | '@typescript-eslint/no-explicit-any': 'off', 17 | '@typescript-eslint/no-namespace': 'off', 18 | '@typescript-eslint/no-use-before-define': 'off', 19 | '@typescript-eslint/quotes': [ 20 | 'error', 21 | 'single', 22 | { avoidEscape: true, allowTemplateLiterals: false } 23 | ], 24 | curly: ['error', 'all'], 25 | eqeqeq: 'error', 26 | 'prefer-arrow-callback': 'error' 27 | } 28 | }; -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: main 6 | pull_request: 7 | branches: "*" 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | os: [ubuntu-latest, windows-latest, macos-latest] 16 | python-version: ["3.7", "3.10"] 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1 22 | 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install -U codecov 26 | npm install -g codecov 27 | - name: Test the extension 28 | run: | 29 | python -m pip install --upgrade -v -e ".[test, examples, docs]" 30 | python -m pytest 31 | yarn run test 32 | 33 | - name: Linting 34 | if: ${{ matrix.os == 'ubuntu-latest' }} 35 | run: | 36 | yarn run lint:check 37 | 38 | - name: Check docs can be build + links 39 | if: ${{ matrix.os == 'ubuntu-latest' }} 40 | working-directory: docs 41 | run: | 42 | sudo apt install -y pandoc 43 | make html 44 | python -m pytest --check-links 45 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | docs/source/_static/embed-bundle.js 65 | docs/source/_static/embed-bundle.js.map 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # IPython Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # dotenv 80 | .env 81 | 82 | # virtualenv 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # ========================= 93 | # Operating System Files 94 | # ========================= 95 | 96 | # OSX 97 | # ========================= 98 | 99 | .DS_Store 100 | .AppleDouble 101 | .LSOverride 102 | 103 | # Thumbnails 104 | ._* 105 | 106 | # Files that might appear in the root of a volume 107 | .DocumentRevisions-V100 108 | .fseventsd 109 | .Spotlight-V100 110 | .TemporaryItems 111 | .Trashes 112 | .VolumeIcon.icns 113 | 114 | # Directories potentially created on remote AFP share 115 | .AppleDB 116 | .AppleDesktop 117 | Network Trash Folder 118 | Temporary Items 119 | .apdisk 120 | 121 | # Windows 122 | # ========================= 123 | 124 | # Windows image file caches 125 | Thumbs.db 126 | ehthumbs.db 127 | 128 | # Folder config file 129 | Desktop.ini 130 | 131 | # Recycle Bin used on file shares 132 | $RECYCLE.BIN/ 133 | 134 | # Windows Installer files 135 | *.cab 136 | *.msi 137 | *.msm 138 | *.msp 139 | 140 | # Windows shortcuts 141 | *.lnk 142 | 143 | 144 | # NPM 145 | # ---- 146 | 147 | **/node_modules/ 148 | {{ cookiecutter.python_package_name }}/nbextension/index.* 149 | 150 | # Coverage data 151 | # ------------- 152 | **/coverage/ 153 | 154 | # Packed lab extensions 155 | {{ cookiecutter.python_package_name }}/labextension 156 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | tests/ 4 | .jshintrc 5 | # Ignore any build output from python: 6 | dist/*.tar.gz 7 | dist/*.wheel 8 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/node_modules 3 | **/lib 4 | **/package.json -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) {% now 'local', '%Y' %} {{ cookiecutter.author_name }} 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include README.md 3 | 4 | include setup.py 5 | include pyproject.toml 6 | include pytest.ini 7 | include .coverage.rc 8 | 9 | include tsconfig.json 10 | include package.json 11 | include webpack.config.js 12 | include {{ cookiecutter.python_package_name }}/labextension/*.tgz 13 | 14 | # Documentation 15 | graft docs 16 | exclude docs/\#* 17 | prune docs/build 18 | prune docs/gh-pages 19 | prune docs/dist 20 | 21 | # Examples 22 | graft examples 23 | 24 | # Tests 25 | graft tests 26 | prune tests/build 27 | 28 | # Javascript files 29 | graft {{ cookiecutter.python_package_name }}/nbextension 30 | graft src 31 | graft css 32 | prune **/node_modules 33 | prune coverage 34 | prune lib 35 | 36 | # Patterns to exclude from any directory 37 | global-exclude *~ 38 | global-exclude *.pyc 39 | global-exclude *.pyo 40 | global-exclude .git 41 | global-exclude .ipynb_checkpoints 42 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/README.md: -------------------------------------------------------------------------------- 1 | 2 | # {{ cookiecutter.github_project_name }} 3 | 4 | [![Build Status](https://travis-ci.org/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}.svg?branch=master)](https://travis-ci.org/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.python_package_name }}) 5 | [![codecov](https://codecov.io/gh/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}/branch/master/graph/badge.svg)](https://codecov.io/gh/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}) 6 | 7 | 8 | {{ cookiecutter.project_short_description }} 9 | 10 | ## Installation 11 | 12 | You can install using `pip`: 13 | 14 | ```bash 15 | pip install {{ cookiecutter.python_package_name }} 16 | ``` 17 | 18 | If you are using Jupyter Notebook 5.2 or earlier, you may also need to enable 19 | the nbextension: 20 | ```bash 21 | jupyter nbextension enable --py [--sys-prefix|--user|--system] {{ cookiecutter.python_package_name }} 22 | ``` 23 | 24 | ## Development Installation 25 | 26 | Create a dev environment: 27 | ```bash 28 | conda create -n {{ cookiecutter.python_package_name }}-dev -c conda-forge nodejs python jupyterlab=4.0.11 29 | conda activate {{ cookiecutter.python_package_name }}-dev 30 | ``` 31 | 32 | Install the python. This will also build the TS package. 33 | ```bash 34 | pip install -e ".[test, examples]" 35 | ``` 36 | 37 | When developing your extensions, you need to manually enable your extensions with the 38 | notebook / lab frontend. For lab, this is done by the command: 39 | 40 | ``` 41 | jupyter labextension develop --overwrite . 42 | jlpm run build 43 | ``` 44 | 45 | For classic notebook, you need to run: 46 | 47 | ``` 48 | jupyter nbextension install --sys-prefix --symlink --overwrite --py {{ cookiecutter.python_package_name }} 49 | jupyter nbextension enable --sys-prefix --py {{ cookiecutter.python_package_name }} 50 | ``` 51 | 52 | Note that the `--symlink` flag doesn't work on Windows, so you will here have to run 53 | the `install` command every time that you rebuild your extension. For certain installations 54 | you might also need another flag instead of `--sys-prefix`, but we won't cover the meaning 55 | of those flags here. 56 | 57 | ### How to see your changes 58 | #### Typescript: 59 | If you use JupyterLab to develop then you can watch the source directory and run JupyterLab at the same time in different 60 | terminals to watch for changes in the extension's source and automatically rebuild the widget. 61 | 62 | ```bash 63 | # Watch the source directory in one terminal, automatically rebuilding when needed 64 | jlpm run watch 65 | # Run JupyterLab in another terminal 66 | jupyter lab 67 | ``` 68 | 69 | After a change wait for the build to finish and then refresh your browser and the changes should take effect. 70 | 71 | #### Python: 72 | If you make a change to the python code then you will need to restart the notebook kernel to have it take effect. 73 | 74 | ## Updating the version 75 | 76 | To update the version, install tbump and use it to bump the version. 77 | By default it will also create a tag. 78 | 79 | ```bash 80 | pip install tbump 81 | tbump 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sourceMap: 'inline', 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | targets: { 8 | node: 'current', 9 | }, 10 | }, 11 | ], 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | # show coverage in CI status, but never consider it a failure 3 | coverage: 4 | status: 5 | project: 6 | default: 7 | target: 0% 8 | patch: 9 | default: 10 | target: 0% 11 | ignore: 12 | - "{{ cookiecutter.python_package_name }}/tests" 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/css/widget.css: -------------------------------------------------------------------------------- 1 | .custom-widget { 2 | background-color: lightseagreen; 3 | padding: 0px 2px; 4 | } 5 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = {{ cookiecutter.python_package_name }} 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/environment.yml: -------------------------------------------------------------------------------- 1 | 2 | name: {{cookiecutter.python_package_name}}_docs 3 | channels: 4 | - conda-forge 5 | dependencies: 6 | - python=3.* 7 | - nodejs 8 | - jupyter_sphinx 9 | - sphinx 10 | - sphinx_rtd_theme 11 | - nbsphinx 12 | - nbsphinx-link 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ={{ cookiecutter.python_package_name }} 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/_static/helper.js: -------------------------------------------------------------------------------- 1 | var cache_require = window.require; 2 | 3 | window.addEventListener('load', function() { 4 | window.require = cache_require; 5 | }); 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # {{ cookiecutter.python_package_name }} documentation build configuration file 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | 16 | # -- General configuration ------------------------------------------------ 17 | 18 | # If your documentation needs a minimal Sphinx version, state it here. 19 | # 20 | # needs_sphinx = '1.0' 21 | 22 | # Add any Sphinx extension module names here, as strings. They can be 23 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 24 | # ones. 25 | extensions = [ 26 | 'sphinx.ext.autodoc', 27 | 'sphinx.ext.viewcode', 28 | 'sphinx.ext.intersphinx', 29 | 'sphinx.ext.napoleon', 30 | 'sphinx.ext.todo', 31 | 'nbsphinx', 32 | 'jupyter_sphinx', 33 | 'nbsphinx_link', 34 | ] 35 | 36 | # Set the nbsphinx JS path to empty to avoid showing twice of the widgets 37 | nbsphinx_requirejs_path = "" 38 | nbsphinx_widgets_path = "" 39 | 40 | # Ensure our extension is available: 41 | import sys 42 | from os.path import dirname, join as pjoin 43 | docs = dirname(dirname(__file__)) 44 | root = dirname(docs) 45 | sys.path.insert(0, root) 46 | sys.path.insert(0, pjoin(docs, 'sphinxext')) 47 | 48 | # Add any paths that contain templates here, relative to this directory. 49 | templates_path = ['_templates'] 50 | 51 | # The suffix(es) of source filenames. 52 | # You can specify multiple suffix as a list of string: 53 | # 54 | # source_suffix = ['.rst', '.md'] 55 | source_suffix = '.rst' 56 | 57 | # The master toctree document. 58 | master_doc = 'index' 59 | 60 | # General information about the project. 61 | project = '{{ cookiecutter.python_package_name }}' 62 | copyright = '{% now 'local', '%Y' %}, {{ cookiecutter.author_name }}' 63 | author = '{{ cookiecutter.author_name }}' 64 | 65 | # The version info for the project you're documenting, acts as replacement for 66 | # |version| and |release|, also used in various other places throughout the 67 | # built documents. 68 | # 69 | # The short X.Y version. 70 | 71 | 72 | # get version from python package: 73 | import os 74 | here = os.path.dirname(__file__) 75 | repo = os.path.join(here, '..', '..') 76 | _version_py = os.path.join(repo, '{{ cookiecutter.python_package_name }}', '_version.py') 77 | version_ns = {} 78 | with open(_version_py) as f: 79 | exec(f.read(), version_ns) 80 | 81 | # The short X.Y version. 82 | version = '%i.%i' % version_ns['version_info'][:2] 83 | # The full version, including alpha/beta/rc tags. 84 | release = version_ns['__version__'] 85 | 86 | # The language for content autogenerated by Sphinx. Refer to documentation 87 | # for a list of supported languages. 88 | # 89 | # This is also used if you do content translation via gettext catalogs. 90 | # Usually you set "language" from the command line for these cases. 91 | language = "en" 92 | 93 | # List of patterns, relative to source directory, that match files and 94 | # directories to ignore when looking for source files. 95 | # This patterns also effect to html_static_path and html_extra_path 96 | exclude_patterns = ['**.ipynb_checkpoints'] 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = 'sphinx' 100 | 101 | # If true, `todo` and `todoList` produce output, else they produce nothing. 102 | todo_include_todos = False 103 | 104 | 105 | # -- Options for HTML output ---------------------------------------------- 106 | 107 | 108 | # Theme options are theme-specific and customize the look and feel of a theme 109 | # further. For a list of options available for each theme, see the 110 | # documentation. 111 | # 112 | # html_theme_options = {} 113 | 114 | # Add any paths that contain custom static files (such as style sheets) here, 115 | # relative to this directory. They are copied after the builtin static files, 116 | # so a file named "default.css" will overwrite the builtin "default.css". 117 | html_static_path = ['_static'] 118 | 119 | 120 | # -- Options for HTMLHelp output ------------------------------------------ 121 | 122 | # Output file base name for HTML help builder. 123 | htmlhelp_basename = '{{ cookiecutter.python_package_name }}doc' 124 | 125 | 126 | # -- Options for LaTeX output --------------------------------------------- 127 | 128 | latex_elements = { 129 | # The paper size ('letterpaper' or 'a4paper'). 130 | # 131 | # 'papersize': 'letterpaper', 132 | 133 | # The font size ('10pt', '11pt' or '12pt'). 134 | # 135 | # 'pointsize': '10pt', 136 | 137 | # Additional stuff for the LaTeX preamble. 138 | # 139 | # 'preamble': '', 140 | 141 | # Latex figure (float) alignment 142 | # 143 | # 'figure_align': 'htbp', 144 | } 145 | 146 | # Grouping the document tree into LaTeX files. List of tuples 147 | # (source start file, target name, title, 148 | # author, documentclass [howto, manual, or own class]). 149 | latex_documents = [ 150 | (master_doc, '{{ cookiecutter.python_package_name }}.tex', '{{ cookiecutter.python_package_name }} Documentation', 151 | '{{ cookiecutter.author_name }}', 'manual'), 152 | ] 153 | 154 | 155 | # -- Options for manual page output --------------------------------------- 156 | 157 | # One entry per manual page. List of tuples 158 | # (source start file, name, description, authors, manual section). 159 | man_pages = [ 160 | (master_doc, 161 | '{{ cookiecutter.python_package_name }}', 162 | '{{ cookiecutter.python_package_name }} Documentation', 163 | [author], 1) 164 | ] 165 | 166 | 167 | # -- Options for Texinfo output ------------------------------------------- 168 | 169 | # Grouping the document tree into Texinfo files. List of tuples 170 | # (source start file, target name, title, author, 171 | # dir menu entry, description, category) 172 | texinfo_documents = [ 173 | (master_doc, 174 | '{{ cookiecutter.python_package_name }}', 175 | '{{ cookiecutter.python_package_name }} Documentation', 176 | author, 177 | '{{ cookiecutter.python_package_name }}', 178 | '{{ cookiecutter.project_short_description }}', 179 | 'Miscellaneous'), 180 | ] 181 | 182 | 183 | # Example configuration for intersphinx: refer to the Python standard library. 184 | intersphinx_mapping = {'https://docs.python.org/': None} 185 | 186 | # Read The Docs 187 | # on_rtd is whether we are on readthedocs.org, this line of code grabbed from 188 | # docs.readthedocs.org 189 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 190 | 191 | if not on_rtd: # only import and set the theme if we're building docs locally 192 | import sphinx_rtd_theme 193 | html_theme = 'sphinx_rtd_theme' 194 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 195 | 196 | # otherwise, readthedocs.org uses their theme by default, so no need to specify it 197 | 198 | 199 | # Uncomment this line if you have know exceptions in your included notebooks 200 | # that nbsphinx complains about: 201 | # 202 | nbsphinx_allow_errors = True # exception ipstruct.py ipython_genutils 203 | 204 | from sphinx.util import logging 205 | logger = logging.getLogger(__name__) 206 | 207 | def setup(app): 208 | def add_scripts(app): 209 | for fname in ['helper.js', 'embed-bundle.js']: 210 | if not os.path.exists(os.path.join(here, '_static', fname)): 211 | logger.warning('missing javascript file: %s' % fname) 212 | app.add_js_file(fname) 213 | app.connect('builder-inited', add_scripts) 214 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/develop-install.rst: -------------------------------------------------------------------------------- 1 | 2 | Developer install 3 | ================= 4 | 5 | 6 | To install a developer version of {{ cookiecutter.python_package_name }}, you will first need to clone 7 | the repository:: 8 | 9 | git clone https://github.com/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }} 10 | cd {{ cookiecutter.github_project_name }} 11 | 12 | Next, install it with a develop install using pip:: 13 | 14 | pip install -e . 15 | 16 | 17 | If you are planning on working on the JS/frontend code, you should also do 18 | a link installation of the extension:: 19 | 20 | jupyter nbextension install [--sys-prefix / --user / --system] --symlink --py {{ cookiecutter.python_package_name }} 21 | 22 | jupyter nbextension enable [--sys-prefix / --user / --system] --py {{ cookiecutter.python_package_name }} 23 | 24 | with the `appropriate flag`_. Or, if you are using Jupyterlab:: 25 | 26 | jupyter labextension install . 27 | 28 | 29 | .. links 30 | 31 | .. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions 32 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/examples/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Examples 3 | ======== 4 | 5 | This section contains several examples generated from Jupyter notebooks. 6 | The widgets have been embedded into the page for demonstrative purposes. 7 | 8 | .. todo:: 9 | 10 | Add links to notebooks in examples folder similar to the initial 11 | one. This is a manual step to ensure only those examples that 12 | are suited for inclusion are used. 13 | 14 | 15 | .. toctree:: 16 | :glob: 17 | 18 | * 19 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/examples/introduction.nblink: -------------------------------------------------------------------------------- 1 | { 2 | "path": "../../../examples/introduction.ipynb" 3 | } 4 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/index.rst: -------------------------------------------------------------------------------- 1 | 2 | {{ cookiecutter.python_package_name }} 3 | ===================================== 4 | 5 | Version: |release| 6 | 7 | {{cookiecutter.project_short_description}} 8 | 9 | 10 | Quickstart 11 | ---------- 12 | 13 | To get started with {{ cookiecutter.python_package_name }}, install with pip:: 14 | 15 | pip install {{ cookiecutter.python_package_name }} 16 | 17 | or with conda:: 18 | 19 | conda install {{ cookiecutter.python_package_name }} 20 | 21 | 22 | Contents 23 | -------- 24 | 25 | .. toctree:: 26 | :maxdepth: 2 27 | :caption: Installation and usage 28 | 29 | installing 30 | introduction 31 | 32 | .. toctree:: 33 | :maxdepth: 1 34 | 35 | examples/index 36 | 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | :caption: Development 41 | 42 | develop-install 43 | 44 | 45 | .. links 46 | 47 | .. _`Jupyter widgets`: https://jupyter.org/widgets.html 48 | 49 | .. _`notebook`: https://jupyter-notebook.readthedocs.io/en/latest/ 50 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/installing.rst: -------------------------------------------------------------------------------- 1 | 2 | .. _installation: 3 | 4 | Installation 5 | ============ 6 | 7 | 8 | The simplest way to install {{ cookiecutter.python_package_name }} is via pip:: 9 | 10 | pip install {{ cookiecutter.python_package_name }} 11 | 12 | or via conda:: 13 | 14 | conda install {{ cookiecutter.python_package_name }} 15 | 16 | 17 | If you installed via pip, and notebook version < 5.3, you will also have to 18 | install / configure the front-end extension as well. If you are using classic 19 | notebook (as opposed to Jupyterlab), run:: 20 | 21 | jupyter nbextension install [--sys-prefix / --user / --system] --py {{ cookiecutter.python_package_name }} 22 | 23 | jupyter nbextension enable [--sys-prefix / --user / --system] --py {{ cookiecutter.python_package_name }} 24 | 25 | with the `appropriate flag`_. If you are using Jupyterlab, install the extension 26 | with:: 27 | 28 | jupyter labextension install {{ cookiecutter.npm_package_name }} 29 | 30 | If you are installing using conda, these commands should be unnecessary, but If 31 | you need to run them the commands should be the same (just make sure you choose the 32 | `--sys-prefix` flag). 33 | 34 | 35 | .. links 36 | 37 | .. _`appropriate flag`: https://jupyter-notebook.readthedocs.io/en/stable/extending/frontend_extensions.html#installing-and-enabling-extensions 38 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/docs/source/introduction.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Introduction 3 | ============= 4 | 5 | .. todo:: 6 | 7 | add prose explaining project purpose and usage here 8 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/examples/introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "import {{ cookiecutter.python_package_name }}" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "w = {{ cookiecutter.python_package_name }}.ExampleWidget()\n", 26 | "w" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "assert w.value == 'Hello World'" 36 | ] 37 | } 38 | ], 39 | "metadata": { 40 | "kernelspec": { 41 | "display_name": "Python 3", 42 | "language": "python", 43 | "name": "python3" 44 | }, 45 | "language_info": { 46 | "codemirror_mode": { 47 | "name": "ipython", 48 | "version": 3 49 | }, 50 | "file_extension": ".py", 51 | "mimetype": "text/x-python", 52 | "name": "python", 53 | "nbconvert_exporter": "python", 54 | "pygments_lexer": "ipython3", 55 | "version": "3.6.3" 56 | } 57 | }, 58 | "nbformat": 4, 59 | "nbformat_minor": 2 60 | } 61 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/install.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageManager": "python", 3 | "packageName": "{{ cookiecutter.python_package_name }}", 4 | "uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package {{ cookiecutter.python_package_name }}" 5 | } 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | automock: false, 3 | moduleNameMapper: { 4 | '\\.(css|less|sass|scss)$': 'identity-obj-proxy', 5 | }, 6 | preset: 'ts-jest/presets/js-with-babel', 7 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 8 | testPathIgnorePatterns: ['/lib/', '/node_modules/'], 9 | testRegex: '/__tests__/.*.spec.ts[x]?$', 10 | transformIgnorePatterns: ['/node_modules/(?!(@jupyter(lab|-widgets)/.*)/)'], 11 | globals: { 12 | 'ts-jest': { 13 | tsconfig: '/tsconfig.json', 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ cookiecutter.npm_package_name }}", 3 | "version": "{{ cookiecutter.npm_package_version }}", 4 | "description": "{{ cookiecutter.project_short_description }}", 5 | "keywords": [ 6 | "jupyter", 7 | "jupyterlab", 8 | "jupyterlab-extension", 9 | "widgets" 10 | ], 11 | "files": [ 12 | "lib/**/*.js", 13 | "dist/*.js", 14 | "css/*.css" 15 | ], 16 | "homepage": "https://github.com/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}", 17 | "bugs": { 18 | "url": "https://github.com/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}/issues" 19 | }, 20 | "license": "BSD-3-Clause", 21 | "author": { 22 | "name": "{{ cookiecutter.author_name }}", 23 | "email": "{{ cookiecutter.author_email }}" 24 | }, 25 | "main": "lib/index.js", 26 | "types": "./lib/index.d.ts", 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}" 30 | }, 31 | "scripts": { 32 | "build": "jlpm run build:lib && jlpm run build:nbextension && jlpm run build:labextension:dev", 33 | "build:prod": "jlpm run build:lib && jlpm run build:nbextension && jlpm run build:labextension", 34 | "build:labextension": "jupyter labextension build .", 35 | "build:labextension:dev": "jupyter labextension build --development True .", 36 | "build:lib": "tsc", 37 | "build:nbextension": "webpack", 38 | "clean": "jlpm run clean:lib && jlpm run clean:nbextension && jlpm run clean:labextension", 39 | "clean:lib": "rimraf lib", 40 | "clean:labextension": "rimraf {{ cookiecutter.python_package_name }}/labextension", 41 | "clean:nbextension": "rimraf {{ cookiecutter.python_package_name }}/nbextension/static/index.js", 42 | "lint": "eslint . --ext .ts,.tsx --fix", 43 | "lint:check": "eslint . --ext .ts,.tsx", 44 | "prepack": "jlpm run build:lib", 45 | "test": "jest", 46 | "watch": "npm-run-all -p watch:*", 47 | "watch:lib": "tsc -w", 48 | "watch:nbextension": "webpack --watch --mode=development", 49 | "watch:labextension": "jupyter labextension watch ." 50 | }, 51 | "dependencies": { 52 | "@jupyter-widgets/base": "^1.1.10 || ^2 || ^3 || ^4 || ^5 || ^6" 53 | }, 54 | "devDependencies": { 55 | "@babel/core": "^7.23.7", 56 | "@babel/preset-env": "^7.23.8", 57 | "@jupyter-widgets/base-manager": "^1.0.7", 58 | "@jupyterlab/builder": "^4.0.11", 59 | "@lumino/application": "^2.3.0", 60 | "@lumino/widgets": "^2.3.1", 61 | "@types/jest": "^29.5.11", 62 | "@types/webpack-env": "^1.18.4", 63 | "@typescript-eslint/eslint-plugin": "^6.19.1", 64 | "@typescript-eslint/parser": "^6.19.1", 65 | "acorn": "^8.11.3", 66 | "css-loader": "^6.9.1", 67 | "eslint": "^8.56.0", 68 | "eslint-config-prettier": "^9.1.0", 69 | "eslint-plugin-prettier": "^5.1.3", 70 | "fs-extra": "^11.2.0", 71 | "identity-obj-proxy": "^3.0.0", 72 | "jest": "^29.7.0", 73 | "mkdirp": "^3.0.1", 74 | "npm-run-all": "^4.1.5", 75 | "prettier": "^3.2.4", 76 | "rimraf": "^5.0.5", 77 | "source-map-loader": "^5.0.0", 78 | "style-loader": "^3.3.4", 79 | "ts-jest": "^29.1.2", 80 | "ts-loader": "^9.5.1", 81 | "typescript": "~5.3.3", 82 | "webpack": "^5.90.0", 83 | "webpack-cli": "^5.1.4" 84 | }, 85 | "devDependenciesComments": { 86 | "@jupyterlab/builder": "pinned to the latest JupyterLab 3.x release", 87 | "@lumino/application": "pinned to the latest Lumino 1.x release", 88 | "@lumino/widgets": "pinned to the latest Lumino 1.x release" 89 | }, 90 | "jupyterlab": { 91 | "extension": "lib/plugin", 92 | "outputDir": "{{ cookiecutter.python_package_name }}/labextension/", 93 | "sharedPackages": { 94 | "@jupyter-widgets/base": { 95 | "bundled": false, 96 | "singleton": true 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=1.21.1", 4 | "jupyterlab>=4.0.0,<5", 5 | "hatch-nodejs-version>=0.3.2", 6 | ] 7 | build-backend = "hatchling.build" 8 | 9 | [project] 10 | name = "{{ cookiecutter.python_package_name }}" 11 | description = "{{ cookiecutter.project_short_description }}" 12 | readme = "README.md" 13 | license = { file = "LICENSE.txt" } 14 | requires-python = ">=3.9" 15 | authors = [ 16 | { name = "{{ cookiecutter.author_name }}", email = "{{ cookiecutter.author_email }}" }, 17 | ] 18 | keywords = [ 19 | "IPython", 20 | "Jupyter", 21 | "Widgets", 22 | ] 23 | classifiers = [ 24 | "Framework :: Jupyter", 25 | "Intended Audience :: Developers", 26 | "Intended Audience :: Science/Research", 27 | "License :: OSI Approved :: BSD License", 28 | "Programming Language :: Python", 29 | "Programming Language :: Python :: 3", 30 | "Programming Language :: Python :: 3.7", 31 | "Programming Language :: Python :: 3.8", 32 | "Programming Language :: Python :: 3.9", 33 | "Programming Language :: Python :: 3.10", 34 | "Programming Language :: Python :: 3.11", 35 | "Programming Language :: Python :: 3.12", 36 | ] 37 | dependencies = [ 38 | "ipywidgets>=8.0.0", 39 | ] 40 | version = "0.1.0.dev0" 41 | 42 | [project.optional-dependencies] 43 | docs = [ 44 | "jupyter_sphinx", 45 | "nbsphinx", 46 | "nbsphinx-link", 47 | "pypandoc", 48 | "pytest_check_links", 49 | "recommonmark", 50 | "sphinx>=1.5", 51 | "sphinx_rtd_theme", 52 | ] 53 | examples = [] 54 | test = [ 55 | "nbval", 56 | "pytest-cov", 57 | "pytest>=6.0", 58 | ] 59 | 60 | [project.urls] 61 | Homepage = "https://github.com/{{ cookiecutter.github_organization_name }}/{{ cookiecutter.github_project_name }}" 62 | 63 | [tool.hatch.metadata] 64 | allow-direct-references = true 65 | 66 | [tool.hatch.build] 67 | artifacts = [ 68 | "{{ cookiecutter.python_package_name }}/nbextension/index.*", 69 | "{{ cookiecutter.python_package_name }}/labextension/*.tgz", 70 | "{{ cookiecutter.python_package_name }}/labextension", 71 | ] 72 | 73 | [tool.hatch.build.targets.wheel.shared-data] 74 | "{{ cookiecutter.python_package_name }}/nbextension" = "share/jupyter/nbextensions/{{ cookiecutter.python_package_name }}" 75 | "{{ cookiecutter.python_package_name }}/labextension" = "share/jupyter/labextensions/{{ cookiecutter.npm_package_name }}" 76 | "./install.json" = "share/jupyter/labextensions/{{ cookiecutter.npm_package_name }}/install.json" 77 | "./{{ cookiecutter.python_package_name }}.json" = "etc/jupyter/nbconfig/notebook.d/{{ cookiecutter.python_package_name }}.json" 78 | 79 | [tool.hatch.build.targets.sdist] 80 | exclude = [ 81 | ".github", 82 | ] 83 | 84 | [tool.hatch.build.hooks.jupyter-builder] 85 | build-function = "hatch_jupyter_builder.npm_builder" 86 | ensured-targets = [ 87 | "{{ cookiecutter.python_package_name }}/nbextension/index.js", 88 | "{{ cookiecutter.python_package_name }}/labextension/package.json", 89 | ] 90 | skip-if-exists = [ 91 | "{{ cookiecutter.python_package_name }}/nbextension/index.js", 92 | "{{ cookiecutter.python_package_name }}/labextension/package.json", 93 | ] 94 | dependencies = [ 95 | "hatch-jupyter-builder>=0.8.3", 96 | ] 97 | 98 | [tool.hatch.build.hooks.jupyter-builder.build-kwargs] 99 | path = "." 100 | build_cmd = "build:prod" 101 | npm = ["jlpm"] 102 | 103 | [tool.tbump] 104 | field = [ 105 | { name = "channel", default = "" }, 106 | { name = "release", default = "" }, 107 | ] 108 | file = [ 109 | { src = "pyproject.toml", version_template = "version = \"{major}.{minor}.{patch}{channel}{release}\"" }, 110 | { src = "{{ cookiecutter.python_package_name }}/_version.py" }, 111 | ] 112 | 113 | [tool.tbump.version] 114 | current = "0.1.0.dev0" 115 | regex = "(?P\\d+)\\.(?P\\d+)\\.(?P\\d+)((?Pa|b|rc|.dev)(?P\\d+))?" 116 | 117 | [tool.tbump.git] 118 | message_template = "Bump to {new_version}" 119 | tag_template = "v{new_version}" 120 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | testpaths = {{ cookiecutter.python_package_name }}/tests examples 3 | norecursedirs = node_modules .ipynb_checkpoints 4 | addopts = --nbval --current-env 5 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/readthedocs.yml: -------------------------------------------------------------------------------- 1 | type: sphinx 2 | python: 3 | version: 3.5 4 | pip_install: true 5 | extra_requirements: 6 | - examples 7 | - docs 8 | conda: 9 | file: docs/environment.yml 10 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/setup.py: -------------------------------------------------------------------------------- 1 | # setup.py shim for use with applications that require it. 2 | __import__("setuptools").setup() 3 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | // Add any needed widget imports here (or from controls) 5 | // import {} from '@jupyter-widgets/base'; 6 | 7 | import { createTestModel } from './utils'; 8 | 9 | import { ExampleModel } from '..'; 10 | 11 | describe('Example', () => { 12 | describe('ExampleModel', () => { 13 | it('should be createable', () => { 14 | const model = createTestModel(ExampleModel); 15 | expect(model).toBeInstanceOf(ExampleModel); 16 | expect(model.get('value')).toEqual('Hello World'); 17 | }); 18 | 19 | it('should be createable with a value', () => { 20 | const state = { value: 'Foo Bar!' }; 21 | const model = createTestModel(ExampleModel, state); 22 | expect(model).toBeInstanceOf(ExampleModel); 23 | expect(model.get('value')).toEqual('Foo Bar!'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/__tests__/utils.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | import * as widgets from '@jupyter-widgets/base'; 5 | import * as baseManager from '@jupyter-widgets/base-manager'; 6 | import * as services from '@jupyterlab/services'; 7 | 8 | let numComms = 0; 9 | 10 | export class MockComm implements widgets.IClassicComm { 11 | constructor() { 12 | this.comm_id = `mock-comm-id-${numComms}`; 13 | numComms += 1; 14 | } 15 | on_close(fn: ((x?: any) => void) | null): void { 16 | this._on_close = fn; 17 | } 18 | on_msg(fn: (x?: any) => void): void { 19 | this._on_msg = fn; 20 | } 21 | _process_msg(msg: services.KernelMessage.ICommMsgMsg): void | Promise { 22 | if (this._on_msg) { 23 | return this._on_msg(msg); 24 | } else { 25 | return Promise.resolve(); 26 | } 27 | } 28 | close(): string { 29 | if (this._on_close) { 30 | this._on_close(); 31 | } 32 | return 'dummy'; 33 | } 34 | send(): string { 35 | return 'dummy'; 36 | } 37 | 38 | open(): string { 39 | return 'dummy'; 40 | } 41 | 42 | comm_id: string; 43 | target_name = 'dummy'; 44 | _on_msg: ((x?: any) => void) | null = null; 45 | _on_close: ((x?: any) => void) | null = null; 46 | } 47 | 48 | export class DummyManager extends baseManager.ManagerBase { 49 | constructor() { 50 | super(); 51 | this.el = window.document.createElement('div'); 52 | } 53 | 54 | display_view( 55 | msg: services.KernelMessage.IMessage, 56 | view: widgets.DOMWidgetView, 57 | options: any 58 | ) { 59 | // TODO: make this a spy 60 | // TODO: return an html element 61 | return Promise.resolve(view).then((view) => { 62 | this.el.appendChild(view.el); 63 | view.on('remove', () => console.log('view removed', view)); 64 | return view.el; 65 | }); 66 | } 67 | 68 | protected loadClass( 69 | className: string, 70 | moduleName: string, 71 | moduleVersion: string 72 | ): Promise { 73 | if (moduleName === '@jupyter-widgets/base') { 74 | if ((widgets as any)[className]) { 75 | return Promise.resolve((widgets as any)[className]); 76 | } else { 77 | return Promise.reject(`Cannot find class ${className}`); 78 | } 79 | } else if (moduleName === 'jupyter-datawidgets') { 80 | if (this.testClasses[className]) { 81 | return Promise.resolve(this.testClasses[className]); 82 | } else { 83 | return Promise.reject(`Cannot find class ${className}`); 84 | } 85 | } else { 86 | return Promise.reject(`Cannot find module ${moduleName}`); 87 | } 88 | } 89 | 90 | _get_comm_info() { 91 | return Promise.resolve({}); 92 | } 93 | 94 | _create_comm() { 95 | return Promise.resolve(new MockComm()); 96 | } 97 | 98 | el: HTMLElement; 99 | 100 | testClasses: { [key: string]: any } = {}; 101 | } 102 | 103 | export interface Constructor { 104 | new (attributes?: any, options?: any): T; 105 | } 106 | 107 | export function createTestModel( 108 | constructor: Constructor, 109 | attributes?: any 110 | ): T { 111 | const id = widgets.uuid(); 112 | const widget_manager = new DummyManager(); 113 | const modelOptions = { 114 | widget_manager: widget_manager, 115 | model_id: id, 116 | }; 117 | 118 | return new constructor(attributes, modelOptions); 119 | } 120 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/extension.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) Jupyter Development Team. 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | // Entry point for the notebook bundle containing custom model definitions. 5 | // 6 | // Setup notebook base URL 7 | // 8 | // Some static assets may be required by the custom widget javascript. The base 9 | // url for the notebook is not known at build time and is therefore computed 10 | // dynamically. 11 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 12 | (window as any).__webpack_public_path__ = 13 | document.querySelector('body')!.getAttribute('data-base-url') + 14 | 'nbextensions/{{ cookiecutter.python_package_name }}'; 15 | 16 | export * from './index'; 17 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/index.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) {{ cookiecutter.author_name }} 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | export * from './version'; 5 | export * from './widget'; 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/plugin.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) {{ cookiecutter.author_name }} 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | import { Application, IPlugin } from '@lumino/application'; 5 | 6 | import { Widget } from '@lumino/widgets'; 7 | 8 | import { IJupyterWidgetRegistry } from '@jupyter-widgets/base'; 9 | 10 | import * as widgetExports from './widget'; 11 | 12 | import { MODULE_NAME, MODULE_VERSION } from './version'; 13 | 14 | const EXTENSION_ID = '{{ cookiecutter.npm_package_name }}:plugin'; 15 | 16 | /** 17 | * The example plugin. 18 | */ 19 | const examplePlugin: IPlugin, void> = { 20 | id: EXTENSION_ID, 21 | requires: [IJupyterWidgetRegistry], 22 | activate: activateWidgetExtension, 23 | autoStart: true, 24 | } as unknown as IPlugin, void>; 25 | // the "as unknown as ..." typecast above is solely to support JupyterLab 1 26 | // and 2 in the same codebase and should be removed when we migrate to Lumino. 27 | 28 | export default examplePlugin; 29 | 30 | /** 31 | * Activate the widget extension. 32 | */ 33 | function activateWidgetExtension( 34 | app: Application, 35 | registry: IJupyterWidgetRegistry 36 | ): void { 37 | registry.registerWidget({ 38 | name: MODULE_NAME, 39 | version: MODULE_VERSION, 40 | exports: widgetExports, 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/version.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) {{ cookiecutter.author_name }} 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 5 | // @ts-ignore 6 | // eslint-disable-next-line @typescript-eslint/no-var-requires 7 | const data = require('../package.json'); 8 | 9 | /** 10 | * The _model_module_version/_view_module_version this package implements. 11 | * 12 | * The html widget manager assumes that this is the same as the npm package 13 | * version number. 14 | */ 15 | export const MODULE_VERSION = data.version; 16 | 17 | /* 18 | * The current package name. 19 | */ 20 | export const MODULE_NAME = data.name; 21 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/src/widget.ts: -------------------------------------------------------------------------------- 1 | // Copyright (c) {{ cookiecutter.author_name }} 2 | // Distributed under the terms of the Modified BSD License. 3 | 4 | import { 5 | DOMWidgetModel, 6 | DOMWidgetView, 7 | ISerializers, 8 | } from '@jupyter-widgets/base'; 9 | 10 | import { MODULE_NAME, MODULE_VERSION } from './version'; 11 | 12 | // Import the CSS 13 | import '../css/widget.css'; 14 | 15 | export class ExampleModel extends DOMWidgetModel { 16 | defaults() { 17 | return { 18 | ...super.defaults(), 19 | _model_name: ExampleModel.model_name, 20 | _model_module: ExampleModel.model_module, 21 | _model_module_version: ExampleModel.model_module_version, 22 | _view_name: ExampleModel.view_name, 23 | _view_module: ExampleModel.view_module, 24 | _view_module_version: ExampleModel.view_module_version, 25 | value: 'Hello World', 26 | }; 27 | } 28 | 29 | static serializers: ISerializers = { 30 | ...DOMWidgetModel.serializers, 31 | // Add any extra serializers here 32 | }; 33 | 34 | static model_name = 'ExampleModel'; 35 | static model_module = MODULE_NAME; 36 | static model_module_version = MODULE_VERSION; 37 | static view_name = 'ExampleView'; // Set to null if no view 38 | static view_module = MODULE_NAME; // Set to null if no view 39 | static view_module_version = MODULE_VERSION; 40 | } 41 | 42 | export class ExampleView extends DOMWidgetView { 43 | render() { 44 | this.el.classList.add('custom-widget'); 45 | 46 | this.value_changed(); 47 | this.model.on('change:value', this.value_changed, this); 48 | } 49 | 50 | value_changed() { 51 | this.el.textContent = this.model.get('value'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src/**/*.ts", "src/**/*.tsx"], 4 | "exclude": [] 5 | } -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "esModuleInterop":true, 5 | "lib": ["es2015", "dom"], 6 | "module": "commonjs", 7 | "moduleResolution": "node", 8 | "noEmitOnError": true, 9 | "noUnusedLocals": true, 10 | "outDir": "lib", 11 | "resolveJsonModule": true, 12 | "rootDir": "src", 13 | "skipLibCheck": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "strictPropertyInitialization": false, 17 | "target": "es2015", 18 | "types": ["jest"] 19 | }, 20 | "include": [ 21 | "src/**/*.ts", 22 | "src/**/*.tsx", 23 | ], 24 | "exclude": ["src/**/__tests__"] 25 | } 26 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const version = require('./package.json').version; 3 | 4 | // Custom webpack rules 5 | const rules = [ 6 | { test: /\.ts$/, loader: 'ts-loader' }, 7 | { test: /\.js$/, loader: 'source-map-loader' }, 8 | { test: /\.css$/, use: ['style-loader', 'css-loader']} 9 | ]; 10 | 11 | // Packages that shouldn't be bundled but loaded at runtime 12 | const externals = ['@jupyter-widgets/base']; 13 | 14 | const resolve = { 15 | // Add '.ts' and '.tsx' as resolvable extensions. 16 | extensions: [".webpack.js", ".web.js", ".ts", ".js"] 17 | }; 18 | 19 | module.exports = [ 20 | /** 21 | * Notebook extension 22 | * 23 | * This bundle only contains the part of the JavaScript that is run on load of 24 | * the notebook. 25 | */ 26 | { 27 | entry: './src/extension.ts', 28 | output: { 29 | filename: 'index.js', 30 | path: path.resolve(__dirname, '{{ cookiecutter.python_package_name }}', 'nbextension'), 31 | libraryTarget: 'amd', 32 | publicPath: '', 33 | }, 34 | module: { 35 | rules: rules 36 | }, 37 | devtool: 'source-map', 38 | externals, 39 | resolve, 40 | }, 41 | 42 | /** 43 | * Embeddable {{ cookiecutter.npm_package_name }} bundle 44 | * 45 | * This bundle is almost identical to the notebook extension bundle. The only 46 | * difference is in the configuration of the webpack public path for the 47 | * static assets. 48 | * 49 | * The target bundle is always `dist/index.js`, which is the path required by 50 | * the custom widget embedder. 51 | */ 52 | { 53 | entry: './src/index.ts', 54 | output: { 55 | filename: 'index.js', 56 | path: path.resolve(__dirname, 'dist'), 57 | libraryTarget: 'amd', 58 | library: "{{ cookiecutter.npm_package_name }}", 59 | publicPath: 'https://unpkg.com/{{ cookiecutter.npm_package_name }}@' + version + '/dist/' 60 | }, 61 | devtool: 'source-map', 62 | module: { 63 | rules: rules 64 | }, 65 | externals, 66 | resolve, 67 | }, 68 | 69 | 70 | /** 71 | * Documentation widget bundle 72 | * 73 | * This bundle is used to embed widgets in the package documentation. 74 | */ 75 | { 76 | entry: './src/index.ts', 77 | output: { 78 | filename: 'embed-bundle.js', 79 | path: path.resolve(__dirname, 'docs', 'source', '_static'), 80 | library: "{{ cookiecutter.npm_package_name }}", 81 | libraryTarget: 'amd' 82 | }, 83 | module: { 84 | rules: rules 85 | }, 86 | devtool: 'source-map', 87 | externals, 88 | resolve, 89 | } 90 | 91 | ]; 92 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}.json: -------------------------------------------------------------------------------- 1 | { 2 | "load_extensions": { 3 | "{{ cookiecutter.python_package_name}}/extension": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | from .example import ExampleWidget 8 | from ._version import __version__, version_info 9 | 10 | def _jupyter_labextension_paths(): 11 | """Called by Jupyter Lab Server to detect if it is a valid labextension and 12 | to install the widget 13 | Returns 14 | ======= 15 | src: Source directory name to copy files from. Webpack outputs generated files 16 | into this directory and Jupyter Lab copies from this directory during 17 | widget installation 18 | dest: Destination directory name to install widget files to. Jupyter Lab copies 19 | from `src` directory into /labextensions/ directory 20 | during widget installation 21 | """ 22 | return [{ 23 | 'src': 'labextension', 24 | 'dest': '{{ cookiecutter.npm_package_name}}', 25 | }] 26 | 27 | 28 | def _jupyter_nbextension_paths(): 29 | """Called by Jupyter Notebook Server to detect if it is a valid nbextension and 30 | to install the widget 31 | Returns 32 | ======= 33 | section: The section of the Jupyter Notebook Server to change. 34 | Must be 'notebook' for widget extensions 35 | src: Source directory name to copy files from. Webpack outputs generated files 36 | into this directory and Jupyter Notebook copies from this directory during 37 | widget installation 38 | dest: Destination directory name to install widget files to. Jupyter Notebook copies 39 | from `src` directory into /nbextensions/ directory 40 | during widget installation 41 | require: Path to importable AMD Javascript module inside the 42 | /nbextensions/ directory 43 | """ 44 | return [{ 45 | 'section': 'notebook', 46 | 'src': 'nbextension', 47 | 'dest': '{{ cookiecutter.python_package_name }}', 48 | 'require': '{{ cookiecutter.python_package_name }}/extension' 49 | }] 50 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/_frontend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | """ 8 | Information about the frontend package of the widgets. 9 | """ 10 | 11 | module_name = "{{ cookiecutter.npm_package_name }}" 12 | module_version = "^{{ cookiecutter.npm_package_version }}" 13 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | version_info = (0, 1, 0, 'dev') 8 | __version__ = ".".join(map(str, version_info)) 9 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | """ 8 | TODO: Add module docstring 9 | """ 10 | 11 | from ipywidgets import DOMWidget 12 | from traitlets import Unicode 13 | from ._frontend import module_name, module_version 14 | 15 | 16 | class ExampleWidget(DOMWidget): 17 | """TODO: Add docstring here 18 | """ 19 | _model_name = Unicode('ExampleModel').tag(sync=True) 20 | _model_module = Unicode(module_name).tag(sync=True) 21 | _model_module_version = Unicode(module_version).tag(sync=True) 22 | _view_name = Unicode('ExampleView').tag(sync=True) 23 | _view_module = Unicode(module_name).tag(sync=True) 24 | _view_module_version = Unicode(module_version).tag(sync=True) 25 | 26 | value = Unicode('Hello World').tag(sync=True) 27 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/nbextension/extension.js: -------------------------------------------------------------------------------- 1 | // Entry point for the notebook bundle containing custom model definitions. 2 | // 3 | define(function() { 4 | "use strict"; 5 | 6 | window['requirejs'].config({ 7 | map: { 8 | '*': { 9 | '{{ cookiecutter.npm_package_name }}': 'nbextensions/{{ cookiecutter.python_package_name }}/index', 10 | }, 11 | } 12 | }); 13 | // Export the required load_ipython_extension function 14 | return { 15 | load_ipython_extension : function() {} 16 | }; 17 | }); -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jupyter-widgets/widget-ts-cookiecutter/7e3339c6d32fc21396c5d32c750db9ea246a9346/{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/tests/__init__.py -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/tests/conftest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | import pytest 8 | 9 | from ipykernel.comm import Comm 10 | from ipywidgets import Widget 11 | 12 | class MockComm(Comm): 13 | """A mock Comm object. 14 | 15 | Can be used to inspect calls to Comm's open/send/close methods. 16 | """ 17 | comm_id = 'a-b-c-d' 18 | kernel = 'Truthy' 19 | 20 | def __init__(self, *args, **kwargs): 21 | self.log_open = [] 22 | self.log_send = [] 23 | self.log_close = [] 24 | super(MockComm, self).__init__(*args, **kwargs) 25 | 26 | def open(self, *args, **kwargs): 27 | self.log_open.append((args, kwargs)) 28 | 29 | def send(self, *args, **kwargs): 30 | self.log_send.append((args, kwargs)) 31 | 32 | def close(self, *args, **kwargs): 33 | self.log_close.append((args, kwargs)) 34 | 35 | _widget_attrs = {} 36 | undefined = object() 37 | 38 | 39 | @pytest.fixture 40 | def mock_comm(): 41 | _widget_attrs['_comm_default'] = getattr(Widget, '_comm_default', undefined) 42 | Widget._comm_default = lambda self: MockComm() 43 | _widget_attrs['_ipython_display_'] = Widget._ipython_display_ 44 | def raise_not_implemented(*args, **kwargs): 45 | raise NotImplementedError() 46 | Widget._ipython_display_ = raise_not_implemented 47 | 48 | yield MockComm() 49 | 50 | for attr, value in _widget_attrs.items(): 51 | if value is undefined: 52 | delattr(Widget, attr) 53 | else: 54 | setattr(Widget, attr, value) 55 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/tests/test_example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | import pytest 8 | 9 | from ..example import ExampleWidget 10 | 11 | 12 | def test_example_creation_blank(): 13 | w = ExampleWidget() 14 | assert w.value == 'Hello World' 15 | -------------------------------------------------------------------------------- /{{cookiecutter.github_project_name}}/{{cookiecutter.python_package_name}}/tests/test_nbextension_path.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright (c) {{ cookiecutter.author_name }}. 5 | # Distributed under the terms of the Modified BSD License. 6 | 7 | 8 | def test_nbextension_path(): 9 | # Check that magic function can be imported from package root: 10 | from {{ cookiecutter.python_package_name }} import _jupyter_nbextension_paths 11 | # Ensure that it can be called without incident: 12 | path = _jupyter_nbextension_paths() 13 | # Some sanity checks: 14 | assert len(path) == 1 15 | assert isinstance(path[0], dict) 16 | --------------------------------------------------------------------------------