diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 00000000..e573c336 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,88 @@ +# Three kinds of check, split by what each one can actually go wrong from. +# +# notes parses every snippet in the lesson files and resolves every relative link and image. +# Offline, seconds, so it runs on every push. +# examples installs, tests and builds each example project. This is the job that matters: every +# defect fixed in this branch was in the examples, and none of them was visible from the +# markdown. Three of the seven could not even be installed on a current npm. +# external-links fetches the outbound URLs. Scheduled monthly rather than per push, because a link to +# somebody else's site rots on a clock rather than on commits, and this repository went +# from 2020 to 2026 almost untouched: a push-triggered check would have looked on the few +# days it was edited and never in between. +# +# NODE_OPTIONS=--openssl-legacy-provider is set for the example jobs because webpack 4 hashes modules with +# md4 and OpenSSL 3 removed md4 from its default provider. Without it every webpack build here fails with +# ERR_OSSL_EVP_UNSUPPORTED before it starts. That is the era-appropriate answer for notes pinned to 2019 +# tooling; upgrading webpack would fix it and would stop these being notes about the toolchain they describe. +# +# `npm install`, not `npm ci`: every example carries a yarn.lock and no package-lock.json. + +name: checks + +on: + push: + branches: [master] + pull_request: + schedule: + # 06:00 UTC on the first of each month. + - cron: '0 6 1 * *' + workflow_dispatch: + +# Read-only. Nothing here writes anything, and the default token grants more than these need. +permissions: + contents: read + +jobs: + notes: + if: github.event_name != 'schedule' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm install --no-audit --no-fund + - run: npm run test:snippets + - run: npm run test:links + + examples: + if: github.event_name != 'schedule' + runs-on: ubuntu-latest + strategy: + # Report every example rather than stopping at the first failure: the point is to know which ones work. + fail-fast: false + matrix: + example: + - basic-react-example-state + - basic-react-example-map-with-key + - basic-react-example-lifecycle + - basic-webpack-default + - react-redux-webpack-client + - react-redux-webpack-client-server + - react-redux-webpack-client-server-scripts + defaults: + run: + working-directory: examples/${{ matrix.example }} + env: + # webpack 4 needs md4; see the note at the top of this file. + NODE_OPTIONS: --openssl-legacy-provider + CI: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm install --no-audit --no-fund + - run: npm test + - run: npm run build + + external-links: + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm install --no-audit --no-fund + - run: npm run test:links:external diff --git a/.gitignore b/.gitignore index 55e42602..e409f006 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,46 @@ yarn-error.log* # misc .DS_Store -.env -.env.* + +# Environment files. BOTH glob forms, because neither implies the other. `.env*` +# is a prefix glob and catches .env, .env.local and .envrc, the last of which +# direnv reads and people export credentials from. `*.env` is a suffix glob, for +# names like audit.env or production.env. This file used to list `.env` plus +# `.env.*`, and the dot is not cosmetic: `.env.*` requires a SECOND dot, so it +# never matched .envrc and nothing matched the suffix form at all. +# +# The negation re-includes the one file in this family you are meant to commit, +# and it has to come after the patterns it undoes, since the last matching rule +# wins. +.env* +*.env !.env.example +# Credentials and secrets. None of these examples need any, which is exactly when +# an accidental commit happens. +secrets/ +credentials* +*.pem +*.key + +# SSH private keys, which *.pem and *.key do not cover: a key in its default +# filename has no extension at all. `id_rsa*` alone is not enough either, because +# ssh-keygen's default type is ed25519 now rather than rsa (man ssh-keygen: +# "ed25519 (the default)"). Enumerated rather than globbed as `id_*`, which would +# also match ordinary source files. +id_rsa* +id_ed25519* +id_ecdsa* +id_dsa* +*.ppk +.ssh/ + +# Local databases. Both sqlite spellings: the extension is a convention rather +# than a rule. +*.sqlite +*.sqlite3 +*.db + # npm cache dir .npm @@ -21,6 +57,17 @@ yarn-error.log* build/ dist/ +# Generated bundles inside the examples' public/ directories. `public/` cannot be +# ignored wholesale, because it also holds template.html, manifest.json, +# favicon.ico and an image, which are sources. These patterns are the webpack +# output that used to be committed alongside them, including the pre-compressed +# copies that express-static-gzip serves and the content-hashed CSS. +**/public/*bundle.js +**/public/*bundle.js.map +**/public/*bundle.js.gz +**/public/*bundle.js.br +**/public/*-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]*.css + # Runtime data pids *.pid @@ -29,7 +76,21 @@ pids # testing coverage/ -**/__snapshots__ + +# NOTE ON A RULE THAT USED TO BE HERE: `**/__snapshots__`. +# +# Jest snapshots belong in version control. With them ignored, a snapshot test +# has nothing to compare against on a fresh clone, and what it does instead +# depends on where it runs: measured on this repository's own Intro.test.js, +# jest WRITES a new snapshot and passes when it does not think it is in CI, and +# refuses to write and FAILS when it does. Neither outcome verifies anything, and +# the passing one is worse, because `toMatchSnapshot()` reads like an assertion +# while recording whatever it was handed. + +# Editor and OS noise. +.idea/ +.vscode/ +__pycache__/ # TS v1 declarations files typings/ @@ -37,6 +98,7 @@ typings/ # Yarn Integrity file .yarn-integrity +# webpack bundle stats *stats.* *-old* diff --git a/00_2_intro_JS-patterns.md b/00_2_intro_JS-patterns.md index 15c2db2b..eacb602a 100644 --- a/00_2_intro_JS-patterns.md +++ b/00_2_intro_JS-patterns.md @@ -114,6 +114,7 @@ console.log([].push) Result: ```javascript +// check: skip the returned function on its own, shown as the result of the call above function (arr) { let tempArr = [...arr] tempArr.push(element) diff --git a/01_0_starting.md b/01_0_starting.md index da62bca4..13225990 100644 --- a/01_0_starting.md +++ b/01_0_starting.md @@ -14,7 +14,7 @@ You can try `React online` through any of the major JS "sandboxes" or "playgroun ``` **For production:** @16.x.x -```javascript +```html ``` diff --git a/02_1_props.md b/02_1_props.md index caf429b9..d299feaf 100644 --- a/02_1_props.md +++ b/02_1_props.md @@ -174,6 +174,7 @@ Second, install the package `uuid` and use the `version 1` (aka, timestamp). The Before: ```javascript +// check: skip the opening tag only, shown as the "before" of a before-and-after pair