diff --git a/docker/templates/new-vanilla-js-app/cookiecutter.json b/docker/templates/new-vanilla-js-app/cookiecutter.json new file mode 100644 index 0000000..3e63502 --- /dev/null +++ b/docker/templates/new-vanilla-js-app/cookiecutter.json @@ -0,0 +1,6 @@ +{ + "directory_name": "my-javascript-app", + "app_name": "my-javascript-app", + "app_verbose_name": "My JavaScript app", + "app_description": "A JavaScript app" +} \ No newline at end of file diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/.gitignore b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/README.md b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/README.md new file mode 100644 index 0000000..c18dd81 --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/README.md @@ -0,0 +1,49 @@ +# {{cookiecutter.app_verbose_name}} + +_{{cookiecutter.app_description}}_ + +## Developing + +Development requires a local installation of [Yarn](https://yarnpkg.com/). + +Run the app: + +``` +yarn run develop +``` + +The app will be available at http://localhost:8000. + +### Ensuring browser compatibility + +The default `babel.config.json` file will transpile JavaScript in your `js/` +directory to syntax that's friendly to modern and legacy browsers but it will +not transpile third-party plug-ins by default. + +Some plug-ins target Node versions above ES5, which means that they aren't +compatible for older browsers. Luckily, we can tell Babel to transpile these +dependencies to ensure our apps remain broadly compatible across browsers. + +To identify problematic plug-ins, you can use [the `es6-sniffer` CLI](https://github.com/hancush/python-es6-sniffer). + +```bash +# Build the es6-sniffer image from GitHub +docker build -t es6-sniffer https://github.com/hancush/python-es6-sniffer.git + +# Sniff out potentially incompatible modules +docker run -v {{ cookiecutter.app_name }}_{{ cookiecutter.app_name }}-node-modules:/node_modules --rm es6-sniffer +``` + +Once you've found the culprits, add them to the `only` array in +`babel.config.json`. For example: + +```json +{ + "only": [ + "./js", // Your JavaScript - default + "./node_modules/problem_module_a", + "./node_modules/problem_module_b" + ], + // The rest of your config +} +``` diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/babel.config.json b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/babel.config.json new file mode 100644 index 0000000..e298175 --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/babel.config.json @@ -0,0 +1,17 @@ +{ + "only": [ + "./js" + ], + "presets": [ + [ + "@babel/preset-env", { + "targets": "> 0.25%, not dead", + "useBuiltIns": "usage", + "corejs": { + "version": "3.8", + "proposals": "true" + } + } + ] + ] +} diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/css/main.css b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/css/main.css new file mode 100644 index 0000000..e69de29 diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/index.html b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/index.html new file mode 100644 index 0000000..2a68250 --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/index.html @@ -0,0 +1,102 @@ +{% raw %} + + + + + + + {% endraw %}{{cookiecutter.app_verbose_name}}{% raw %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

Hello, world!

+
+
+ + + + + + + + + + + +{% endraw %} diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/js/main.js b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/js/main.js new file mode 100644 index 0000000..404eb15 --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/js/main.js @@ -0,0 +1,2 @@ +let foo = 'bar'; +console.log(foo); diff --git a/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/package.json b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/package.json new file mode 100644 index 0000000..e164caa --- /dev/null +++ b/docker/templates/new-vanilla-js-app/{{cookiecutter.directory_name}}/package.json @@ -0,0 +1,19 @@ +{ + "name": "{{cookiecutter.app_name}}", + "author": "DataMade ", + "version": "0.0.0", + "description": "{{cookiecutter.app_description}}", + "dependencies": { + "@babel/core": "^7.14.6", + "@babel/preset-env": "^7.14.7", + "babelify": "^10.0.0", + "browserify": "^17.0.0", + "core-js": "^3.15.2", + "watchify": "^4.0.0" + }, + "scripts": { + "develop": "yarn install && (watchify -t [ babelify --global ] js/main.js -o js/bundle.js & python3 -m http.server)", + "build": "yarn install && browserify -t [ babelify --global ] js/main.js -o js/bundle.js" + }, + "license": "MIT" +} diff --git a/javascript/README.md b/javascript/README.md new file mode 100644 index 0000000..e738093 --- /dev/null +++ b/javascript/README.md @@ -0,0 +1,10 @@ +# Docker + +This directory records best practices for working with [JavaScript]. + +## Contents + +- [Stack](stack.md) +- Static Sites + - [Vanilla JavaScript](static-sites-in-vanilla-js.md) + - [Gatsby](gatsby/) diff --git a/gatsby/README.md b/javascript/gatsby/README.md similarity index 100% rename from gatsby/README.md rename to javascript/gatsby/README.md diff --git a/gatsby/best-practices.md b/javascript/gatsby/best-practices.md similarity index 100% rename from gatsby/best-practices.md rename to javascript/gatsby/best-practices.md diff --git a/gatsby/recharts.md b/javascript/gatsby/recharts.md similarity index 100% rename from gatsby/recharts.md rename to javascript/gatsby/recharts.md diff --git a/gatsby/research/comparisons-with-existing-tools.md b/javascript/gatsby/research/comparisons-with-existing-tools.md similarity index 100% rename from gatsby/research/comparisons-with-existing-tools.md rename to javascript/gatsby/research/comparisons-with-existing-tools.md diff --git a/gatsby/research/recommendation-of-adoption.md b/javascript/gatsby/research/recommendation-of-adoption.md similarity index 100% rename from gatsby/research/recommendation-of-adoption.md rename to javascript/gatsby/research/recommendation-of-adoption.md diff --git a/gatsby/stack.md b/javascript/stack.md similarity index 65% rename from gatsby/stack.md rename to javascript/stack.md index 3bf5249..f6a70de 100644 --- a/gatsby/stack.md +++ b/javascript/stack.md @@ -1,23 +1,53 @@ -# The DataMade Gatsby Stack +# The DataMade JavaScript Stack -A number of tools comprise our development stack for Gatsby project. They are: +A number of tools comprise our development stack for projects written in or +containing JavaScript. -1. [Gatsby](https://www.gatsbyjs.org/) (obviously) - static site generator +## Static sites + +### Vanilla JavaScript + +1. [ES6](http://es6-features.org/) - A number of big improvements to core JavaScript syntax +2. [Yarn](https://yarnpkg.com/) - package manager +3. [Browserify](https://browserify.org/) - build tool to bundle dependencies with code +4. [Babelify](https://www.npmjs.com/package/babelify) - Browserify plug-in that transpiles ES6 to ES5 for browser compatibility using [Babel](https://babeljs.io/) +5. [Watchify](https://www.npmjs.com/package/watchify) - build tool to update bundled/transpiled code during development +6. [GitHub Pages](https://pages.github.com/) or [Netlify](https://www.netlify.com/) - deployment platforms + +### Gatsby + +1. [Gatsby](https://www.gatsbyjs.org/) - static site generator 2. [React](https://reactjs.org/) - JavaScript framework for building user interfaces -3. [Node.js](https://nodejs.org/en/) / [npm](https://www.npmjs.com/) - runtime environment (run JavaScript outside of a browser) and bundled package manager -4. [Yarn](https://yarnpkg.com/) - package manager -5. [ESLint](https://eslint.org/) - linter +3. [Yarn](https://yarnpkg.com/) - package manager +4. [ESLint](https://eslint.org/) - linter 5. [Netlify](https://www.netlify.com/) - deployment platform 6. [Docker](https://www.docker.com/products/docker-desktop) / [Docker Compose](https://docs.docker.com/compose/) - container engine +## Django/React Integration + +1. [ES6](http://es6-features.org/) - A number of big improvements to core JavaScript syntax +2. [React](https://reactjs.org/) - JavaScript framework for building user interfaces +3. [Yarn](https://yarnpkg.com/) - package manager +4. [Browserify](https://browserify.org/) - build tool to bundle dependencies with code +5. [Babelify](https://www.npmjs.com/package/babelify) - Browserify plug-in that transpiles ES6 to ES5 for browser compatibility using [Babel](https://babeljs.io/) + ## Some example setups -Building a Gatsby project? Check out these projects from the DataMade team for example architecture and component design patterns. +### Vanilla JavaScript + +- Buildless: [IHS Displacement Risk Map](https://github.com/datamade/ihs-displacement-risk-in-chicago) +- Bundled code and dependencies: [NWSS Demo Site](https://github.com/datamade/nwss-data-standard) + +### Gatsby - Basic static app: [`static-app-template`](https://github.com/datamade/static-app-template/) (to be iterated upon) - Static app with `recharts` charts: [`how-to-recharts`](https://github.com/datamade/how-to-recharts/) - Static app with `react-leaflet` map: [`lisc-cnda-map`](https://github.com/datamade/lisc-cnda-map/) +## React in Django + +Integrating React with Django? See [the docs on our approach](https://github.com/datamade/how-to/blob/master/django/django-react-integration.md). + ## Managing packages and plugins As in Python, there are a few competing package managers in JavaScript. The two most popular are [npm](https://www.npmjs.com/get-npm), which comes bundled with Node.js, and [Yarn](https://yarnpkg.com/). At DataMade, we like Yarn, in particular for its build speed. @@ -38,7 +68,7 @@ error Command "yarn" not found. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command. ``` -\- this is almost certainly the problem! Run your commands like this, instead: +- this is almost certainly the problem! Run your commands like this, instead: ```bash # The application's entry point is "yarn" so we only need to pass the "add" command. diff --git a/javascript/static-sites-in-vanilla-js.md b/javascript/static-sites-in-vanilla-js.md new file mode 100644 index 0000000..a55e300 --- /dev/null +++ b/javascript/static-sites-in-vanilla-js.md @@ -0,0 +1,167 @@ +# Static Sites in Vanilla JavaScript + +Plain old HTML, CSS, and a JavaScript file or two offer a simple developer +experience during development and deployment. This document contains an +assessment of this stack, as well as guidance on when to use it, and some tips +for publishing your work. + +## The case for vanilla ES6 + +### Does DataMade currently create many apps that could be "little sites"? + +Yes, several DataMade projects, old and new, qualify as little sites, including: + +- The budget visualization family, including [Look at Cook]( +https://github.com/datamade/look-at-cook), [NY Budget]( +https://github.com/datamade/ny-budget), and [Budget Oak Park](https://github.com/derekeder/oak-park-budget) +- [IHS Displacement Risk Map](https://github.com/datamade/ihs-displacement-risk-in-chicago) +- [IHS Price Index](https://github.com/datamade/ihs-price-index) +- [NWSS Demo](https://github.com/datamade/nwss-data-standard/tree/main/docs) +- [Public Land Survey](https://github.com/fgregg/public-land-survey) + +I anticipate that we can find even more opportunities to create little apps once +we officially add this development stack to our toolkit. + +While we don't create a huge volume of single-page apps, this stack is +particularly well suited to demos and proofs of concepts, especially in +instances where the proof of concept will eventually become part of a Django +app. In this case, we can avoid the overhead of writing a Gatsby app we'll +ultimately abandon by using vanilla JavaScript instead. + +### What are the pros and cons of writing single page apps in vanilla JavaScript? How does it compare to Gatsby? + +#### Developer experience + +Vanilla JavaScript offers many advantages over Gatsby. Chief among them, the +learning curve for vanilla JavaScript is much less steep. It does not require +any knowledge of React or GraphQL. + +Vanilla JavaScript also requires few or no build steps. In the event that your +vanilla JavaScript app does have a build step to bundle your code with its +dependencies and transpile it for browser compatibility, it is generally quicker +and more transparent than Gatsby builds, which can be slow and abstract away +important context, particularly while debugging. + +Vanilla JavaScript can also be configured to match Gatsby's live reloading with +[watchify](https://www.npmjs.com/package/watchify). + +N.b., [our `new-vanilla-js-app` cookiecutter](../docker/templates/new-vanilla-js-app) +is set up with a `develop` script that runs a local server and uses `watchify` +to rebundle and retranspile JavaScript as it changes, as well as a `build` +script to bundle and transpile code during deployment. + +#### Deployment + +Vanilla JavaScript can be deployed on [GitHub Pages](https://guides.github.com/features/pages/), a feature none of our +other development stacks offers. If a staging site is needed, vanilla JavaScript +can be deployed on Netlify, the same as our Gatsby apps. + +#### Browser compatibility + +Gatsby [comes with broad browser support out of the box]( +https://www.gatsbyjs.com/docs/how-to/custom-configuration/browser-support/), +while vanilla JavaScript apps must add a build step to transpile code with +Babel. + +N.b. (redux), [our `new-vanilla-js-app` cookiecutter](../docker/templates/new-vanilla-js-app) +is set up with a `develop` script that runs a local server and uses `watchify` +to rebundle and retranspile JavaScript as it changes, as well as a `build` +script to bundle and transpile code during deployment. + +#### Maintainability + +Gatsby continues to grow in popularity, and we don't anticipate that it will +fall out of active support or development any time soon. With that said, its +pre-requisite skills do make maintainability slightly more challenging as we +cycle in new staff, who may or may not have those skills. + +Conversely, a lower learning curve means any staff could probably pick up a +vanilla JavaScript app without issue. + +## When to use Vanilla ES6 + +Does your site need: + +- Only one or two pages? +- A minimally complicated map? +- Lightweight data, e.g., a single JSON or CSV file or low cost external requests + +It could be well suited to the vanilla JavaScript stack! Read on for more +details. + +On the other hand, does your site need: + +- Multiple pages, particularly pages based on data? +- Heavyweight data, e.g., multiple data files, an external datastore (such as +Hasura), or heavy external requests? +- Complex maps, charts, or other visualizations? + +Then it's probably better suited for Gatsby. [Head over to our Gatsby docs to +learn more](gatsby/). + +## Prerequisites + +For a maximally productive time, you'll want to familiarize yourself with +[our JavaScript stack](https://github.com/datamade/how-to/blob/hcg/lil-js/javascript/stack.md), +namely the `yarn` package manager and build tool, as well as ES6 itself. + +## Patterns + +### Development + +If your app doesn't have any dependencies, or if you can ship them with your +code or source them from a CDN, simply create an `index.html` file and a +`main.js` file, and off you go. + +You can serve your files in local development like: + +```bash +python3 -m http.server +``` + +If you're using ES6 syntax, though, this approach will not be compatible with +all browsers. + +If you're targeting a broader user base, and/or if you prefer the ease of using +a CLI to manage packages, we recommend using `yarn` to install dependencies +and Browserify and Babelify to bundle third-party packages with your code and +compile them to ES5 for maximum compatibility. + +See [our `new-vanilla-js-app` cookiecutter](../docker/templates/new-vanilla-js-app) +for an example of this setup. It contains the following components: + +1. A `package.json` file defining `develop` and `build` scripts that bundle and +transpile your JavaScript during development and deployment, respectively. +2. A `babel.config.json` file specifying target browsers. +3. Starter HTML, CSS, and JS files for your website. + +To serve your site locally, simply: + +```bash +yarn run develop +``` + +This command will detect changes to your main JavaScript file and automatically +rebundle and retranspile it as you work. + +### Deployment + +#### GitHub Pages + +Vanilla JavaScript sites can be hosted seamlessly on GitHub Pages. + +Working without build steps (e.g., using a CDN for dependencies)? Simply +navigate to your repository settings > Pages, then enable GitHub Pages. + +If you need a build step, you can define a GitHub Action to bundle and transpile +your code. See [the NWSS project]( +https://github.com/datamade/nwss-data-standard/blob/main/.github/workflows/gh-pages.yml) +for an example. Once you've defined the action, head to your repository settings > +Pages, then enable GitHub Pages from the branch your action pushes builds to +(`gh-pages`, by default). + +#### Netlify + +If you need staging and production instances of your site, you can follow [our +documentation for deploying static sites to Netlify]( +https://github.com/datamade/how-to/tree/master/deployment/netlify).