├── .eslintignore ├── .eslintrc.cjs ├── .github └── workflows │ └── test.yml ├── .gitignore ├── .markdownlintrc ├── .ocularrc.js ├── .prettierignore ├── .prettierrc.cjs ├── .travis.yml ├── LICENSE ├── README.md ├── dev-docs └── RFCs │ ├── README.md │ └── v1.0 │ └── gatsby-rfc.md ├── docs ├── BASEURL ├── README.md ├── table-of-contents.json └── wip │ └── gatsby-theme-ocular │ ├── README.md │ ├── get-started.md │ └── upgrade-guide.md ├── examples └── dev-tools │ ├── .eslintrc.js │ ├── .prettierrc.js │ ├── babel.config.js │ ├── dist │ ├── es5 │ │ ├── app.js │ │ └── app.js.map │ └── esm │ │ ├── app.js │ │ └── app.js.map │ ├── index.html │ ├── ocular-dev-tools.config.js │ ├── package.json │ ├── src │ └── app.js │ └── webpack.config.js ├── modules └── dev-tools │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ ├── README.md │ ├── api-reference │ │ ├── get-babel-config.md │ │ ├── get-eslint-config.md │ │ └── get-prettier-config.md │ ├── cli │ │ ├── ocular-bootstrap.md │ │ ├── ocular-build.md │ │ ├── ocular-bump.md │ │ ├── ocular-clean.md │ │ ├── ocular-lint.md │ │ ├── ocular-metrics.md │ │ ├── ocular-publish.md │ │ ├── ocular-test.md │ │ └── ocular-tsify.md │ ├── faq.md │ ├── upgrade-guide.md │ └── whats-new.md │ ├── package.json │ ├── scripts │ ├── bootstrap.js │ ├── bootstrap.sh │ ├── build.js │ ├── build.sh │ ├── bump.js │ ├── bundle.js │ ├── clean.js │ ├── clean.sh │ ├── lint.js │ ├── lint.sh │ ├── metrics.js │ ├── metrics.sh │ ├── publish.js │ ├── publish.sh │ ├── shell.js │ ├── test.js │ └── test.sh │ ├── src │ ├── build-cjs.ts │ ├── configuration │ │ ├── eslint-config-uber-es2015 │ │ │ ├── best-practices.json │ │ │ ├── ecmascript-6.json │ │ │ ├── eslintrc.json │ │ │ ├── miscellaneous.json │ │ │ └── stylistic-issues.json │ │ ├── eslint-config-uber-es5 │ │ │ ├── best-practices.json │ │ │ ├── errors.json │ │ │ ├── eslintrc.json │ │ │ ├── miscellaneous.json │ │ │ ├── node-js-and-common-js.json │ │ │ ├── strict-mode.json │ │ │ ├── stylistic-issues.json │ │ │ └── variables.json │ │ ├── eslint-config-uber-jsx │ │ │ ├── best-practices.json │ │ │ ├── eslintrc.json │ │ │ ├── miscellaneous.json │ │ │ └── stylistic-issues.json │ │ ├── get-babel-config.ts │ │ ├── get-esbuild-config.ts │ │ ├── get-eslint-config.ts │ │ ├── get-prettier-config.ts │ │ ├── index.ts │ │ └── vite.config.js │ ├── helpers │ │ ├── aliases.ts │ │ ├── cjs-register.cjs │ │ ├── esm-alias.ts │ │ ├── esm-register.ts │ │ ├── get-cjs-entry-points.ts │ │ ├── get-config.ts │ │ └── get-ocular-config.ts │ ├── index.ts │ ├── test.ts │ ├── ts-plugins │ │ ├── ts-transform-append-extension │ │ │ └── index.ts │ │ ├── ts-transform-inline-webgl-constants │ │ │ └── index.ts │ │ ├── ts-transform-remove-glsl-comments │ │ │ └── index.ts │ │ └── ts-transform-version-inline │ │ │ └── index.ts │ └── utils │ │ ├── types.ts │ │ └── utils.ts │ ├── templates │ ├── .eslintignore │ ├── .eslintrc.gatsby-todo │ ├── .nycrc │ ├── .prettierignore │ ├── .prettierrc │ └── tsconfig.json │ ├── test │ ├── index.ts │ ├── lib │ │ ├── configuration.spec.ts │ │ └── utils.spec.ts │ └── ts-plugins │ │ ├── test-transformer.ts │ │ ├── ts-transform-append-extension.spec.ts │ │ ├── ts-transform-inline-webgl-constants.spec.ts │ │ ├── ts-transform-remove-glsl-comments │ │ ├── index.spec.ts │ │ ├── test-case-0-expected.ts │ │ ├── test-case-0.ts │ │ ├── test-case-1-expected.ts │ │ ├── test-case-1.ts │ │ ├── test-case-2-expected.ts │ │ └── test-case-2.ts │ │ └── ts-transform-version-inline.spec.ts │ └── tsconfig.json ├── package.json ├── scripts └── remove-refs-to-unpm.pl ├── test ├── browser.ts ├── index.html ├── modules.ts └── node.ts ├── tsconfig.build.json ├── tsconfig.json ├── website ├── .eslintignore ├── .gitignore ├── gatsby-config.js ├── package.json ├── src │ ├── about.md │ ├── home.md │ └── theme.json └── static │ └── images │ ├── favicon.ico │ ├── hero.jpg │ ├── icon-custom.svg │ ├── icon-react.svg │ ├── icon-target.svg │ └── uber-logo.png └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | test/ 3 | test-case-*.ts 4 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | const {getESLintConfig} = require('ocular-dev-tools/configuration'); 2 | 3 | module.exports = getESLintConfig({ 4 | overrides: { 5 | parserOptions: { 6 | project: ['./tsconfig.json'] 7 | }, 8 | 9 | rules: { 10 | 'import/no-extraneous-dependencies': 0, 11 | 'import/no-unresolved': 0, 12 | 'no-console': 0, 13 | 'no-continue': 0, 14 | 'no-process-env': 0, 15 | 'no-process-exit': 0 16 | }, 17 | 18 | env: { 19 | node: true 20 | }, 21 | 22 | ignorePatterns: ['modules/gatsby-theme-ocular'] 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | # On every pull request, but only on push to master 4 | on: 5 | push: 6 | branches: 7 | - master 8 | pull_request: 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: [18] 16 | 17 | steps: 18 | - uses: actions/checkout@v2.1.1 19 | 20 | - uses: c-hive/gha-yarn-cache@v1 21 | 22 | - name: Set up Node ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | 27 | - name: Bootstrap 28 | run: | 29 | yarn bootstrap 30 | 31 | - name: Run tests 32 | run: | 33 | npm run lint 34 | npm run cover 35 | 36 | - name: Coveralls 37 | if: matrix.node-version == 18 38 | uses: coverallsapp/github-action@master 39 | with: 40 | github-token: ${{ secrets.GITHUB_TOKEN }} 41 | path-to-lcov: ${{ github.workspace }}/coverage/lcov.info -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | 4 | .idea/ 5 | .nx/ 6 | yarn-error.log 7 | .DS_Store 8 | .vscode/ 9 | website/dist 10 | website/.DS_Store 11 | website/node_modules 12 | website/yarn-error.log 13 | 14 | package-lock.json 15 | 16 | website-test/* 17 | 18 | .reify-cache 19 | modules/dev-tools/.alias.json 20 | coverage/ 21 | .nyc_output/ 22 | tsconfig.tsbuildinfo 23 | -------------------------------------------------------------------------------- /.markdownlintrc: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "colors": true, 4 | "line-length": false, 5 | "ul-style": {"style": "sublist"}, 6 | "no-duplicate-header": false, 7 | "no-inline-html": false, 8 | "no-hard-tabs": false, 9 | "whitespace": false 10 | } 11 | -------------------------------------------------------------------------------- /.ocularrc.js: -------------------------------------------------------------------------------- 1 | /** @typedef {import('ocular-dev-tools').OcularConfig} OcularConfig */ 2 | import {resolve} from 'path'; 3 | 4 | /** @type {OcularConfig} */ 5 | let ocularConfig = { 6 | typescript: { 7 | project: 'tsconfig.build.json' 8 | }, 9 | 10 | babel: false, 11 | 12 | lint: { 13 | paths: ['modules'] 14 | }, 15 | 16 | aliases: { 17 | test: resolve('./test') 18 | }, 19 | 20 | entry: { 21 | test: 'test/node.ts', 22 | 'test-browser': 'test/index.html', 23 | size: 'test/size/import-nothing.js' 24 | } 25 | }; 26 | 27 | export default ocularConfig; 28 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/dist*/**/*.js 2 | test-case-*.ts -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | const {getPrettierConfig} = require('ocular-dev-tools/configuration'); 2 | 3 | module.exports = getPrettierConfig(); 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: linux 3 | sudo: required 4 | dist: trusty 5 | addons: 6 | node_js: 7 | - '12' 8 | - '14' 9 | install: 10 | - yarn bootstrap 11 | script: 12 | - npm run test 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Uber Technologies, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ocular 2 | 3 | Ocular is a monorepo for development tools primarily designed for building github-based javascript frameworks. 4 | 5 | It currently contains: 6 | 7 | - `gatsby-theme-ocular` - A documentation generator packaged as a pluggable theme for gatsbyjs documentation generation system. 8 | - `ocular-dev-tools` - A set tools that packages up extensive installation and configuration of babel, webpack, lint, prettier and other state-of-the art JS build tools. 9 | -------------------------------------------------------------------------------- /dev-docs/RFCs/README.md: -------------------------------------------------------------------------------- 1 | # RFCs and Roadmaps (ocular) 2 | 3 | Implementation of non-trivial new features should typically be started off with the creation of an RFC (Request for Comments). 4 | 5 | 6 | ## Roadmaps 7 | 8 | Writeups of directions in major areas of interest 9 | 10 | | Roadmap | Description | 11 | | --- | --- | 12 | | N/A | TBD | 13 | 14 | 15 | ## v1.0 RFCs 16 | 17 | ocular 1.0 will support static page generation based on gatsby: 18 | 19 | | RFC | Author | Status | Description | 20 | | --- | --- | --- | --- | 21 | | [**gatsby-rfc.md) | @jckr/@ibgreen | **Draft** | Port ocular to use `gatsby` and support static content generation. | 22 | 23 | -------------------------------------------------------------------------------- /dev-docs/RFCs/v1.0/gatsby-rfc.md: -------------------------------------------------------------------------------- 1 | # RCF: Rebuild ocular on top of gatsby 2 | 3 | Author: @jckr / @ibgreen 4 | Date: Jan 2019 5 | Status: Draft 6 | 7 | 8 | ## Requirements: 9 | 10 | * Treat documentation as code 11 | * Build from checked-in markdown 12 | * Handle examples 13 | * Build pages for examples (React and pure-js) 14 | * Inject examples from checked-in code. 15 | 16 | 17 | ## Static Website Generators 18 | 19 | Gatsby - Written in React (Note: not a documentation generator for React) - A downside is extensive use of complicated JS ecosystem. Requires a high bar to learn, but overlaps with the front-end skills used by React developers. 20 | 21 | Some strong alternatives 22 | - Jekyll, written in Ruby. 23 | - Hugo, is written in Go and uses Go's template libraries. 24 | 25 | Some articles 26 | - [Quick Thoughts on Gatsby JS vs. Jekyll](https://medium.com/@ajkueterman/quick-thoughts-on-gatsby-js-vs-jekyll-c13c1337c24a) 27 | - [Gatsby vs Hugo article](https://medium.freecodecamp.org/gatsby-vs-hugo-a-detailed-comparison-e78d94f640fc) 28 | - [Gatsby vs Jekyll vs Hugo forum](https://www.reddit.com/r/FreeCodeCamp/comments/923js6/jekyll_vs_hugo_vs_gatsby/) 29 | 30 | 31 | ## Markdown Support 32 | 33 | Markdown support is very strong, handled through the remark based plugin which itself has an entire system of sub-plugins for code syntax formatting, image handling, etc. Also a number of styling sheets are available. 34 | 35 | The main effort is deciding how to load all the markdown into gatsby, how to query it from the resulting graphql tables, and then generating table of contents, and styling the markdown. 36 | 37 | 38 | ## Code Injection Support 39 | 40 | ### Use Cases 41 | 42 | * Examples need to be browsable from their own table of contents - Just publish examples as separate pages and use iframes? 43 | * We want to be able to inject a code sample on a page 44 | 45 | 46 | ###Alternatives 47 | 48 | #### repl (code sandbox) links 49 | 50 | The gasby remark plugin [gatsby-remark-code-repls](https://www.gatsbyjs.org/packages/gatsby-remark-code-repls/) is an interesting of option for "REPL-type environments" (like codepen). This can POST example code from the repo to the codepen using the "Codepen Prefill API". 51 | * See [React docs](https://reactjs.org/docs/rendering-elements.html) for example usage. 52 | 53 | * Currently examples are not inline, the plugin generates links that open the code in the repl sandbox 54 | 55 | COMMENT: Adding this feature is obviously extremely nice for any complete code snippets, but does not solve the example code injection problem as such 56 | 57 | 58 | #### [gatsby-remark-responsive-iframe](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-responsive-iframe) 59 | 60 | Another gatsby remark plugin, wraps iframes or objects (e.g. embedded YouTube videos) within markdown files in a responsive elastic container with a fixed aspect ratio. This ensures that the iframe or object will scale proportionally and to the full width of its container. 61 | -------------------------------------------------------------------------------- /docs/BASEURL: -------------------------------------------------------------------------------- 1 | https://uber-web.github.io/ocular -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Welcome 2 | 3 | ocular is a set of tools to help build and publish open source frameworks. It contains: 4 | - a `dev-tools` module that installs and provides base configurations for tools like webpack, babel, lerna, eslint prettier etc. 5 | - a `gatsby-theme-ocular` module that contains a markdown to HTML converter to make it easy to build websites. 6 | ## About ocular-dev-tools 7 | 8 | ocular-dev-tools installs a set of configurable development scripts to handle build, test and publish tasks for JavaScript framework repositories. 9 | 10 | While highly configurable ocular-dev-tools is very opinionated in choice of tooling etc, and mainly targets vis.gl frameworks, like deck.gl, luma.gl etc. 11 | 12 | ## About gatsby-theme-ocular 13 | 14 | The vis.gl team needed a system to build documentation websites with the least amount of friction. Our first use case has been the websites for the various visualization projects such as [deck.gl](https://deck.gl) [luma.gl](https://luma.gl) or [loaders.gl](https://loaders.gl). 15 | 16 | We wanted: 17 | - to organize documentation files with a table of contents, navigation and search; 18 | - to have interactive examples; 19 | - to have some control on formatting; 20 | - to generate websites discoverable by search engines; 21 | - to make it easy to publish these websites, especially on github pages; 22 | - to make this experience possible without writing a line of code; 23 | - to provide sensible defaults in terms of navigation and styling; 24 | - but to allow advanced users to overwrite and customize anything they want. 25 | 26 | Happy documenting! 27 | 28 | To find out more, go to [get started](get-started.md) 29 | 30 | -------------------------------------------------------------------------------- /docs/table-of-contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "table-of-contents", 3 | "chapters": [ 4 | { 5 | "title": "Overview", 6 | "entries": [ 7 | { 8 | "entry": "docs" 9 | } 10 | ] 11 | }, 12 | { 13 | "title": "ocular-dev-tools", 14 | "chapters": [ 15 | { 16 | "title": "Overview", 17 | "entries": [ 18 | { 19 | "entry": "modules/dev-tools/docs/" 20 | }, 21 | { 22 | "entry": "modules/dev-tools/docs/whats-new" 23 | }, 24 | { 25 | "entry": "modules/dev-tools/docs/upgrade-guide" 26 | }, 27 | { 28 | "entry": "modules/dev-tools/docs/faq" 29 | } 30 | ] 31 | }, 32 | { 33 | "title": "Configuration Guide", 34 | "entries": [ 35 | { 36 | "entry": "modules/dev-tools/docs/" 37 | }, 38 | { 39 | "entry": "modules/dev-tools/docs/developer-guide/command-line-interface" 40 | }, 41 | { 42 | "entry": "modules/dev-tools/docs/developer-guide/configuring-tests" 43 | }, 44 | { 45 | "entry": "modules/dev-tools/docs/developer-guide/aliases" 46 | } 47 | ] 48 | }, 49 | { 50 | "title": "Command Line Reference", 51 | "entries": [ 52 | { 53 | "entry": "modules/dev-tools/docs/cli/ocular-bootstrap" 54 | }, 55 | { 56 | "entry": "modules/dev-tools/docs/cli/ocular-build" 57 | }, 58 | { 59 | "entry": "modules/dev-tools/docs/cli/ocular-clean" 60 | }, 61 | { 62 | "entry": "modules/dev-tools/docs/cli/ocular-lint" 63 | }, 64 | { 65 | "entry": "modules/dev-tools/docs/cli/ocular-metrics" 66 | }, 67 | { 68 | "entry": "modules/dev-tools/docs/cli/ocular-publish" 69 | }, 70 | { 71 | "entry": "modules/dev-tools/docs/cli/ocular-test" 72 | }, 73 | { 74 | "entry": "modules/dev-tools/docs/cli/ocular-bump" 75 | } 76 | ] 77 | }, 78 | { 79 | "title": "API Reference", 80 | "entries": [ 81 | { 82 | "entry": "modules/dev-tools/docs/api-reference/get-babel-config" 83 | }, 84 | { 85 | "entry": "modules/dev-tools/docs/api-reference/get-eslint-config" 86 | }, 87 | { 88 | "entry": "modules/dev-tools/docs/api-reference/get-prettier-config" 89 | }, 90 | { 91 | "entry": "modules/dev-tools/docs/api-reference/get-webpack-config" 92 | } 93 | ] 94 | } 95 | ] 96 | }, 97 | { 98 | "title": "gatsby-theme-ocular", 99 | "chapters": [ 100 | { 101 | "title": "Overview", 102 | "entries": [ 103 | { 104 | "entry": "modules/gatsby-theme-ocular/docs" 105 | }, 106 | { 107 | "entry": "modules/gatsby-theme-ocular/docs/get-started" 108 | }, 109 | { 110 | "entry": "modules/gatsby-theme-ocular/docs/whats-new" 111 | }, 112 | { 113 | "entry": "modules/gatsby-theme-ocular/docs/upgrade-guide" 114 | } 115 | ] 116 | }, 117 | { 118 | "title": "Creating content", 119 | "entries": [ 120 | { 121 | "entry": "modules/gatsby-theme-ocular/docs" 122 | }, 123 | { 124 | "entry": "modules/gatsby-theme-ocular/docs/get-started" 125 | }, 126 | { 127 | "entry": "modules/gatsby-theme-ocular/docs/whats-new" 128 | }, 129 | { 130 | "entry": "modules/gatsby-theme-ocular/docs/upgrade-guide" 131 | } 132 | ] 133 | }, 134 | { 135 | "title": "Creating content", 136 | "entries": [ 137 | { 138 | "entry": "modules/gatsby-theme-ocular/docs/creating-content/writing-documentation" 139 | }, 140 | { 141 | "entry": "modules/gatsby-theme-ocular/docs/creating-content/interactive-examples" 142 | } 143 | ] 144 | }, 145 | { 146 | "title": "Developer Guide", 147 | "entries": [ 148 | { 149 | "entry": "modules/gatsby-theme-ocular/docs/developer-guide/configuring" 150 | }, 151 | { 152 | "entry": "modules/gatsby-theme-ocular/docs/developer-guide/deploying" 153 | }, 154 | { 155 | "entry": "modules/gatsby-theme-ocular/docs/developer-guide/debugging" 156 | } 157 | ] 158 | }, 159 | { 160 | "title": "API Reference", 161 | "entries": [ 162 | { 163 | "entry": "modules/gatsby-theme-ocular/docs/api-reference/options" 164 | }, 165 | { 166 | "entry": "modules/gatsby-theme-ocular/docs/api-reference/generated-pages" 167 | } 168 | ] 169 | } 170 | ] 171 | } 172 | ] 173 | } 174 | -------------------------------------------------------------------------------- /docs/wip/gatsby-theme-ocular/README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Ocular is a tool primarily designed for building documentation websites for github-based javascript frameworks, built using the gatsbyjs documentation generation system. 4 | 5 | Using ocular in your framework requires: 6 | * a directory with github markdown 7 | * examples... 8 | 9 | 10 | ## Quick Setup 11 | 12 | If you already have markdown documentation written for your framework, setting up an initial ocular based page is very quick: 13 | 14 | * Create a `website` folder in your framework repository's root folder. 15 | * Create a package.json and install `ocular` in the website folder. 16 | * Create the file `website/gatsby-config.js`, copied from ... 17 | * Create the file `website/gatsby-node.js`, copied from ... 18 | * Create the file `website/site-config.js`, copied from and modified to fit your site. 19 | * Create a `table-of-contents.json` at the root of your markdown documentation folder. 20 | 21 | 22 | ## Deep Configuration 23 | 24 | Since your website contains the gatsbyjs entry point files (`gatsby-config.js` and `gatsby-node.js`) it is possible to take full control and override selected parts of ocular's documenation generation system. 25 | 26 | The ocular package exports most internal components used by ocular so that you can reuse them in building customized functionality. 27 | 28 | 29 | ## Functions 30 | 31 | ### getGatsbyConfig(options : Object) : Object 32 | 33 | This function takes your site configuration object and builds a complete gatsby configuration object, and populates it with a number of preinstalled plugins. This object is intended to be passed to gatsby as the exported value from your `website/gatsby-config.js` file. 34 | 35 | `getGatsbyConfig` is intended to be called in your `website/gatsby-config.js` file as follows: 36 | 37 | ``` 38 | require('reify'); // Enables ES6 "import" in imported files 39 | 40 | const {setSiteConfig, getGatsbyConfig} = require('ocular'); 41 | 42 | const config = require('./ocular-config'); 43 | 44 | setSiteConfig(config); 45 | 46 | module.exports = getGatsbyConfig(config); 47 | ``` 48 | 49 | NOTE: You can forward the returned object directly to gatsby, or you can modify it first, e.g. to delete or add plugins before passing it on to gatsby. Consult [gatsby docs](https://www.gatsbyjs.org/) for more information on `gatsby-config.js` options. 50 | 51 | 52 | ### getGatsbyNodeCallbacks() : Object 53 | 54 | Forward these callbacks to gatsby in your `website/gatsby-node.js` file to set up default ocular configuration. 55 | 56 | ``` 57 | require('reify'); // Enables ES6 "import" in imported files 58 | const {getGatsbyNodeCallbacks} = require('ocular'); 59 | 60 | module.exports = getGatsbyNodeCallbacks(); 61 | ``` 62 | 63 | NOTE: It is possible to override the ocular-provided callbacks and thus take full control of any aspect of gatsby's document generation process. Consult [gatsby docs](https://www.gatsbyjs.org/) for more information on `gatsby-node.js` callbacks. 64 | 65 | ``` 66 | require('reify'); // Enables ES6 "import" in imported files 67 | const {getGatsbyNodeCallbacks} = require('ocular'); 68 | 69 | module.exports = getGatsbyNodeCallbacks(); 70 | module.exports.onCreateNode = ({ node, actions, getNode }) => { 71 | // Do other things 72 | ... 73 | /// Call ocular default handling (or not) 74 | getGatsbyNodeCallbacks.onCreateNode({ node, actions, getNode }); 75 | }; 76 | ``` 77 | 78 | 79 | ## Components 80 | 81 | ocular provides a number of common React components that can be used to create custom pages. 82 | 83 | 84 | ### Layouts 85 | 86 | Layout components are components that remain static between pages 87 | 88 | 89 | #### Header 90 | 91 | 92 | #### Footer 93 | 94 | 95 | #### Table of Contents 96 | 97 | 98 | ### Common Components 99 | 100 | 101 | #### SEO 102 | 103 | Search engine optimization helper 104 | -------------------------------------------------------------------------------- /docs/wip/gatsby-theme-ocular/get-started.md: -------------------------------------------------------------------------------- 1 | # Quick start 2 | 3 | ## Installing Ocular 4 | 5 | From the project you want to create a website for, create a new folder: 6 | 7 | ``` 8 | mkdir website 9 | ``` 10 | 11 | Initialize that folder as a new project with its own package.json. 12 | 13 | From now on, we'll call that folder you've just created the Ocular folder. 14 | 15 | ``` 16 | npm init -y 17 | ``` 18 | 19 | then install Ocular as a devDependency. 20 | 21 | ``` 22 | yarn add -D gatsby-theme-ocular 23 | ``` 24 | or 25 | ``` 26 | npm install gatsby-theme-ocular --save-dev 27 | ``` 28 | 29 | ## Creating and running your Ocular website 30 | 31 | Now, start the Ocular project: 32 | 33 | ``` 34 | ocular init 35 | ``` 36 | 37 | This will prompt you with a few questions, and create a number of files and folders in the Ocular folder. 38 | The most important of these file is `gatsby-config.js` in the Ocular folder, which contains all the settings for your website. You can edit it later. 39 | 40 | Now install any remaining packages: 41 | ``` 42 | yarn 43 | ``` 44 | or 45 | ``` 46 | npm install 47 | ``` 48 | 49 | Your project will need a `table-of-contents.json` file in the same location you have your documentation files. You can create one manually but Ocular can also create one for you by typing: 50 | ``` 51 | yarn build-toc 52 | ``` 53 | or 54 | ``` 55 | npm run build-toc 56 | ``` 57 | 58 | At this stage, you can see your website by typing: 59 | 60 | ``` 61 | yarn start 62 | ``` 63 | or 64 | ``` 65 | npm run start 66 | ``` 67 | 68 | ## Writing content 69 | 70 | You're going to need documentation files for your documentation website. 71 | Your `gatsby-config.js` file will contain the location of these files. Read [Writing documentation](./creating-content/writing-documentation) to know all about that part. 72 | And your documentation files will be available on your website! 73 | 74 | ## Publishing your website 75 | 76 | That's all you need if you just want to have your website running on your machine. But if you want to have your site running somewhere else, such as GitHub, that's not enough. 77 | 78 | From the ocular folder, type 79 | 80 | ``` 81 | yarn build 82 | ``` 83 | or 84 | ``` 85 | npm run build 86 | ``` 87 | And this will generate a static website in the folder `public` (a sub-folder of your Ocular folder) 88 | You can go to that folder and test your built website by typing 89 | ``` 90 | yarn serve 91 | ``` 92 | or 93 | ``` 94 | npm run serve 95 | ``` 96 | 97 | You can now safely upload the contents of this folder on a web server. If you want to deploy this website to GitHub Pages, and your project is already hosted on github, you can instead type: 98 | 99 | ``` 100 | yarn deploy 101 | ``` 102 | or 103 | ``` 104 | npm run deploy 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/wip/gatsby-theme-ocular/upgrade-guide.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | 4 | ## gatsby-theme-ocular 5 | 6 | ### Upgrading from v1.1 to v1.2 7 | 8 | Breaking changes: 9 | - INDEX_PAGE_URL 10 | 11 | ### Upgrading from ocular-gatsby to gatsby-theme-ocular 12 | 13 | Your website needs to update its `gatsby-config.js` and `gatsby-node.js` in the root, and unless you have added additional code, you can remove your `gatsby-browser.js` and `gatsby-ssr.js` as the default implementations can now be supplied by `gatsby-theme-ocular`. 14 | 15 | `gatsby-config.js`: 16 | ```js 17 | const ocularConfig = require('./ocular-config'); 18 | 19 | module.exports = { 20 | plugins: [{resolve: `gatsby-theme-ocular`, options: ocularConfig}], 21 | }; 22 | ``` 23 | 24 | `gatsby-node.js`: 25 | ```js 26 | const {setOcularConfig} = require('gatsby-theme-ocular'); 27 | 28 | const ocularConfig = require('./ocular-config'); 29 | setOcularConfig(ocularConfig); 30 | ``` -------------------------------------------------------------------------------- /examples/dev-tools/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const {getESLintConfig} = require('ocular-dev-tools'); 2 | 3 | const config = getESLintConfig({react: '16.8.2'}); 4 | 5 | // Make any changes to default config here 6 | 7 | // Uncomment to log the eslint config 8 | // console.debug(config); 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /examples/dev-tools/.prettierrc.js: -------------------------------------------------------------------------------- 1 | const {getPrettierConfig} = require('ocular-dev-tools'); 2 | 3 | const config = getPrettierConfig({react: '16.8.2'}); 4 | 5 | // Make any changes to default config here 6 | 7 | // Uncomment to log the eslint config 8 | // console.debug(config); 9 | 10 | module.exports = config; 11 | -------------------------------------------------------------------------------- /examples/dev-tools/babel.config.js: -------------------------------------------------------------------------------- 1 | const {getBabelConfig} = require('ocular-dev-tools'); 2 | 3 | module.exports = (api) => { 4 | const config = getBabelConfig(api, {react: true}); 5 | 6 | // Make any changes to default config here 7 | 8 | // Uncomment to log the config 9 | // console.debug(config); 10 | 11 | return config; 12 | }; 13 | -------------------------------------------------------------------------------- /examples/dev-tools/dist/es5/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); 4 | 5 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 6 | 7 | Object.defineProperty(exports, "__esModule", { 8 | value: true 9 | }); 10 | exports.renderToDOM = renderToDOM; 11 | exports.default = void 0; 12 | 13 | var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); 14 | 15 | var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); 16 | 17 | var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); 18 | 19 | var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); 20 | 21 | var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); 22 | 23 | var _react = _interopRequireWildcard(require("react")); 24 | 25 | var _reactDom = require("react-dom"); 26 | 27 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2.default)(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2.default)(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2.default)(this, result); }; } 28 | 29 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 30 | 31 | var App = function (_PureComponent) { 32 | (0, _inherits2.default)(App, _PureComponent); 33 | 34 | var _super = _createSuper(App); 35 | 36 | function App(props) { 37 | var _this; 38 | 39 | (0, _classCallCheck2.default)(this, App); 40 | _this = _super.call(this, props); 41 | _this.state = {}; 42 | return _this; 43 | } 44 | 45 | (0, _createClass2.default)(App, [{ 46 | key: "render", 47 | value: function render() { 48 | return _react.default.createElement("div", { 49 | style: { 50 | color: 'red' 51 | } 52 | }, _react.default.createElement("p", null, "This is a minimal React example"), _react.default.createElement("p", null, "Line..."), _react.default.createElement("p", null, "Line..."), _react.default.createElement("p", null, "Line..."), _react.default.createElement("p", null, "Line...")); 53 | } 54 | }]); 55 | return App; 56 | }(_react.PureComponent); 57 | 58 | exports.default = App; 59 | 60 | function renderToDOM(container) { 61 | (0, _reactDom.render)(_react.default.createElement(App, null), container); 62 | } 63 | //# sourceMappingURL=app.js.map -------------------------------------------------------------------------------- /examples/dev-tools/dist/es5/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../src/app.js"],"names":["App","props","state","color","PureComponent","renderToDOM","container"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;;AACA;;;;;;IAEqBA,G;;;;;AACnB,eAAYC,KAAZ,EAAmB;AAAA;;AAAA;AACjB,8BAAMA,KAAN;AACA,UAAKC,KAAL,GAAa,EAAb;AAFiB;AAGlB;;;;WAED,kBAAS;AACP,aACE;AAAK,QAAA,KAAK,EAAE;AAACC,UAAAA,KAAK,EAAE;AAAR;AAAZ,SACE,0EADF,EAEE,kDAFF,EAGE,kDAHF,EAIE,kDAJF,EAKE,kDALF,CADF;AASD;;;EAhB8BC,oB;;;;AAmB1B,SAASC,WAAT,CAAqBC,SAArB,EAAgC;AACrC,wBAAO,6BAAC,GAAD,OAAP,EAAgBA,SAAhB;AACD","sourcesContent":["import React, {PureComponent} from 'react';\nimport {render} from 'react-dom';\n\nexport default class App extends PureComponent {\n constructor(props) {\n super(props);\n this.state = {};\n }\n\n render() {\n return (\n
\n

This is a minimal React example

\n

Line...

\n

Line...

\n

Line...

\n

Line...

\n
\n );\n }\n}\n\nexport function renderToDOM(container) {\n render(, container);\n}\n"],"file":"app.js"} -------------------------------------------------------------------------------- /examples/dev-tools/dist/esm/app.js: -------------------------------------------------------------------------------- 1 | import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck"; 2 | import _createClass from "@babel/runtime/helpers/esm/createClass"; 3 | import _inherits from "@babel/runtime/helpers/esm/inherits"; 4 | import _possibleConstructorReturn from "@babel/runtime/helpers/esm/possibleConstructorReturn"; 5 | import _getPrototypeOf from "@babel/runtime/helpers/esm/getPrototypeOf"; 6 | 7 | function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } 8 | 9 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } 10 | 11 | import React, { PureComponent } from 'react'; 12 | import { render } from 'react-dom'; 13 | 14 | var App = function (_PureComponent) { 15 | _inherits(App, _PureComponent); 16 | 17 | var _super = _createSuper(App); 18 | 19 | function App(props) { 20 | var _this; 21 | 22 | _classCallCheck(this, App); 23 | 24 | _this = _super.call(this, props); 25 | _this.state = {}; 26 | return _this; 27 | } 28 | 29 | _createClass(App, [{ 30 | key: "render", 31 | value: function render() { 32 | return React.createElement("div", { 33 | style: { 34 | color: 'red' 35 | } 36 | }, React.createElement("p", null, "This is a minimal React example"), React.createElement("p", null, "Line..."), React.createElement("p", null, "Line..."), React.createElement("p", null, "Line..."), React.createElement("p", null, "Line...")); 37 | } 38 | }]); 39 | 40 | return App; 41 | }(PureComponent); 42 | 43 | export { App as default }; 44 | export function renderToDOM(container) { 45 | render(React.createElement(App, null), container); 46 | } 47 | //# sourceMappingURL=app.js.map -------------------------------------------------------------------------------- /examples/dev-tools/dist/esm/app.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../src/app.js"],"names":["React","PureComponent","render","App","props","state","color","renderToDOM","container"],"mappings":";;;;;;;;;;AAAA,OAAOA,KAAP,IAAeC,aAAf,QAAmC,OAAnC;AACA,SAAQC,MAAR,QAAqB,WAArB;;IAEqBC,G;;;;;AACnB,eAAYC,KAAZ,EAAmB;AAAA;;AAAA;;AACjB,8BAAMA,KAAN;AACA,UAAKC,KAAL,GAAa,EAAb;AAFiB;AAGlB;;;;WAED,kBAAS;AACP,aACE;AAAK,QAAA,KAAK,EAAE;AAACC,UAAAA,KAAK,EAAE;AAAR;AAAZ,SACE,iEADF,EAEE,yCAFF,EAGE,yCAHF,EAIE,yCAJF,EAKE,yCALF,CADF;AASD;;;;EAhB8BL,a;;SAAZE,G;AAmBrB,OAAO,SAASI,WAAT,CAAqBC,SAArB,EAAgC;AACrCN,EAAAA,MAAM,CAAC,oBAAC,GAAD,OAAD,EAAUM,SAAV,CAAN;AACD","sourcesContent":["import React, {PureComponent} from 'react';\nimport {render} from 'react-dom';\n\nexport default class App extends PureComponent {\n constructor(props) {\n super(props);\n this.state = {};\n }\n\n render() {\n return (\n
\n

This is a minimal React example

\n

Line...

\n

Line...

\n

Line...

\n

Line...

\n
\n );\n }\n}\n\nexport function renderToDOM(container) {\n render(, container);\n}\n"],"file":"app.js"} -------------------------------------------------------------------------------- /examples/dev-tools/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | minimal ocular example 6 | 16 | 17 | 18 |
19 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /examples/dev-tools/ocular-dev-tools.config.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | 3 | module.exports = { 4 | lint: { 5 | paths: ['./src'], 6 | extensions: ['js'] 7 | }, 8 | 9 | aliases: { 10 | test: resolve(__dirname, 'test') 11 | }, 12 | 13 | entry: { 14 | test: 'test/node.js', 15 | 'test-browser': 'test/browser.js', 16 | bench: 'test/bench/node.js', 17 | 'bench-browser': 'test/bench/browser.js', 18 | size: 'test/size/import-nothing.js' 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /examples/dev-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocular-examples-dev-tools", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "bootstrap": "yarn install-fast && ocular-bootstrap", 8 | "install-fast": "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true yarn", 9 | "build": "ocular-clean && ocular-build", 10 | "clean": "ocular-clean", 11 | "cover": "ocular-test cover", 12 | "lint": "ocular-lint", 13 | "test": "ocular-test" 14 | }, 15 | "dependencies": { 16 | "lodash": "^4.17.19", 17 | "prop-types": "^15.7.2", 18 | "react": "^16.3.0", 19 | "react-dom": "^16.3.0", 20 | "react-map-gl": "^4.1.2", 21 | "styled-components": "^4.2.0" 22 | }, 23 | "devDependencies": { 24 | "ocular-dev-tools": "1.0.0-alpha.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/dev-tools/src/app.js: -------------------------------------------------------------------------------- 1 | import React, {PureComponent} from 'react'; 2 | import {render} from 'react-dom'; 3 | 4 | export default class App extends PureComponent { 5 | constructor(props) { 6 | super(props); 7 | this.state = {}; 8 | } 9 | 10 | render() { 11 | return ( 12 |
13 |

This is a minimal React example

14 |

Line...

15 |

Line...

16 |

Line...

17 |

Line...

18 |
19 | ); 20 | } 21 | } 22 | 23 | export function renderToDOM(container) { 24 | render(, container); 25 | } 26 | -------------------------------------------------------------------------------- /examples/dev-tools/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | const getWebpackConfig = require('ocular-dev-tools/config/webpack.config'); 3 | 4 | module.exports = (env = {}) => { 5 | const config = getWebpackConfig(env); 6 | 7 | // Make any changes to default config here 8 | 9 | // Uncomment to debug config 10 | // console.log(JSON.stringify(config, null, 2)); 11 | 12 | return [config]; 13 | }; 14 | 15 | /* 16 | 17 | const COMMON_CONFIG = { 18 | mode: "development", 19 | 20 | entry: { 21 | app: "./app.js" 22 | }, 23 | 24 | output: { 25 | library: "App" 26 | }, 27 | 28 | module: { 29 | rules: [ 30 | { 31 | // Transpile ES6 to ES5 with babel 32 | // Remove if your app does not use JSX or you don't need to support old browsers 33 | test: /\.js$/, 34 | loader: "babel-loader", 35 | exclude: [/node_modules/], 36 | options: { 37 | presets: ["@babel/preset-env", "@babel/preset-react"] 38 | } 39 | } 40 | ] 41 | } 42 | }; 43 | 44 | function addDevConfig(config, env) { 45 | // config = require('../webpack.config.local')(config)(env); 46 | return config; 47 | } 48 | 49 | function addProdConfig(config) { 50 | config.plugins = config.plugins || []; 51 | 52 | return Object.assign(config, { 53 | mode: "production" 54 | }); 55 | } 56 | 57 | module.exports = env => { 58 | env = env || {}; 59 | 60 | let config = COMMON_CONFIG; 61 | 62 | if (env.local) { 63 | config = addDevConfig(config, env); 64 | } 65 | 66 | if (env.prod) { 67 | config = addProdConfig(config); 68 | } 69 | 70 | // Enable to debug config 71 | // console.warn(JSON.stringify(config, null, 2)); 72 | 73 | return config; 74 | }; 75 | */ 76 | -------------------------------------------------------------------------------- /modules/dev-tools/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG (ocular-dev-tools) 2 | 3 | ## v2.0.0-alpha.35 4 | 5 | - fix: test node-debug (#470 6 | 7 | ## v2.0.0-alpha.34 8 | 9 | - fix(dev-tools): prod version (#469) 10 | 11 | ## v2.0.0-alpha.33 12 | 13 | - fix(dev-tools): skip interaction when running ocular-publish on CI 14 | 15 | ## v2.0.0-alpha.32 16 | 17 | - fix(dev-tools): switch to use lerna publish from-package 18 | 19 | ## v2.0.0-alpha.30 20 | 21 | - feat(dev-tools): add new modes to ocular-publish (#461) 22 | 23 | ## v2.0.0-alpha.29 24 | 25 | - fix(dev-tools): remove log prefix (#459) 26 | 27 | ## v2.0.0-alpha.28 28 | 29 | - Fix commands with yarn 4 (#457) 30 | 31 | ## v2.0.0-alpha.27 32 | 33 | - Add ts-transform-inline-webgl-constants (#456) 34 | 35 | ## v2.0.0-alpha.26 36 | 37 | - Fix coverage reporting format 38 | 39 | ## v2.0.0-alpha.25 40 | 41 | - Revert changes to headless mode 42 | 43 | ## v2.0.0-alpha.24 44 | 45 | - feat(dev-tools): add ts-transform-remove-glsl-comments (#455) 46 | - Add tests for TypeScript plugins (#452) 47 | - chore(dev-tools): clean up package.json (#454) 48 | - chore(test-utils): Migrate to typescript (#453) 49 | - Use ocular-build to build dev-tools (#451) 50 | - Add option to report code coverage against browser test (#450) 51 | - fix: prettier log level (#449) 52 | 53 | ## v2.0.0-alpha.23 54 | 55 | - Remove smoosh script (#448) 56 | - Remove puppeteer global install (#447) 57 | 58 | ## v2.0.0-alpha.21 59 | 60 | - ESM module build command improvements (#446) 61 | 62 | ## v2.0.0-alpha.20 63 | 64 | - Bump major versions of tooling: prettier to 3.x, eslint to 8.5.x, typescript to 5.2.2 (#437) 65 | 66 | ## v2.0.0-alpha.19 67 | 68 | - fix(dev-tools) use new headless mode in browser tests (#436) 69 | 70 | ## v2.0.0-alpha.18 71 | 72 | - bundle command always merges babel config (#435) 73 | 74 | ## v2.0.0-alpha.17 75 | 76 | - Add react support to dev bundle (#433) 77 | 78 | ## v2.0.0-alpha.16 79 | 80 | - Minor fix of ESM resolution (#431) 81 | - Change cjs entry point target (#432) 82 | 83 | ## v2.0.0-alpha.15 84 | 85 | - Support building esm module with multiple entry points (#430) 86 | 87 | ## v2.0.0-alpha.14 88 | 89 | - Update babel config for esm target (#429) 90 | 91 | ## v2.0.0-alpha.13 92 | 93 | - Fix metrics import 94 | 95 | ## v2.0.0-alpha.12 96 | 97 | - Start vite server from JS API (#424) 98 | 99 | ## v2.0.0-alpha.11 100 | 101 | - Set NODE_ENV during test 102 | 103 | ## v2.0.0-alpha.10 104 | 105 | - Bump esbuild plugins versions (#422) 106 | 107 | ## v2.0.0-alpha.9 108 | 109 | - Fix umd bundle settings (#420) 110 | 111 | ## v2.0.0-alpha.8 112 | 113 | - Fix test dist alias resolution (#418) 114 | 115 | ## v2.0.0-alpha.7 116 | 117 | - clean up dependencies (#416) 118 | - Remove shelljs (#415) 119 | 120 | ## v2.0.0-alpha.6 121 | 122 | - Add ocular-bundle command (#414) 123 | 124 | ## v2.0.0-alpha.5 125 | 126 | - Suppress commit hooks in lerna publish (#413) 127 | - Add Typescript build commands (#412) 128 | - eslintrc auto determine babel config path (#411) 129 | - Drop import assertion for better compatibility (#410) 130 | 131 | ## v2.0.0-alpha.4 132 | 133 | - support for ESM repos 134 | 135 | ## v2.0.0-alpha.3 136 | 137 | - edge case handling and more flexible configs (#406) 138 | 139 | ## v2.0.0-alpha.2 140 | 141 | - Try fix missing Chromium error 142 | 143 | ## v2.0.0-alpha.1 144 | 145 | - Update test harness to use ts-node and vite #405 146 | 147 | ## v1.0.0-alpha.8 148 | 149 | yarn 3 fixes 150 | 151 | - fix: Fix error codes returned by ocular-test, ocular-lint, etc (fork#7) 152 | - Fix error codes returned by ocular-test, ocular-lint, etc 153 | - Drop node 12 from test matrix 154 | - chore: Run bash scripts using Node (fork#6) 155 | - Fix package.json format (fork#5) 156 | - Remove browserslist update from bootstrap (fork#3) 157 | - ESLint and prettier upgrade (fork#2) 158 | 159 | ## v1.0.0-alpha.7 160 | 161 | - chore(dev-tools): dependencies (#374) 162 | 163 | ## v1.0.0-alpha.6 164 | 165 | - feat(dev-tools): Add typescript build support (#372) 166 | - chore: prettier fixes (#373) 167 | - babel targets: explicit support for async functions in ESM, reduces runtime dependencies. (#366) 168 | - chore(docs): Split docs per module (#371) 169 | - feat(dev-tools): Partial webpack 5 support (#370) 170 | - chore(dev-tools): update browser list on bootstrap (#367) 171 | - chore(dev-tools): improve typings (#369) 172 | - fix(dev-tools): Fix Node 16 build (#368) 173 | - fix(dev-tools): For ES Modules, look also for .ocularrc.cjs (#365) 174 | 175 | ## v1.0.0-alpha.5 176 | 177 | - feat(dev-tools): export getOcularConfig (#363) 178 | - chore: Add github CI (#364) 179 | 180 | ## v1.0.0-alpha.4 181 | 182 | - chore: Move deepmerge dep to dev-tools 183 | 184 | ## v1.0.0-alpha.3 185 | 186 | - feat(dev-tools): Add typescript support (#361) 187 | 188 | ## v1.0.0-alpha.3 189 | 190 | - feat(dev-tools): Add deepMerge export 191 | 192 | ## v1.0.0-alpha.1 193 | 194 | - feat: ocular-tsify using ts-smoosh (#357) 195 | - chore: Improve JSDoc (#354) 196 | - feat: log commands issued by ocular (#353) 197 | - feat: Add typescript exports for ocular functions, and test example (#352) 198 | 199 | ## v0.3.0 200 | 201 | - Change build targets (#349) 202 | 203 | ## v0.2.3 204 | 205 | - Fix coverage calculation (#348) 206 | 207 | ## v0.2.2 208 | 209 | - Support custom tag in publish script (#347) 210 | 211 | ## v0.2.1 212 | 213 | - Fix: extensions list from config wasn't used when executing ESLint (#345) 214 | 215 | ## v0.2.0 216 | 217 | - Support `extensions` setting for Babel in config 218 | 219 | ## v0.1.8 220 | 221 | - remove more jq usage 222 | 223 | ## v0.1.7 224 | 225 | - remove dependency on jq (#302) 226 | 227 | ## v0.1.6 228 | 229 | - dev-tools: Bump es6 build targets to Node 10 (#287) 230 | - Fix eslint error when using 'ocular-lint fix' (#286) 231 | 232 | ## v0.1.5 233 | 234 | - Fix bump script to detect peerDependencies (#284) 235 | 236 | ## v0.1.4 237 | 238 | - Fix building selected modules (#282) 239 | 240 | ## v0.1.1 241 | 242 | - Add `tape-promise` to dependencies 243 | 244 | ## v0.1.0 245 | 246 | - Ensure `test` folder is published 247 | 248 | ## v0.0.33 249 | 250 | - Fix bump script (#278) 251 | 252 | ## v0.0.32 253 | 254 | - ocular-build: babel now called with --copy-files (#276) 255 | 256 | ## v0.0.31 257 | 258 | - Update bump script to use full package name (#265) 259 | 260 | ## v0.0.30 261 | 262 | - test assertions (#248) 263 | - tweak build to accept options to build selected targets. (#251) 264 | - Improve pre-commit lint script (#258) 265 | - Fix warnings in switch case (#259) 266 | 267 | ## v0.0.29 268 | 269 | - Fix targets 270 | 271 | ## v0.0.28 272 | 273 | - Stricter es6 targets to avoid transforming async/await (#240) 274 | 275 | ## v0.0.27 276 | 277 | - Report coverage using src (#209) 278 | 279 | ## v0.0.26 280 | 281 | - Update publish script for unirepo (#201) 282 | 283 | ## v0.0.25 284 | 285 | - Bump webpack-bundle-analyzer version (#192) 286 | - Update Lerna version (#197) 287 | 288 | ## v0.0.24 289 | 290 | - Only install one copy of chromium (#176) 291 | 292 | ## v0.0.23 293 | 294 | - fix module aliasing for probe.gl (#158) 295 | 296 | ## v0.0.22 297 | 298 | - Expose BrowserTestRunner configurations (#155) 299 | 300 | ## v0.0.21 301 | 302 | - Remove hard reference of reify (#154) 303 | 304 | ## v0.0.20 305 | 306 | - Fix build error when package.json is missing (#152) 307 | 308 | ## v0.0.19 309 | 310 | - Upgrade to probe.gl@3.0.0 311 | 312 | ## v0.0.18 313 | 314 | - Fix lint script (prettier error check) 315 | 316 | ## v0.0.17 317 | 318 | - Monorepo publish now force publishes all packages, and uses exact dependencies 319 | 320 | ## v0.0.16 321 | 322 | - Update lint script (#121) 323 | - update publish script for unirepo (#122) 324 | 325 | ## v0.0.15 326 | 327 | - Fix script shebangs (#120) 328 | 329 | ## v0.0.14 330 | 331 | - Add auto aliases to module/test (#117) 332 | 333 | ## v0.0.13 334 | 335 | - remove babel config override logic (#116) 336 | - allow configs to be used outside of package root (#115) 337 | 338 | ## v0.0.12 339 | 340 | - Fix bootstrap script 341 | 342 | ## v0.0.11 343 | 344 | - Run coverage on ci (#112) 345 | - Support babel/webpack config files in package (#113) 346 | - Update documentation (#114) 347 | 348 | ## v0.0.10 349 | 350 | - Move reify to peerDependency 351 | 352 | ## v0.0.8 353 | 354 | - Fix publish script (#106) 355 | - use prettier on markdowns (#102) 356 | - add config system (#104) 357 | - Add test harness (#105) 358 | - Fix metrics collection (#107) 359 | - Add user config for lint (#108) 360 | - Fix coverage script (#111) 361 | 362 | ## v0.0.7 363 | 364 | - Fix build script in Linux (#87) 365 | - Remove puppeteer dependency (#88) 366 | 367 | ## v0.0.6 368 | 369 | - Support monorepo in build & clean scripts (#86) 370 | - Expose bootstrap script; consolidate monorepo scripts (#85) 371 | 372 | ## v0.0.5 373 | 374 | - Remove transform-builtin-extend from common babel.config.js 375 | -------------------------------------------------------------------------------- /modules/dev-tools/README.md: -------------------------------------------------------------------------------- 1 | # ocular-dev-tools 2 | 3 | Experimental dev tools for our open source Javascript frameworks 4 | 5 | For documentation, see [https://uber-web.github.io/ocular] 6 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/README.md: -------------------------------------------------------------------------------- 1 | # ocular-dev-tools 2 | 3 | Dev tools for vis.gl open source Javascript frameworks 4 | 5 | Contains developer targets for building, cleaning, linting, testing and publishing frameworks. 6 | 7 | * The testing script has a number of modes, it can run tests on both browser and node, it can run test on src or built distributions etc. 8 | * The linting feature supports both code and markdown, and runs both eslint and prettier. 9 | * Supports both single module repos (all code in src) and monorepos (code in `modules//src`). 10 | 11 | Note: flow is not currently integrated into ocular-dev-tools as we restrict its use to React related code bases. 12 | 13 | ## Covered tools 14 | 15 | ocular installs the necessary dependencies and provides working default configurations for 16 | 17 | - eslint 18 | - prettier 19 | - ts-node 20 | - vite 21 | 22 | Note that this list may grow over time. 23 | 24 | ## Installation 25 | 26 | ```bash 27 | yarn add ocular-dev-tools 28 | ``` 29 | 30 | Your `package.json` should looks something like: 31 | 32 | ```json 33 | "devDependencies": { 34 | "ocular-dev-tools": "^2.0.0-alpha", 35 | "puppeteer": "^22.0.0" 36 | } 37 | ``` 38 | 39 | After installing you can set up your build scripts in package.json as follows: 40 | 41 | ```json 42 | "scripts": { 43 | "bootstrap": "yarn & ocular-bootstrap", 44 | "build": "ocular-clean && ocular-build", 45 | "lint": "ocular-lint", 46 | "metrics": "ocular-metrics", 47 | "publish": "ocular-publish", 48 | "test": "ocular-test" 49 | }, 50 | ``` 51 | 52 | ## Usage 53 | 54 | ### Command Line Tools 55 | 56 | | Typical Build Script | Ocular Script | Description | 57 | | --- | --- | --- | 58 | | [`ocular-bootstrap`](docs/dev-tools/cli/ocular-bootstrap) | `bootstrap` | Install dependencies for monorepos | 59 | | [`ocular-clean`](docs/dev-tools/cli/ocular-clean) | `clean` | Remove all transpiled files in preparation for a new build. | 60 | | [`ocular-build`](docs/dev-tools/cli/ocular-build) | `build` | Transpile all modules. | 61 | | [`ocular-lint`](docs/dev-tools/cli/ocular-lint) | `lint` | Run eslint & prettier on the code base. | 62 | | [`ocular-test`](docs/dev-tools/cli/ocular-test) | `test` | Run tests. | 63 | | [`ocular-metrics`](docs/dev-tools/cli/ocular-metrics) | `metrics` | Bundle the source and report the bundle size. | 64 | | [`ocular-publish`](docs/dev-tools/cli/ocular-publish) | `publish` | Publish the packages, create git tag and push. | 65 | 66 | 67 | ### Configuration 68 | 69 | To provide maximum control to the user, ocular build scripts use config files in the framework repo. In cases where such files allow for importing other templates, ocular provides exports that can be used, if not it provides a template that the user can copy into the frameworks root directory. 70 | 71 | #### .ocularrc.js 72 | 73 | A file `.ocularrc.js` can be placed at the root of the package to customize the dev scripts. The config file may export a JSON object that contains the following keys, or a callback function that returns such object: 74 | 75 | - `esm` (Boolean) - set if tests should run using Node.js's ES module resolution. By default `true` if and only if `type: "module"` is found in the root package.json. 76 | - `lint` - options to control eslint behavior 77 | + `paths` (Arrray) - directories to include when linting. Default `['modules', 'src']` 78 | + `extensions` (Array) - file extensions to include when linting. Default `['js', 'md']` 79 | - `babel` - options to control babel behavior 80 | + `extensions` - List of file extensions (prefixed with `.`) that `babel` will process. Default `['.es6', '.js', '.es', '.jsx', '.mjs']` 81 | - `aliases` (Object) - Module aliases to use in tests. Any import from a submodule is mapped to its source. Use this object to define additional mappings, for example `"test-data": "./test/sample-data`. 82 | - `nodeAliases` (Object) - Module aliases to use in node tests only. 83 | - `typescript` 84 | + `project` (String) - path to the project's tsconfig 85 | - `bundle` - options to control esbuild behavior 86 | + `target` (String) 87 | + `globalName` (String) 88 | + `format` (String) - one of `cjs`, `esm`, `umd`, `iife` 89 | + `externals` (String[]) 90 | + `globals` (Object) - import package from global variable. 91 | - `entry` (Object) - entry points for tests. 92 | + `test` (String) - unit test entry point. Can be a `.js` or `.ts` file. Default `./test/index.ts`. 93 | + `test-browser` (String) - unit test browser entry point. Can be a `.js`, `.ts` or `.html` file. Default `./test/browser.ts`. 94 | + `bench` (String) - benchmark entry point. Can be a `.js` or `.ts` file. Default `./test/bench/index.ts`. 95 | + `bench-browser` (String) - benchmark browser entry point. Can be a `.js`, `.ts` or `.html` file. Default `./test/bench/browser.ts`. 96 | + `size` (String | String[]) - metrics entry point(s). Can be a `.js` or `.ts` file. Default `./test/size.ts`. 97 | - `browserTest` (Object) - options for browser tests. Passed to [BrowserTestDriver.run](https://uber-web.github.io/probe.gl/#/documentation/api-reference-testing/browsertestdriver). 98 | 99 | 100 | #### babel 101 | 102 | You may extend the default eslint config with a `.babelrc.js` or `babel.config.js` at the project root: 103 | 104 | ```js 105 | // .babelrc.js 106 | const {getBabelConfig} = require('ocular-dev-tools/configuration'); 107 | 108 | module.exports = getBabelConfig({ 109 | react: true, 110 | // specify custom configs 111 | overrides: { 112 | overrides: [ 113 | // babel overrides api 114 | ] 115 | } 116 | }); 117 | ``` 118 | 119 | #### eslint 120 | 121 | You may extend the default eslint config with a `.eslintrc.js` or `eslint.config.js` at the project root: 122 | 123 | ```js 124 | // .eslintrc.js 125 | const {getEslintConfig} = require('ocular-dev-tools/configuration'); 126 | 127 | module.exports = getEslintConfig({ 128 | react: '18.0', 129 | // specify custom configs 130 | overrides: {} 131 | }); 132 | ``` 133 | 134 | #### prettier 135 | 136 | You may extend the default eslint config with a `.prettier.js` or `prettier.config.js` at the project root: 137 | 138 | ```js 139 | // .prettier.js 140 | const {getPrettierConfig} = require('ocular-dev-tools/configuration'); 141 | 142 | module.exports = getPrettierConfig({ 143 | // specify custom configs 144 | overrides: {} 145 | }); 146 | ``` 147 | 148 | #### vite 149 | 150 | If `vite.config.js` is found at the root of the package, it is used to bundle units tests and benchmark tests for the browser. Otherwise, a default vite config is used. 151 | 152 | 153 | ## ESM Repo 154 | 155 | ocular-dev-tools v2.0 can be used in a ESM repo. When enabled, all imports/exports are handled with Node.js's native [ESM](https://nodejs.org/api/esm.html#introduction) support, instead of being transpiled to commonjs. 156 | 157 | To enable ESM mode: 158 | 159 | - Add `type: 'module'` to the root `package.json` and each submodule's `package.json`s. 160 | - Add `compilerOptions.module: 'esnext'` to `tsconfig.json`. 161 | - ES5-style `require()` and `module.exports` must be removed from all `.js` files. Some dev dependencies, for example babel and eslint, may not support ESM syntax. In this case, rename the config files to use the `.cjs` extension so that they can be imported successfully. 162 | - When importing directly from a non-TypeScript file, the file extension must be specified. E.g. `import './init'` now becomes `import './init.js'`. 163 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/api-reference/get-babel-config.md: -------------------------------------------------------------------------------- 1 | # getBabelConfig 2 | 3 | Create a `ocular-dev-tools` default babel config. 4 | 5 | ## Usage 6 | 7 | ```js 8 | // babel.config.js 9 | const {getBabelConfig} = require('ocular-dev-tools/configuration'); 10 | 11 | module.exports = getBabelConfig({ 12 | /** Enable React preset */ 13 | react: true, 14 | /** This will be deep merged with the default config */ 15 | overrides: { 16 | plugins: [ 17 | // custom plugins 18 | ] 19 | }, 20 | /** Print full config JSON for inspection */ 21 | debug: true 22 | }); 23 | ``` 24 | 25 | ## Environments 26 | 27 | The following environments may be used by various commands: 28 | 29 | - `es5` - default commonjs entry point for non-ESM module used by `ocular-build` 30 | - `esm` - default ESM entry point for non-ESM module used by `ocular-build` 31 | - `esm-strict` - default ESM entry point for ESM module used by `ocular-build` 32 | - `bundle` - production bundle settings used by `ocular-bundle` 33 | - `bundle-dev` - developer bundle settings used by `ocular-bundle --env=dev` 34 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/api-reference/get-eslint-config.md: -------------------------------------------------------------------------------- 1 | # getEslintConfig 2 | 3 | Create a `ocular-dev-tools` default eslint config. 4 | 5 | ## Usage 6 | 7 | ```js 8 | // .eslintrc.js 9 | const {getESlintConfig} = requre('ocular-dev-tools/configuration'); 10 | 11 | modules.export = getESlintConfig({ 12 | /** Set React version, if any */ 13 | react: '18.0.0', 14 | /** This will be deep merged with the default config */ 15 | overrides: { 16 | parserOptions: { 17 | project: ['./tsconfig.json'] 18 | }, 19 | rules: { 20 | // custom rules 21 | } 22 | }, 23 | /** Print full config JSON for inspection */ 24 | debug: true 25 | }); 26 | ``` 27 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/api-reference/get-prettier-config.md: -------------------------------------------------------------------------------- 1 | # getPrettierConfig 2 | 3 | Get `ocular-dev-tools` default prettier config. 4 | 5 | ## Usage 6 | 7 | ```js 8 | // .prettierrc.js 9 | const {getPrettierConfig} = requre('ocular-dev-tools/configuration'); 10 | 11 | modules.export = getPrettierConfig({ 12 | /** This will be deep merged with the default config */ 13 | overrides: { 14 | // custom config 15 | }, 16 | /** Print full config JSON for inspection */ 17 | debug: true 18 | }); 19 | ``` 20 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-bootstrap.md: -------------------------------------------------------------------------------- 1 | # ocular-bootstrap 2 | 3 | Installing dependencies for a monorepo. 4 | 5 | 6 | ## Remarks 7 | 8 | Uses yarn workspaces and lerna to initialize monorepos. 9 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-build.md: -------------------------------------------------------------------------------- 1 | # ocular-build 2 | 3 | Build the source. 4 | 5 | ```bash 6 | ocular-build [module_name] 7 | ``` 8 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-bump.md: -------------------------------------------------------------------------------- 1 | # ocular-bump 2 | 3 | ```bash 4 | ocular-bump [package_name] 5 | ocular-bump [package_name]=beta 6 | ocular-bump [package_name]=[target_version] 7 | ``` 8 | This script helps replace packages with specified version inside a repo. Replace the dependency with the target version of all the `package.json` files under directories `root`. 9 | 10 | For monerepos, you can use the package name to bump all the modules to the corresponding version. 11 | 12 | Given a `package-name`, `modules` to bump are the results from `npm search [package-name]` 13 | 14 | - deck.gl: `ocular-bump deck.gl` or `ocular-bump deck.gl=beta` 15 | - luma.gl: `ocular-bump luma.gl` or `ocular-bump luma.gl=beta` 16 | - math.gl: `ocular-bump math.gl` or `ocular-bump math.gl=beta` 17 | - probe.gl: `ocular-bump probe.gl` or `ocular-bump probe.gl=beta` 18 | 19 | ## Examples 20 | 21 | ```shell script 22 | 23 | # deck.gl, luma.gl, math.gl, probe.gl 24 | ocular-bump luma.gl # default to latest 25 | ocular-bump luma.gl=latest 26 | ocular-bump deck.gl=beta luma.gl=beta 27 | ocular-bump math.gl=3.0.0 28 | 29 | # other package 30 | ocular-bump babel-loader # =latest, =beta, =1.0.0 31 | ``` 32 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-clean.md: -------------------------------------------------------------------------------- 1 | # ocular-clean 2 | 3 | Remove all transpiled files in preparation for a new build. 4 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-lint.md: -------------------------------------------------------------------------------- 1 | # ocular-lint 2 | 3 | Run eslint & prettier on the code base. 4 | 5 | ```bash 6 | ocular-lint [mode] 7 | ``` 8 | 9 | ## Modes 10 | 11 | - `full` (default) - run on all files. 12 | - `pre-commit` - only run on changed files since the last commit. 13 | - `fix` - run prettier and eslint --fix on all files. 14 | 15 | ## Configuration 16 | 17 | [Configurations](#ocular-dev-tools-1): `lint` 18 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-metrics.md: -------------------------------------------------------------------------------- 1 | # ocular-metrics 2 | 3 | Bundle the source and report the bundle size. 4 | 5 | ## Configuration 6 | 7 | [Configurations](#ocular-dev-tools-1): `entry` 8 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-publish.md: -------------------------------------------------------------------------------- 1 | # ocular-publish 2 | 3 | Publish the packages, create git tag and push. 4 | 5 | ## Usage 6 | 7 | This script will usually be mapped to `publish`: 8 | ```bash 9 | yarn publish [mode] [npm-tag] 10 | ``` 11 | 12 | To run it directly 13 | ```bash 14 | npx ocular-publish [mode] [npm-tag] 15 | ``` 16 | 17 | ## mode 18 | 19 | - `beta` - bump pre-release version and publish with beta flag. 20 | - `prod` - bump patch version and publish. 21 | - `version-only-beta` - bump pre-release version only. 22 | - `version-only-prod` - bump patch version only. 23 | - `from-git`: publish from the current git tag. 24 | 25 | ## npm-tag 26 | 27 | Custom tag for the release. If not specified, the release is tagged with `beta` if `mode: beta` and `latest` if `mode: prod`. 28 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-test.md: -------------------------------------------------------------------------------- 1 | # ocular-test 2 | 3 | Run tests. 4 | 5 | ## Usage 6 | 7 | This script will usually be mapped to `test`: 8 | ```bash 9 | yarn test [mode] 10 | ``` 11 | 12 | To run it directly 13 | ```bash 14 | npx ocular-test [mode] 15 | ``` 16 | 17 | ## Modes 18 | 19 | - `full` (default) - run lint, unit tests in node and headless browser 20 | - `fast` - run lint in pre-commit mode, unit tests in node 21 | - `dist` - run unit tests with transpiled (es5) code 22 | - `cover` - run unit tests and generate coverage report 23 | - `ci` - run lint, coverage, metrics and unit tests in headless browser 24 | - `node` - run unit tests in node 25 | - `node-debug` - run unit tests in node debugger 26 | - `browser` - run unit tests in browser (kept open for debugging) 27 | - `browser-headless` - run unit tests in headless browser 28 | - `bench` - run benchmarks in node 29 | - `bench-browser` - run benchmarks in browser (kept open for debugging) 30 | - other - custom mode: 31 | + If -browser is in the mode name, run in browser, otherwise run in node 32 | + If -browser-headless is in the mode name, run in headless browser 33 | + The rest of the name is used to look up the entry point from the entry config. 34 | 35 | ## Configuration 36 | 37 | [Configurations](#ocular-dev-tools-1): `aliases`, `entry` 38 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/cli/ocular-tsify.md: -------------------------------------------------------------------------------- 1 | # ocular-tsify 2 | 3 | Combine type declarations with related source files. 4 | 5 | ## Use 6 | 7 | Given a JavaScript file (or list of files) like so: 8 | 9 | ```bash 10 | npx ocular-tsify ./src/some-file.js 11 | ``` 12 | 13 | Will produce `.ts` files using nearby `.d.ts` files. 14 | 15 | ## Supported syntax 16 | The script parses `.d.ts` file, collects type declarations and runs typescript parser on `.js` file, injects types wherever a `JSDoc` type declaration is available. It supports most of the common `JSDoc` type declarations. 17 | 18 | ### 1. Type imports 19 | 20 | If importing from matching `.d.ts`, it will inline type declaration: 21 | 22 | from 23 | 24 | ```js 25 | /** @typedef {import('./geo-utils').GeoKey} GeoKey */ 26 | ``` 27 | 28 | to 29 | 30 | ```js 31 | export type GeoKey = { 32 | id: string, 33 | label: string, 34 | dataId: string 35 | }; 36 | ``` 37 | 38 | If importing from files other than matching `.d.ts`, will convert to type import. 39 | 40 | from 41 | 42 | ```js 43 | /** @typedef {import('./external-types').GeoState} GeoState */ 44 | ``` 45 | 46 | to 47 | 48 | ```js 49 | import type {GeoState} from './external-types'; 50 | ``` 51 | 52 | ### 2. Functions 53 | 54 | Functions with `JSDoc` type declaration using the `@type {typeof import('./').updater}` will be converted 55 | 56 | From 57 | 58 | ```js 59 | // js 60 | /** @type {typeof import('./reducers').createMapUpdater} */ 61 | const createMapUpdater = (state, action) => state 62 | 63 | // d.ts 64 | export declare function createMapUpdater(state: State, action: Action): State; 65 | ``` 66 | 67 | To 68 | 69 | ```js 70 | export function createMapUpdater(state: State, action: Action): State { 71 | ... 72 | } 73 | ``` 74 | 75 | # Known issues 76 | 77 | Here are some limitations and drawbacks of the scripts 78 | 79 | #### 1. Missing empty line inside functions 80 | Typescript compiler [doesn't preserve empty lines](https://github.com/microsoft/TypeScript/issues/843). When ocular-tsify injects types into functions, it will replace the existing JSDoc comment with an empty line, but empty lines inside the functions will be missing. So are empty lines between variable declarations that does not have JSDoc types. More improvements can be made such as insert empty before return statements. 81 | 82 | #### 2. Mixed import and export statement 83 | 84 | When type declarations are injected, they might come between import statements. Some manual cleanup is required. 85 | 86 | #### 3. Unsupported JSDoc `@returns` `@params` tags 87 | Only type declarations with the `@type` tag are supported. Some functions use separate tags such as below, future work can be done to support it. 88 | ```js 89 | /** 90 | * Currently not supported 91 | * @param {Dataset} dataset 92 | * @param {GeoKey} geoKey 93 | * @returns {Array} layers 94 | */ 95 | function defaultLayersForGeoKey(dataset, geoKey) {...} 96 | ``` 97 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## How to debug ocular builds 4 | 5 | * Set `logLevel` in ocular-config.js, numbers between `1`-`4` give increasing levels of logging. 6 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/upgrade-guide.md: -------------------------------------------------------------------------------- 1 | # Upgrade Guide 2 | 3 | ## ocular-dev-tools 1.0.0 4 | 5 | Functional entry points replace subpath imports 6 | -------------------------------------------------------------------------------- /modules/dev-tools/docs/whats-new.md: -------------------------------------------------------------------------------- 1 | # What's New 2 | 3 | `ocular-dev-tools` release details are available in the [CHANGELOG](https://github.com/uber-web/ocular/blob/master/modules/dev-tools/CHANGELOG.md) 4 | 5 | ### v1.0.0 (alpha) 6 | 7 | Release Date: This release is still in development 8 | 9 | #### Command logging 10 | 11 | The various scripts now log the actual commands they issue, making it easier 12 | to see what ocular is doing under the hood. 13 | 14 | ```sh 15 | dev-tools (ib/log-commands *)$ yarn lint fix 16 | yarn run v1.22.5 17 | $ ocular-lint fix 18 | Running prettier in ./src... 19 | + npx prettier --log-level warn --write './src/**/*.js' '*.js' 20 | Running eslint in ./src... 21 | + npx eslint --fix './src/**/*.js' 22 | Lockfile valid. 23 | ✨ Done in 2.15s. 24 | ``` 25 | 26 | #### **Functional entry points** 27 | 28 | Functional entry points to get ocular default configurations for various build tools are now exported. 29 | 30 | ```js 31 | const {getESLintConfig, deepMerge} = require('ocular-dev-tools'); 32 | 33 | const defaultConfig = getESLintConfig({react: '16.8.2'}); 34 | 35 | // Make any changes to default config here 36 | const config = deepMerge(defaultConfig, { 37 | // your overrides 38 | }); 39 | 40 | module.exports = config; 41 | ``` 42 | 43 | ### **Typescript for function entry points** 44 | 45 | The new entry points have JSDoc and typescript definitions. This enables 46 | better checking of arguments and also allows users to see intellisense 47 | information in vscode etc. 48 | 49 | 50 | ### v0.3.0 51 | 52 | Some release details are available in the [CHANGELOG](https://github.com/uber-web/ocular/blob/master/modules/dev-tools/CHANGELOG.md) 53 | 54 | - `ocular-test node-debug` - New mode - starts node debugger 55 | -------------------------------------------------------------------------------- /modules/dev-tools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocular-dev-tools", 3 | "description": "Dev tools for our Javascript frameworks", 4 | "license": "MIT", 5 | "version": "2.0.0-alpha.35", 6 | "type": "module", 7 | "keywords": [ 8 | "javascript" 9 | ], 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/uber-web/ocular/" 13 | }, 14 | "files": [ 15 | "src", 16 | "scripts", 17 | "templates", 18 | "dist", 19 | "CHANGELOG.md" 20 | ], 21 | "exports": { 22 | ".": { 23 | "types": "./dist/index.d.ts", 24 | "require": "./dist/index.cjs", 25 | "import": "./dist/index.js" 26 | }, 27 | "./configuration": { 28 | "types": "./dist/configuration/index.d.ts", 29 | "require": "./dist/configuration/index.cjs", 30 | "import": "./dist/configuration/index.js" 31 | }, 32 | "./ts-transform-version-inline": { 33 | "require": "./dist/ts-plugins/ts-transform-version-inline/index.cjs", 34 | "import": "./dist/ts-plugins/ts-transform-version-inline/index.js" 35 | }, 36 | "./ts-transform-append-extension": { 37 | "require": "./dist/ts-plugins/ts-transform-append-extension/index.cjs", 38 | "import": "./dist/ts-plugins/ts-transform-append-extension/index.js" 39 | }, 40 | "./ts-transform-remove-glsl-comments": { 41 | "require": "./dist/ts-plugins/ts-transform-remove-glsl-comments/index.cjs", 42 | "import": "./dist/ts-plugins/ts-transform-remove-glsl-comments/index.js" 43 | }, 44 | "./ts-transform-inline-webgl-constants": { 45 | "require": "./dist/ts-plugins/ts-transform-inline-webgl-constants/index.cjs", 46 | "import": "./dist/ts-plugins/ts-transform-inline-webgl-constants/index.js" 47 | } 48 | }, 49 | "types": "./dist/index.d.ts", 50 | "main": "./dist/index.js", 51 | "bin": { 52 | "ocular-bootstrap": "./scripts/bootstrap.js", 53 | "ocular-build": "./scripts/build.js", 54 | "ocular-bundle": "./scripts/bundle.js", 55 | "ocular-bump": "./scripts/bump.js", 56 | "ocular-clean": "./scripts/clean.js", 57 | "ocular-lint": "./scripts/lint.js", 58 | "ocular-metrics": "./scripts/metrics.js", 59 | "ocular-publish": "./scripts/publish.js", 60 | "ocular-test": "./scripts/test.js" 61 | }, 62 | "scripts": { 63 | "build": "(cd ../.. && ocular-build)", 64 | "publish-prod": "npm run build && npm publish", 65 | "publish-beta": "npm run build && npm publish --tag beta" 66 | }, 67 | "dependencies": { 68 | "@babel/cli": "^7.14.5", 69 | "@babel/core": "^7.14.5", 70 | "@babel/eslint-parser": "^7.14.5", 71 | "@babel/plugin-transform-runtime": "^7.14.5", 72 | "@babel/preset-env": "^7.14.5", 73 | "@babel/preset-react": "^7.14.5", 74 | "@babel/preset-typescript": "^7.14.5", 75 | "@babel/runtime": "7.14.5", 76 | "@esbuild-plugins/node-globals-polyfill": "^0.2.0", 77 | "@esbuild-plugins/node-modules-polyfill": "^0.2.0", 78 | "@probe.gl/test-utils": "^4.0.6", 79 | "babel-plugin-version-inline": "^1.0.0", 80 | "c8": "^7.12.0", 81 | "coveralls": "^3.0.3", 82 | "deepmerge": "^4.2.2", 83 | "esbuild": "^0.16.7", 84 | "esbuild-plugin-external-global": "^1.0.1", 85 | "eslint": "^8.52.0", 86 | "eslint-config-prettier": "^8.0.0", 87 | "eslint-plugin-babel": "^5.3.1", 88 | "eslint-plugin-import": "^2.28.0", 89 | "eslint-plugin-jsx-a11y": "^6.1.2", 90 | "eslint-plugin-markdown": "^2.2.0", 91 | "eslint-plugin-react": "^7.22.0", 92 | "eslint-plugin-react-hooks": "^4.0.0", 93 | "glob": "^7.1.4", 94 | "lerna": "^8.1.0", 95 | "minimatch": "^3.0.0", 96 | "prettier": "^3.2.0", 97 | "prettier-check": "2.0.0", 98 | "tape": "^4.11.0", 99 | "tape-promise": "^4.0.0", 100 | "tap-spec": "^5.0.0", 101 | "typescript": "^5.2.2", 102 | "typescript-eslint": "^7.7.0", 103 | "ts-node": "~10.9.0", 104 | "ts-patch": "^3.1.2", 105 | "tsconfig-paths": "^4.1.1", 106 | "vite": "^4.5.0" 107 | }, 108 | "devDependencies": { 109 | "puppeteer": "^22.0.0" 110 | }, 111 | "engines": { 112 | "node": ">= 18" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../bootstrap.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to bootstrap repo for development 3 | 4 | set -e 5 | 6 | # install dependencies 7 | yarn 8 | 9 | # prepare module directories 10 | PACKAGE_DIR=`pwd` 11 | ROOT_NODE_MODULES_DIR=$PACKAGE_DIR/node_modules 12 | 13 | if [ -d "modules" ]; then 14 | # monorepo 15 | cd modules 16 | for D in *; do ( 17 | [ -d $D ] 18 | cd $D 19 | 20 | # create symlink to dev dependencies at root 21 | # this is a bug of yarn: https://github.com/yarnpkg/yarn/issues/4964 22 | # TODO - remove when fixed 23 | mkdir -p node_modules 24 | rm -rf ./node_modules/.bin 25 | ln -sf $ROOT_NODE_MODULES_DIR/.bin ./node_modules 26 | ); done 27 | 28 | cd $PACKAGE_DIR 29 | else 30 | packageName=`node -e "console.log(require('./package.json').name)"` 31 | yarn link 32 | yarn link $packageName 33 | fi 34 | 35 | # build the submodules 36 | npm run build 37 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/build.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../build.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | DEV_TOOLS_DIR=$(dirname $0)/.. 6 | CONFIG=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".babel.configPath"` 7 | MODULES=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".modules" | sed -E "s/,/ /g"` 8 | EXTENSIONS=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".babel.extensions"` 9 | TS_PROJECT=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".typescript.project"` 10 | IS_ESM=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".esm"` 11 | 12 | check_target() { 13 | if [[ ! "$1" =~ ^es5|esm ]]; then 14 | echo -e "\033[91mUnknown build target $1. ocular-build [--dist es5|esm,...] [module1,...]\033[0m" 15 | exit 1 16 | fi 17 | } 18 | 19 | build_src() { 20 | OUT_DIR=$1 21 | TARGET=$2 22 | check_target $TARGET 23 | 24 | if [ ! -z "$CONFIG" ]; then 25 | (set -x; BABEL_ENV=$TARGET npx babel src --config-file $CONFIG --out-dir $OUT_DIR --copy-files --source-maps --extensions $EXTENSIONS) 26 | fi 27 | } 28 | 29 | build_module_esm() { 30 | build_src dist esm-strict 31 | node $DEV_TOOLS_DIR/dist/build-cjs.js 32 | } 33 | 34 | build_module() { 35 | if [ -z "$1" ]; then 36 | TARGETS="esm es5" 37 | else 38 | TARGETS=$* 39 | fi 40 | N=`echo "$TARGETS" | wc -w` 41 | if [ $N -eq 1 ]; then 42 | build_src dist $TARGETS 43 | else 44 | for T in ${TARGETS}; do( 45 | build_src dist/$T $T 46 | ); done 47 | fi 48 | } 49 | 50 | build_unirepo() { 51 | if [ "$IS_ESM" = "true" ]; then 52 | build_module_esm 53 | else 54 | build_module 55 | fi 56 | } 57 | 58 | build_monorepo() { 59 | while [ -n "$1" ]; do 60 | if [[ "$1" =~ ^\-\-[A-Za-z]+ ]]; then 61 | case "$1" in 62 | --dist) 63 | TARGET=$2 64 | shift ;; 65 | *) 66 | echo -e "\033[91mUnknown option $1. ocular-build [--dist es5|esm,...] [module1,...]\033[0m" 67 | exit 1 ;; 68 | esac 69 | else 70 | # Build selected modules 71 | # build.sh MODULE1,MODULE2 72 | MODULES=`echo $1 | sed -e 's/,/ modules\//g' | sed -e 's/^/modules\//g'` 73 | fi 74 | shift 75 | done 76 | 77 | if [ -z "$MODULES" ]; then 78 | # Build all modules 79 | MODULES=`find modules -mindepth 1 -maxdepth 1 -not \( -name ".*" \)` 80 | fi 81 | 82 | for D in ${MODULES}; do ( 83 | if [ -e "${D}/package.json" ]; then 84 | echo -e "\033[1mBuilding $D\033[0m" 85 | cd $D 86 | if [ "$IS_ESM" = "true" ]; then 87 | build_module_esm 88 | else 89 | build_module `echo $TARGET | sed -e 's/,/ /g'` 90 | fi 91 | echo "" 92 | elif [ ! -e "${D}" ]; then 93 | echo -e "\033[1mWarning: skipping $D because it doesn't match any file.\033[0m" 94 | echo -e "\033[1mHint: modules must be specified using full path relative to the project root.\033[0m" 95 | echo "" 96 | fi 97 | ); done 98 | } 99 | 100 | if [ ! -z "$TS_PROJECT" ]; then 101 | npx tspc -b $TS_PROJECT --verbose 102 | fi 103 | 104 | if [ -d "modules" ]; then 105 | build_monorepo $* 106 | else 107 | build_unirepo 108 | fi 109 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/bump.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import fs from 'fs'; 4 | import {resolve} from 'path'; 5 | import glob from 'glob'; 6 | import {execSync} from 'child_process'; 7 | 8 | const packageJsonFiles = glob.sync(resolve('**/package.json'), {ignore: '**/node_modules/**'}); 9 | console.log(packageJsonFiles); 10 | 11 | function getVersions(packageName) { 12 | const versions = execSync(`npm v ${packageName} dist-tags --json`, {encoding: 'utf8'}); 13 | return versions ? JSON.parse(versions) : null; 14 | } 15 | 16 | function getTargetVersion(packageAndVersion, moduleName) { 17 | const [, targetVersion] = packageAndVersion; 18 | let version = targetVersion; 19 | if (targetVersion === 'beta' || targetVersion === 'latest') { 20 | const versions = getVersions(moduleName); 21 | version = versions && versions[targetVersion]; 22 | } 23 | return version; 24 | } 25 | 26 | function bumpPackages(packages) { 27 | for (const file of packageJsonFiles) { 28 | let changed = false; 29 | let content = JSON.parse(fs.readFileSync(file, 'utf8')); 30 | const dependencies = content.dependencies || {}; 31 | const devDependencies = content.devDependencies || {}; 32 | const peerDependencies = content.peerDependencies || {}; 33 | 34 | for (const package_ of packages) { 35 | if (dependencies[package_.name]) { 36 | dependencies[package_.name] = `^${package_.version}`; 37 | changed = true; 38 | } 39 | if (devDependencies[package_.name]) { 40 | devDependencies[package_.name] = `^${package_.version}`; 41 | changed = true; 42 | } 43 | if (peerDependencies[package_.name]) { 44 | peerDependencies[package_.name] = `^${package_.version}`; 45 | changed = true; 46 | } 47 | } 48 | 49 | if (changed) { 50 | content = JSON.stringify(content, null, 2); 51 | fs.writeFileSync(file, `${content}\n`); 52 | } 53 | } 54 | } 55 | 56 | function main() { 57 | let packages = []; 58 | const args = process.argv; 59 | if (!args || args.length < 3) { 60 | console.error('Should provide at lease one package.'); 61 | return; 62 | } 63 | 64 | const argLen = args.length; 65 | for (let i = 2; i < argLen; i++) { 66 | const packageAndVersion = args[i].split('='); 67 | if (!packageAndVersion) { 68 | console.error('Should use format "yarn bump package" or "yarn bump package=target_version".'); 69 | return; 70 | } 71 | 72 | // default to latest version 73 | if (packageAndVersion.length === 1) { 74 | packageAndVersion.push('latest'); 75 | } 76 | 77 | const [packageName] = packageAndVersion; 78 | const modules = JSON.parse(execSync(`npm search ${packageName} --json`, {encoding: 'utf8'})); 79 | 80 | if (modules) { 81 | packages = packages.concat( 82 | modules.map(function (module) { 83 | const version = getTargetVersion(packageAndVersion, module.name); 84 | return { 85 | name: module.name, 86 | version 87 | }; 88 | }) 89 | ); 90 | } 91 | } 92 | 93 | if (packages.length) { 94 | bumpPackages(packages); 95 | } 96 | } 97 | 98 | main(); 99 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/bundle.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import esbuild from 'esbuild'; 4 | import {getBundleConfig} from '../dist/configuration/get-esbuild-config.js'; 5 | 6 | // Parse command line arguments 7 | let entryPoint; 8 | const env = {}; 9 | 10 | for (let i = 1; i < process.argv.length; i++) { 11 | const arg = process.argv[i]; 12 | if (arg.startsWith('--')) { 13 | const tokens = arg.slice(2).split('='); 14 | env[tokens[0]] = tokens[1] === undefined ? true : tokens[1]; 15 | } else if (!entryPoint && arg.match(/\.(js|ts|cjs|mjs|jsx|tsx)$/)) { 16 | entryPoint = arg; 17 | } 18 | } 19 | 20 | run(); 21 | 22 | async function run() { 23 | const buildConfig = await getBundleConfig({ 24 | ...env, 25 | input: entryPoint 26 | }); 27 | 28 | if (env.watch) { 29 | buildConfig.watch = true; 30 | await esbuild.build(buildConfig); 31 | /* eslint-disable no-console */ 32 | console.log('watching...'); 33 | } else { 34 | const result = await esbuild.build(buildConfig); 35 | if (result.errors.length > 0) { 36 | process.exit(1); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/clean.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../clean.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | clean() { 6 | if [ -z "$1" ]; then 7 | (set -x; rm -fr dist && mkdir -p dist) 8 | elif [ "$1" = "all" ]; then 9 | (set -x; rm -fr dist) 10 | else 11 | echo -e "\033[91mUnknown option $1. ocular-clean [all]\033[0m" 12 | exit 1 13 | fi 14 | } 15 | 16 | if [ -d "modules" ]; then 17 | # Monorepo 18 | cd modules 19 | 20 | for D in *; do ( 21 | cd $D 22 | clean $1 23 | ); done 24 | else 25 | clean $1 26 | fi 27 | 28 | find . -name tsconfig.tsbuildinfo -exec rm {} \; 29 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/lint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../lint.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to check code styles 3 | 4 | set -e 5 | 6 | # Lint selected directories 7 | # lint.sh DIR1,DIR2 8 | MODE=$1 9 | 10 | DEV_TOOLS_DIR=$(dirname $0)/.. 11 | 12 | DIRECTORIES=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".lint.paths"` 13 | if [[ $DIRECTORIES == *","* ]]; then 14 | DIRECTORIES={$DIRECTORIES} 15 | fi 16 | 17 | EXTENSIONS=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".lint.extensions"` 18 | if [[ $EXTENSIONS == *","* ]]; then 19 | EXTENSIONS={$EXTENSIONS} 20 | fi 21 | 22 | DIR_PATTERN="$DIRECTORIES/**/*.$EXTENSIONS" 23 | ROOT_PATTERN="*.$EXTENSIONS" 24 | 25 | usage() { 26 | # TODO: Add more specific url 27 | open "https://uber-web.github.io/ocular/docs/dev-tools/cli/ocular-lint" 28 | } 29 | 30 | print_yellow() { 31 | echo -e "\033[93m${1}\033[0m" 32 | } 33 | 34 | case $MODE in 35 | "help") 36 | usage 37 | ;; 38 | 39 | "pre-commit") 40 | print_yellow "Running prettier & eslint on changed files..." 41 | 42 | NAME_PATTERN=`echo "^$DIRECTORIES/.*\.$EXTENSIONS$" | sed -e 's/,/|/g' | sed -e 's/{/(/g' | sed -e 's/}/)/g'` 43 | 44 | # only check changed files 45 | set +e 46 | FILES=`git diff HEAD --name-only | grep -E "${NAME_PATTERN}"` 47 | set -e 48 | 49 | FILES_LIST="" 50 | 51 | if [ ! -z "${FILES}" ]; then 52 | for f in $FILES 53 | do 54 | if [ -e "${f}" ]; then 55 | FILES_LIST+="${f} " 56 | fi 57 | done 58 | 59 | (set -x; npx prettier-check $FILES_LIST) 60 | (set -x; npx eslint $FILES_LIST) 61 | fi 62 | ;; 63 | 64 | "fix") 65 | print_yellow "Running eslint in $DIRECTORIES..." 66 | (set -x; npx eslint --fix "$DIRECTORIES/**/*.$EXTENSIONS") 67 | 68 | print_yellow "Running prettier in $DIRECTORIES..." 69 | (set -x; npx prettier --log-level warn --write "$DIR_PATTERN" "$ROOT_PATTERN") 70 | ;; 71 | 72 | *) 73 | print_yellow "Running eslint in $DIRECTORIES..." 74 | (set -x; npx eslint "$DIRECTORIES/**/*.$EXTENSIONS") 75 | 76 | print_yellow "Checking prettier code style in $DIRECTORIES..." 77 | (set -x; npx prettier-check "$DIR_PATTERN" "$ROOT_PATTERN" || 78 | (echo -e "\033[91mNot all files using prettier code style. This may be fixed by running\033[0m \033[1mnpm run lint fix\033[0m" && 79 | exit 1)) 80 | 81 | ;; 82 | esac 83 | 84 | # check if yarn.lock contains private registry information 85 | !(grep -q unpm.u yarn.lock) && echo "Lockfile valid." || (echo -e "\033[91mPlease rebuild yarn file using public npmrc\033[0m" && false) 86 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/metrics.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../metrics.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/metrics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to collect build size information 3 | 4 | # set -ex 5 | 6 | export PATH=$PATH:node_modules/.bin 7 | 8 | DEV_TOOLS_DIR=$(dirname $0)/.. 9 | WORKING_DIR=`pwd` 10 | TMP_DIR=$WORKING_DIR/tmp 11 | 12 | # Get size metric entry point 13 | ENTRY_POINTS=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".entry.size"` 14 | IFS=',' 15 | read -a ENTRY_POINTS_ARR <<< "$ENTRY_POINTS" 16 | IFS=' ' 17 | 18 | # Get name from package.json 19 | module=`node -e "console.log(require('./package.json').name)"` 20 | # Get version 21 | if [ -d "modules" ]; then 22 | # Use lerna version if monorepo 23 | packageInfo=./lerna.json 24 | else 25 | # Use package.json version if monorepo 26 | packageInfo=./package.json 27 | fi 28 | # Get version from packag.json and remove quotes 29 | version=`node -e "console.log(require('${packageInfo}').version)"` 30 | 31 | # Helper functions 32 | 33 | print_size_header() { 34 | echo -e "\033[1m| Version | Dist | Bundle Size | Compressed | Imports |\033[0m" 35 | echo "| --- | --- | --- | --- | --- |" 36 | } 37 | 38 | print_size() { 39 | DIST=$1 40 | cd $TMP_DIR 41 | 42 | for f in *; do ( 43 | # Size it 44 | size=$(wc -c $f | awk '{ print int($1 / 1024) "KB (" $1 ")" }') 45 | # Zip it 46 | gzip -9f $f 47 | # Size it again 48 | zipsize=$(wc -c $f.gz | awk '{ print int($1 / 1024) "KB (" $1 ")" }') # Size it 49 | # Remove our copy 50 | rm $f.gz 51 | # Print version, size, compressed size with markdown 52 | 53 | echo "| $version | $DIST | $size KB | $zipsize KB | $f " 54 | ); done 55 | 56 | cd $WORKING_DIR 57 | } 58 | 59 | build_bundle() { 60 | if [ "$2" == es5 ]; then 61 | DIST="main,module" 62 | else 63 | DIST="module,browser,main" 64 | fi 65 | (npx esbuild $1 --outdir="$TMP_DIR" --bundle --minify --main-fields=$DIST --log-level=error) 66 | } 67 | 68 | # Main Script 69 | 70 | echo 71 | echo -e "\033[93mAutomatically collecting metrics for $module\033[0m" 72 | echo 73 | 74 | rm -rf $TMP_DIR 75 | mkdir $TMP_DIR 76 | 77 | print_size_header 78 | 79 | for f in "${ENTRY_POINTS_ARR[@]}"; do 80 | build_bundle $f es5 81 | done 82 | print_size es5 83 | 84 | for f in "${ENTRY_POINTS_ARR[@]}"; do 85 | build_bundle $f esm 86 | done 87 | print_size esm 88 | 89 | rm -rf $TMP_DIR 90 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/publish.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../publish.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Script to publish modules 3 | 4 | set -e 5 | 6 | usage() { 7 | # TODO: Add more specific url 8 | open "https://uber-web.github.io/ocular/docs/dev-tools/cli/ocular-publish" 9 | } 10 | 11 | # beta, prod, version-only-beta, version-only-prod or from-git 12 | MODE=$1 13 | 14 | bumpVersion() { 15 | local versionType 16 | if [[ $1 == "beta" ]]; then 17 | versionType=prerelease 18 | else 19 | versionType=patch 20 | fi 21 | 22 | if [ -d "modules" ]; then 23 | (set -x; npx lerna version $versionType --force-publish --exact --no-commit-hooks) 24 | else 25 | # -f includes any changes in the version commit 26 | (set -x; npm version $versionType --force) 27 | # push to branch 28 | (set -x; git push && git push --tags) 29 | fi 30 | } 31 | 32 | publishToNPM() { 33 | local tag=$1 34 | if [ -d "modules" ]; then 35 | if [ -z $tag ]; then 36 | # use default tag ('latest' or publishConfig.tag in package.json) 37 | (set -x; npx lerna publish from-package --force-publish --yes --no-commit-hooks) 38 | else 39 | (set -x; npx lerna publish from-package --force-publish --yes --dist-tag $tag --no-commit-hooks) 40 | fi 41 | else 42 | if [ -z $tag ]; then 43 | (set -x; npm publish) 44 | else 45 | (set -x; npm publish --tag $tag) 46 | fi 47 | fi 48 | } 49 | 50 | if [[ $MODE != "version-only-"* && $MODE != "help" ]]; then 51 | # will build and publish to NPM 52 | ocular-bootstrap 53 | ocular-test 54 | ocular-test dist 55 | else 56 | # When a dependency change is cherry-picked between branches the lock file may not merge correctly 57 | # Refresh the lock file so that a release can be built from a clean state 58 | yarn 59 | fi 60 | git add yarn.lock 61 | 62 | case $MODE in 63 | "help") 64 | usage 65 | ;; 66 | 67 | "version-only-beta") 68 | bumpVersion beta 69 | ;; 70 | 71 | "version-only-prod") 72 | bumpVersion prod 73 | ;; 74 | 75 | "beta") 76 | bumpVersion beta 77 | publishToNPM ${2:-beta} 78 | ;; 79 | 80 | "prod") 81 | bumpVersion prod 82 | publishToNPM $2 83 | ;; 84 | 85 | "from-git") 86 | # publish from existing tag 87 | gitTag=$(git describe --tags) 88 | if [[ $gitTag == *"-"* ]]; then 89 | publishToNPM ${2:-beta} 90 | else 91 | publishToNPM $2 92 | fi 93 | ;; 94 | 95 | *) 96 | echo -e "\033[91mUnknown publish mode. ocular-publish ['prod' | 'beta' | 'version-only-prod' | 'version-only-beta' | 'from-git']\033[0m" 97 | exit 1;; 98 | esac 99 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/shell.js: -------------------------------------------------------------------------------- 1 | import {execSync} from 'child_process'; 2 | 3 | export function execShellCommand(command, args = []) { 4 | try { 5 | execSync(`${command} ${args.join(' ')}`, { 6 | stdio: 'inherit' 7 | }); 8 | } catch (err) { 9 | process.exit(err.status); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/test.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import {execShellCommand} from './shell.js'; 4 | import {join} from 'path'; 5 | 6 | const scriptDir = new URL(import.meta.url).pathname; 7 | // Runs the bash script and forward the arguments, exiting with the same code 8 | execShellCommand(join(scriptDir, '../test.sh'), process.argv.slice(2)); 9 | -------------------------------------------------------------------------------- /modules/dev-tools/scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Automated tests 3 | 4 | set -e 5 | 6 | BASEDIR=$(dirname "$0") 7 | 8 | MODE=$1 9 | 10 | DEV_TOOLS_DIR=$(dirname $0)/.. 11 | TEST_SCRIPT=$DEV_TOOLS_DIR/dist/test.js 12 | COVERAGE_TEST=`node $DEV_TOOLS_DIR/dist/helpers/get-config.js ".coverage.test"` 13 | 14 | usage() { 15 | # TODO: Add more specific url 16 | open "https://uber-web.github.io/ocular/docs/dev-tools/cli/ocular-test" 17 | } 18 | 19 | run_test_script() { 20 | (set -x; NODE_ENV=test node $TEST_SCRIPT $1) 21 | } 22 | 23 | run_test_script_pretty() { 24 | run_test_script $1 | tap-spec 25 | } 26 | 27 | generate_coverage_report() { 28 | (set -x; npx c8 report --reporter=text --reporter=lcov) 29 | } 30 | 31 | run_full_test() { 32 | npm run lint 33 | run_test_script_pretty node 34 | run_test_script_pretty browser-headless 35 | ocular-metrics 36 | } 37 | 38 | case $MODE in 39 | "") 40 | echo "test [ 'full' | 'fast' | 'dist' | 'bench' | 'ci' | 'cover' | 'browser' | 'browser-headless' ]" 41 | echo "Running 'full' test by default" 42 | run_full_test 43 | ;; 44 | 45 | "full") 46 | run_full_test 47 | ;; 48 | 49 | "fast") 50 | ocular-lint pre-commit 51 | run_test_script_pretty node 52 | ;; 53 | 54 | "node") 55 | run_test_script_pretty node 56 | ;; 57 | 58 | "node-debug") 59 | echo "Open chrome://inspect/#devices to attach debugger." 60 | (set -x; node $TEST_SCRIPT node-debug) 61 | ;; 62 | 63 | "dist") 64 | run_test_script_pretty dist 65 | ;; 66 | 67 | "cover") 68 | run_test_script_pretty cover 69 | generate_coverage_report 70 | ;; 71 | 72 | "ci") 73 | # run by CI 74 | npm run lint 75 | if [ "$COVERAGE_TEST" == "browser" ]; then 76 | run_test_script_pretty node 77 | else 78 | run_test_script_pretty browser-headless 79 | fi 80 | run_test_script_pretty cover 81 | generate_coverage_report 82 | ocular-metrics 83 | ;; 84 | 85 | "browser-headless") 86 | run_test_script_pretty browser-headless 87 | ;; 88 | 89 | *) 90 | # default test 91 | run_test_script $MODE 92 | ;; 93 | 94 | esac 95 | -------------------------------------------------------------------------------- /modules/dev-tools/src/build-cjs.ts: -------------------------------------------------------------------------------- 1 | import esbuild from 'esbuild'; 2 | import fs from 'fs/promises'; 3 | import {getCJSEntryPoints} from './helpers/get-cjs-entry-points.js'; 4 | import {getCJSExportConfig} from './configuration/get-esbuild-config.js'; 5 | 6 | async function main() { 7 | for (const entry of getCJSEntryPoints()) { 8 | try { 9 | await fs.stat(entry.inputFile); 10 | 11 | const esbuildConfig = await getCJSExportConfig({ 12 | input: entry.inputFile, 13 | output: entry.outputFile 14 | }); 15 | const result = await esbuild.build(esbuildConfig); 16 | if (result.errors.length > 0) { 17 | process.exit(1); 18 | } 19 | } catch { 20 | // File does not exist 21 | console.error(`\x1b[33mCannot find file ${entry.inputFile}\x1b[0m`); 22 | } 23 | } 24 | } 25 | 26 | main(); 27 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es2015/best-practices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "accessor-pairs": [ 4 | 2, 5 | { 6 | "getWithoutSet": true 7 | } 8 | ] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es2015/ecmascript-6.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-class-assign": 2, 4 | "arrow-spacing": [ 5 | 1, 6 | { 7 | "before": true, 8 | "after": true 9 | } 10 | ], 11 | "no-const-assign": 2, 12 | "no-dupe-class-members": 2, 13 | "no-this-before-super": 2, 14 | "no-var": 2, 15 | "prefer-arrow-callback": 0, 16 | "prefer-const": 2, 17 | "prefer-spread": 2, 18 | "prefer-template": 2, 19 | "arrow-parens": 0, 20 | "arrow-spacing": [ 21 | 2, 22 | { 23 | "before": true, 24 | "after": true 25 | } 26 | ], 27 | "constructor-super": 2, 28 | "generator-star-spacing": [ 29 | 2, 30 | { 31 | "before": false, 32 | "after": true 33 | } 34 | ], 35 | "object-shorthand": [ 36 | 2, 37 | "always" 38 | ], 39 | "require-yield": 2 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es2015/eslintrc.json: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | { 21 | "extends": [ 22 | "./best-practices.json", 23 | "./ecmascript-6.json", 24 | "./miscellaneous.json", 25 | "./stylistic-issues.json", 26 | "../eslint-config-uber-es5/eslintrc.json" 27 | ], 28 | "env": { 29 | "es6": true 30 | }, 31 | "parserOptions": { 32 | "sourceType": "module", 33 | "ecmaFeatures": { 34 | "experimentalObjectRestSpread": true 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es2015/miscellaneous.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "prefer-reflect": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es2015/stylistic-issues.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-continue": 2, 4 | "func-style": [ 5 | 2, 6 | "declaration", 7 | { 8 | "allowArrowFunctions": true 9 | } 10 | ], 11 | "lines-around-comment": [ 12 | 0, 13 | { 14 | "beforeBlockComment": true, 15 | "afterBlockComment": false, 16 | "beforeLineComment": false, 17 | "afterLineComment": false, 18 | "allowBlockStart": true, 19 | "allowBlockEnd": true 20 | } 21 | ], 22 | "quote-props": [ 23 | 2, 24 | "as-needed" 25 | ], 26 | "sort-vars": 2, 27 | "wrap-regex": 2 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/best-practices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-alert": 2, 4 | "no-caller": 2, 5 | "no-div-regex": 2, 6 | "no-else-return": 2, 7 | "no-eq-null": 2, 8 | "no-extend-native": 2, 9 | "no-labels": 2, 10 | "no-eval": 2, 11 | "no-extra-bind": 2, 12 | "no-fallthrough": 2, 13 | "no-floating-decimal": 2, 14 | "no-implicit-coercion": 2, 15 | "no-implied-eval": 2, 16 | "no-invalid-this": 2, 17 | "no-iterator": 2, 18 | "no-lone-blocks": 2, 19 | "no-loop-func": 2, 20 | "no-multi-spaces": 2, 21 | "no-multi-str": 2, 22 | "no-new": 2, 23 | "no-new-func": 2, 24 | "no-new-wrappers": 2, 25 | "no-octal": 2, 26 | "no-octal-escape": 2, 27 | "no-param-reassign": 0, 28 | "no-proto": 2, 29 | "no-redeclare": 2, 30 | "no-return-assign": 2, 31 | "no-script-url": 2, 32 | "no-self-compare": 2, 33 | "no-sequences": 2, 34 | "no-throw-literal": 2, 35 | "no-unused-expressions": 2, 36 | "no-useless-call": 2, 37 | "no-void": 1, 38 | "no-warning-comments": [ 39 | 0, 40 | { 41 | "terms": [ 42 | "todo", 43 | "fixme", 44 | "xxx" 45 | ], 46 | "location": "start" 47 | } 48 | ], 49 | "no-with": 2, 50 | "block-scoped-var": 2, 51 | "complexity": [ 52 | 2, 53 | 11 54 | ], 55 | "consistent-return": 2, 56 | "curly": [ 57 | 2, 58 | "all" 59 | ], 60 | "default-case": 2, 61 | "dot-location": [ 62 | 2, 63 | "property" 64 | ], 65 | "dot-notation": [ 66 | 2, 67 | { 68 | "allowKeywords": true 69 | } 70 | ], 71 | "eqeqeq": 2, 72 | "guard-for-in": 2, 73 | "radix": 2, 74 | "vars-on-top": 0, 75 | "wrap-iife": 2, 76 | "yoda": [ 77 | 2, 78 | "never" 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/errors.json: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | // These rules relate to possible syntax or logic errors 22 | // Categorized as "errors" here: http://eslint.org/docs/rules/ 23 | { 24 | "rules": { 25 | /** 26 | * Possible Errors Section from http://eslint.org/docs/rules/: 27 | */ 28 | 29 | // http://eslint.org/docs/rules/no-cond-assign 30 | "no-cond-assign": 2, 31 | 32 | // http://eslint.org/docs/rules/no-constant-condition 33 | "no-constant-condition": 2, 34 | 35 | // http://eslint.org/docs/rules/no-console 36 | // highly agreed upon 37 | "no-console": 2, 38 | 39 | // http://eslint.org/docs/rules/no-control-regex 40 | "no-control-regex": 2, 41 | 42 | // http://eslint.org/docs/rules/no-debugger 43 | // highly agreed upon 44 | "no-debugger": 2, 45 | 46 | // http://eslint.org/docs/rules/no-dupe-args 47 | "no-dupe-args": 2, 48 | 49 | // http://eslint.org/docs/rules/no-dupe-keys 50 | "no-dupe-keys": 2, 51 | 52 | // http://eslint.org/docs/rules/no-duplicate-case 53 | "no-duplicate-case": 2, 54 | 55 | // http://eslint.org/docs/rules/no-empty-character-class 56 | "no-empty-character-class": 2, 57 | 58 | // http://eslint.org/docs/rules/no-empty 59 | // highly agreed upon 60 | "no-empty": 2, 61 | 62 | // http://eslint.org/docs/rules/no-ex-assign 63 | // abnormal behavior in ie 6-8 64 | "no-ex-assign": 2, 65 | 66 | // http://eslint.org/docs/rules/no-extra-boolean-cast 67 | // highly agreed upon 68 | "no-extra-boolean-cast": 2, 69 | 70 | // http://eslint.org/docs/rules/no-extra-parens 71 | "no-extra-parens": [2, "functions"], 72 | 73 | // http://eslint.org/docs/rules/no-extra-semi 74 | // highly agreed upon 75 | "no-extra-semi": 2, 76 | 77 | // http://eslint.org/docs/rules/no-func-assign 78 | "no-func-assign": 2, 79 | 80 | // http://eslint.org/docs/rules/no-inner-declarations 81 | "no-inner-declarations": [2, "functions"], 82 | 83 | "no-invalid-regexp": 2, 84 | 85 | "no-irregular-whitespace": 2, 86 | 87 | "no-obj-calls": 2, 88 | 89 | // http://eslint.org/docs/rules/no-prototype-builtins 90 | "no-prototype-builtins": 0, 91 | 92 | "no-regex-spaces": 2, 93 | 94 | "no-sparse-arrays": 2, 95 | 96 | // http://eslint.org/docs/rules/no-template-curly-in-string 97 | "no-template-curly-in-string": 2, 98 | 99 | "no-unexpected-multiline": 2, 100 | 101 | "no-unreachable": 2, 102 | 103 | // http://eslint.org/docs/rules/no-unsafe-finally 104 | "no-unsafe-finally": 2, 105 | 106 | // http://eslint.org/docs/rules/no-unsafe-negation 107 | "no-unsafe-negation": 2, 108 | 109 | "use-isnan": 2, 110 | 111 | "valid-jsdoc": 0, 112 | 113 | "valid-typeof": 2 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/eslintrc.json: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | { 22 | "extends": [ 23 | "./best-practices.json", 24 | "./errors.json", 25 | "./miscellaneous.json", 26 | "./node-js-and-common-js.json", 27 | "./strict-mode.json", 28 | "./stylistic-issues.json", 29 | "./variables.json" 30 | ], 31 | "parser": "espree", // TODO: remove? 32 | "env": { 33 | "browser": false, 34 | "node": false, 35 | "amd": false, 36 | "mocha": false, 37 | "jasmine": false, 38 | "es6": false 39 | }, 40 | "globals": { 41 | "__dirname": false, 42 | "__filename": false, 43 | "require": false, 44 | "module": false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/miscellaneous.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-native-reassign": 2, 4 | "no-negated-in-lhs": 2, 5 | "no-reserved-keys": 0, 6 | "no-spaced-func": 2 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/node-js-and-common-js.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-mixed-requires": [ 4 | 2, 5 | false 6 | ], 7 | "no-new-require": 2, 8 | "no-path-concat": 2, 9 | "no-process-env": 2, 10 | "no-process-exit": 2, 11 | "no-restricted-modules": 0, 12 | "no-sync": 0, 13 | "callback-return": [ 14 | 2, 15 | [ 16 | "callback", 17 | "cb", 18 | "next", 19 | "done" 20 | ] 21 | ], 22 | "handle-callback-err": [ 23 | 2, 24 | "^(err|error|anySpecificError)$" 25 | ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/strict-mode.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "strict": [ 4 | 2, 5 | "global" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/stylistic-issues.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-array-constructor": 2, 4 | "no-bitwise": 0, 5 | "no-inline-comments": 2, 6 | "no-lonely-if": 2, 7 | "no-mixed-spaces-and-tabs": [ 8 | 2, 9 | false 10 | ], 11 | "linebreak-style": [ 12 | 2, 13 | "unix" 14 | ], 15 | "no-multiple-empty-lines": [ 16 | 2, 17 | { 18 | "max": 1 19 | } 20 | ], 21 | "no-nested-ternary": 0, 22 | "no-new-object": 2, 23 | "no-plusplus": 0, 24 | "no-ternary": 0, 25 | "no-trailing-spaces": 2, 26 | "no-underscore-dangle": 0, 27 | "no-unneeded-ternary": 2, 28 | "array-bracket-spacing": [ 29 | 2, 30 | "never" 31 | ], 32 | "block-spacing": [ 33 | 2, 34 | "always" 35 | ], 36 | "brace-style": [ 37 | 2, 38 | "1tbs" 39 | ], 40 | "camelcase": 2, 41 | "comma-dangle": [ 42 | 2, 43 | "never" 44 | ], 45 | "comma-spacing": [ 46 | 2, 47 | { 48 | "before": false, 49 | "after": true 50 | } 51 | ], 52 | "comma-style": [ 53 | 2, 54 | "last" 55 | ], 56 | "computed-property-spacing": [ 57 | 2, 58 | "never" 59 | ], 60 | "consistent-this": [ 61 | 2, 62 | "self" 63 | ], 64 | "eol-last": 2, 65 | "func-names": 2, 66 | "func-style": [ 67 | 0, 68 | "declaration" 69 | ], 70 | "id-length": 0, 71 | "id-match": 0, 72 | "indent": [ 73 | 2, 74 | 2 75 | ], 76 | "key-spacing": [ 77 | 2, 78 | { 79 | "beforeColon": false, 80 | "afterColon": true 81 | } 82 | ], 83 | "max-depth": [ 84 | 2, 85 | 3 86 | ], 87 | "max-len": [ 88 | 2, 89 | 100, 90 | 4 91 | ], 92 | "max-nested-callbacks": [ 93 | 2, 94 | 3 95 | ], 96 | "max-params": [ 97 | 2, 98 | 5 99 | ], 100 | "max-statements": [ 101 | 2, 102 | 25 103 | ], 104 | "new-cap": [ 105 | 2, 106 | { 107 | "newIsCap": true, 108 | "capIsNew": false 109 | } 110 | ], 111 | "new-parens": 2, 112 | "newline-after-var": 0, 113 | "object-curly-spacing": [ 114 | 2, 115 | "never" 116 | ], 117 | "one-var": [ 118 | 2, 119 | "never" 120 | ], 121 | "operator-assignment": [ 122 | 0, 123 | "always" 124 | ], 125 | "operator-linebreak": [ 126 | 2, 127 | "after" 128 | ], 129 | "padded-blocks": 0, 130 | "quotes": [ 131 | 2, 132 | "single" 133 | ], 134 | "semi": 2, 135 | "semi-spacing": [ 136 | 2, 137 | { 138 | "before": false, 139 | "after": true 140 | } 141 | ], 142 | "keyword-spacing": [ 143 | 2 144 | ], 145 | "space-before-blocks": [ 146 | 2, 147 | "always" 148 | ], 149 | "space-before-function-paren": [ 150 | 2, 151 | "never" 152 | ], 153 | "space-in-parens": [ 154 | 2, 155 | "never" 156 | ], 157 | "space-infix-ops": 2, 158 | "space-unary-ops": [ 159 | 2, 160 | { 161 | "words": true, 162 | "nonwords": false 163 | } 164 | ], 165 | "spaced-comment": [ 166 | 2, 167 | "always", 168 | { 169 | "exceptions": [ 170 | "-", 171 | "=", 172 | "+", 173 | "*" 174 | ] 175 | } 176 | ] 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-es5/variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-catch-shadow": 2, 4 | "no-delete-var": 2, 5 | "no-label-var": 2, 6 | "no-shadow": 2, 7 | "no-shadow-restricted-names": 2, 8 | "no-undef-init": 2, 9 | "no-undef": 2, 10 | "no-undefined": 0, 11 | "no-unused-vars": [ 12 | 2, 13 | { 14 | "vars": "all", 15 | "args": "none" 16 | } 17 | ], 18 | "no-use-before-define": [ 19 | 2, 20 | "nofunc" 21 | ], 22 | "init-declarations": 0 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-jsx/best-practices.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-invalid-this": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-jsx/eslintrc.json: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 Uber Technologies, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | { 22 | "extends": [ 23 | "../eslint-config-uber-es2015/eslintrc.json", 24 | "./best-practices.json", 25 | "./miscellaneous.json", 26 | "./stylistic-issues.json" 27 | ], 28 | "parserOptions": { 29 | "ecmaFeatures": { 30 | "jsx": true, 31 | "experimentalObjectRestSpread": true 32 | } 33 | }, 34 | "plugins": ["react"] 35 | } 36 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-jsx/miscellaneous.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "react/jsx-no-undef": 2, 4 | "react/no-unknown-property": 1, 5 | "react/jsx-uses-react": 2, 6 | "react/jsx-uses-vars": 2, 7 | "react/no-did-mount-set-state": 1, 8 | "react/no-did-update-set-state": 1, 9 | "react/prop-types": 0, 10 | "react/react-in-jsx-scope": 2, 11 | "react/self-closing-comp": 2, 12 | "react/sort-comp": 1, 13 | "react/jsx-wrap-multilines": 2, 14 | "react/display-name": 1, 15 | "react/no-danger": 1, 16 | "react/no-deprecated": 1, 17 | "react/no-direct-mutation-state": 1, 18 | "react/jsx-indent": [ 19 | 1, 20 | 2 21 | ], 22 | "react/jsx-no-target-blank": 1, 23 | "react/forbid-prop-types": [ 24 | 1, 25 | { 26 | "forbid": [ 27 | "any", 28 | "array" 29 | ] 30 | } 31 | ] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/eslint-config-uber-jsx/stylistic-issues.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "jsx-quotes": [ 4 | 2, 5 | "prefer-double" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/get-babel-config.ts: -------------------------------------------------------------------------------- 1 | import deepMerge from 'deepmerge'; 2 | import {inspect} from 'util'; 3 | 4 | // The following targets are designed to support the most commonly used evergreen browsers. 5 | // As of Feb 2021 they all support async function, async iterator, and spread operator. 6 | const ES5_TARGETS = ['>0.2%', 'maintained node versions', 'not ie 11', 'not dead', 'not chrome 49']; 7 | const ESM_TARGETS = ['>0.2% and supports async-functions', 'maintained node versions', 'not dead']; 8 | // Reduce verbosity 9 | const ESM_PLUGIN_BLACKLIST = [ 10 | // Template literals are supported in all latest versions of environments 11 | '@babel/plugin-transform-template-literals' 12 | ]; 13 | 14 | const DEFAULT_CONFIG = { 15 | comments: false, 16 | // These settings reduce the verbosity of transpile outputs 17 | assumptions: { 18 | // When declaring classes, assume that methods don't shadow getters on the superclass and that the program doesn't depend on methods being non-enumerable. 19 | setClassMethods: true, 20 | // When using public class fields, assume that they don't shadow any getter in the current class, in its subclasses or in its superclass. 21 | setPublicClassFields: true 22 | } 23 | }; 24 | 25 | const COMMON_PRESETS = [ 26 | // Accepts typescript syntax 27 | // Note that this still has limits (requires typescript isolated modules) 28 | '@babel/preset-typescript' 29 | ]; 30 | 31 | const COMMON_PLUGINS = []; 32 | 33 | const ENV_CONFIG: any = { 34 | // fully transpiled build 35 | es5: { 36 | presets: [ 37 | ...COMMON_PRESETS, 38 | [ 39 | '@babel/env', 40 | { 41 | targets: ES5_TARGETS, 42 | modules: 'commonjs' 43 | } 44 | ] 45 | ], 46 | plugins: [...COMMON_PLUGINS, '@babel/transform-runtime'] 47 | }, 48 | // es module style build 49 | esm: { 50 | presets: [ 51 | ...COMMON_PRESETS, 52 | [ 53 | '@babel/env', 54 | { 55 | targets: ESM_TARGETS, 56 | exclude: ESM_PLUGIN_BLACKLIST, 57 | modules: false 58 | } 59 | ] 60 | ], 61 | plugins: [ 62 | ...COMMON_PLUGINS, 63 | // TODO - we likely do not need runtime transforms for the esm setting 64 | ['@babel/transform-runtime', {useESModules: true}] 65 | ] 66 | }, 67 | 68 | 'esm-strict': { 69 | presets: [ 70 | ...COMMON_PRESETS, 71 | [ 72 | '@babel/env', 73 | { 74 | targets: ESM_TARGETS, 75 | exclude: ESM_PLUGIN_BLACKLIST, 76 | modules: false 77 | } 78 | ] 79 | ], 80 | plugins: [ 81 | ...COMMON_PLUGINS, 82 | 'babel-plugin-add-import-extension', 83 | // TODO - we likely do not need runtime transforms for the esm setting 84 | ['@babel/transform-runtime', {useESModules: true}] 85 | ] 86 | }, 87 | 88 | bundle: { 89 | presets: [ 90 | ...COMMON_PRESETS, 91 | [ 92 | '@babel/env', 93 | { 94 | targets: ESM_TARGETS, 95 | modules: false 96 | } 97 | ] 98 | ] 99 | }, 100 | 101 | 'bundle-dev': { 102 | presets: [...COMMON_PRESETS] 103 | } 104 | }; 105 | 106 | // Ensure we have an entry for the default BABEL_ENV 107 | ENV_CONFIG.development = ENV_CONFIG.es5; 108 | 109 | export function getBabelConfig( 110 | options: { 111 | react?: boolean; 112 | overrides?: any; 113 | debug?: boolean; 114 | } = {} 115 | ) { 116 | return (api) => { 117 | if (api.cache) { 118 | api.cache.using(() => process.env.BABEL_ENV); 119 | } 120 | 121 | let config = { 122 | ...DEFAULT_CONFIG, 123 | ...ENV_CONFIG[api.env()] 124 | }; 125 | if (options.react) { 126 | config = deepMerge(config, { 127 | presets: ['@babel/preset-react'] 128 | }); 129 | } 130 | if (options.overrides) { 131 | config = deepMerge(config, options.overrides); 132 | } 133 | 134 | if (options.debug) { 135 | console.log(inspect(config, {colors: true, depth: null})); 136 | } 137 | 138 | return config; 139 | }; 140 | } 141 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/get-esbuild-config.ts: -------------------------------------------------------------------------------- 1 | // / For bundles published to npm 2 | import fs from 'fs'; 3 | import {join} from 'path'; 4 | import util from 'util'; 5 | import {getOcularConfig} from '../helpers/get-ocular-config.js'; 6 | import ext from 'esbuild-plugin-external-global'; 7 | import type {BuildOptions} from 'esbuild'; 8 | 9 | /** 10 | * Get list of dependencies to exclude using esbuild-plugin-external-global 11 | * @param externalPackages string[] 12 | */ 13 | // function getExternalGlobalsAMD(externalPackages) { 14 | // const externals = {}; 15 | // for (const packageName of externalPackages) { 16 | // externals[packageName] = `typeof require === 'function' ? require('${packageName}') : null`; 17 | // } 18 | // return externals; 19 | // } 20 | 21 | /** 22 | * Get list of dependencies to exclude using esbuild-plugin-external-global 23 | * @param externalPackages string[] 24 | * @param mapping {[pattern: string]: replacement} 25 | */ 26 | function getExternalGlobalsIIFE(externalPackages: string[], mapping: Record) { 27 | const externals: Record = {}; 28 | for (const packageName of externalPackages) { 29 | for (const key in mapping) { 30 | if (packageName.search(key) === 0) { 31 | externals[packageName] = mapping[key]; 32 | break; 33 | } 34 | } 35 | } 36 | return externals; 37 | } 38 | 39 | // esbuild does not support umd format 40 | // Work around from https://github.com/evanw/esbuild/issues/819 41 | // Template: https://webpack.js.org/configuration/output/#type-umd 42 | function umdWrapper(libName: string | undefined) { 43 | return { 44 | format: 'iife', 45 | globalName: '__exports__', 46 | banner: { 47 | js: `\ 48 | (function webpackUniversalModuleDefinition(root, factory) { 49 | if (typeof exports === 'object' && typeof module === 'object') 50 | module.exports = factory(); 51 | else if (typeof define === 'function' && define.amd) define([], factory); 52 | ${ 53 | libName 54 | ? `else if (typeof exports === 'object') exports['${libName}'] = factory(); 55 | else root['${libName}'] = factory();` 56 | : `else { 57 | var a = factory(); 58 | for (var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; 59 | }` 60 | }})(globalThis, function () {` 61 | }, 62 | footer: { 63 | js: `\ 64 | return __exports__; 65 | });` 66 | } 67 | }; 68 | } 69 | 70 | /** Returns esbuild config for building .cjs bundles */ 71 | export async function getCJSExportConfig(opts: { 72 | input: string; 73 | output: string; 74 | }): Promise { 75 | return { 76 | entryPoints: [opts.input], 77 | outfile: opts.output, 78 | bundle: true, 79 | format: 'cjs', 80 | // Node 16 is out of support, kept for compatibility. Move to 18? 81 | target: 'node16', 82 | packages: 'external', 83 | sourcemap: true, 84 | logLevel: 'info' 85 | }; 86 | } 87 | 88 | type BundleOptions = { 89 | input: string; 90 | env?: 'dev' | 'prod'; 91 | output?: string; 92 | format?: 'iife' | 'cjs' | 'esm' | 'umd'; 93 | target?: string[]; 94 | externals?: string[]; 95 | globalName?: string; 96 | globals?: {[pattern: string]: string}; 97 | debug?: boolean; 98 | sourcemap?: boolean; 99 | }; 100 | 101 | /* eslint-disable max-statements,complexity */ 102 | /** Returns esbuild config for building standalone bundles */ 103 | export async function getBundleConfig(opts: BundleOptions): Promise { 104 | // This script must be executed in a submodule's directory 105 | const packageRoot = process.cwd(); 106 | const packageInfo = JSON.parse(fs.readFileSync(join(packageRoot, 'package.json'), 'utf-8')); 107 | 108 | const devMode = opts.env === 'dev'; 109 | 110 | const ocularConfig = await getOcularConfig({ 111 | root: join(packageRoot, '../..'), 112 | aliasMode: devMode ? 'src' : 'dist' 113 | }); 114 | 115 | opts = {...ocularConfig.bundle, ...opts}; 116 | 117 | const { 118 | input, 119 | output = devMode ? './dist/dist.dev.js' : './dist.min.js', 120 | format = 'iife', 121 | target = ['esnext'], 122 | externals, 123 | globalName, 124 | debug, 125 | sourcemap = false 126 | } = opts; 127 | 128 | let babelConfig; 129 | 130 | let externalPackages = Object.keys(packageInfo.peerDependencies || {}); 131 | if (typeof externals === 'string') { 132 | externalPackages = externalPackages.concat((externals as string).split(',')); 133 | } else if (Array.isArray(externals)) { 134 | externalPackages = externalPackages.concat(externals); 135 | } 136 | 137 | const config: BuildOptions = { 138 | entryPoints: [input], 139 | outfile: output, 140 | bundle: true, 141 | // @ts-expect-error umd is not supported by esbuild, will be overwritten below 142 | format, 143 | minify: !devMode, 144 | alias: ocularConfig.aliases, 145 | platform: 'browser', 146 | target, 147 | logLevel: 'info', 148 | sourcemap, 149 | plugins: [] 150 | }; 151 | if (globalName) { 152 | config.globalName = globalName; 153 | } 154 | 155 | let externalGlobals; 156 | switch (format) { 157 | case 'cjs': 158 | case 'esm': 159 | // Use esbuild's built-in external functionality 160 | config.packages = 'external'; 161 | if (externals) { 162 | config.external = externals; 163 | } 164 | break; 165 | 166 | case 'umd': 167 | Object.assign(config, umdWrapper(globalName)); 168 | externalGlobals = getExternalGlobalsIIFE(externalPackages, ocularConfig.bundle.globals); 169 | break; 170 | 171 | case 'iife': 172 | externalGlobals = getExternalGlobalsIIFE(externalPackages, ocularConfig.bundle.globals); 173 | break; 174 | 175 | default: 176 | break; 177 | } 178 | if (externalGlobals) { 179 | config.plugins!.unshift(ext.externalGlobalPlugin(externalGlobals)); 180 | } 181 | 182 | if (debug) { 183 | const printableConfig = { 184 | ...config, 185 | plugins: config.plugins!.map((item) => { 186 | return { 187 | name: item.name, 188 | options: item.name === 'babel' ? babelConfig : externalGlobals 189 | }; 190 | }) 191 | }; 192 | 193 | console.log(util.inspect(printableConfig, {showHidden: false, depth: null, colors: true})); 194 | } 195 | 196 | return config; 197 | } 198 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/get-eslint-config.ts: -------------------------------------------------------------------------------- 1 | import deepMerge from 'deepmerge'; 2 | import {getValidPath, ocularRoot} from '../utils/utils.js'; 3 | import {inspect} from 'util'; 4 | import {resolve} from 'path'; 5 | 6 | const localRules = (path) => resolve(ocularRoot, 'src/configuration', path); 7 | 8 | const DEFAULT_OPTIONS = { 9 | react: false 10 | } as const; 11 | 12 | const babelConfig = getValidPath( 13 | './.babelrc', 14 | './.babelrc.js', 15 | './.babelrc.cjs', 16 | './babel.config.js', 17 | './babel.config.cjs' 18 | ); 19 | 20 | const DEFAULT_CONFIG = { 21 | extends: [ 22 | localRules('./eslint-config-uber-es2015/eslintrc.json'), 23 | 'prettier', 24 | 'plugin:import/recommended' 25 | ], 26 | plugins: ['import'], 27 | parser: babelConfig ? '@babel/eslint-parser' : '', 28 | parserOptions: { 29 | ecmaVersion: 2020, 30 | // @babel/eslint-parser issues https://github.com/babel/babel/issues/11975 31 | requireConfigFile: false, 32 | babelOptions: { 33 | configFile: babelConfig 34 | } 35 | }, 36 | env: { 37 | // Note: also sets ecmaVersion 38 | es2020: true 39 | }, 40 | globals: { 41 | globalThis: 'readonly', 42 | __VERSION__: 'readonly' 43 | }, 44 | rules: { 45 | 'guard-for-in': 'off', 46 | 'func-names': 'off', 47 | 'no-inline-comments': 'off', 48 | 'no-multi-str': 'off', 49 | camelcase: 'warn', 50 | // Let prettier handle this 51 | indent: 'off', 52 | 'accessor-pairs': ['error', {getWithoutSet: false, setWithoutGet: false}], 53 | 'import/no-extraneous-dependencies': ['error', {devDependencies: false, peerDependencies: true}] 54 | }, 55 | settings: { 56 | // Ensure eslint finds typescript files 57 | 'import/resolver': { 58 | node: { 59 | extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx', '.d.ts'] 60 | } 61 | } 62 | }, 63 | ignorePatterns: ['node_modules', '**/dist*/**/*.js'], 64 | overrides: [ 65 | { 66 | // babel-eslint can process TS files, but it doesn't understand types 67 | // typescript-eslint has some more advanced rules with type checking 68 | files: ['**/*.ts', '**/*.tsx', '**/*.d.ts'], 69 | parser: '@typescript-eslint/parser', 70 | parserOptions: { 71 | sourceType: 'module', // we want to use ES modules 72 | project: './tsconfig.json' 73 | }, 74 | extends: ['plugin:@typescript-eslint/recommended-type-checked'], 75 | plugins: ['@typescript-eslint'], 76 | rules: { 77 | '@typescript-eslint/no-dupe-class-members': 'error', 78 | '@typescript-eslint/switch-exhaustiveness-check': 'error', 79 | 80 | // Rules disabled because they conflict with our preferred style 81 | // We use function hoisting to put exports at top of file 82 | '@typescript-eslint/no-use-before-define': 'off', 83 | // We encourage explicit typing, e.g `field: string = ''` 84 | '@typescript-eslint/no-inferrable-types': 'off', 85 | // Allow noOp as default value for callbacks 86 | '@typescript-eslint/no-empty-function': 'off', 87 | 88 | // Rules downgraded because they are deemed acceptable 89 | '@typescript-eslint/ban-ts-comment': [ 90 | 'error', 91 | { 92 | 'ts-expect-error': 'allow-with-description', 93 | 'ts-ignore': 'allow-with-description', 94 | 'ts-nocheck': 'allow-with-description', 95 | 'ts-check': false, 96 | minimumDescriptionLength: 3 97 | } 98 | ], 99 | '@typescript-eslint/no-floating-promises': 'warn', 100 | '@typescript-eslint/restrict-template-expressions': 'warn', 101 | '@typescript-eslint/no-empty-interface': 'warn', 102 | '@typescript-eslint/require-await': 'warn', 103 | 104 | // Rules that restrict the use of `any` 105 | // Might be too strict for our code base, but should be gradually enabled 106 | '@typescript-eslint/no-explicit-any': 'warn', 107 | '@typescript-eslint/no-unsafe-argument': 'warn', 108 | '@typescript-eslint/no-unsafe-assignment': 'warn', 109 | '@typescript-eslint/no-unsafe-call': 'warn', 110 | '@typescript-eslint/no-unsafe-enum-comparison': 'warn', 111 | '@typescript-eslint/no-unsafe-member-access': 'warn', 112 | '@typescript-eslint/no-unsafe-return': 'warn', 113 | '@typescript-eslint/explicit-module-boundary-types': 'warn' 114 | } 115 | }, 116 | { 117 | // We can lint through code examples in Markdown as well, 118 | // but we don't need to enable all of the rules there 119 | files: ['**/*.md'], 120 | plugins: ['markdown'], 121 | // extends: 'plugin:markdown/recommended', 122 | rules: { 123 | 'no-undef': 'off', 124 | 'no-unused-vars': 'off', 125 | 'no-unused-expressions': 'off', 126 | 'no-console': 'off', 127 | 'padded-blocks': 'off' 128 | } 129 | } 130 | ] 131 | }; 132 | 133 | function getReactConfig(options) { 134 | return { 135 | extends: [ 136 | localRules('./eslint-config-uber-jsx/eslintrc.json'), 137 | 'prettier', 138 | 'plugin:import/recommended' 139 | ], 140 | plugins: ['import', 'react'], 141 | settings: { 142 | react: { 143 | version: options.react 144 | } 145 | } 146 | }; 147 | } 148 | 149 | export function getESLintConfig( 150 | options: {react?: string | false; overrides?: any; debug?: boolean} = {} 151 | ) { 152 | options = {...DEFAULT_OPTIONS, ...options}; 153 | 154 | let config = {...DEFAULT_CONFIG}; 155 | if (options.react) { 156 | config = deepMerge(config, getReactConfig(options)); 157 | } 158 | if (options.overrides) { 159 | config = deepMerge(config, options.overrides); 160 | } 161 | if (options.debug) { 162 | // eslint-disable-next-line 163 | console.log(inspect(config, {colors: true, depth: null})); 164 | } 165 | 166 | return config; 167 | } 168 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/get-prettier-config.ts: -------------------------------------------------------------------------------- 1 | import deepMerge from 'deepmerge'; 2 | 3 | const DEFAULT_CONFIG = { 4 | printWidth: 100, 5 | semi: true, 6 | singleQuote: true, 7 | trailingComma: 'none', 8 | bracketSpacing: false 9 | }; 10 | 11 | export function getPrettierConfig(options: {overrides?: any; debug?: boolean} = {}) { 12 | let config = {...DEFAULT_CONFIG}; 13 | if (options.overrides) { 14 | config = deepMerge(config, options.overrides); 15 | } 16 | if (options.debug) { 17 | // eslint-disable-next-line 18 | console.log(config); 19 | } 20 | return config; 21 | } 22 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/index.ts: -------------------------------------------------------------------------------- 1 | // JS Tool Configurations 2 | export {getBabelConfig} from './get-babel-config.js'; 3 | export {getESLintConfig} from './get-eslint-config.js'; 4 | export {getPrettierConfig} from './get-prettier-config.js'; 5 | -------------------------------------------------------------------------------- /modules/dev-tools/src/configuration/vite.config.js: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vite'; 2 | import {getOcularConfig} from '../helpers/get-ocular-config.js'; 3 | import {NodeGlobalsPolyfillPlugin} from '@esbuild-plugins/node-globals-polyfill'; 4 | import {NodeModulesPolyfillPlugin} from '@esbuild-plugins/node-modules-polyfill'; 5 | 6 | export default defineConfig(async ({mode}) => { 7 | const ocularConfig = await getOcularConfig({aliasMode: 'browser'}); 8 | const entryPoint = ocularConfig.entry[`${mode}-browser`]; 9 | 10 | return { 11 | optimizeDeps: { 12 | // Disable crawling the whole repo 13 | entries: [entryPoint], 14 | // Polyfill for Node environment (required by tape-promise) 15 | esbuildOptions: { 16 | define: { 17 | global: 'globalThis', 18 | __dirname: '"."' 19 | }, 20 | plugins: [ 21 | NodeGlobalsPolyfillPlugin({ 22 | process: true 23 | }), 24 | NodeModulesPolyfillPlugin() 25 | ] 26 | } 27 | }, 28 | resolve: { 29 | alias: ocularConfig.aliases 30 | } 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/aliases.ts: -------------------------------------------------------------------------------- 1 | // Registers an alias for this module 2 | import {resolve} from 'path'; 3 | import fs from 'fs'; 4 | import {getValidPath} from '../utils/utils.js'; 5 | import type {PackageJson} from '../utils/types.js'; 6 | 7 | type ModuleInfo = { 8 | name: string; 9 | path: string; 10 | packageInfo: PackageJson; 11 | }; 12 | 13 | export function getModuleInfo(path: string): ModuleInfo | null { 14 | if (fs.lstatSync(path).isDirectory()) { 15 | try { 16 | const packageInfoText = fs.readFileSync(resolve(path, 'package.json'), 'utf-8'); 17 | const packageInfo = JSON.parse(packageInfoText) as PackageJson; 18 | return { 19 | name: packageInfo.name, 20 | path, 21 | packageInfo 22 | }; 23 | } catch (err) { 24 | // ignore if sub directory does not contain package.json 25 | } 26 | } 27 | return null; 28 | } 29 | 30 | // Get information of all submodules 31 | function getSubmodules(packageRoot: string): {[name: string]: ModuleInfo} { 32 | const submodules: Record = {}; 33 | const parentPath = resolve(packageRoot, './modules'); 34 | 35 | if (fs.existsSync(parentPath)) { 36 | // monorepo 37 | fs.readdirSync(parentPath).forEach((item) => { 38 | const itemPath = resolve(parentPath, item); 39 | const moduleInfo = getModuleInfo(itemPath); 40 | if (moduleInfo) { 41 | submodules[moduleInfo.name] = moduleInfo; 42 | } 43 | }); 44 | } else { 45 | const moduleInfo = getModuleInfo(packageRoot); 46 | if (moduleInfo) { 47 | submodules[moduleInfo.name] = moduleInfo; 48 | } 49 | } 50 | 51 | return submodules; 52 | } 53 | 54 | export default function getAliases( 55 | mode: string, 56 | packageRoot: string = process.env.PWD! 57 | ): Record { 58 | const aliases: Record = {}; 59 | const submodules = getSubmodules(packageRoot); 60 | 61 | for (const moduleName in submodules) { 62 | const {path} = submodules[moduleName]; 63 | 64 | const testPath = resolve(path, 'test'); 65 | if (fs.existsSync(testPath)) { 66 | aliases[`${moduleName}/test`] = testPath; 67 | } 68 | 69 | if (mode === 'dist') { 70 | aliases[moduleName] = getValidPath( 71 | resolve(path, 'dist/es5'), 72 | resolve(path, 'dist/esm'), 73 | resolve(path, 'dist') 74 | )!; 75 | } else { 76 | aliases[moduleName] = resolve(path, 'src'); 77 | } 78 | } 79 | 80 | return aliases; 81 | } 82 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/cjs-register.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Support module alias in CJS mode 3 | */ 4 | const tsConfigPaths = require('tsconfig-paths'); 5 | // @ts-expect-error 6 | const paths = require('../../.alias.json'); 7 | 8 | tsConfigPaths.register({ 9 | baseUrl: '.', 10 | paths: parseModuleAlias(paths) 11 | }); 12 | 13 | /** Convert ocular alias object to TS config paths object */ 14 | function parseModuleAlias(aliases) { 15 | // Cast user config to tsconfig-style paths 16 | const result = {}; 17 | for (const key in aliases) { 18 | const alias = aliases[key]; 19 | result[key] = [alias]; 20 | if (!alias.match(/(\/\*|\.jsx?|\.tsx?|\.cjs)$/)) { 21 | result[`${key}/*`] = [`${alias}/*`]; 22 | } 23 | } 24 | return result; 25 | } 26 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/esm-alias.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Support module alias in ESM mode by implementing Node.js custom module resolver 3 | * tsconfig-paths does not work in ESM, see https://github.com/dividab/tsconfig-paths/issues/122 4 | * Adapted from https://github.com/TypeStrong/ts-node/discussions/1450 5 | */ 6 | import path from 'path'; 7 | import fs from 'fs'; 8 | import {pathToFileURL} from 'url'; 9 | import {getValidPath, ocularRoot} from '../utils/utils.js'; 10 | 11 | /** Node.js resolve hook, 12 | * https://nodejs.org/api/module.html#resolvespecifier-context-nextresolve 13 | */ 14 | type ResolveHook = ( 15 | specifier: string, 16 | context: { 17 | conditions?: unknown; 18 | importAssertions?: unknown; 19 | parentURL: string; 20 | }, 21 | nextResolve: ResolveHook 22 | ) => Promise<{ 23 | url: string; 24 | format?: 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'; 25 | shortCircuit?: boolean; 26 | }>; 27 | 28 | // Load alias from file 29 | const pathJSON = fs.readFileSync(path.resolve(ocularRoot, '.alias.json'), 'utf-8'); 30 | const paths: Record = JSON.parse(pathJSON); 31 | const matchPath = createMatchPath(paths); 32 | 33 | export const resolve: ResolveHook = (specifier, context, nextResolver) => { 34 | const mappedSpecifier = matchPath(specifier); 35 | if (mappedSpecifier) { 36 | if (mappedSpecifier.match(/(\/\*|\.jsx?|\.tsx?|\.cjs|\.json)$/)) { 37 | specifier = pathToFileURL(mappedSpecifier).pathname; 38 | } else if (mappedSpecifier.includes('/dist/')) { 39 | specifier = `${pathToFileURL(mappedSpecifier)}.js`; 40 | } else { 41 | specifier = `${pathToFileURL(mappedSpecifier)}`; 42 | } 43 | } 44 | // @ts-expect-error omitted arguments are populated by Node.js 45 | return nextResolver(specifier); 46 | }; 47 | 48 | /** Checks if a path matches aliased pattern. 49 | * If so, returns mapped path, otherwise returns null 50 | */ 51 | type AliasTest = (specifier: string) => string | null; 52 | 53 | /** Get alias mapping function from ocular config */ 54 | function createMatchPath(aliases: Record): AliasTest { 55 | const tests: AliasTest[] = []; 56 | 57 | for (const key in aliases) { 58 | const alias = aliases[key]; 59 | let testFunc: AliasTest; 60 | if (key.includes('*')) { 61 | const regex = new RegExp(`^${key.replace('*', '(.+)')}`); 62 | testFunc = (specifier: string) => { 63 | const match = specifier.match(regex); 64 | if (match) { 65 | return specifier.replace(match[0], alias.replace('*', match[1])); 66 | } 67 | return null; 68 | }; 69 | } else { 70 | let defaultEntry = alias; 71 | 72 | if (!alias.match(/(\/\*|\.jsx?|\.tsx?|\.cjs)$/)) { 73 | defaultEntry = getValidPath(`${alias}/index.ts`, `${alias}/index.js`) || defaultEntry; 74 | } 75 | 76 | testFunc = (specifier: string) => { 77 | if (key === specifier) { 78 | return defaultEntry; 79 | } 80 | if (specifier.startsWith(`${key}/`)) { 81 | return `${alias}${specifier.slice(key.length)}`; 82 | } 83 | return null; 84 | }; 85 | } 86 | tests.push(testFunc); 87 | } 88 | 89 | return (specifier: string) => { 90 | for (const test of tests) { 91 | const result = test(specifier); 92 | if (result) { 93 | return result; 94 | } 95 | } 96 | return null; 97 | }; 98 | } 99 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/esm-register.ts: -------------------------------------------------------------------------------- 1 | import {register} from 'node:module'; 2 | 3 | register('ts-node/esm', import.meta.url); 4 | register('./esm-alias.js', import.meta.url); 5 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/get-cjs-entry-points.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import type {PackageJson} from '../utils/types.js'; 3 | 4 | export function getCJSEntryPoints(): { 5 | inputFile: string; 6 | outputFile: string; 7 | }[] { 8 | const packageInfo = JSON.parse(fs.readFileSync('package.json', 'utf-8')) as PackageJson; 9 | 10 | if (packageInfo.exports) { 11 | const result: { 12 | inputFile: string; 13 | outputFile: string; 14 | }[] = []; 15 | for (const key in packageInfo.exports) { 16 | const entry = packageInfo.exports[key]; 17 | let outputFile: string = ''; 18 | if (typeof entry === 'string') { 19 | outputFile = entry; 20 | } else if (entry.require) { 21 | outputFile = entry.require; 22 | } else if (entry.default) { 23 | outputFile = entry.default; 24 | } 25 | if (outputFile && outputFile.endsWith('.cjs')) { 26 | let inputFile: string; 27 | 28 | if (typeof entry === 'object' && entry.import) { 29 | inputFile = entry.import; 30 | } else { 31 | inputFile = outputFile.replace('.cjs', '.js'); 32 | } 33 | result.push({inputFile, outputFile}); 34 | } 35 | } 36 | return result; 37 | } 38 | 39 | // Default entry 40 | return [{inputFile: './dist/index.js', outputFile: './dist.index.cjs'}]; 41 | } 42 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/get-config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used by command line scripts to print a field from the local ocular config. 3 | Path is period separated. 4 | Example: 5 | $ node get-config.js ".babel.configPath" 6 | */ 7 | import {getOcularConfig, MaterializedOcularConfig} from './get-ocular-config.js'; 8 | 9 | let ocularConfig: MaterializedOcularConfig; 10 | try { 11 | ocularConfig = await getOcularConfig(); 12 | } catch (ex) { 13 | console.error('Error resolving ocular config'); 14 | console.error(ex); 15 | process.exit(1); 16 | } 17 | 18 | const configPath = process.argv[2] || ''; 19 | 20 | let config: any = ocularConfig; 21 | 22 | configPath 23 | .split('.') 24 | .filter(Boolean) 25 | .forEach((path) => { 26 | config = config ? config[path] : undefined; 27 | }); 28 | 29 | if (config === undefined) { 30 | config = ''; 31 | } 32 | 33 | if (Array.isArray(config)) { 34 | config = config.join(','); 35 | } 36 | 37 | console.log(config); 38 | -------------------------------------------------------------------------------- /modules/dev-tools/src/helpers/get-ocular-config.ts: -------------------------------------------------------------------------------- 1 | /* @typedef {import('./get-ocular-config')} default */ 2 | 3 | import fs from 'fs'; 4 | import {resolve} from 'path'; 5 | import getAliases, {getModuleInfo} from './aliases.js'; 6 | import {shallowMerge, getValidPath, ocularRoot} from '../utils/utils.js'; 7 | import type {BrowserTestDriver} from '@probe.gl/test-utils'; 8 | 9 | // TODO - export from probe.gl 10 | type BrowserTestOptions = Parameters[0]; 11 | 12 | /** User configuration from .ocularrc.js */ 13 | export type OcularConfig = { 14 | root?: string; 15 | 16 | esm?: boolean; 17 | 18 | aliases?: {[module: string]: string}; 19 | 20 | nodeAliases?: {[module: string]: string}; 21 | 22 | babel?: 23 | | { 24 | configPath?: string; 25 | extensions?: string[]; 26 | } 27 | | false; 28 | 29 | bundle?: { 30 | target?: string[]; 31 | globalName?: string; 32 | format?: 'cjs' | 'esm' | 'umd' | 'iife'; 33 | externals?: string[]; 34 | globals?: {[pattern: string]: string}; 35 | }; 36 | 37 | typescript?: { 38 | project: string; 39 | }; 40 | 41 | lint?: { 42 | paths?: string[]; 43 | extensions?: string[]; 44 | }; 45 | 46 | vite?: { 47 | version?: number; 48 | configPath?: string; 49 | }; 50 | 51 | coverage?: { 52 | test?: 'node' | 'browser'; 53 | }; 54 | 55 | browserTest?: { 56 | server?: BrowserTestOptions['server']; 57 | browser?: BrowserTestOptions['browser']; 58 | }; 59 | 60 | entry?: { 61 | test?: string; 62 | 'test-browser'?: `${string}.html`; 63 | bench?: string; 64 | 'bench-browser'?: `${string}.html`; 65 | size?: string[] | string; 66 | }; 67 | }; 68 | 69 | /** Internal type to typecheck resolved ocular config inside ocular-dev-tools */ 70 | export type MaterializedOcularConfig = { 71 | root: string; 72 | ocularPath: string; 73 | esm: boolean; 74 | 75 | aliases: {[module: string]: string}; 76 | 77 | babel: 78 | | { 79 | configPath: string; 80 | extensions: string[]; 81 | } 82 | | false; 83 | 84 | bundle: { 85 | target?: string[]; 86 | globalName?: string; 87 | format?: 'cjs' | 'esm' | 'umd' | 'iife'; 88 | externals?: string[]; 89 | globals: {[pattern: string]: string}; 90 | }; 91 | 92 | typescript: { 93 | project: string; 94 | }; 95 | 96 | lint: { 97 | paths: string[]; 98 | extensions: string[]; 99 | }; 100 | 101 | vite: { 102 | version: number; 103 | configPath: string; 104 | }; 105 | 106 | coverage: { 107 | test: 'node' | 'browser'; 108 | }; 109 | 110 | browserTest?: { 111 | server?: BrowserTestOptions['server']; 112 | browser?: BrowserTestOptions['browser']; 113 | }; 114 | 115 | entry: { 116 | test: string; 117 | 'test-browser': `${string}.html`; 118 | bench: string; 119 | 'bench-browser': `${string}.html`; 120 | size: string[]; 121 | }; 122 | }; 123 | 124 | export async function getOcularConfig( 125 | options: { 126 | root?: string; 127 | aliasMode?: string; 128 | } = {} 129 | ): Promise { 130 | const packageRoot = options.root || process.env.PWD!; 131 | 132 | const IS_MONOREPO = fs.existsSync(resolve(packageRoot, './modules')); 133 | 134 | const userConfig = await getUserConfig(packageRoot); 135 | 136 | const config: MaterializedOcularConfig = { 137 | root: packageRoot, 138 | ocularPath: ocularRoot, 139 | 140 | esm: getModuleInfo(packageRoot)?.packageInfo.type === 'module', 141 | 142 | babel: 143 | userConfig.babel !== false 144 | ? { 145 | configPath: getValidPath( 146 | resolve(packageRoot, './.babelrc'), 147 | resolve(packageRoot, './.babelrc.js'), 148 | resolve(packageRoot, './.babelrc.cjs'), 149 | resolve(packageRoot, './babel.config.js'), 150 | resolve(packageRoot, './babel.config.cjs') 151 | )!, 152 | extensions: ['.js', '.jsx', '.mjs', '.ts', '.tsx'] 153 | } 154 | : false, 155 | 156 | bundle: { 157 | globals: {} 158 | }, 159 | 160 | typescript: { 161 | project: '' 162 | }, 163 | 164 | lint: { 165 | paths: IS_MONOREPO ? ['modules'] : ['src'], 166 | extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx', 'd.ts'] 167 | }, 168 | 169 | coverage: { 170 | test: 'node' 171 | }, 172 | 173 | aliases: {}, 174 | 175 | entry: { 176 | test: 'test/index.ts', 177 | 'test-browser': 'test/index.html', 178 | bench: 'test/bench/index.ts', 179 | 'bench-browser': 'test/bench/index.html', 180 | size: ['test/size.ts'] 181 | }, 182 | 183 | vite: { 184 | version: 4, 185 | configPath: getValidPath( 186 | resolve(packageRoot, './vite.config.js'), 187 | resolve(packageRoot, './vite.config.cjs'), 188 | resolve(ocularRoot, 'dist/configuration/vite.config.js') 189 | )! 190 | } 191 | }; 192 | 193 | shallowMerge(config, userConfig); 194 | 195 | // Backward compatibility 196 | if (typeof userConfig.entry?.size === 'string') { 197 | userConfig.entry.size = [userConfig.entry.size]; 198 | } 199 | 200 | const aliasMode = options.aliasMode || 'src'; 201 | 202 | // User's aliases need to come first, due to module-alias resolve order 203 | Object.assign(config.aliases, getAliases(aliasMode, packageRoot)); 204 | 205 | if (aliasMode && !aliasMode.includes('browser')) { 206 | Object.assign(config.aliases, userConfig.nodeAliases); 207 | } 208 | 209 | return config; 210 | } 211 | 212 | // HELPERS 213 | 214 | /** 215 | * TODO better error messages 216 | */ 217 | async function getUserConfig(packageRoot: string): Promise { 218 | const userConfigPath = getValidPath( 219 | resolve(packageRoot, './.ocularrc.js'), 220 | resolve(packageRoot, './.ocularrc.cjs'), 221 | resolve(packageRoot, './.ocular.config.js'), 222 | resolve(packageRoot, './.ocular.config.cjs'), 223 | // deprecated 224 | resolve(packageRoot, './ocular-dev-tools.config.js') 225 | ); 226 | 227 | if (userConfigPath) { 228 | const userConfigModule = (await import(userConfigPath)) as 229 | | OcularConfig 230 | | {default: OcularConfig}; 231 | if ('default' in userConfigModule) { 232 | return userConfigModule.default; 233 | } 234 | return userConfigModule; 235 | } 236 | 237 | throw new Error('No valid user config found'); 238 | } 239 | -------------------------------------------------------------------------------- /modules/dev-tools/src/index.ts: -------------------------------------------------------------------------------- 1 | export {getOcularConfig} from './helpers/get-ocular-config.js'; 2 | 3 | export type {OcularConfig} from './helpers/get-ocular-config.js'; 4 | -------------------------------------------------------------------------------- /modules/dev-tools/src/test.ts: -------------------------------------------------------------------------------- 1 | // Launch script for various Node test configurations 2 | import fs from 'fs'; 3 | import {resolve} from 'path'; 4 | import {execShellCommand} from './utils/utils.js'; 5 | 6 | import {getOcularConfig} from './helpers/get-ocular-config.js'; 7 | 8 | import {BrowserTestDriver} from '@probe.gl/test-utils'; 9 | import {createServer} from 'vite'; 10 | 11 | const mode = process.argv.length >= 3 ? process.argv[2] : 'default'; 12 | const ocularConfig = await getOcularConfig({aliasMode: mode}); 13 | const viteConfigPath = ocularConfig.vite.configPath; 14 | // c8 default directory for coverage data 15 | const CoverageTempDir = './coverage/tmp'; 16 | 17 | console.log(`Running ${mode} tests...`); // eslint-disable-line 18 | 19 | switch (mode) { 20 | case 'cover': 21 | if (ocularConfig.coverage.test === 'node') { 22 | runNodeTest(resolveNodeEntry('test'), 'npx c8 --reporter=none'); 23 | } else { 24 | await runBrowserTest({ 25 | server: { 26 | start: createViteServer, 27 | options: { 28 | mode: 'test' 29 | } 30 | }, 31 | url: resolveBrowserEntry('test'), 32 | headless: 'new', 33 | onStart: async ({page}) => { 34 | clearCoverage(); 35 | await page.coverage.startJSCoverage({includeRawScriptCoverage: true}); 36 | }, 37 | onFinish: async ({page, isSuccessful}) => { 38 | const coverage = await page.coverage.stopJSCoverage(); 39 | if (!isSuccessful) return; 40 | writeCoverage(coverage); 41 | } 42 | }); 43 | } 44 | break; 45 | 46 | case 'node-debug': 47 | runNodeTest(resolveNodeEntry('test'), '', {breakAndInspect: true}); 48 | break; 49 | 50 | case 'node': 51 | case 'dist': 52 | runNodeTest(resolveNodeEntry('test')); // Run the tests 53 | break; 54 | 55 | case 'browser': 56 | case 'browser-headless': 57 | await runBrowserTest({ 58 | server: { 59 | start: createViteServer, 60 | options: { 61 | mode: 'test' 62 | } 63 | }, 64 | url: resolveBrowserEntry('test'), 65 | headless: mode === 'browser-headless' ? 'new' : false 66 | }); 67 | break; 68 | 69 | default: 70 | if (/\bbrowser\b/.test(mode)) { 71 | const testMode = mode.replace('-browser', '').replace('-headless', ''); 72 | await runBrowserTest({ 73 | server: { 74 | start: createViteServer, 75 | options: { 76 | mode: testMode 77 | } 78 | }, 79 | url: resolveBrowserEntry(testMode), 80 | headless: /\bheadless\b/.test(mode) ? 'new' : false 81 | }); 82 | } else if (mode in ocularConfig.entry) { 83 | runNodeTest(resolveNodeEntry(mode)); 84 | } else { 85 | throw new Error(`Unknown test mode ${mode}`); 86 | } 87 | } 88 | 89 | function resolveNodeEntry(key: string): string { 90 | const entry = ocularConfig.entry[key]; 91 | if (typeof entry === 'string') { 92 | return resolve(entry); 93 | } 94 | throw new Error(`Cannot find entry point ${key} in ocular config.`); 95 | } 96 | 97 | function resolveBrowserEntry(key: string): string { 98 | const fileName = ocularConfig.entry[`${key}-browser`]; 99 | if (typeof fileName === 'string' && fileName.endsWith('.html')) { 100 | return fileName; 101 | } else if (fileName) { 102 | return 'index.html'; 103 | } 104 | throw new Error(`Cannot find entry point ${key}-browser in ocular config.`); 105 | } 106 | 107 | function runNodeTest( 108 | entry: string, 109 | command: string = '', 110 | {breakAndInspect}: {breakAndInspect: boolean} = {breakAndInspect: false} 111 | ) { 112 | // Save module alias 113 | fs.writeFileSync( 114 | resolve(ocularConfig.ocularPath, '.alias.json'), 115 | JSON.stringify(ocularConfig.aliases) 116 | ); 117 | 118 | const inspectBrk = breakAndInspect ? '--inspect-brk' : ''; 119 | 120 | if (ocularConfig.esm) { 121 | execShellCommand( 122 | `${command} node ${inspectBrk} --import "${ocularConfig.ocularPath}/dist/helpers/esm-register.js" --es-module-specifier-resolution=node "${entry}"` 123 | ); 124 | } else { 125 | execShellCommand( 126 | `${command} ts-node -r "${ocularConfig.ocularPath}/dist/helpers/cjs-register.cjs" "${entry}"` 127 | ); 128 | } 129 | } 130 | 131 | function runBrowserTest(opts) { 132 | const userConfig = ocularConfig.browserTest || {}; 133 | return new BrowserTestDriver().run({ 134 | ...opts, 135 | ...userConfig, 136 | server: {...opts.server, ...userConfig.server}, 137 | browser: {...opts.browser, ...userConfig.browser} 138 | }); 139 | } 140 | 141 | async function createViteServer(config) { 142 | const server = await createServer({ 143 | configFile: viteConfigPath, 144 | mode: config.options?.mode, 145 | server: { 146 | port: config.port 147 | } 148 | }); 149 | await server.listen(); 150 | 151 | return { 152 | url: server.resolvedUrls?.local[0], 153 | stop: () => { 154 | server.close(); 155 | } 156 | }; 157 | } 158 | 159 | function clearCoverage() { 160 | fs.rmSync(CoverageTempDir, {force: true, recursive: true}); 161 | fs.mkdirSync(CoverageTempDir, {recursive: true}); 162 | } 163 | 164 | /** Write raw coverage data to disk for c8 to report */ 165 | function writeCoverage(coverage) { 166 | const outputFile = `${CoverageTempDir}/coverage-${Date.now()}`; 167 | 168 | // Convert Chrome coverage format to v8 169 | let idx = 0; 170 | for (const cov of coverage) { 171 | const it = cov.rawScriptCoverage; 172 | const filePath = it.url.replace(/^http:\/\/localhost:\d+\//, ''); 173 | 174 | // Excluded directories 175 | if (filePath.match(/(^|\/)(node_modules|test|@vite)\//)) continue; 176 | // Remap file url to path on local disk 177 | const fileUrl = `file://${resolve(filePath)}`; 178 | it.url = fileUrl; 179 | 180 | const sourcemapCache = {}; 181 | const [generatedSource, sourcemapDataUrl] = cov.text.split(/\/\/# sourceMappingURL=/); 182 | if (sourcemapDataUrl) { 183 | // Save source mapping for c8 reporter 184 | sourcemapCache[fileUrl] = { 185 | lineLengths: generatedSource.split('\n').map((l) => l.length), 186 | data: sourcemapFromDataUrl(sourcemapDataUrl) 187 | }; 188 | } 189 | 190 | fs.writeFileSync( 191 | `${outputFile}-${idx++}.json`, 192 | JSON.stringify({ 193 | result: [it], 194 | 'source-map-cache': sourcemapCache 195 | }), 196 | 'utf8' 197 | ); 198 | } 199 | } 200 | 201 | function sourcemapFromDataUrl(url: string): string | null { 202 | const [format, data] = url.split(','); 203 | const base64 = format.endsWith('base64'); 204 | const decodedData = base64 ? Buffer.from(data, 'base64').toString('utf8') : data; 205 | try { 206 | return JSON.parse(decodedData); 207 | } catch (err) { 208 | return null; 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /modules/dev-tools/src/ts-plugins/ts-transform-append-extension/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript transform to append file extension to import statements in the compiled JS files 3 | * Usage with ts-patch: 4 | { 5 | "plugins": [ 6 | { 7 | "transform": "ocular-dev-tools/ts-transform-append-extension", 8 | "extensions": [".js"], 9 | "after": true 10 | } 11 | ] 12 | } 13 | * Adapted from https://github.com/murolem/ts-transformer-append-js-extension to support custom extensions 14 | */ 15 | import * as path from 'path'; 16 | import type {Program, TransformationContext, SourceFile, Node} from 'typescript'; 17 | import type {TransformerExtras, PluginConfig} from 'ts-patch'; 18 | 19 | type AppendExtensionPluginConfig = PluginConfig & { 20 | /** List of file extensions, for example: 21 | * '.js': applies to paths without an extension, e.g. `import {add} from './math'` => `import {add} from './math.js'` 22 | * '.lib.cjs': applies to paths ending with .lib, e.g. `import fft from './fft.lib` => `import fft from './fft.lib.cjs'` 23 | * @default [".js"] 24 | */ 25 | extensions?: string[]; 26 | }; 27 | 28 | export default function ( 29 | program: Program, 30 | pluginConfig: AppendExtensionPluginConfig, 31 | {ts}: TransformerExtras 32 | ) { 33 | // only append .js when module specifier has no extension or user-provided extensions 34 | const {extensions = ['.js']} = pluginConfig; 35 | const extMappings = new Map(); 36 | for (const ext of extensions) { 37 | const addition = path.extname(ext) || ext; 38 | const base = path.basename(ext, addition); 39 | extMappings.set(base, addition); 40 | } 41 | 42 | function shouldMutateModuleSpecifier(node: Node): string | false { 43 | if (!ts.isImportDeclaration(node) && !ts.isExportDeclaration(node)) return false; 44 | if (node.moduleSpecifier === undefined) return false; 45 | // only when module specifier is valid 46 | if (!ts.isStringLiteral(node.moduleSpecifier)) return false; 47 | // only when path is relative 48 | if (!node.moduleSpecifier.text.startsWith('./') && !node.moduleSpecifier.text.startsWith('../')) 49 | return false; 50 | // only when module specifier has accepted extension 51 | const ext = path.extname(node.moduleSpecifier.text); 52 | if (!extMappings.has(ext)) return false; 53 | return node.moduleSpecifier.text + extMappings.get(ext); 54 | } 55 | 56 | return (ctx: TransformationContext) => { 57 | const {factory} = ctx; 58 | 59 | return (sourceFile: SourceFile) => { 60 | function visit(node: Node): Node { 61 | const newImportSource = shouldMutateModuleSpecifier(node); 62 | if (newImportSource) { 63 | if (ts.isImportDeclaration(node)) { 64 | const newModuleSpecifier = factory.createStringLiteral(newImportSource); 65 | node = factory.updateImportDeclaration( 66 | node, 67 | node.modifiers, 68 | node.importClause, 69 | newModuleSpecifier, 70 | node.assertClause 71 | ); 72 | } else if (ts.isExportDeclaration(node)) { 73 | const newModuleSpecifier = factory.createStringLiteral(newImportSource); 74 | node = factory.updateExportDeclaration( 75 | node, 76 | node.modifiers, 77 | node.isTypeOnly, 78 | node.exportClause, 79 | newModuleSpecifier, 80 | node.assertClause 81 | ); 82 | } 83 | } 84 | 85 | return ts.visitEachChild(node, visit, ctx); 86 | } 87 | 88 | return ts.visitNode(sourceFile, visit); 89 | }; 90 | }; 91 | } 92 | -------------------------------------------------------------------------------- /modules/dev-tools/src/ts-plugins/ts-transform-inline-webgl-constants/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript transform to replaces `gl.` or `GL.` references with 3 | * the corresponding WebGL constant value. Requires `@luma.gl/constants` as peer dependency. 4 | * Usage with ts-patch: 5 | { 6 | "plugins": [ 7 | { 8 | "transform": "ocular-dev-tools/ts-transform-inline-webgl-constants" 9 | } 10 | ] 11 | } 12 | */ 13 | import type {Program, TransformationContext, SourceFile, Node} from 'typescript'; 14 | import type {TransformerExtras, PluginConfig} from 'ts-patch'; 15 | import {GL} from '@luma.gl/constants'; 16 | 17 | export default function (program: Program, pluginConfig: PluginConfig, {ts}: TransformerExtras) { 18 | return (ctx: TransformationContext) => { 19 | const {factory} = ctx; 20 | 21 | function filterLeftIdentifier(node: Node): boolean { 22 | const left = node.getChildAt(0); 23 | return ts.isIdentifier(left) && (left.text === 'GL' || left.text === 'gl'); 24 | } 25 | 26 | return (sourceFile: SourceFile) => { 27 | function visit(node: Node): Node { 28 | if (ts.isPropertyAccessExpression(node) && filterLeftIdentifier(node)) { 29 | const key = node.getChildAt(2); 30 | if (ts.isIdentifier(key) && key.text in GL) { 31 | return factory.createNumericLiteral(GL[key.text]); 32 | } 33 | } 34 | if (ts.isElementAccessExpression(node) && filterLeftIdentifier(node)) { 35 | const key = node.getChildAt(2); 36 | if (ts.isStringLiteral(key) && key.text in GL) { 37 | return factory.createNumericLiteral(GL[key.text]); 38 | } 39 | } 40 | return ts.visitEachChild(node, visit, ctx); 41 | } 42 | return ts.visitNode(sourceFile, visit); 43 | }; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /modules/dev-tools/src/ts-plugins/ts-transform-remove-glsl-comments/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript transform to remove comments and unnecessary white space from GLSL source. 3 | * A template string is considered GLSL source if: 4 | a) the file matches the pattern specified in the plugin config; or 5 | b) it is tagged as glsl`...` 6 | * Usage with ts-patch: 7 | { 8 | "plugins": [ 9 | { 10 | "transform": "ocular-dev-tools/ts-transform-remove-glsl-comments", 11 | "pattern": ["*.glsl.ts"] 12 | } 13 | ] 14 | } 15 | */ 16 | import * as path from 'path'; 17 | import type {Program, TransformationContext, SourceFile, Node} from 'typescript'; 18 | import type {TransformerExtras, PluginConfig} from 'ts-patch'; 19 | import minimatch from 'minimatch'; 20 | 21 | // inline comment is only safe to remove if it's followed by a return (i.e. end of comment) 22 | const INLINE_COMMENT_REGEX = /\s*\/\/.*[\n\r]/g; 23 | const BLOCK_COMMENT_REGEX = /\s*\/\*(\*(?!\/)|[^*])*\*\//g; 24 | const WHITESPACE_REGEX = /\s*[\n\r]\s*/gm; 25 | const DEFAULT_PATTERNS = []; 26 | 27 | type RemoveGLSLCommentsPluginConfig = PluginConfig & { 28 | /** Glob patterns of shader files to include. */ 29 | pattern?: string[]; 30 | }; 31 | 32 | export default function ( 33 | program: Program, 34 | pluginConfig: RemoveGLSLCommentsPluginConfig, 35 | {ts}: TransformerExtras 36 | ) { 37 | const {pattern = DEFAULT_PATTERNS} = pluginConfig; 38 | 39 | return (ctx: TransformationContext) => { 40 | const {factory} = ctx; 41 | 42 | return (sourceFile: SourceFile) => { 43 | const isShaderFile = matchFilePath(sourceFile.fileName, pattern); 44 | 45 | function replaceShaderString(node: Node): Node { 46 | if (ts.isNoSubstitutionTemplateLiteral(node)) { 47 | const text = node.rawText ?? ''; 48 | // Convert source text to string content 49 | const newText = filterShaderSource(text); 50 | if (newText === text) { 51 | return node; 52 | } 53 | return factory.createNoSubstitutionTemplateLiteral(newText, newText); 54 | } 55 | if (ts.isTemplateLiteralToken(node)) { 56 | const text = node.rawText ?? ''; 57 | const newText = filterShaderSource(text); 58 | if (newText === text) { 59 | return node; 60 | } 61 | if (ts.isTemplateHead(node)) { 62 | return factory.createTemplateHead(newText, newText); 63 | } 64 | if (ts.isTemplateMiddle(node)) { 65 | return factory.createTemplateMiddle(newText, newText); 66 | } 67 | if (ts.isTemplateTail(node)) { 68 | return factory.createTemplateTail(newText, newText); 69 | } 70 | return node; 71 | } 72 | return ts.visitEachChild(node, replaceShaderString, ctx); 73 | } 74 | 75 | function visit(node: Node): Node { 76 | if ( 77 | ts.isTaggedTemplateExpression(node) && 78 | // First child is the tag identifier 79 | node.getChildAt(0).getText() === 'glsl' 80 | ) { 81 | // Strip the template tag 82 | return replaceShaderString(node.getChildAt(1)); 83 | } 84 | if (isShaderFile && ts.isTemplateLiteral(node)) { 85 | return replaceShaderString(node); 86 | } 87 | return ts.visitEachChild(node, visit, ctx); 88 | } 89 | return ts.visitNode(sourceFile, visit); 90 | }; 91 | }; 92 | } 93 | 94 | function matchFilePath(filePath: string, includePatterns: string[]): boolean { 95 | const relPath = path.relative(process.env.PWD ?? '', filePath); 96 | for (const pattern of includePatterns) { 97 | if (minimatch(relPath, pattern)) { 98 | return true; 99 | } 100 | } 101 | return false; 102 | } 103 | 104 | function filterShaderSource(source: string): string { 105 | return source 106 | .replace(INLINE_COMMENT_REGEX, '\n') 107 | .replace(BLOCK_COMMENT_REGEX, '') 108 | .replace(WHITESPACE_REGEX, '\n'); 109 | } 110 | -------------------------------------------------------------------------------- /modules/dev-tools/src/ts-plugins/ts-transform-version-inline/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * TypeScript transform to inject the current version of the package as string. 3 | * Usage with ts-patch: 4 | { 5 | "plugins": [ 6 | { 7 | "transform": "ocular-dev-tools/ts-transform-version-inline", 8 | "identifier": "PACKAGE_VERSION" 9 | } 10 | ] 11 | } 12 | */ 13 | import * as fs from 'fs'; 14 | import * as path from 'path'; 15 | import type {Program, TransformationContext, SourceFile, Node} from 'typescript'; 16 | import type {TransformerExtras, PluginConfig} from 'ts-patch'; 17 | 18 | type VersionInlinePluginConfig = PluginConfig & { 19 | /** Identifier name to replace in code. 20 | * @default "__VERSION__" 21 | */ 22 | identifier?: string; 23 | }; 24 | 25 | export default function ( 26 | program: Program, 27 | pluginConfig: VersionInlinePluginConfig, 28 | {ts}: TransformerExtras 29 | ) { 30 | const {identifier = '__VERSION__'} = pluginConfig; 31 | 32 | return (ctx: TransformationContext) => { 33 | const {factory} = ctx; 34 | 35 | return (sourceFile: SourceFile) => { 36 | let packageVersion: string | null; 37 | 38 | function visit(node: Node): Node { 39 | if ( 40 | ts.isIdentifier(node) && 41 | !ts.isPropertyAccessExpression(node.parent) && 42 | node.getText() === identifier 43 | ) { 44 | if (packageVersion === undefined) { 45 | packageVersion = getPackageVersion(sourceFile.fileName); 46 | } 47 | if (packageVersion) { 48 | return factory.createStringLiteral(packageVersion); 49 | } 50 | } 51 | return ts.visitEachChild(node, visit, ctx); 52 | } 53 | return ts.visitNode(sourceFile, visit); 54 | }; 55 | }; 56 | } 57 | 58 | /** 59 | * Retrieve the version string from the closest package.json 60 | */ 61 | function getPackageVersion(fileName: string): string | null { 62 | let currentDir = fileName; 63 | while (currentDir !== '/') { 64 | try { 65 | currentDir = path.dirname(currentDir); 66 | const packageJson = path.join(currentDir, 'package.json'); 67 | const stat = fs.statSync(packageJson); 68 | if (stat.isFile()) { 69 | const content = fs.readFileSync(packageJson, 'utf8'); 70 | return JSON.parse(content).version as string; 71 | } 72 | } catch { 73 | // file does not exist, try going up 74 | } 75 | } 76 | return null; 77 | } 78 | -------------------------------------------------------------------------------- /modules/dev-tools/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | // https://docs.npmjs.com/cli/v10/configuring-npm/package-json 2 | // Only relevant fields are typed 3 | export type PackageJson = { 4 | name: string; 5 | version: string; 6 | type?: 'module' | 'commonjs'; 7 | main?: string; 8 | module?: string; 9 | exports?: { 10 | [path: string]: string | {import?: string; require?: string; type?: string; default?: string}; 11 | }; 12 | dependencies?: {[name: string]: string}; 13 | devDependencies?: {[name: string]: string}; 14 | peerDependencies?: {[name: string]: string}; 15 | [key: string]: unknown; 16 | }; 17 | -------------------------------------------------------------------------------- /modules/dev-tools/src/utils/utils.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import {resolve, dirname} from 'path'; 3 | import {fileURLToPath} from 'node:url'; 4 | import {execSync} from 'child_process'; 5 | 6 | export function execShellCommand(command: string, args: string[] = []) { 7 | try { 8 | execSync(`${command} ${args.join(' ')}`, { 9 | stdio: 'inherit' 10 | }); 11 | } catch (err: unknown) { 12 | process.exit((err as any).status); 13 | } 14 | } 15 | 16 | /** Returns the path to the root directory of ocular-dev-tools */ 17 | export const ocularRoot: string = (function () { 18 | let dir; 19 | try { 20 | dir = __dirname; 21 | } catch { 22 | dir = dirname(fileURLToPath(import.meta.url)); 23 | } 24 | return resolve(dir, '../..'); 25 | })(); 26 | 27 | export function shallowMerge(base: T, override: any): T { 28 | for (const key in override) { 29 | if (base[key] && typeof base[key] === 'object') { 30 | Object.assign(base[key], override[key]); 31 | } else { 32 | base[key] = override[key]; 33 | } 34 | } 35 | return base; 36 | } 37 | 38 | /** Given a list of paths, return the first one that exists */ 39 | export function getValidPath(...resolveOrder: string[]): string | null { 40 | for (const path of resolveOrder) { 41 | if (fs.existsSync(path)) { 42 | return path; 43 | } 44 | } 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | dist/* 3 | coverage/* 4 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/.eslintrc.gatsby-todo: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb", "prettier"], 3 | "plugins": ["react", "jsx-a11y", "import"], 4 | "rules": { 5 | "react/prefer-stateless-function": "off", 6 | "react/prop-types": "off", 7 | "react/no-danger": "off", 8 | "jsx-a11y/anchor-is-valid": [ "error", { 9 | "components": [ "Link" ], 10 | "specialLink": [ "hrefLeft", "hrefRight", "to" ], 11 | "aspects": [ "noHref", "invalidHref", "preferButton" ] 12 | }], 13 | "no-restricted-syntax": "off", 14 | "no-param-reassign": "off" 15 | }, 16 | "settings": { 17 | "import/core-modules": [] 18 | }, 19 | "env": { 20 | "browser": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "sourceMap": false, 4 | "instrument": true, 5 | "include": [ 6 | "src/**/*.+(js|jsx|cjs|mjs|ts|tsx)", 7 | "modules/**/src/**/*.+(js|jsx|cjs|mjs|ts|tsx)" 8 | ], 9 | "exclude": [ 10 | "**/deprecated", 11 | "**/wip", 12 | "**/*.spec.+(js|ts)", 13 | "**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/.prettierignore: -------------------------------------------------------------------------------- 1 | **/dist*/**/*.js 2 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/.prettierrc: -------------------------------------------------------------------------------- 1 | printWidth: 100 2 | semi: true 3 | singleQuote: true 4 | trailingComma: none 5 | bracketSpacing: false 6 | -------------------------------------------------------------------------------- /modules/dev-tools/templates/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "outDir": "dist", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "sourceMap": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "lib": [ 16 | "esnext", 17 | "dom" 18 | ] 19 | }, 20 | "include": [ 21 | "src/**/*" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /modules/dev-tools/test/index.ts: -------------------------------------------------------------------------------- 1 | import './lib/utils.spec'; 2 | import './lib/configuration.spec'; 3 | 4 | import './ts-plugins/ts-transform-version-inline.spec'; 5 | import './ts-plugins/ts-transform-append-extension.spec'; 6 | import './ts-plugins/ts-transform-remove-glsl-comments/index.spec'; 7 | import './ts-plugins/ts-transform-inline-webgl-constants.spec'; 8 | -------------------------------------------------------------------------------- /modules/dev-tools/test/lib/configuration.spec.ts: -------------------------------------------------------------------------------- 1 | import test from 'tape-promise/tape'; 2 | // @ts-expect-error Aliased import 3 | import {getBabelConfig, getESLintConfig, getPrettierConfig} from 'ocular-dev-tools/configuration'; 4 | 5 | test('dev-tools#getConfig', (t) => { 6 | const mockBabelApi = {cache: {using: () => {}}, env: (env) => env}; 7 | 8 | let config = getESLintConfig(); 9 | t.equals(typeof config, 'object'); 10 | 11 | config = getPrettierConfig(); 12 | t.equals(typeof config, 'object'); 13 | 14 | config = getBabelConfig(mockBabelApi); 15 | t.equals(typeof config, 'function'); 16 | 17 | t.end(); 18 | }); 19 | -------------------------------------------------------------------------------- /modules/dev-tools/test/lib/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import test from 'tape-promise/tape'; 2 | // @ts-expect-error Aliased import 3 | import {shallowMerge} from 'ocular-dev-tools/utils/utils'; 4 | 5 | test('dev-tools#utils', (t) => { 6 | t.equals(typeof shallowMerge, 'function'); 7 | 8 | t.end(); 9 | }); 10 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/test-transformer.ts: -------------------------------------------------------------------------------- 1 | import {Project, ts} from 'ts-morph'; 2 | import type {PluginConfig} from 'ts-patch'; 3 | 4 | /** 5 | * Transpile ts code with TypeScript compiler API 6 | */ 7 | export function transpile({ 8 | sourceFileName = 'test.ts', 9 | source, 10 | transformer, 11 | config = {}, 12 | outputType = 'js' 13 | }: { 14 | sourceFileName?: string; 15 | source: string; 16 | transformer: Function; 17 | config?: PluginConfig; 18 | outputType?: 'js' | 'd.ts'; 19 | }): string { 20 | const dts = outputType === 'd.ts'; 21 | 22 | const project = new Project({ 23 | compilerOptions: { 24 | target: ts.ScriptTarget.ESNext, 25 | module: ts.ModuleKind.ESNext, 26 | declaration: dts 27 | } 28 | }); 29 | 30 | project.createSourceFile(sourceFileName, source); 31 | 32 | const customTransformers: ts.CustomTransformers = {}; 33 | const transform: ts.TransformerFactory = transformer( 34 | project.getProgram(), 35 | config, 36 | {ts} 37 | ); 38 | if (config.after) { 39 | customTransformers.after = [transform]; 40 | } else if (config.afterDeclarations) { 41 | // @ts-ignore 42 | customTransformers.afterDeclarations = [transform]; 43 | } else { 44 | customTransformers.before = [transform]; 45 | } 46 | 47 | const result = project.emitToMemory({ 48 | customTransformers, 49 | emitOnlyDtsFiles: dts 50 | }); 51 | 52 | return result.getFiles()[0].text; 53 | } 54 | 55 | /** 56 | * Compare two pieces of source code. Returns a description of the difference, or null if identical. 57 | */ 58 | export function assertSourceEqual( 59 | actual: string, 60 | expected: string, 61 | options: { 62 | /** If true, ignore difference in indent 63 | * @default true 64 | */ 65 | ignoreIndent?: boolean; 66 | /** If true, ignore empty lines 67 | * @default true 68 | */ 69 | ignoreEmptyLines?: boolean; 70 | } = {} 71 | ): true | Error { 72 | const {ignoreIndent = true, ignoreEmptyLines = true} = options; 73 | const actualLines = actual.split('\n'); 74 | const expectedLines = expected.split('\n'); 75 | let i1 = 0; 76 | let i2 = 0; 77 | 78 | while (i1 < actualLines.length || i2 < expectedLines.length) { 79 | let t1 = actualLines[i1] ?? ''; 80 | let t2 = expectedLines[i2] ?? ''; 81 | if (ignoreIndent) { 82 | t1 = t1.trimStart(); 83 | t2 = t2.trimStart(); 84 | } 85 | if (t1 === t2) { 86 | i1++; 87 | i2++; 88 | } else if (ignoreEmptyLines && !t1) { 89 | i1++; 90 | } else if (ignoreEmptyLines && !t2) { 91 | i2++; 92 | } else { 93 | return new Error(`Mismatch at line ${i1} 94 | Actual: ${t1} 95 | Expected: ${t2} 96 | `); 97 | } 98 | } 99 | return true; 100 | } 101 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-append-extension.spec.ts: -------------------------------------------------------------------------------- 1 | import test from 'tape-promise/tape'; 2 | import {transpile, assertSourceEqual} from './test-transformer.js'; 3 | // @ts-expect-error Aliased import, remapped to valid path in esm-loader 4 | import appendExtension from 'ocular-dev-tools/ts-plugins/ts-transform-append-extension'; 5 | 6 | const input = `\ 7 | export type { TypedArray } from "./types"; 8 | export { add } from "./math/add"; 9 | import vs from "../shaders/vs.glsl"; 10 | import { Shader } from "@luma.gl/core"; 11 | export { vs, Shader };`; 12 | 13 | const testCases = [ 14 | { 15 | title: 'add default extension to js imports', 16 | config: {after: true}, 17 | output: `\ 18 | export { add } from "./math/add.js"; 19 | import vs from "../shaders/vs.glsl"; 20 | import { Shader } from "@luma.gl/core"; 21 | export { vs, Shader };` 22 | }, 23 | { 24 | title: 'add default extension to d.ts imports', 25 | config: {afterDeclarations: true}, 26 | output: `\ 27 | export type { TypedArray } from "./types.js"; 28 | export { add } from "./math/add.js"; 29 | import vs from "../shaders/vs.glsl"; 30 | import { Shader } from "@luma.gl/core"; 31 | export { vs, Shader };` 32 | }, 33 | { 34 | title: 'add custom extension to js imports', 35 | config: {after: true, extensions: ['.mjs', '.glsl.mjs']}, 36 | output: `\ 37 | export { add } from "./math/add.mjs"; 38 | import vs from "../shaders/vs.glsl.mjs"; 39 | import { Shader } from "@luma.gl/core"; 40 | export { vs, Shader };` 41 | } 42 | ]; 43 | 44 | test('ts-transform-append-extension', (t) => { 45 | for (const testCase of testCases) { 46 | const result = transpile({ 47 | source: input, 48 | transformer: appendExtension, 49 | config: testCase.config, 50 | outputType: testCase.config.afterDeclarations ? 'd.ts' : 'js' 51 | }); 52 | 53 | t.is(assertSourceEqual(result, testCase.output), true, testCase.title); 54 | } 55 | 56 | t.end(); 57 | }); 58 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-inline-webgl-constants.spec.ts: -------------------------------------------------------------------------------- 1 | import test from 'tape-promise/tape'; 2 | import {transpile, assertSourceEqual} from './test-transformer.js'; 3 | // @ts-expect-error Aliased import, remapped to valid path in esm-loader 4 | import inlineConstants from 'ocular-dev-tools/ts-plugins/ts-transform-inline-webgl-constants'; 5 | 6 | const testCases = [ 7 | { 8 | title: 'drop GL import', 9 | input: `\ 10 | device.setParametersWebGL({ 11 | blendFunc: [GL.ONE, GL.ONE_MINUS_DST_COLOR, GL.SRC_ALPHA, GL.DST_ALPHA] 12 | }); 13 | `, 14 | output: `\ 15 | device.setParametersWebGL({ 16 | blendFunc: [1, 775, 770, 772] 17 | }); 18 | ` 19 | }, 20 | { 21 | title: 'gl constants replaced', 22 | input: `gl.getParameter(gl.CULL_FACE_MODE);`, 23 | output: `gl.getParameter(2885);` 24 | }, 25 | { 26 | title: 'static property replaced', 27 | input: `console.log(GL['TRIANGLES']);`, 28 | output: `console.log(4);` 29 | }, 30 | { 31 | title: 'dynamic property not replaced', 32 | input: `\ 33 | const name = 'TRIANGLES'; 34 | console.log(GL[name]); 35 | `, 36 | output: `\ 37 | const name = 'TRIANGLES'; 38 | console.log(GL[name]); 39 | ` 40 | } 41 | ]; 42 | 43 | test('ts-transform-inline-webgl-constants', (t) => { 44 | for (const testCase of testCases) { 45 | const result = transpile({ 46 | source: testCase.input, 47 | transformer: inlineConstants, 48 | config: {} 49 | }); 50 | 51 | t.is(assertSourceEqual(result, testCase.output), true, testCase.title); 52 | } 53 | 54 | t.end(); 55 | }); 56 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/index.spec.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import {fileURLToPath} from 'node:url'; 4 | 5 | import test from 'tape-promise/tape'; 6 | import {transpile, assertSourceEqual} from '../test-transformer.js'; 7 | // @ts-expect-error Aliased import, remapped to valid path in esm-loader 8 | import removeGLSLComments from 'ocular-dev-tools/ts-plugins/ts-transform-remove-glsl-comments'; 9 | 10 | const __dirname = path.dirname(fileURLToPath(import.meta.url)); 11 | 12 | function loadSourceFromFile(fileName: string): string { 13 | return fs.readFileSync(path.join(__dirname, fileName), 'utf8'); 14 | } 15 | 16 | const testCases = [ 17 | { 18 | title: 'no comments', 19 | fileName: 'test.glsl.ts', 20 | config: {pattern: ['**/*.glsl.ts']}, 21 | input: 'test-case-0.ts', 22 | output: 'test-case-0-expected.ts' 23 | }, 24 | { 25 | title: 'remove comments from template literal', 26 | fileName: 'test.glsl.ts', 27 | config: {pattern: ['**/*.glsl.ts']}, 28 | input: 'test-case-1.ts', 29 | output: 'test-case-1-expected.ts' 30 | }, 31 | { 32 | title: 'excluded by file name', 33 | fileName: 'test.ts', 34 | config: {}, 35 | input: 'test-case-1.ts', 36 | output: 'test-case-1.ts' 37 | }, 38 | { 39 | title: 'included by template tag', 40 | fileName: 'test.ts', 41 | config: {}, 42 | input: 'test-case-2.ts', 43 | output: 'test-case-2-expected.ts' 44 | } 45 | ]; 46 | 47 | test('ts-transform-remove-glsl-comments', (t) => { 48 | for (const testCase of testCases) { 49 | const result = transpile({ 50 | sourceFileName: testCase.fileName, 51 | source: loadSourceFromFile(testCase.input), 52 | transformer: removeGLSLComments, 53 | config: testCase.config 54 | }); 55 | const expected = loadSourceFromFile(testCase.output); 56 | 57 | t.is(assertSourceEqual(result, expected, {ignoreEmptyLines: false}), true, testCase.title); 58 | } 59 | 60 | t.end(); 61 | }); 62 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-0-expected.ts: -------------------------------------------------------------------------------- 1 | export function getTime() { 2 | // Template literal that is not shader 3 | return `The time is ${Date.now()}`; 4 | } 5 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-0.ts: -------------------------------------------------------------------------------- 1 | export function getTime() { 2 | // Template literal that is not shader 3 | return `The time is ${Date.now()}`; 4 | } 5 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-1-expected.ts: -------------------------------------------------------------------------------- 1 | // Constants 2 | const COORDINATE_SYSTEM = { 3 | CARTESIAN: 0, 4 | LNGLAT: 1 5 | }; 6 | const constantDefinitions = Object.keys(COORDINATE_SYSTEM) 7 | .map((key) => `const int COORDINATE_SYSTEM_${key} = ${COORDINATE_SYSTEM[key]};`) 8 | .join('\n'); 9 | // Vertex shader 10 | export const vs = `\ 11 | #version 300 es 12 | ${constantDefinitions} 13 | in vec4 position; 14 | in vec4 color; 15 | uniform mat4 pMatrix; 16 | uniform mat4 mMatrix; 17 | uniform float opacity; 18 | out vec4 vColor; 19 | main() { 20 | gl_Position = pMatrix * mMatrix * position; 21 | vColor = vec4(color, color.a * opacity); 22 | } 23 | `; 24 | // Fragment shader 25 | export const fs = `\ 26 | #version 300 es 27 | in vec4 vColor; 28 | main() { 29 | if (vColor.a == 0.0) { 30 | discard; 31 | } 32 | gl_FragColor = vColor; 33 | } 34 | `; 35 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-1.ts: -------------------------------------------------------------------------------- 1 | // Constants 2 | const COORDINATE_SYSTEM = { 3 | CARTESIAN: 0, 4 | LNGLAT: 1 5 | }; 6 | const constantDefinitions = Object.keys(COORDINATE_SYSTEM) 7 | .map((key) => `const int COORDINATE_SYSTEM_${key} = ${COORDINATE_SYSTEM[key]};`) 8 | .join('\n'); 9 | // Vertex shader 10 | export const vs = `\ 11 | #version 300 es 12 | ${constantDefinitions} 13 | 14 | in vec4 position; 15 | in vec4 color; 16 | 17 | uniform mat4 pMatrix; // Projection matrix 18 | uniform mat4 mMatrix; // Model matrix 19 | uniform float opacity; 20 | 21 | out vec4 vColor; 22 | 23 | main() { 24 | gl_Position = pMatrix * mMatrix * position; 25 | vColor = vec4(color, /* inline comment */ color.a * opacity); 26 | } 27 | `; 28 | // Fragment shader 29 | export const fs = `\ 30 | #version 300 es 31 | 32 | in vec4 vColor; 33 | 34 | main() { 35 | if (vColor.a == 0.0) { 36 | /* 37 | Remove transparent fragment 38 | */ 39 | discard; 40 | } 41 | gl_FragColor = vColor; 42 | } 43 | `; 44 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-2-expected.ts: -------------------------------------------------------------------------------- 1 | const glsl = (x) => `${x}`; 2 | export default { 3 | name: 'shader-module', 4 | getUniforms: () => ({ 5 | useFloatColors: false 6 | }), 7 | inject: { 8 | 'vs:DECKGL_FILTER_COLOR': ` 9 | picking_setPickingColor(geometry.pickingColor); 10 | `, 11 | 'fs:DECKGL_FILTER_COLOR': { 12 | order: 99, 13 | injection: ` 14 | color = picking_filterHighlightColor(color); 15 | color = picking_filterPickingColor(color); 16 | ` 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-remove-glsl-comments/test-case-2.ts: -------------------------------------------------------------------------------- 1 | const glsl = (x: TemplateStringsArray) => `${x}`; 2 | export default { 3 | name: 'shader-module', 4 | getUniforms: () => ({ 5 | useFloatColors: false 6 | }), 7 | inject: { 8 | 'vs:DECKGL_FILTER_COLOR': glsl` 9 | // pickingColor is expected to be populated by this point 10 | picking_setPickingColor(geometry.pickingColor); 11 | `, 12 | 'fs:DECKGL_FILTER_COLOR': { 13 | order: 99, 14 | injection: glsl` 15 | // use highlight color if this fragment belongs to the selected object. 16 | color = picking_filterHighlightColor(color); 17 | 18 | // use picking color if rendering to picking FBO. 19 | color = picking_filterPickingColor(color); 20 | ` 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /modules/dev-tools/test/ts-plugins/ts-transform-version-inline.spec.ts: -------------------------------------------------------------------------------- 1 | import test from 'tape-promise/tape'; 2 | import {transpile, assertSourceEqual} from './test-transformer.js'; 3 | // @ts-expect-error Aliased import, remapped to valid path in esm-loader 4 | import versionInline from 'ocular-dev-tools/ts-plugins/ts-transform-version-inline'; 5 | 6 | const testCases = [ 7 | { 8 | title: 'default identifier replaced', 9 | config: {}, 10 | input: 'const version: string = __VERSION__;', 11 | output: 'const version = "0.0.0";' 12 | }, 13 | { 14 | title: 'no matching identifier', 15 | config: {}, 16 | input: 'const version: string = deck.__VERSION__;', 17 | output: 'const version = deck.__VERSION__;' 18 | }, 19 | { 20 | title: 'custom identifier replaced', 21 | config: {identifier: '__package_version'}, 22 | input: 'const version: string = __package_version;', 23 | output: 'const version = "0.0.0";' 24 | } 25 | ]; 26 | 27 | test('ts-transform-version-inline', (t) => { 28 | for (const testCase of testCases) { 29 | const result = transpile({ 30 | source: testCase.input, 31 | transformer: versionInline, 32 | config: testCase.config 33 | }); 34 | 35 | t.is(assertSourceEqual(result, testCase.output), true, testCase.title); 36 | } 37 | 38 | t.end(); 39 | }); 40 | -------------------------------------------------------------------------------- /modules/dev-tools/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "noImplicitAny": false, 6 | "allowJs": true, 7 | "checkJs": false, 8 | "declaration": true, 9 | "declarationMap": true, 10 | "moduleResolution": "node", 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "baseUrl": ".", 14 | "rootDir": "src", 15 | "outDir": "dist", 16 | "composite": true, 17 | "skipLibCheck": true, 18 | "strict": true 19 | }, 20 | "include":[ 21 | "src/**/*" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocular-monorepo", 3 | "version": "0.0.0", 4 | "private": true, 5 | "type": "module", 6 | "workspaces": [ 7 | "modules/dev-tools" 8 | ], 9 | "scripts": { 10 | "bootstrap": "yarn install-fast && ocular-bootstrap", 11 | "install-fast": "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true yarn", 12 | "build": "ocular-clean && tspc -b modules/dev-tools/tsconfig.json && ocular-build", 13 | "clean": "ocular-clean", 14 | "cover": "ocular-test cover", 15 | "lint": "ocular-lint", 16 | "test": "ocular-test node", 17 | "publish-devtools-beta": "(cd modules/dev-tools; npm run publish-beta)", 18 | "publish-devtools-prod": "(cd modules/dev-tools; npm run publish-prod)", 19 | "pre-commit": "yarn lint", 20 | "pre-push": "ocular-test fast" 21 | }, 22 | "devDependencies": { 23 | "@babel/plugin-syntax-import-assertions": "^7.20.0", 24 | "@luma.gl/constants": "^9.0.0-beta", 25 | "pre-commit": "^1.2.2", 26 | "pre-push": "^0.1.1", 27 | "ts-morph": "^21.0.0" 28 | }, 29 | "pre-commit": "pre-commit", 30 | "pre-push": "pre-push", 31 | "dependencies": {}, 32 | "volta": { 33 | "node": "18.19.0", 34 | "yarn": "1.22.19" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /scripts/remove-refs-to-unpm.pl: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | perl -pi -e 's/unpm\.uberinternal\.com/registry\.yarnpkg\.com/g' `find . -name yarn.lock` 4 | -------------------------------------------------------------------------------- /test/browser.ts: -------------------------------------------------------------------------------- 1 | import './modules'; 2 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | luma.gl tests 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /test/modules.ts: -------------------------------------------------------------------------------- 1 | import '../modules/dev-tools/test'; 2 | -------------------------------------------------------------------------------- /test/node.ts: -------------------------------------------------------------------------------- 1 | import './modules'; 2 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | }, 5 | "include":[ 6 | "modules/*/src" 7 | ], 8 | "references": [ 9 | {"path": "modules/dev-tools"} 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "esnext", 5 | "jsx": "react", 6 | "strict": true, 7 | "noImplicitAny": false, 8 | "allowJs": true, 9 | "checkJs": false, 10 | "moduleResolution": "node", 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "noEmit": true, 14 | "resolveJsonModule": true, 15 | "baseUrl": ".", 16 | "skipLibCheck": true 17 | }, 18 | "include": [ 19 | "modules", 20 | "test" 21 | ], 22 | "exclude":[ 23 | "node_modules" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /website/.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | ### Node ### 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | *.pid.lock 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional eslint cache 39 | .eslintcache 40 | 41 | # Optional REPL history 42 | .node_repl_history 43 | 44 | # Output of 'npm pack' 45 | *.tgz 46 | 47 | # Yarn Integrity file 48 | .yarn-integrity 49 | 50 | 51 | # Build Files 52 | public/ 53 | .cache/ 54 | 55 | # Gatsby context 56 | .gatsby-context.js 57 | 58 | # Bundle stats 59 | bundle-stats.json 60 | -------------------------------------------------------------------------------- /website/gatsby-config.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | 3 | // default location for table of contents 4 | const DOCS = require('../docs/table-of-contents.json'); 5 | 6 | module.exports = { 7 | plugins: [{ 8 | resolve: 'gatsby-theme-ocular', 9 | options: { 10 | // Adjusts amount of debug information from gatsby-theme-ocular 11 | logLevel: 1, 12 | 13 | DOC_FOLDERS: [ 14 | `${__dirname}/../docs/`, 15 | `${__dirname}/../modules/` 16 | ], 17 | ROOT_FOLDER: `${__dirname}/../`, 18 | SOURCE: [ 19 | `${__dirname}/static`, 20 | `${__dirname}/src` 21 | ], 22 | 23 | PROJECT_TYPE: 'github', 24 | PROJECT_NAME: 'Ocular', 25 | PROJECT_ORG: 'uber-web', 26 | PROJECT_ORG_LOGO: 'images/uber-logo.png', 27 | PROJECT_URL: 'https://github.com/uber-web/ocular', 28 | PROJECT_DESC: 'Uber\'s open source documentation system', 29 | PATH_PREFIX: '', 30 | 31 | LINK_TO_GET_STARTED: '/docs', 32 | PAGES: [ 33 | { 34 | path: '/', 35 | content: resolve('./src/home.md') 36 | }, 37 | { 38 | path: '/about', 39 | content: resolve('./src/about.md') 40 | } 41 | ], 42 | 43 | // your table of contents goes here 44 | DOCS, 45 | 46 | EXAMPLES: [ 47 | // { 48 | // title: 'Minimal Example', 49 | // path: 'examples/minimal', 50 | // image: 'images/hero.jpg', 51 | // componentUrl: resolve(__dirname, '../examples/minimal/app.js') 52 | // } 53 | ], 54 | 55 | // THEME_OVERRIDES: require('./src/theme.json'), 56 | 57 | PROJECTS: [ 58 | {name: 'deck.gl', url: 'https://deck.gl'}, 59 | {name: 'luma.gl', url: 'https://luma.gl'}, 60 | {name: 'loaders.gl', url: 'https://loaders.gl'} 61 | ], 62 | ADDITIONAL_LINKS: [ 63 | {name: 'About', href: '/about'} 64 | ], 65 | 66 | GA_TRACKING_ID: 'dummy_tracking_id', 67 | 68 | // For showing star counts and contributors. 69 | // Should be like btoa('YourUsername:YourKey') and should be readonly. 70 | GITHUB_KEY: null 71 | } 72 | }] 73 | }; 74 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ocular-website", 3 | "version": "1.0.0", 4 | "description": "Website for the vis.gl open source documentation system", 5 | "main": "index.js", 6 | "author": "", 7 | "license": "MIT", 8 | "scripts": { 9 | "start": "yarn clean && yarn develop", 10 | "clean": "rm -rf ./.cache ./public", 11 | "develop": "yarn clean && gatsby develop --port=8002", 12 | "build": "yarn clean && gatsby build --prefix-paths", 13 | "serve": "gatsby serve --prefix-paths", 14 | "deploy": "NODE_DEBUG=gh-pages gh-pages -d public" 15 | }, 16 | "dep_comment": "Ensure gatsby-theme-ocular references unpublished version to trigger local workspaces link", 17 | "dependencies": { 18 | "gatsby": "^2.18.0", 19 | "gatsby-theme-ocular": "^1.2.4", 20 | "react": "^16.8.6", 21 | "react-dom": "^16.8.6", 22 | "sharp": "^0.31.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /website/src/about.md: -------------------------------------------------------------------------------- 1 | # About us 2 | -------------------------------------------------------------------------------- /website/src/home.md: -------------------------------------------------------------------------------- 1 | ## Uber's open source documentation system 2 | 3 | - ![react](images/icon-react.svg) Designed for React 4 | - ![customizable](images/icon-custom.svg) Highly customizable 5 | - ![production-ready](images/icon-target.svg) Totally ready for production 6 | -------------------------------------------------------------------------------- /website/src/theme.json: -------------------------------------------------------------------------------- 1 | { 2 | "primary400": "#FFC000" 3 | } -------------------------------------------------------------------------------- /website/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-web/ocular/6d84ec50d8c80dc2cf361e818e9edb35f597042a/website/static/images/favicon.ico -------------------------------------------------------------------------------- /website/static/images/hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-web/ocular/6d84ec50d8c80dc2cf361e818e9edb35f597042a/website/static/images/hero.jpg -------------------------------------------------------------------------------- /website/static/images/icon-custom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | wrench 4 | Based on font-awesome and made with vim by balthazar ;) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /website/static/images/icon-react.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /website/static/images/icon-target.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | high-precision 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /website/static/images/uber-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uber-web/ocular/6d84ec50d8c80dc2cf361e818e9edb35f597042a/website/static/images/uber-logo.png --------------------------------------------------------------------------------