diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..b0fc978 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +dist/* +public/* +test/*.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..183eb03 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,33 @@ +{ + "parser": "@typescript-eslint/parser", + "ignorePatterns": ["lib.es5.d.ts"], + "parserOptions": { + "requireConfigFile": false + }, + "extends": [ + "standard", + "plugin:@typescript-eslint/recommended" + ], + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/no-explicit-any": "off", + "operator-linebreak": ["off"], + "multiline-ternary": "off", + "no-multiple-empty-lines": [ + "error", + { + "max": 1, + "maxEOF": 1 + } + ], + "no-undef": "off", + "indent": ["error", 4, { + "SwitchCase": 1, + "ignoredNodes": ["TemplateLiteral *"] + }], + "comma-dangle": "off", + "no-multi-spaces": ["error", { "ignoreEOLComments": true }] + } +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..443a45d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +version: 2 +updates: + # Enable version updates for npm + - package-ecosystem: "npm" + # Look for `package.json` and `lock` files in the `root` directory + directory: "/" + # Check the npm registry for updates every day (weekdays) + schedule: + interval: "daily" + # Enable updates to github actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..07896bf --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,52 @@ +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: GitHub Pages deploy + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Install + run: npm install + - name: Build + run: npm run build-example + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: public/ + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml new file mode 100644 index 0000000..8b0ce0c --- /dev/null +++ b/.github/workflows/nodejs.yml @@ -0,0 +1,31 @@ +name: Node CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-20.04 + timeout-minutes: 5 + + strategy: + matrix: + node-version: [18.x] + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - name: npm install, build + run: | + npm install + npm run build --if-present + npm run lint + env: + CI: true + - name: Run tape tests + run: | + npm run build-tests + xvfb-run --server-args="-screen 0 1920x1080x24" npm run test-tape-run diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ba72a0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules +bundle.js +.DS_Store +test/*.js +dist +.env +public diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..57e45d4 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +!dist +node_modules +.env +.DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a724cf0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b56812e --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +THE MIT LICENSE (MIT) + +Copyright © 2024 Nick Thomas + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..73f33df --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# template ts browser +![tests](https://github.com/nichoth/template-ts-browser/actions/workflows/nodejs.yml/badge.svg) +[![types](https://img.shields.io/npm/types/@nichoth/catch-links?style=flat-square)](README.md) +[![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md) +[![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&style=flat-square)](https://semver.org/) +[![license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) + +A template for typescript *dependency* modules that run in a browser environment. +Uses `tape-run` for tests in a browser. See [template-ts](https://github.com/nichoth/template-ts) for the same thing but targeting Node. + +## use +1. Use the template button in github. Or clone this then +`rm -rf .git && git init`. Then `npm i && npm init`. + +2. Edit the source code in `src/index.ts`. + +## featuring + +* compile the source to both ESM and CJS format, and put compiled files in `dist`. +* ignore `dist` and `*.js` in git, but don't ignore them in npm. That way we + don't commit any compiled code to git, but it is available to consumers. +* use npm's `prepublishOnly` hook to compile the code before publishing to npm. +* use [exports](./package.json#L41) field in `package.json` to make sure the right format is used + by consumers. +* `preversion` npm hook -- lint +* `postversion` npm hook -- `git push --follow-tags && npm publish` +* eslint -- `npm run lint` +* tests run in a browser environment via `tape-run` -- see [`npm test`](./package.json#L12). + Includes `tap` testing tools -- [tapzero](https://github.com/bicycle-codes/tapzero) + and [tap-arc](https://www.npmjs.com/package/tap-arc) +* CI via github actions diff --git a/_public/_headers b/_public/_headers new file mode 100644 index 0000000..cbf4f43 --- /dev/null +++ b/_public/_headers @@ -0,0 +1,4 @@ +# set for all pages +# https://docs.netlify.com/routing/headers/#syntax-for-the-headers-file +/* + X-Robots-Tag: noindex diff --git a/_public/robots.txt b/_public/robots.txt new file mode 100644 index 0000000..b21f088 --- /dev/null +++ b/_public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: / diff --git a/example/index.html b/example/index.html new file mode 100644 index 0000000..a9f3240 --- /dev/null +++ b/example/index.html @@ -0,0 +1,12 @@ + + + + + + Example + + +
+ + + diff --git a/example/index.tsx b/example/index.tsx new file mode 100644 index 0000000..60b941c --- /dev/null +++ b/example/index.tsx @@ -0,0 +1,10 @@ +import { render } from 'preact' +import { example } from '../src/index.js' + +example() + +function Example () { + return (
hello
) +} + +render((), document.getElementById('root')!) diff --git a/lib.es5.d.ts b/lib.es5.d.ts new file mode 100644 index 0000000..a2b2ce5 --- /dev/null +++ b/lib.es5.d.ts @@ -0,0 +1,19 @@ +/** See https://stackoverflow.com/a/51390763/1470607 */ +type Falsy = false | 0 | '' | null | undefined; + +/** + * see https://www.karltarvas.com/typescript-array-filter-boolean.html + */ +interface Array { + /** + * Returns the elements of an array that meet the condition specified in a + * callback function. + * @param predicate A function that accepts up to three arguments. The filter + * method calls the predicate function one time for each element in the array. + * @param thisArg An object to which the this keyword can refer in the + * predicate function. If thisArg is omitted, undefined is used as the + * this value. + */ + filter(predicate: BooleanConstructor, thisArg?: any) + : Exclude[]; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..a2d3284 --- /dev/null +++ b/package.json @@ -0,0 +1,58 @@ +{ + "private": true, + "type": "module", + "version": "0.0.0", + "main": "dist/index.js", + "files": [ + "./dist/*" + ], + "scripts": { + "lint": "eslint \"./**/*.{ts,js}\"", + "build-tests": "esbuild test/index.ts --target=es2020 --bundle --keep-names > test/test-bundle.js", + "test": "npm run lint && npm run build && npm run build-tests && npm run test-tape-run", + "test-tape-run": "cat test/index.html | tape-run --input=html --static=test | tap-spec", + "build-cjs": "esbuild src/*.ts --format=cjs --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.cjs --sourcemap=inline", + "build-esm": "tsc --project tsconfig.build.json", + "build": "mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm", + "start": "vite", + "preversion": "npm run lint", + "version": "auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md", + "postversion": "git push --follow-tags && npm publish", + "prepublishOnly": "npm run build" + }, + "devDependencies": { + "@bicycle-codes/tapzero": "^0.9.2", + "@nichoth/debug": "^0.6.7", + "@preact/preset-vite": "^2.8.2", + "@typescript-eslint/eslint-plugin": "^7.6.0", + "@typescript-eslint/parser": "^7.6.0", + "auto-changelog": "^2.4.0", + "esbuild": "^0.20.2", + "eslint": "^8.57.0", + "eslint-config-standard": "^17.1.0", + "postcss-nesting": "^12.1.1", + "preact": "^10.20.2", + "tap-spec": "^5.0.0", + "tape-run": "^11.0.0", + "typescript": "^5.4.4", + "vite": "^5.1.6" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "./*": { + "import": [ + "./dist/*.js", + "./dist/*" + ], + "require": [ + "./dist/*.cjs", + "./dist/*" + ] + } + }, + "author": "nichoth (https://nichoth.com)", + "license": "MIT" +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..cd5e1f7 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +import { createDebug } from '@nichoth/debug' +const debug = createDebug() + +export function example ():void { + debug('hello') +} diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..3e519df --- /dev/null +++ b/test/index.html @@ -0,0 +1,12 @@ + + + + + tests + + + + + + + diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 0000000..390f787 --- /dev/null +++ b/test/index.ts @@ -0,0 +1,7 @@ +import { test } from '@bicycle-codes/tapzero' +import { example } from '../src/index.js' + +test('example', async t => { + t.ok('ok', 'should be an example') + example() +}) diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..cfbd238 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "exclude": [ + "example", + "test" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5ad365b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "listFiles": true, + "module": "NodeNext", + "target": "ES2022", + "moduleResolution": "nodenext", + "esModuleInterop": false, + "lib": ["ES2022", "DOM", "WebWorker"], + "allowJs": false, + "skipLibCheck": true, + "outDir": "dist", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "strict": true, + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "jsxImportSource": "preact", + "noImplicitAny": false, + "declaration": true, + "declarationDir": "dist", + "declarationMap": true + }, + "include": [ + "example", + "src/**/*", + "test", + "lib.es5.d.ts" + ] +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..fbe1de5 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,43 @@ +import { defineConfig } from 'vite' +import preact from '@preact/preset-vite' +import postcssNesting from 'postcss-nesting' + +// https://vitejs.dev/config/ +export default defineConfig({ + define: { + global: 'globalThis' + }, + root: 'example', + plugins: [ + preact({ + devtoolsInProd: false, + prefreshEnabled: true, + babel: { + sourceMaps: 'both' + } + }) + ], + // https://github.com/vitejs/vite/issues/8644#issuecomment-1159308803 + esbuild: { + logOverride: { 'this-is-undefined-in-esm': 'silent' } + }, + publicDir: '_public', + css: { + postcss: { + plugins: [ + postcssNesting + ], + }, + }, + server: { + port: 8888, + host: true, + open: true, + }, + build: { + minify: false, + outDir: '../public', + emptyOutDir: true, + sourcemap: 'inline' + } +})