๐A starter for any TypeScript project meant to be published on NPM๐
๐ฃ๏ธ Sorry, since a recent GitHub update you need to manually allow GitHub Action to push on your REPO.
Fo this reason the inital setup will fail.
You need to enabled permission and re-run failed job: see video
ts-ci.mp4
ts-ci
is a project starter like TSDX or typescript-starter but (arguably) better because:
- It's not a CLI tool, the automation happens within Github Actions.
Update yourpackage.json
version number, push. Voila, your new version is published on NPM. - It enables you to publish prerelease simply by updating your package version to something like
1.2.3-rc.3
. - When someone opens submit a PR the tests are run agaist their fork.
- It doesn't bundle your library into a single file so users can cherry-pick what they want to import from your lib, your modules will be tree shakable.
E.g:
import { aSpecificFunctionย } from "your-module/aSpecificFile"
- Click on
- The repo name you will choose will be used as a module name for NPM.
- Go to the repository
Settings
tab, thenSecrets
you will need to add a new secret:NPM_TOKEN
, you NPM authorization token. - To trigger publishing edit the
package.json
version
field (0.0.0
->0.0.1
for example) then push changes... that's all ! - Publish pre-release by setting your version number to
X.Y.Z-rc.U
(example:1.0.0-rc.32
). On NPM the version will be taggednext
. - The CI runs on
main
and on the branches that have a PR open onmain
.
- โ๏ธ Filling up the
package.json
- โ Testing on multiple Node version running on Ubuntu and Windows before publishing.
- ๐ฆ Publishing on NPM and creating corresponding GitHub releases.
- ๐งช Enable you to test your local copy of the module on an external app.
- ๐ You can use a different repo image for dark and light mode.
Example with: i18nifty: Light - Dark
See here the special GitHub syntax (#gh-dark-mode-only
) that enable this to work.
TS-CI provides an extra action that strips the dark mode specific image from your README.md before publishing on NPM (NPM do not recognize#gh-dark-mode-only
yet). - ๐ฉณ TS-CI comes by default with a step in the workflow that move your dist files to the root before releasing.
This enables your user to import specific file of your module like:
import {...} from "my_module/theFile"
(instead of "my_module/dist/theFile" )
Feel free to remove this action if you don't like this behavior (or if you juste have an index.ts and users are not supposed to cherry pick what they want to import from your module.) - โ๏ธ ESlint and Prettier are automatically run against files staged for commit. (Optional, you can disable this feature)
By default your module relase in CJS with ESM module interop.
If you want to only release as ESM just set "module": "ES2020"
in your tsconfig.json
(and remove esModuleInterop
).
If you want to release for both CJS and ESM, it's a bit less straign forward. You have to:
- Have a
tsconfig.json
that targets CSM (as by default): example - Perfome two build, one for CJS, one for ESM. example
- Provide a
module
andexports
property in yourpackage.json
, example. - Exclude things you don't want in the NPM bundle. Example
Click to expand
Yes, just remove the yarn.lock
file and edit .github/workflows/ci.yaml
, replace all yarn ***
by npm run ****
.
Note however that the the script (src/link-in-app.ts
) that enable you to test in an external app will no longer work.
All filles listed in the files property of your package JSON.
You can increase the verbosity by creating a new secret ACTIONS_STEP_DEBUG
and setting it to true.
Remove this, this and this from your package.json
Remove this and this from github/workflows/ci.yaml
Remove .eslintignore
, .eslintrc.js
, .prettierignore
and .prettierrc.json
.
Accessing files outside the dist/
directory (when this line is present in your repo)
The drawback of having short import path is that the dir structure
is not exactly the same in production ( in the npm bundle ) and in development.
The files and directories in dist/
will be moved to the root of the project.
As a result this won't work in production:
src/index.ts
import * as fs from "fs";
import * as path from "path";
const str = fs.readFileSync(
path.join(__dirname,"..", "package.json")
).toString("utf8");
Because /dist/index.js
will be moved to /index.js
You'll have to do:
src/index.ts
import * as fs from "fs";
import * as path from "path";
import { getProjectRoot } from "./tools/getProjectRoot";
const str = fs.readFileSync(
path.join(getProjectRoot(),"package.json")
).toString("utf8");
With getProjectRoot.ts
being:
import * as fs from "fs";
import * as path from "path";
function getProjectRootRec(dirPath: string): string {
if (fs.existsSync(path.join(dirPath, "package.json"))) {
return dirPath;
}
return getProjectRootRec(path.join(dirPath, ".."));
}
let result: string | undefined = undefined;
export function getProjectRoot(): string {
if (result !== undefined) {
return result;
}
return (result = getProjectRootRec(__dirname));
}