Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exporting my own Flow type with npm package?

I have a npm package of React components which are using flow for type-checking.

It would be useful for the users of my components to have access to my flow types. However at the moment I am compiling my code using Babel which strips all type information.

My project structure is as follows :

|
|- flowdecls
       myTypes.js
| -components
    - Component1
        Component1.js
| - lib
    - Component1.js (compiled using Babel)
    - Component1.js.flow (created using flow-copy-source)


For example one of my types in myTypes.js is

declare type DataItemIconType = {
    iconElement: React$Element<React$ElementType>,
    color?: string,
    hoverColor?: string
}

which I would using in Component1. For example one of the props of Component1 would be

iconList : Array<DataItemIconType>

I have already published several versions of my library of React components as an npm package without Flow and my components are being widely used. However I would really like to provide flow support.

In my most recent I tried using flow-copy-source as specified in this article (Authoring and publishing JavaScript modules with Flow) but users of my library still can't access my types.

How would I make a type such as DataItemIconType available to someone using Component1 in my library ?

package.json

{
  "name": "@company/react-common-components-build-template",
  "version": "0.6.0",
  "main": "./lib/index.js",
  "private": true,
  "engines": {
    "node": ">=4.0.0"
  },

  "files": [
    "lib"
  ],
  "description": "Common component library",
  "peerDependencies": {
    "react": "16.10.0",
    "react-dom": "16.10.0",
    "prop-types": "15.7.2"
  },
  "dependencies": {
    "@material-ui/core": "4.9.5",
    "@material-ui/icons": "4.9.1",
    "@material-ui/lab": "3.0.0-alpha.30",
    "@material-ui/styles": "4.9.0",
    "lodash": "4.17.15"
  },
  "devDependencies": {
    "@babel/cli": "7.4.4",
    "@babel/core": "7.6.0",
    "@babel/node": "7.2.2",
    "@babel/plugin-proposal-class-properties": "7.2.1",
    "@babel/plugin-syntax-dynamic-import": "7.2.0",
    "@babel/plugin-transform-object-assign": "7.2.0",
    "@babel/plugin-transform-react-constant-elements": "7.6.0",
    "@babel/plugin-transform-runtime": "7.6.2",
    "@babel/preset-env": "7.4.2",
    "@babel/preset-flow": "7.0.0",
    "@babel/preset-react": "7.0.0",
    "@babel/register": "7.0.0",
    "@svgr/webpack": "4.3.2",
    "@typescript-eslint/eslint-plugin": "^2.2.0",
    "@typescript-eslint/parser": "^2.2.0",
    "babel-eslint": "10.0.3",
    "babel-jest": "24.9.0",
    "babel-loader": "8.0.6",
    "babel-plugin-named-asset-import": "0.3.4",
    "babel-plugin-react-remove-properties": "0.3.0",
    "babel-preset-react-app": "9.0.2",
    "camelcase": "^5.2.0",
    "case-sensitive-paths-webpack-plugin": "2.2.0",
    "chokidar": "1.6.1",
    "classnames": "2.2.6",
    "cpx": "1.5.0",
    "cross-env": "6.0.3",
    "css-loader": "2.1.1",
    "dotenv": "6.2.0",
    "dotenv-expand": "5.1.0",
    "enzyme": "3.10.0",
    "enzyme-adapter-react-16": "1.15.1",
    "enzyme-to-json": "3.4.3",
    "eslint": "6.6.0",
    "eslint-config-react-app": "5.0.2",
    "eslint-loader": "3.0.0",
    "eslint-plugin-flowtype": "3.13.0",
    "eslint-plugin-flowtype-errors": "4.1.0",
    "eslint-plugin-import": "2.18.2",
    "eslint-plugin-jsx-a11y": "6.2.3",
    "eslint-plugin-react": "7.16.0",
    "eslint-plugin-react-hooks": "^2.3.0",
    "file-loader": "3.0.1",
    "flow-bin": "0.113.0",
    "flow-copy-source": "^2.0.9",
    "flow-typed": "^2.6.2",
    "fs-extra": "7.0.1",
    "glob-gitignore": "1.0.14",
    "hard-source-webpack-plugin": "^0.13.1",
    "highlight": "^0.2.4",
    "highlight.js": "^9.10.0",
    "html-webpack-plugin": "4.0.0-beta.5",
    "husky": "3.0.8",
    "identity-obj-proxy": "3.0.0",
    "is-wsl": "^1.1.0",
    "jest": "24.9.0",
    "jest-environment-jsdom-fourteen": "1.0.1",
    "jest-enzyme": "^7.1.2",
    "jest-resolve": "24.9.0",
    "jest-watch-typeahead": "0.4.0",
    "mini-css-extract-plugin": "0.9.0",
    "npm-run-all": "4.0.2",
    "optimize-css-assets-webpack-plugin": "5.0.3",
    "pnp-webpack-plugin": "1.5.0",
    "postcss-flexbugs-fixes": "4.1.0",
    "postcss-loader": "3.0.0",
    "postcss-normalize": "7.0.1",
    "postcss-preset-env": "6.7.0",
    "postcss-safe-parser": "4.0.1",
    "prettier": "1.19.1",
    "react": "16.10.0",
    "react-addons-test-utils": "15.5.1",
    "react-app-polyfill": "^1.0.3",
    "react-dev-utils": "10.2.0",
    "react-docgen": "3.0.0",
    "react-dom": "16.10.0",
    "react-highlight": "^0.12.0",
    "react-test-renderer": "16.10.0",
    "resolve": "1.15.0",
    "resolve-url-loader": "3.1.1",
    "sass-loader": "8.0.2",
    "semver": "6.3.0",
    "style-loader": "1.0.0",
    "terser-webpack-plugin": "2.3.4",
    "ts-pnp": "1.1.5",
    "url-loader": "2.3.0",
    "webpack": "4.41.5",
    "webpack-dev-server": "3.10.2",
    "webpack-manifest-plugin": "2.2.0",
    "workbox-webpack-plugin": "4.3.1"
  },
  "scripts": {
    "prestart": "npm run gen:docs",
    "start": "npm-run-all --parallel start:docs gen:docs-watch",
    "start:docs": "node scripts/start.js",
    "gen:docs": "node scripts/generateComponentData.js",
    "gen:docs-watch": "npm run gen:docs -- --watch",
    "build:docs": "node scripts/build.js",
    "test": "node scripts/test.js",
    "predeploy:docs": "npm run build:docs",
    "flow": "flow",
    "lint": "eslint src --debug",
    "lint:flow-typed": "flow-typed install --ignoreDeps dev",
    "build:images": "cpx \"./src/components/images/**/*.*\" ./lib/images",
    "prebuild:common-components-lib": "rimraf lib",
    "build:common-components-lib": "npm-run-all --parallel build:components build:utils build:images build:copy-files build:copyflowsource",
    "build:components": "cross-env NODE_ENV=production BABEL_ENV=cjs babel ./src/components --out-dir ./lib/ --ignore spec.js",
    "build:utils": "cross-env NODE_ENV=production BABEL_ENV=cjs babel src/components/utils --out-dir ./lib/utils --ignore spec.js",
    "build:copyflowsource": "flow-copy-source ./src/components ./lib ",
    "build:copy-files": "node scripts/copyBuildFiles.js",
    "prettier:changed": "node ./scripts/prettier.js",
    "prettier:all": "node ./scripts/prettier.js write",
    "format-check": "prettier --check \"./src/**/*.{js,test.js,spec.js}\""
  },
  "publishConfig": {
    "registry": "http://srv-ie-nexus/repository/npm-hosted/"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "jest": {
    "roots": [
      "<rootDir>/src/components"
    ],
    "collectCoverageFrom": [
      "src/**/*.{js,jsx,ts,tsx}",
      "!src/**/*.d.ts"
    ],
    "setupFiles": [
      "react-app-polyfill/jsdom"
    ],
    "setupFilesAfterEnv": [
      "<rootDir>/jest-test-setup.js"
    ],
    "testMatch": [
      "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
      "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
    ],
    "testEnvironment": "jest-environment-jsdom-fourteen",
    "transform": {
      "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
      "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
      "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
    },
    "transformIgnorePatterns": [
      "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
      "^.+\\.module\\.(css|sass|scss)$"
    ],
    "modulePaths": [],
    "moduleNameMapper": {
      "^react-native$": "react-native-web",
      "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
    },
    "moduleFileExtensions": [
      "web.js",
      "js",
      "web.ts",
      "ts",
      "web.tsx",
      "tsx",
      "json",
      "web.jsx",
      "jsx",
      "node"
    ],
    "watchPlugins": [
      "jest-watch-typeahead/filename",
      "jest-watch-typeahead/testname"
    ]
  }
}
like image 402
Simon Long Avatar asked May 19 '20 11:05

Simon Long


Video Answer


2 Answers

Flow will automatically look for adjecent module.js.flow declarations files, if present.

For example, if you have a main field that points to dist/index.js, then adding dist/index.js.flow to your package's outputs will have it picked up by the user's language server.

like image 61
Filip Dupanović Avatar answered Sep 27 '22 22:09

Filip Dupanović


I don't think flow has a solution to this yet. But I was in the same boat as you.

Pretty much the reason your consumers aren't able to use your package types are because they read global types from their .*/flow-typed/* not your one which when they install your package will be in .*/node_modules/@company/react-common-components-build-template. It has nothing to do with any package.json settings.

You have two options though to ship your package with types.

Option 1:

Either you put them as part of your src code and allow them to import it, which means your types won't be global anymore and you need to import the types you want to use within your source code. But this means so can you consumers.

Option 2:

Or keep them in your flowdecls and ensure they're published. Once they are, your users will have to copy your type defs into their flow-typed dir, which will now make them global within their project also.

You can add something like the following into your README to give users some insight:


react-common-components-build-template also uses global type definitions internally. If you would like to take advantage of this, you can copy /node_modules/@company/react-common-components-build-template/flowdecls/myTypes.js to your local flow-typed directory. It's probably a good idea to do a copy once per version upgrade so you stay in-sync with the latest changes which you can do via npm scripts.

"scripts": {
  // ...
  "postinstall": "cp /node_modules/@company/react-common-components-build-template/flowdecls/myTypes.js flow-typed"
},

The second option is actually what I've gone with, and the above is what I have in my repo. As a side note, I would recommend though that you rename your dir to flow-typed and your types file to something like react-common-components-build-template.js so that when the copy process happens, people don't wonder what myTypes.js is used for.

like image 23
Brianzchen Avatar answered Sep 27 '22 23:09

Brianzchen