-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
feat(learn): add article for publishing a typescript package #7279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
b20dcbf
feat(learn): add article for publishing a typescript package
JakobJingleheimer 2d5d359
WIP: initial content for article
JakobJingleheimer 86394b6
WIP: polish sample code, & dir overviews
JakobJingleheimer d583e03
rename article to be more specific
JakobJingleheimer 48e2ad9
fix unsuported lang
AugustinMauroy 081b7f2
fix navigation.json
JakobJingleheimer 2a20202
fix links
JakobJingleheimer 52d3a13
extract note from codeblock into article
JakobJingleheimer 2228ab3
tidy codeblocks
JakobJingleheimer 5034918
wordsmith
JakobJingleheimer ddb1cf1
fixup!: remove controversial "optionalDependencies"
JakobJingleheimer 9258389
fixup!: wordsmith & align code samples
JakobJingleheimer 4906609
fixup!: tsconfig
JakobJingleheimer 07095c6
fixup!: switch sequence of repo vs package
JakobJingleheimer 33e744a
fixup!: note types and unit tests are complementary
JakobJingleheimer f1703ad
fixup!: `IDE` → `editor`
JakobJingleheimer 3fd8076
fixup!: note file extensions in package.json fields (js vs ts)
JakobJingleheimer 64fd531
fixup!: add alternative samples & configs
JakobJingleheimer 7305f8e
fixup!: remove version from npm links
JakobJingleheimer 13b8e1c
fixup!: shorter code sample display names
JakobJingleheimer 0f7f993
fixup!: add note about `NPM_TOKEN`
JakobJingleheimer 53793f1
fixup!: switch node version matrix to LTS matrix action
JakobJingleheimer 4b06b6c
fixup!: shorten displayNames (they were breaking page layout)
JakobJingleheimer d250a86
fixup!: update references to samples
JakobJingleheimer 6056802
fixup!: replace `npm publish` step from `publish.yml` with note
JakobJingleheimer d1f67dc
fixup!: replace ref to TS's own publishing guide with generic intro
JakobJingleheimer 0b7a3df
fixup!: add note about `erasableSyntaxOnly`
JakobJingleheimer b146a69
fixup!: restore box vert lines
JakobJingleheimer c68edf0
fixup!: add "dist output" tsconfig sample
JakobJingleheimer 40e8d7e
fixup!: handle flavours of `.ts` file extensions in `.gitignore` sample
JakobJingleheimer bd6909d
fixup!: correct code block lang for gitignore samples
JakobJingleheimer eaf9b21
fixup!: remove extra word
JakobJingleheimer 4d36877
enable `ini` lang in codeblocks
JakobJingleheimer 9bb1dde
fixup!: expand description of article's purpose
JakobJingleheimer 111564c
fixup!: adjust `tsconfig`s to support oldest node LTS version
JakobJingleheimer cc78347
fixup!: ts project → ts package
JakobJingleheimer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
--- | ||
title: Publishing a TypeScript package | ||
layout: learn | ||
authors: JakobJingleheimer | ||
--- | ||
|
||
# Publishing a TypeScript package | ||
|
||
This article augments TypeScript's [Publishing guide](https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html) with specifics for native node support. | ||
|
||
Some important things to note: | ||
|
||
- Node runs typescript via a process called "type stripping", wherein node (via SWC under the hood of [Amaro](https://github.com/nodejs/amaro)) removes TypeScript-specific syntax, leaving behind vanilla JavaScript (which node already understands). This behaviour is enabled by default of node version 23.6.0. | ||
|
||
- Node does **not** strip types in `node_modules`. This decision was at the request of TypeScript maintainers because it can cause significant performance issues for the official compiler (`tsc`). | ||
|
||
- TypeScript-specific features like `enum` still require a flag ([`--experimental-transform-types`](https://nodejs.org/api/typescript.html#typescript-features)) | ||
|
||
## What to do with your types | ||
|
||
### Treat them like a test | ||
|
||
The purpose of types are to warn an implementation will not work: | ||
|
||
```ts | ||
const foo = 'a'; | ||
const bar: number = 1 + foo; | ||
// ^^^ Type 'string' is not assignable to type 'number'. | ||
``` | ||
|
||
TypeScript has warned you that the above code will not behave as intended, just like a unit test warns you code does not behave as intended. | ||
|
||
Your IDE (ex VS Code) likely has built-in support for TypeScript, displaying errors as you work. If not, and/or you missed those, CI will have your back. | ||
|
||
```yaml displayName=".github/workflows/ci.yml" | ||
name: Tests | ||
|
||
on: | ||
push: | ||
branches: ['main'] | ||
pull_request: | ||
branches: ['main'] | ||
|
||
jobs: | ||
lint-and-check-types: | ||
# Separate these from tests because | ||
# they are platform and node-version independent. | ||
|
||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version-file: '.nvmrc' | ||
cache: 'npm' | ||
- name: npm clean install | ||
run: npm ci | ||
- run: node --run lint | ||
- run: node --run types:check | ||
|
||
test: | ||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
matrix: | ||
node: | ||
- version: 23.x | ||
- version: 22.x | ||
# glob is not backported below 22.x | ||
fail-fast: false # prevent a failure in one version cancelling other runs | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Use Node.js ${{ matrix.node.version }} | ||
uses: actions/setup-node@v4 | ||
with: | ||
node-version: ${{ matrix.node.version }} | ||
cache: 'npm' | ||
- name: npm clean install | ||
run: npm ci | ||
- run: node --run test | ||
``` | ||
|
||
```json displayName="package.json" | ||
{ | ||
"version": "0.0.0", | ||
"name": "example-ts-pkg", | ||
"scripts": { | ||
"lint": "…", | ||
"types:check": "tsc --noEmit" | ||
}, | ||
"optionalDependencies": { | ||
JakobJingleheimer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// This is used only in CI. | ||
// Marking it 'optional' avoids installing on your local | ||
// (where you probably won't use it). | ||
"typescript": "^5.7.2" | ||
} | ||
} | ||
``` | ||
|
||
```json displayName="tsconfig.json" | ||
{ | ||
"compilerOptions": { | ||
"declarationMap": true, | ||
"declaration": true, | ||
"emitDeclarationOnly": true, | ||
"esModuleInterop": true, // Flux Capacitor: The universe breaks without it, but nobody knows what it does. | ||
JakobJingleheimer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"module": "NodeNext", | ||
"moduleResolution": "NodeNext", | ||
"target": "ESNext" | ||
}, | ||
// These may be different for your repo: | ||
"include": "./src", | ||
"exclude": ["**/*/*.test.*"] | ||
} | ||
``` | ||
|
||
### Generating type declarations | ||
|
||
Type declarations (`.d.ts` and friends) provide type information as a sidecar file, allowing the execution code to be vanilla JavaScript whilst still having types. | ||
|
||
Since these are generated based on source code, they can be built as part of your publication process and do not need to be checked into your repository. | ||
|
||
Take the following example (a [GitHub Action](https://github.com/features/actions)), where the type declarations are generated just before publishing to the NPM registry. | ||
|
||
```yaml displayName=".github/workflows/publish.yml" | ||
name: Publish to NPM | ||
on: | ||
push: | ||
tags: | ||
- '**@*' | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
permissions: | ||
contents: read | ||
id-token: write | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-node@v4 | ||
with: | ||
node-version-file: '.nvmrc' | ||
registry-url: 'https://registry.npmjs.org' | ||
- run: npm ci | ||
|
||
# You can probably ignore the boilerplate config above | ||
|
||
- name: Generate types | ||
run: node --run types:generate | ||
|
||
- name: Publish with provenance | ||
env: | ||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
run: npm publish --access public --provenance | ||
JakobJingleheimer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
``` | ||
|
||
```diff displayName="package.json" | ||
{ | ||
"name": "example-ts-pkg", | ||
"scripts": { | ||
"types:check": "tsc --noEmit", | ||
+ "types:generate": "tsc" | ||
} | ||
} | ||
``` | ||
|
||
```text displayName=".npmignore" | ||
*.test.* | ||
*.fixture.* | ||
fixture.* | ||
fixtures | ||
``` | ||
|
||
#### Breaking this down | ||
|
||
Generating type declarations is deterministic: you'll get the same output from the same input, every time. So there is no need to commit these to git. | ||
|
||
[`npm publish`](https://docs.npmjs.com/cli/v11/commands/npm-publish) grabs everything applicable and available at the moment the command is run; so generating type declarations immediately before means those are available and will get picked up. | ||
|
||
By default, `npm publish` grabs (almost) everything (see [Files included in package](https://docs.npmjs.com/cli/v11/commands/npm-publish#files-included-in-package)). In order to keep your published package minimal (see the "Heaviest Objects in the Universe" meme about `node_modules`), you want to exclude certain files (like tests and test fixtures) from from packaging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.