monorepo with vue-cli 3 and lerna - node.js

I'm trying to create a monorepo using vue-cli 3 and lerna. I now have two packages:
common and app. Both common and app use Vue and import it. common has its main set like this. "main": "dist/common.umd.min.js"
When I import common in app, the process crashes with this error message trying to process common.umd.min.js:
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
Even running with node --max-old-space-size=4096 node_modules/#vue/cli-service/bin/vue-cli-service.js serve throws this error.
If I use "main": "src/main.ts", the build process works, but when I use vue-cli-service build, Vue is bundled twice, once for common and once for app.

Like you noticed, you are bundling Vue twice in your app. This is likely because you are including Vue as a dependency of both your common and app packages.
Have the app package be the only package that depends on Vue, and then you can make your common package export a Vue plugin. Your app package can then install the common plugin like so:
// main.js (in the app package)
import Vue from 'vue';
import plugin from 'my-common-package';
Vue.use(plugin);
I'm unsure of what is causing the out of memory error, but only using one Vue instance is a good start.

Related

How to import from a bundle created in webpack?

I'm working on a project that's based on a given sample project that uses a package named Seeso.
The sample project uses the 'cross-env' and 'parcel-bundler' packages which are both deprecated, and I would like to replace them with pure node.
I managed to create a backend that statically serves requested files and thus handles imports,
but the Seeso package seems to dynamically resolve its imports using a webpack bundle (I have little to no knowledge of webpack but not even a configuration file is present; just the bundle), and I get the following error after I changed the import path of Seeso in the easy-seeso.js file to its actual path:
Uncaught SyntaxError: The requested module '/external_modules/seeso/dist/seeso.js' does not provide an export named 'CalibrationAccuracyCriteria' (at easy-seeso.js:2:34)
because of
import Seeso, {InitializationErrorType, CalibrationAccuracyCriteria} from '/external_modules/seeso/dist/seeso.js';
How can I import the needed Seeso files from the webpack bundle with minimum work and understanding of webpack as possible? (Preferably without running webpack commands before running - would like to run 'node server.js' and that's it)
Here is the sample project:
https://github.com/visualcamp/seeso-sample-web

"Cannot find module" when running unit tests on node.js with react as peer dependency in common package

I have following app structure:
Application A
Application B
Common package
Now Application A and B have in package.json the common package added:
{
dependencies: {
"commonPackage": "file:../../../commonPackage"
}
}
both apps use React, as well as the common package, all had React added with npm, and it worked, before we started to use react hooks.
Because when we started, we got an Invalid Hook Call Warning due to having "more than one copy of React", so to avoid that, in the common package, the react dependency was moved to peerDependencies so that the react instance from the app is used and not from the package.
It works great in the browser when we run both apps A and B, but when I run my mocha tests in the console, I get:
ERROR in ../commonPackage/~/#uifabric/utilities/lib/customizations/Customizer.js
Module not found: Error: Can't resolve 'react' in 'D:\myProject\commonPackage\node_modules\#uifabric\utilities\lib\customizations'
this is from the office-ui-fabric-react package we use, but it seems like a more general issue with dependency resolution.
Project is in TypeScript, we use webpack for the compilation of the app for the browser, and tsc to compile for the unit tests.
I found some answers, suggesting to npm link react in the common package to the react package in the application node_modules, but it seems wrong, since the common package is used by two applications, it would solve the issue only for one.
In the case above we finally came to a solution which was
adding back react as devDependency to Common package
using esm package to help our test runs understand es6 module export/import that came with fabric package. Just using mocha --require esm ...
ejecting and adding alias to webpack.config.js in Application
alias: {
'react': path.resolve('./node_modules/react')
}
Application A, B and hooks work now.

Cannot load zmg.node after webpack bundle

I'm using the zerorpc package within an Electron-Vue project as a client to communicate with a remove server. Before using webpack to bundle the project, I was able to call these functions both in the main process and in the renderer process of Electron but after using vue command line to create a project which automatically adds in webpack and babel, it cannot load the module.
I've created a basic Vue.js project using its command line interface and then installed vue-electron-builder plugin and selected electron 5 using the following commands:
vue create myproject
yarn install
vue add electron-builder
yarn install zerorpc --save
yarn electron:serve
Some had suggested to rebuild the zeromq library against the Electron headers for a similar problem but that didn't solve the problem.
When I run yarn electron:serve, it finishes the normal serving procedure successfully but faces an error it's bundling the main process:
error in ./node_modules/zeromq/build/Release/zmq.node
Module parse failed: Unexpected character '' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)

Electron-forge + Babel + React + JSX: "unknown option base.Children" in production app

This seems related to BABEL: Unknown option: base.Children, but the fixes provided there don't help my situation. Two days ago I had an Electron application that ran in development mode (via 'electron-forge start') and as a packaged application (starting the executable in the folder produced by 'electron-forge package'). The app continues to run in development, and it will execute in production, but Babel produces an error in the Web console:
Unknown option: base.Children. Check out http://...
This occurs on the first require statement calling for one of my JSX files (there's another thing: react-forge doesn't transpile the JSX, and I suspect I'm about to be told to RTFM over that matter). I can get the same error to pop up whenever I want; all I have to do is enter "require('somefile.jsx')" in the console, and it'll do the same thing. Investigation of the error reveals that the options manager's mergeOptions function is passed a copy of React at one point during the loop that's supposed to incorporate the presets and plugins. Again, this did not start happening after a change to the application code; I tried to update some packages in NPM, and the next build I did produced this error.
I've wiped the node_modules directory completely and run a fresh 'npm install' followed by 'electron-rebuild' and a repackaging of the app produces the same results. I've tried incorporating the .babelrc contents in package.json according to the docs at the Babel website. Again, dev works fine and production fails. Creating a compliant .compilerc produced similarly disparate results. How is my production app getting a React component where it should have the Babel options?
I just found the solution. It's a combination problem. React itself and the React preset for Babel both answer to 'react' as a preset name. If the plugin is missing but React is present, Babel will pull React and pass it to mergeOptions, producing the error described.
On the other side, if you've made the mistake of requiring a module (like the React preset) in your package.json under both dev dependencies and general dependencies, the packaging subprocess run by electron-forge will ignore the entry under general dependencies. Result: no preset, and instead of spitting out a "missing a preset" error, Babel just sucks up React itself and pretends it's found the preset it was told to look for.

What is the correct way of adding a dependency to react in your package.json for a react component

I have made a few simple reusable react components and wanted to know the correct way to include a dependency to react in my package.json for publishing with npm.
I am currently doing this:
Assuming my component will use the most recent version of react and I have tested and it works with that version. e.g. 0.13.3
"peerDependencies": {
"react": "^0.13.3"
},
For reusable components:
Put a react dependency in both peerDependencies and devDependencies.
Never put a react dependency in dependencies.
peerDependencies specifies which version(s) of React your reusable component supports/requires. When using npm 2 this also adds React to the list of modules to be installed, but this is no longer the case with npm 3.
devDependencies ensures React will be installed when you run npm install while developing your component, or when running tests on Travis or similar.
Putting react in dependencies will cause multiple versions of React to be installed if somebody uses your component but has a different version of React in their own package.json - having multiple versions of React not only bloats the build, but also causes errors when different versions try to interact.
The selected answer is definitely the prescribed approach here however I've started favoring the use of inversion of control as opposed to relying on npm peer dependencies for my libraries dependencies and its served me well.
Libraries are easier if you build them functional. It seems to be easier to maintain libraries that export a single function which takes in an object with all of their heavy dependencies and export an object containing each of your libraries typical exports.
Library 'injected'
lib/index.js
export default ({ React }) => {
const InjectedComponent = props => (
<p style={{color: props.color}}>This component has no React npm dependencies.</p>
)
/** other stuff */
return { InjectedComponent }
}
Consuming App
app.js
import React from 'react'
import { render } from 'react-dom'
/** Import the default export factory from our library */
import createInjectedComponent from 'injected'
/** Call the factory, passing its dependencies (guaranteed to match what we're bundling) and get back our component */
const { InjectedComponent } = createInjectedComponent({ React })
render(<InjectedComponent color="blue" />, document.getElementById('root'))
If your component only works with a given version of react or some other dependency, you can write some assertions around the version for the React parameter that is passed in. Overall, building libraries in this fashion should be less prone to new build issues appearing anytime version of React is published and will more importantly ensure that you are not causing your library consumers to bundle multiple versions of React and other heavy libraries. This pattern works well with npm link (I generally have 16+ libraries running from npm link simultaneous and experienced issues when I didn't use this pattern).
In your main app I would recommend always splitting out react, react dom and any react lib components you use into a vendor bundle (webpack) and mark it as external in your main bundle so that you do not unintentionally bundle two versions.
You can have react in either peerDependencies or in dependencies. The difference being that with a peerDependencies, react is only installed once for the package using your package. If you put it in dependencies, react will be installed twice, one time for the package using your package, and once time for your package.
React itself seems to favor peerDependencies for some reason. You obviously don't want two separate versions of react in your Javascript bundle (which happens by default if you use dependencies), but that is easy to fix with npm dedupe.
So there's no correct way to do it, both peerDependencies and dependencies work. Using dependencies is more in line with the node/NPM way, but using peerDependencies is friendlier to users of your package that doesn't know about npm dedupe and why it's needed.

Resources