NX How to
npx create-nx-workspace
cd monorepo
React lib
npm install --save-dev @nx/react
npx nx g @nx/react:lib packages/{LIB_NAME} --linter eslint --bundler vite --style scss --unitTestRunner jest
npx nx run {LIB_NAME}:build
in vite.config.js, set all dependencies external:
rollupOptions: {
external: (id) => !id.startsWith('.') && !id.startsWith('/'),
},
node lib
npm install -D @nx/node
npx nx g @nx/node:lib packages/{LIB_NAME} --buildable --linter eslint --unitTestRunner jest
npx nx run {LIB_NAME}:build
add compilerOptions in {LIB_NAME}/tsconfig.lib.json
{
"compilerOptions": {
"module": "ESNext", // Change from default (CommonJS) to ES module output
"moduleResolution": "Node" // Use Node-style resolution for ES modules
}
}
react app
npx nx g @nx/react:app packages/{APP_NAME} --style scss --bundler vite --linter eslint
npx nx run {APP_NAME}:build
npx nx run {APP_NAME}:serve
node app
npx nx g @nx/node:app packages/{APP_NAME} --linter eslint --e2eTestRunner jest --framework none --unitTestRunner jest
In packages/{APP_NAME}/package.json :
- (optional) set bundle to true and thirdParty to true
- set react and react-dom as external to not bundle them when importing a lib that use jsx
- set runBuildTargetDependencies in serve target to force recompilation on file changes
In packages/{APP_NAME}/tsconfig.app.json :
remove :
{
"compilerOptions": {
"module": "nodenext",
"moduleResolution": "nodenext"
}
}
if necessary, in package.json
"external": [
"react",
"react-dom",
"*.css",
"*.svg",
"*.woff",
"*.woff2",
"*.eot",
"*.ttf",
"*.otf"
],
...
"esbuildOptions": {
"sourcemap": true,
"outExtension": {
".js": ".js"
},
"loader": {
".css": "empty",
".svg": "empty",
".woff": "empty",
".woff2": "empty",
".eot": "empty",
".ttf": "empty",
".otf": "empty"
}
}
app node ESM
Rename all .ts file to .mts
Use .mjs extension in relative local import
In packages/{APP_NAME}/package.json :
- add type: module
- change outExtension from .js to .mjs
- change main path from .ts to .mts
{
"type": "module",
"nx": {
"targets": {
"build": {
"options": {
"format": ["esm"],
"bundle": true,
"thirdParty": true,
"external": ["react", "react-dom"],
"main": "packages/app-node-1/src/main.mts",
"esbuildOptions": {
"outExtension": {
".js": ".mjs"
}
}
},
"configurations": {
"production": {
"esbuildOptions": {
"outExtension": {
".js": ".mjs"
}
}
}
}
},
"serve": {
"options": {
"runBuildTargetDependencies": true
}
}
}
}
}
In packages/{APP_NAME}/tsconfig.app.json : Add .mts to include array
{
"include": ["src/**/*.ts", "src/**/*.mts"]
}
storybook
npm install --save-dev @nx/storybook
npx nx g @nx/react:storybook-configuration {LIB_NAME}
add nodePolyfills in vite.config.ts
tailwind
npm install -D tailwindcss@3.4.17 autoprefixer postcss
create tailwind.config.js, postcss.config.js
# in monorepo root
npx tailwindcss init -p
edit tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [],
};
edit postcss.config.js
const { join } = require('path');
module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, 'tailwind.config.js'),
},
autoprefixer: {},
},
};
Create a scss file that import tailwind styles
packages/{LIB_NAME}/src/lib/index.scss :
@tailwind base;
@tailwind components;
@tailwind utilities;
Import it from index.ts
packages/{LIB_NAME}/src/index.ts :
import './lib/index.scss';
// ...
// export { ... } from ...
In package.json, declare style and export the resulting compiled css file
packages/{LIB_NAME}/package.json :
{
"style": "./dist/style.css",
"exports": {
"./style": "./dist/style.css"
}
}
In the app using the react lib, import the lib styles
packages/{APP_NAME}/src/main.tsx :
import '@holistix/{LIB_NAME}/style';
setup storybook
If necessary, create a storybook global wrapper for adding contexts and mock
packages/{LIB_NAME}/.storybook/global-wrapper.tsx :
// add common wrapper, mock context etc in this component
export const GlobalWrapper = (Story: any) => <Story />;
Import the index.scss (containing tailwind styles) file from preview.ts
packages/{LIB_NAME}/.storybook/preview.ts :
import type { Preview } from '@storybook/react';
import { GlobalWrapper } from './global-wrapper';
import '../src/lib/index.scss';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
backgrounds: {
default: 'dark',
values: [
{
name: 'dark',
value: '#222',
},
],
},
},
decorators: [GlobalWrapper],
tags: [],
};
export default preview;
json
add "resolveJsonModule": true in compilerOptions on tsconfig.app.json or tsconfig.lib.json
{
"compilerOptions": {
"resolveJsonModule": true
},
"files": [
"src/app/oas30.json",
"src/app/exec-pipes.json",
"src/app/data-connections.json",
"src/app/sql-api-pg.json"
],
"include": ["src/**/*.ts"]
}
Others Actions
build all
npx nx run-many -t build
Rename lib
nx g @nrwl/workspace:mv --project XXXXXXX --destination YYYYYYY
Delete remove
nx g remove three-flow
Update everything
# upgrade nodejs
# remove package.json: overrides: {}
npm install -g npm@latest
nx migrate latest
nx migrate --run-migrations
npm install --global npm-check-updates@latest
npm-check-updates
npm install xxxxx@X.Y.Z
npm audit
# package.json: overrides: {}
Run NX monorepo jest tests
npx nx run-many --all --target=test --parallel