Setup Babel/Browserify build for (private) npm module - node.js

I have an npm module my_lib that is providing some objects I would like available for use by several downstream modules. my_lib is written in ES6, and uses babel to transpile. It lives in a private github repo accessible to the building hosts of the downstream modules. It is important that the downstream modules consuming my_lib are able to import symbols from my_lib like any other node module.
I would like to be able to write import { libA, libB } from 'my_lib'; in any of the downstream modules and have libA defined as expected by my_lib, assuming that downstream module's package.json includes in dependencies an entry like "my_lib": "orgname/reponame#branchname"
I have an entry in my_lib package.json under scripts like so :
"postinstall" : "npm run build",
"build": "mkdir ./dist && browserify ./src/index.js -t babelify -t envify -o index.js",`
where ./src/index.js is
// Entry point for bundling
export * as libB from "../sublib1/adapter";
export * as libA from "../sublib2/adapter";
If necessary, I could call import {libA, libB} from my_lib/dist/bundle, though this is somewhat undesirable. The main thing is to make all the names I'm exporting from my_lib available in the downstream modules. I'm probably confused about what's going on with browserify and babel. I'm perfectly happy to change the postinstall/build hooks to whatever form is more convenient.
Currently, in the downstream package (an express.js app), I have the line import * as adapters from my_lib; Running npm install on the express app completes without error. I see the index.js file I expect in node_modules/my_lib. When I break in my app after the import (or require for that matter), the adapters object exists, but none of the libB or libA on the object. It seems to be a pure vanilla JS object with no members but __proto__.

Normally in this case i will just use babel and avoid browserify. I use something like this in package.json:
{
"main": "dist/index.js",
"scripts": {
"postinstall": "babel src/ -d dist/"
},
"dependencies": {
"babel-cli": "^6.9.0",
"babel-preset-es2015": "^6.9.0"
}
}
Notice that the main entry point for the package is pointing to dist and not to src. This is to make import my_lib and require('my_lib') (both work well) to use the compiled version of the library.
Additionally this is the content of the .babelrc file that it is side by side with package.json
{
"presets": ["es2015"]
}

I use babel and browserify together.
My package.json
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"build:js": "watchify assets/src/js/main_1.js -o 'exorcist assets/dist/js/main_1.js.map > assets/dist/js/main_1.js' -d -t [babelify --presets [latest]]",
"build:scss": "node-sass -w -r assets/src/scss/ -o assets/dist/css/",
"build": "npm run build:scss & npm run build:js"
},
"devDependencies": {
"babel-cli": "^6.0.0",
"babel-preset-latest": "^6.16.0",
"babelify": "^7.3.0",
"browserify": "^13.1.1",
"exorcist": "^0.4.0",
"node-sass": "^4.5.0",
"watchify": "^3.7.0"
},
"browserify": {
"transform": [
"babelify"
]
}
}
Actual version of package.json: https://gist.github.com/artamonovdev/5f24aaca504e4d1b299bba0413f0a57d

Related

Jest fails when script imports external library

I just started testing with Jest for the first time. This is a very simple Typescript utils library. I basically followed the installation guide, and my first two tests passed. Yay. The next function I tested imported a few functions from lodash-es, and failed (error below).
This is how I added jest:
pnpm add -D jest ts-jest #types/jest
pnpx ts-jest config:init
My jest.config.js file, as generated by pnpx ts-jest config:init, except that I had to change the second line to export default, because my package.json specifies this to be a module.
/** #type {import('ts-jest').JestConfigWithTsJest} */
export default {
preset: 'ts-jest',
testEnvironment: 'node',
transformIgnorePatterns: [
"node_modules/(?!(lodash-es)/)"
],
moduleNameMapper: {
'lodash-es': '../node_modules/lodash-es/lodash.js',
},
}
I've tried adding transformIgnorePatterns and moduleNameMapper, based on others with similar issues. But no luck.
The error message:
D:\Dev\NodeJs\vexna\packages\util>pnpm test
> #vexna/util#1.0.0 test D:\Dev\NodeJs\vexna\packages\util
> jest
PASS test/reverseString.test.ts
PASS test/hash.test.ts
FAIL test/uid.test.ts
● Test suite failed to run
Jest encountered an unexpected token
Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
Details:
D:\Dev\NodeJs\vexna\node_modules\.pnpm\lodash-es#4.17.21\node_modules\lodash-es\lodash.js:10
export { default as add } from './add.js';
^^^^^^
SyntaxError: Unexpected token 'export'
> 1 | import { now, random, padStart, padEnd } from "lodash-es"
| ^
2 | import { reverseString } from "./reverseString"
3 |
4 |
at Runtime.createScriptFromCode (../../node_modules/.pnpm/jest-runtime#29.4.1/node_modules/jest-runtime/build/index.js:1598:14)
at Object.<anonymous> (src/uid.ts:1:1)
at Object.<anonymous> (test/uid.test.ts:1:1)
Test Suites: 1 failed, 2 passed, 3 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 3.494 s
Ran all test suites.
 ELIFECYCLE  Test failed. See above for more details.
A few things, this project exists in pnpm monorepo, in case it matters.
This is the package.json:
{
"name": "#vexna/util",
"version": "1.0.0",
"description": "Generic utilities, uses lodash",
"private": true,
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"sideEffects": false,
"scripts": {
"build": "rimraf dist && tsc && rollup -c rollup.config.js",
"test": "jest",
"pretest": "npm run build"
},
"devDependencies": {
"#rollup/plugin-typescript": "^11.0.0",
"#types/jest": "^29.4.0",
"#types/lodash-es": "^4.17.6",
"jest": "^29.4.1",
"lodash-es": "^4.17.21",
"rimraf": "^4.1.2",
"rollup": "^3.12.1",
"ts-jest": "^29.0.5",
"typescript": "^4.9.5"
},
"files": [
"dist"
],
"peerDependencies": {
"lodash": "^4.17.21"
}
}
I suspect that perhaps Jest goes off into the boonies because I have externalized lodash?
Or perhaps I should add Babel? But could just as well be something else completely.
Some help is much appreciated!
Use Vitest.
Jest is quite painful to install correctly, whereas Vitest works out of the box without any config. This is my personal experience. It took me days to get Jest to sort-of work. I had Vitest working immediately after installation.
I don't want to hate on Jest, I actually think it's a wonderful and intuitive testing tool. But ultimately Vitest "Just Works" (tm), and you can use the same simple Jest style API we all have come to love.
Steps:
pnpm remove jest ts-jest #types/jest
pnpm add -D vite vitest
Delete jest.config.js, and create vite.config.ts:
/// <reference types="vitest" />
import { defineConfig } from 'vite'
export default defineConfig({
test: {
/* for example, use global to avoid globals imports (describe, test, expect): */
// globals: true,
},
})
Add to your tests:
import { assert, expect, test } from 'vitest'
Update your package.json:
"test": "vitest",

Yarn link: Importing graphql-js project into another graphql-js project showing another module or realm error

I am using graphql-js instead of SDL for designing my graphql server. For this, I have created a small library which depends on graphql-js.
Thus, I am linking this library into my main project using yarn (yarn add link:../lib) to build graphql objects and schema.
My package.json files are given below
graphql-lib/package.json
{
"name": "graphql-lib",
"private": true,
"version": "0.1.0",
"description": "",
"main": "index.ts",
"dependencies": {
"graphql-iso-date": "^3.6.1"
},
"devDependencies": {
"#types/graphql-iso-date": "^3.4.0",
"#types/jest": "^25.2.3",
"#types/node": "^14.0.5",
"jest": "^26.0.1",
"ts-jest": "^26.1.0",
"typescript": "^3.9.3"
},
"peerDependencies": {
"graphql": "^15.1.0"
}
}
core/package.json
{
"name": "#core/schema",
"private": true,
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"graphql-lib": "link:../lib",
"graphql": "^15.1.0",
"graphql-iso-date": "^3.6.1"
},
"devDependencies": {
"#types/graphql-iso-date": "^3.4.0",
"#types/jest": "^26.0.0"
}
}
graphql-lib testing using ts-jest is working fine.
However, when I am testing my main project I am getting following error -
Cannot use GraphQLScalarType "Float" from another module or realm.
Ensure that there is only one instance of "graphql" in the node_modules
directory. If different versions of "graphql" are the dependencies of other
relied on modules, use "resolutions" to ensure only one version is installed.
graphql module in the node_modules directory contains only the graphql-js version 15.1.0. I have remove and reinstalled the node_modules in both the packages.
My understanding is that there should single executions instance of graphql. Am I missing something such that graphql instance is created in both the project? Can I link my project using yarn and maintain a single graphql instance?
That error only occurs when there are multiple copies of graphql-js in your dependencies. Most commonly it's because there are multiple versions in your node_modules. You can verify that's the case by running npm ls graphql or yarn ls graphql -- if you see multiple versions listed in your dependencies, that's a problem. Typically, this only happens when you have dependencies that directly depend on graphql-js (instead of making it a peer dependency). If you use yarn, you can use it's selective dependency feature to get around that issue.
When you're doing local development of multiple packages, you can also run into this issue because you have two different copies of graphql-js -- one in each of your two projects. That happens because npm link or yarn add link only creates a symlink from one of your project's node_modules to the other project. As a workaround, you can link graphql-js as well. Go into node_modules/graphql inside project A and run npm link/yarn link. Then go into the root directory for project B and run npm link graphql/yarn link graphql. Now project B will use project A's copy of the library instead of its own.

Node v13 / Jest / ES6 — native support for modules without babel or esm

Is it possible to test ES6 Modules with Jest without esm or babel? Since node v13 supports es6 natively have tried:
//package.json
{
…
"type": "module"
…
}
//__tests__/a.js
import Foo from '../src/Foo.js';
$ npx jest
Jest encountered an unexpected token
…
Details:
/home/node/xxx/__tests__/a.js:1
import Foo from '../src/Foo.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
When babel is added a transpiler, it works, but can es6 modules be used natively as well?
Yes, it is possible from jest#25.4.0. From this version, there is a native support of esm, so you will not have to transpile your code with babel anymore.
It is not documented yet, but according to this issue you have to do 3 easy steps to achieve that (At the time of writing this answer):
Make sure you don't transform away import statements by setting transform: {} in your jest config file
Run node#^12.16.0 || >=13.2.0 with --experimental-vm-modules flag
Run your test with jest-environment-node or jest-environment-jsdom-sixteen.
So your jest config file should contain at least this:
export default {
testEnvironment: 'jest-environment-node',
transform: {}
...
};
And to set --experimental-vm-modules flag, you will have to run Jest from package.json as follows (I hope this will change in the future):
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
}
I hope, this answer was helpful to you.
Note that this is is still experimental, but we have documented how to test this, so there's hopefully less confusion.
https://jestjs.io/docs/en/ecmascript-modules
The steps in https://stackoverflow.com/a/61653104/1850276 are correct
I followed the tips provided in the accepted answer, but I added the property "type": "module" in my package.json in order to jest works properly. This is what I done:
In package.json:
"devDependencies": {
"jest": "^26.1.0",
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-environment-node": "^26.1.0"
},
"scripts": {
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
},
"type": "module",
"jest": {
"transform": {},
"testEnvironment": "jest-environment-jsdom-sixteen"
}
To run jest from "jest" extension in VSCode with "--experimental-vm-modules" flags, put this config in your global or workspaces settings.json:
"jest.nodeEnv": {
"NODE_OPTIONS": "--experimental-vm-modules"
}
In addition to #Radovan Kuka's answer, here's how to run Jest with ES modules, using npx:
"test:monitoring": "npx --node-arg=--experimental-vm-modules jest -f monitoring.test.js --detectOpenHandles",
The benefit is that one doesn't need to provide the absolute node_modules path.
Without Babel, here's a complete, minimal example that works on recent Jest versions. Run with npm test.
$ tree -I node_modules
.
├── package.json
├── src
│   └── foo.js
└── __tests__
└── foo.spec.js
package.json:
{
"type": "module",
"scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
},
"devDependencies": {
"jest": "^29.3.1"
}
}
src/foo.js:
export const bar = () => 42;
__tests__/foo.spec.js:
import {bar} from "../src/foo";
describe("foo.bar()", () => {
it("should return 42", () => {
expect(bar()).toBe(42);
});
});
The secret sauce is in the package.json: "type": "module" and NODE_OPTIONS=--experimental-vm-modules jest.
If you want to add a mock, it's a bit complicated. See this answer.

How to use .ENV variables when publishing npm package

How do I inject my environment variable into the final build that gets published to npm?
I'm using the dotenv package and that works great locally. However when I do npm publish, the env variable is gone since it just runs the file in the main property of the package.json.
Some relevant fields from package json:
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
"files": [
"lib",
".env"
],
"scripts": {
"start" : "tsc && node -r dotenv/config ./lib/index.js"
},
"bin" : {
"run-app" : "./lib/index.js"
},
"dependencies": {
"dotenv": "^8.2.0",
}
.gitignore:
# OSX
.DS_Store
node_modules
notes.MD
lib
.env
I tried to inject my env variable by passing it to my start script and also call the .config() method on dotenv inside the code. Either way the env variable ends up undefined when running my package using npx.
What am I doing wrong?

webpack terminal command results in /config/webpack/development.js not found

When I enter $ webpack into my terminal I receive the following error:
webpack config
/Users/kristenmkulha/Desktop/react-help-queue/config/webpack/development.js
not found, please run 'bundle exec rails webpacker:install' to install
Webpacker with default configs or add the missing config file for your
custom environment.
I found a similar issue here: Webpack command in node brings /config/webpack/development.js not found and changed the deprecated babel-preset-es2015 to babel-preset-env and I already had the -loader suffix. These updates still result in the same error.
Here is my webpack.config.js:
const webpack = require('webpack');
const { resolve } = require('path');
module.exports = {
entry: [
resolve(__dirname, "src") + "/index.jsx"
],
output: {
filename: 'app.bundle.js',
path: resolve(__dirname, 'build')
},
resolve: {
extensions: ['.js', '.jsx']
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
options: {
presets: [
"env",
"react"
]
}
},
],
}
};
Here is my package.json file:
{
"name": "react-help-queue",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"react": "^15.5.4",
"react-dom": "^15.5.4"
},
"devDependencies": {
"babel-core": "^6.24.1",
"babel-loader": "^7.0.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"webpack": "^3.8.1"
}
}
Huh, it looks like running "webpack" on the command line is invoking a different tool (maybe "webpacker"? https://github.com/rails/webpacker)
There are two ways to solve this:
add a additional script to package.json to kick off webpack.
"scripts": {
"build": "webpack -p"
},
With the above added, execute this command in a terminal: npm run build.
When a script is executed via npm run, npm will automatically include binaries (like webpack) in its PATH, so it the script uses the webpack binary that is part of the apps dev dependencies. Read more on this here: https://docs.npmjs.com/cli/run-script.
Install webpack globally: npm install -g webpack.
This will make webpack available at the command line in all cases, which allows the developer to just type webpack and have it run. This should(?) override the existing "webpacker" command. If not, you may need to uninstall that tool or modify your PATH.
Installing npm packages globally is convienient but it can cause headaches. For example, if two different projects need two different versions of webpack, it may not be efficient. Also, you may find there are several steps in a "build" to execute once an application gets more complex, so having a convenient script included in the project may be appealing (especially for projects with multiple developers).
Hope this helps!

Resources