Publishing a NPM Typescript package to Gitlab Package registry
A step-by-step guide to publish a npm package to Gitlab
Introduction
In this post, I will provide a step-by-step guide on how to publish a private NPM package to your Gitlab project’s Package Registry, that can be then reused/imported in your other Gitlab projects installing it as a npm dependency.
Using Typescript
When developing NPM packages, using Typescript and providing type definitions of the public modules and interfaces of your package is a good practice. With type-definitions, the users of the npm package get to see the available modules properties, methods easily in code editor’s intellisense and also can use those type-definitions in their code as needed.
We will also use tools like ESLint(@typescript-eslint
) and Prettier for code quality.
What are we developing
For our example purpose, we will develop a very basic npm package library app-themes,
to define two simple color scheme/themes - dark and light, that other apps can import to use.
Pre-requisites:
- node and (npm or yarn) latest versions
- Gitlab account
- VSCode or your code editor of choice
Let’s get started.
🆃🅻;🅳🆁 This would be a bit long post as we perform below tasks:
▪️ Development setup
- add & configure Typescript,
- ESLint /Prettier settings,
- Lint-staged & Husky
▪️ Prepare to publish the package - addgitlab-ci.yml
, .npmrc
▪️ Using the published npm package in a test app
Gitlab Package Registry
Like NPM, Gitlab’s Package Registry allows you to register your package libraries, which can be used/imported as dependencies in other projects.
With Gitlab Package Registry, you can keep the package library as private (to be used by projects inside your Gitlab account) or can be shared as public.
It supports most of the common package managers like npm, Go, Maven, Ruby Gems etc.
Scoped package name
Gitlab requires that your package must be scoped, i.e. the package-name must be in the format @scope/package-name
.
Gitlab allows two options to register a package - project-level or instance-level.
① project-level: In Gitlab, a project usually corresponds to a single code repository. Gitlab also allows to register multiple packages within a single project.
This option could be useful when you have multiple package libraries that you want to register at one place in a single Gitlab project and import/use those from there in other projects of various other groups/subgroups of various departments of your organization.
When registering a package at project-level, you can use/append your own unique scope to your package name.
A package registered at project-level can be made public if needed.
② instance-level: here you create a project for your package’s code repository within a Gitlab group/sub-group and register the package in that group/sub-group. The package should be named in the format @scope/package-name
, where scope
is the root namespace(root group name) of the project.
instance-level packages cannot be made public
(For more details, check Gitlab Endpoints for packages and Gitlab’s Package Naming conventions.)
In this post, we will create a private npm package at project-level in our Gitlab account.
Gitlab setup
We will create two Gitlab projects:
common-package-library
: This project would act as a common project at organization level, where we would register all the npm (or other) package libraries that would be common for the org. Any app in the organization can import and use the package libraries registered here.app-theme
: This project would be for the code repository ofapp-themes
npm package library’s code. To this project, we would also add a Gitlab CI pipeline to register this package in the abovecommon-package-library
Let’s start:
Login to your Gitlab account and create a new project:
1. Creating 𝚌𝚘𝚖𝚖𝚘𝚗-𝚙𝚊𝚌𝚔𝚊𝚐𝚎-𝚕𝚒𝚋𝚛𝚊𝚛𝚢 project
The above project repository will remain empty as we are going to use this project only to register npm(or others) library packages.
📌 ⓃⓄⓉⒺ ① Note down the Project-ID of this project. We will need it later
2. Creating 𝚊𝚙𝚙-𝚝𝚑𝚎𝚖𝚎𝚜 project for code repository of the package library’s code:
The project we created above is intended to register all common packages that would be used in our org. It will have an empty code repository.
Additionally, we will need project(s) to store the code repositories of our package(s).
Let’s create a Gitlab group named org-packages
, under which we will group the individual project(s) for each of the package’s source code repository.
Now, inside this org-packages
group, create a project app-themes
.
Note down the repository’s git URL 👆, we’ll need this to set up git for our package’s folder.
📌 ⓃⓄⓉⒺ ② Note the URL of app-themes projects's GIT repository:
https://gitlab.com/org-packages/app-themes.git
✔️ That’s it with Gitlab setup for now, we will come back to Gitlab later.
Let’s now move on with the development setup and coding part of our npm package.
Development setup
📂 Create a folder for the npm package
mkdir app-themes
cd app-themes
Initialize npm
Initialize the folder with npm.
npm init (or yarn init)
(or npm/yarn init -y to init with defaults and change values later)
👆 Notice the scope @btiwari-gitlab
provided for the package name.
Initialize git
Next, initialize Git for our project’s folder
git init
git remote add origin https://gitlab.com/org-packages/app-themes.git
echo "Apps Themes Package" >> README.md
echo "node_modules" >> .gitignore
👆For remote origin, use the Gitlab project’s repository URL that we noted at ⓃⓄⓉⒺ ② above.
Open the folder in your code editor. Edit .gitignore
as below:
.DS_Store
# node modules
node_modules/# Log files
npm-debug.log*# Editor settings
.vscode
Stage, commit and push to git remote repository:
git add .
git commit -m "Initial commit"
git push -u origin master
The initial code should now be available in the Gitlab project app-themes
.
Adding Typescript
As discussed before, we will use Typescript for developing our package.
Add TS and other related packages as dev dependencies
npm install typescript jest ts-jest @types/jest -D
ts-jest allows to write our jest test-cases in typescript.
Initialize Typescript configuration
Set up your Typescript config file (tsconfig.json
) using the tsc
command, or simply create the file manually within the project folder.
tsc --init
The command would create tsconfig.json
file in the project folder
Edit tsconfig.json
as below to have minimum configuration required:
👆 Line # 5
: “declaration”: true
This would tell typescript compiler to create and output a type definition file (index.d.ts
) for the exported modules of the app.
(We want this so that the client apps importing our package can get the package’s exported modules, their methods and properties in code-editor’s intellisense and can also use these type-defs if needed.)
Update package.json
With above config, our transpiled javascript code would output to ./dist
folder. Hence update the path of main index.js
in package.json
as below:
Above, to package.json
, we also added:
✱ "types": "./dist/index.d.ts"
: With this setting, a file index.d.ts
would also get added to the output dist
folder.
Because we specified declarations:true
in tsconfig.json
, Typescript compiler would generate and add type-defs for our code to this file.
✱ "files": ["dist"]
: With this setting, only the files/folders from the dist
folder will be included in the published package.
Update .gitignore
Make sure to add dist
folder to .gitignore
Code formatting and linting configuration
In 2019, Typescript team announced their plan to eventually deprecate TSLint and support ESLint as a common linter for both javascript and typescript. typescript-eslint
project from ESLint provides packages to support for linting typescript.
Install dependencies
The detailed installation and configuration guide for typescript-eslint
can be found here, or follow along as below:
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-jest -D
As described on its Github page:
@typescript-eslint/parser
- is an ESLint-specific parser to lint typescript code.@typescript-eslint/eslint-plugin
- an ESLint-specific plugin which, when used in conjunction with@typescript-eslint/parser
, allows for TypeScript-specific linting rules to run.eslint-plugin-jest
ESLint plugin for Jest
Add .eslintrc.js
config file in the root of project folder with below configuration:
Files to be ignored for linting
Add .eslintignore
file under the root of project folder to tell ESLint which files and folders it should ignore to lint.
👆 line#6
:.prettierrc.js
is for Prettier config file that we install next.
Prettier for code formatting
Install Prettier and required packages for it to work with eslint
npm i prettier eslint-config-prettier eslint-plugin-prettier -D
Add .prettierrc.js
config file in the root of project folder with the code formatting rules you prefer:
module.exports = {
singleQuote: true,
trailingComma: "all",
printWidth: 120,
tabWidth: 4
};
and .prettierignore
Update .eslintrc.js
to add prettier
If you are using VSCode, its ESlint
extension can help to auto-fix lint and formatting errors on file save. Install/update latest version of VSCode’ ESLint extension if you don’t have it already, and add below to your VSCode settings.
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
In package.json
, add the lint script command to have linting run from CI/CD script or Git Hooks.
{
...
...
"scripts": {
...
...
"lint": "eslint 'src/**/*.{js,ts,jsx,tsx}' --quiet --fix"
...
...
}
...
...
}
lint-staged
and husky
To make sure files committed to git don’t have any linting or formatting errors, we can use lint-staged
along with husky.
lint-staged
would allow us to run a linting command on a given set of files and stage any auto-formatted/linted files for commit. Then, using husky, we can define a pre-commit hook to run lint-stage
before commit to git.
npm install husky lint-staged -D
Finally, update package.json
to add husky and lint-staged configuration.
👆line#9–19
: lint-stage and husky settings
✔️ That completes our configuration for code formatting and linting. To check our code formatting/linting configuration is working, create a temp file inside <<project root>>/src
folder, with some code contradicting to our eslint/prettier rules; on save those should be auto-corrected. 👇
Next, let’s move on to coding part
Code development
It’s time now to create some actual code. As mentioned in the introduction of this post, in this example package library we’ll define two basic themes dark and light that other applications of the org can use.
Create src
folder and add files/subfolders under it to define our light and dark theme’s color schemes, something basic like below screenshot:
👆 We defined an interface Theme
and implementing it, defined our light and dark themes. Finally, we export the two themes wrapped in orgThemes
object from index.ts
Next, we need to run typescript compiler(tsc
) to transpile our ts code to js. As per our tsconfig.json
settings, tsc
would output the transpiled js code to a folder dist
.
✱ 𝚛𝚒𝚖𝚛𝚊𝚏 to clean dist
folder before building again:
Its a good practice to clean the dist
folder before running the tsc
build command. This would remove any no-longer-used/deleted file, that may be there in the dist
folder, from previous version of code.
To clean the dist
folder, install rimraf
npm install rimraf -D
In package.json
, add a clean
command script and update build
script as below:
{
...
"scripts": {
...
"clean": "rimraf dist",
"tscBuild" : "tsc --build",
"build": "npm run clean && npm run tscBuild"
...
},
...
}
Run the build command
npm run build
This should output the transpiled code with type definitions to dist
folder 👇
Preparing to publish the package
Update package.json
again to add prepare
script (a npm life-cycle script ) that runs before npm publish
. Our prepare
script would simply run the build command that we added earlier above.
{
...
"scripts": {
...
"prepare": "npm run build"
...
},
...
}
.𝚐𝚒𝚝𝚕𝚊𝚋-𝚌𝚒.𝚢𝚖𝚕 to configure Gitlab CI job
Add a .gitlab-ci.yml
file under the project’s root folder to define/configure the Gitlab CI job for publishing the package.
👆 line #6–15
: defines our job’s script and other settings.
.𝚗𝚙𝚖𝚛𝚌
Add a .npmrc
file under the project’s root folder to configure npm package’s registry settings:
👆 line #2
: scope @btiwari-gitlab
is same that’s used in the package’s name in package.json
line #8
: example url to specify token for uploading to a project’s registry.
We specified this on line#10
.
✱ The project id here is the id of common-package-library
project that we noted in ⓃⓄⓉⒺ ①
above.(not the id of app-themes
project)
👈
${CI_JOB_TOKEN}
above is a Gitlab CI-CD variable, but we don’t need to set it in the Gitlab project’s CI-CD variables. (I guess) it gets its value assigned during the CI job by Gitlab runtime. (If anyone has better understanding of how CI_JOB_TOKEN
gets its value, please add to comments).
✱ Using ${CI_JOB_TOKEN}
in the project’s .npmrc
file would throw an error if you run commands like npm version
or npm install
locally.
For this, add export CI_JOB_TOKEN=''
to your ~/.bash_profile
file and restart VSCode (you may have to run npm.load()
, escape from the editor and relaunch VSCode)
Update 𝚙𝚊𝚌𝚔𝚊𝚐𝚎.𝚓𝚜𝚘𝚗
Next, add publishConfig
to specify the package registry URL to package.json
in below format:
<@your-package-scope-name>:registry" : "https://gitlab.com/api/v4/projects/<your-project-id>/packages/npm/"
like below for our example:
Commit and push your changes to the repository.
Publishing package to Gitlab Package Registry
Go to the app-themes
repository on Gitlab.
Merge the changes to master or your release branch as needed 👇
Once the changes are merged, go to CI/CD pipeline from the project’s left side menu.
You should now see the pipeline job. This would be the build
job that we defined above in .gitlab-ci.yml
Trigger the job to build and publish the package:
If all settings has been specified/configured correctly, the job should successfully register our package to the target project’s (common-org-packages
) registry.
Go to the target project org-common-packages
and we should now see our app-themes
npm package registered there:
Using the published package in an app
Obtain Access token for you Gitlab account
📌 ⓃⓄⓉⒺ ③ Note the access token created above
We will need this access token to configure Gitlab authentication for npm locally from the test app project.
2. Create a basic bare bone ES6 project to test our npm package
Next, we need a very basic ES6 project where in we can import and test our above created app-themes
npm package
For quick setup, we will clone some ES6 boilerplate repo. A quick google search yielded me this repo https://github.com/metagrover/ES6-boilerplate
Clone the ES6 boilerplate repo to your folder of choice and rename the cloned repository folder to test-app-themes
or anything as per your requirements.
Open the folder in VSCode
Install npm packages included in the boilerplate
Next, update index.html
as below:
👆 We added two buttons(line# 19–20
) to index.html
to apply dark or light themes.
Next, in src/index.js,
we will import our app-themes
package and implement the onclick
handlers for these two buttons to apply the selected theme, reading the selected theme colors from the package.
For this, lets first install the npm package app-themes
in our test project. The install command can be found from Gitlab’s package registry details 👇
Try running the install command from terminal:
npm i @btiwari-gitlab/app-themes
However, first time below error will be thrown:
👆 Its trying to find our Gitlab npm package at npmjs.org
. For it to check our Gitlab package registry url as well, we need to set the registry url in the local npm config.
For this , create .npmrc
file in the root of the project with below config
# set url for the scoped package@btiwari-gitlab:registry=https://gitlab.com/api/v4/projects/26671151/packages/npm/# Add auth token for the Gitlab scoped package
# //gitlab.com/api/v4/projects/<<<your project id>>>/packages/npm/:_authToken=<<<access token>>>//gitlab.com/api/v4/projects/26671151/packages/npm/:_authToken=<<<access token>>>
👆 replace <<access_token>>
with the token we created and noted down at ⓃⓄⓉⒺ ③
above.
(Alternatively, you can set these config globally on you system using the npm config
command 👇)
npm config set @btiwari-gitlab:registry https://gitlab.com/api/v4/projects/<<your project id>>/packages/npm/npm config set -- '//gitlab.com/api/v4/projects/26671151/packages/npm/:_authToken' "<<<access token>>>"
Once the URL and authentication is configured for our Gitlab package’s registry, try installing the package again. This time the package should install successfully:
👆 Our Gitlab npm package app-themes
got installed successfully. Now we can use the theme colors from the package in our test app.
Update the src/index.js
file as below:
👆 line#1
: we imported orgThemes
from our app-themes
npm package and use it in the applyTheme
function (line#6-7)
to set the selected theme.
You would also get the supported package’s types, methods and properties in the editor’s intellisense window while coding 👇
Run the application and we should be able to change the main div’s background color to dark or light theme’s color by clicking the respective button:
There you have it! Your step-by-step guide for publishing a private NPM package to Gitlab registry. I hope you would find this helpful.
Credits, References and Resources:
That’s all. Thank you!!! Feedback, comments and suggestions are welcome.