Sharing Code between Multiple React Applications with Symlinks - javascript

I currently have two separate frontend applications. A lightweight mobile client and a heavyweight administration panel. Both were created with CRA. We use TypeScript for everything.
Currently, the directory structure is as follows:
root
├── admin (created using create-react-app)
| ├── node_modules
| ├── public
| ├── src
| │ └── common (symlink)
│ │ └── index.ts
| ├── package.json
| └── tsconfig.json
├── mobile (created using create-react-app)
| ├── node_modules
| ├── public
| ├── src
| │ └── common (symlink)
│ │ └── index.ts
| ├── package.json
| └── tsconfig.json
└── common (linked)
├── src
├── package.json
└── tsconfig.json
For whatever reason, CRA does not respect the symlinks. It's as if no files are even there.
Is there a sanctioned way to do something like this?
Right now, we're copying files into the two repositories with another script. I also tried to use yarn link, but Typescript can't resolve the files properly (it keeps expecting to see a JavaScript).

There is a few different approaches.
You could package it as a library and import said library in to your projects. The library can be hosted on a private or public Git host and reference the Git URL in your package.json just like a NPM package.
"dependencies": {
"your-lib": "git+ssh://git#domain.com:name/repo.git",
}
This approach forces you to push your code and re-install on every change though. And might be hard to work with if changes occur often in your code.
You can also use something like Lerna to organize your codebase in to a multi package repository.
https://github.com/lerna/lerna

I believe the easiest way is to use yarn workspaces (https://classic.yarnpkg.com/blog/2017/08/02/introducing-workspaces/), for this you need to define in root package.json with dependencies, like this
"workspaces": [
"admin",
"mobile",
"common"
]
And then you can use yarn install, and it's should work out of the box.
Before you would try it please, unlink common, to ensure that it works as it should.
Also, you need to have dependencies in admin and mobile on common package.

I'm using yarn link and it works perfectly. The difference could be, that I'm fully building (with rollup) the ts library (common) code.
Tips/tricks:
have "declaration": true in the common package's tsconfig.json
sometimes if the new stuff is not picked up, use Restart TS server in VSCode
have some yarn start build watch running in both packages (for tsc-watch or any other build), this way all changes will be picked up immediately
it could be worth checking ls -ald node_modules/<mypkg> if it really is a symlink, because any other npm install could remove it (yarn seems to be better in this)

Related

Imported class in angular is undefined

I have an npm project with the following structure:
app-dep
├── dist
│ ├── bundle.js
│ └── bundle.js.map
├── node_modules/
├── package.json
├── package-lock.json
├── src
│ ├── base-component.ts
│ ├── factory.ts
│ ├── app.ts
│ └── modules/
├── tsconfig.json
└── webpack.config.js
My app.ts code has an App class that utilizes all .ts files inside src/
export default class App extends HTMLElement { //some-content }
I use webpack to build a bundle.js inside the dist/ folder.
I have another angular project where I install this app-dep project using
npm install --save ../app-dep
When I try to use it in my angular component:
import App from 'app-renderer/dist/bundle';
ngOnInit() {
window.customElements.define('micro-app', App);
}
I get this error:
Failed to execute 'define' on 'CustomElementRegistry': parameter 2 is not of type 'Function'
When I try to log it on console, all I can see is undefined.
It seems that I can't import basic functions even.
Can you tell me what's wrong?
I think the problem has to do with the import or the pack, I ellaborate below with the steps to follow.
Also, make sure that you are exporting all the classes that you want to make available from the outside.
For TypeScript libraries
You can simply use "tsc" and then pack the generated code inside the dist folder with "npm pack" and install the dependency in your application with "npm install ".
This may get complicated due to the different module systems and bundlers, check this links for more info on Webpack:
https://marcobotto.com/blog/compiling-and-bundling-typescript-libraries-with-webpack/
https://webpack.js.org/guides/author-libraries/
For CSS libraries
The "npm pack" has to be executed in the root folder. You may want to process your styles with sass before and only pack the result.
For Angular libraries
By default with Angular CLI when you build a library project the code is generated in /dist/mylibrary folder.
If you want to use that code in other project, the steps are:
Build your library with "ng build mylibrary" (add --prod for production).
From your library, move into /dist/mylibrary folder and then execute a "npm pack", that will generate a tgz package.
From your application in which you want to use the library execute "npm install " to install the dependency.
To import code from the library use "import * from 'mylibrary'"
Other option would be using "npm link", that creates a link between your node_nodules and the library code as explained here:
https://www.willtaylor.blog/complete-guide-to-angular-libraries/
That would be the way to go with local libraries, usually these libraries are published into Npm repository (public or private) and installed remotely with "npm install ". These steps are only for local usage.

Manage global aliases for multiple npm packages

I have 3 npm projects, all exists under the same root folder. The projects are more complex than the example below (using webpack, JS frameworks, etc.) but to keep it simple for illustration purposes this is the structure:
root
├── root_index.js
├── package.json
├── project_1
│   ├── index.js
│   └── package.json
├── project_2
│   ├── index.js
│   └── package.json
└── project_3
   ├── index.js
   └── package.json
My problem is I find lots of situations in the code where imports, for example from project_1 to project_2 contains lots of '..', for example - '../../../../project_2/some_module'.
Would be nice if I could define a global variable '#project_2' that will point to the project and then I could change my imports to '#project_2/some_module'.
I saw webpack offers some solutions for aliases but I don't use webpack in my root folder, only in each project individually and so I cant use its configuration to control all projects from a single point. I also found this 'module-alias' package but it didn't work properly together with the configurations (mostly webpack) I have in each of the projects.
any idea how can I define these global variables so I could use them in each of the projects?
It sounds like you want to set up your project as a mono-repo.
I would recommend you to use the yarn workspaces concept and Lerna to help you manage it.
What is mono-repo?
In short, a so-called Mono-Repo is a (git) repository that houses multiple projects. Such projects are called workspaces or packages.
Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.
checkout official website: https://lerna.js.org/
Attaching a good article to help you start with yarn workspaces and lerna:
https://medium.com/hy-vee-engineering/creating-a-monorepo-with-lerna-yarn-workspaces-cf163908965d

How to exclude js file from src directory in webpack config file?

I'm writing react application with given structure (simplified):
src/
├── containers/
│ ├── ...
│ ├── ...
│ ├── ...
│ └── ...
├── SourceResolver/
│ └── SourceResolver.js
│
└── App.js
SourceResolver.js is a class which contains one method. This method is used in files which are localized into containers folder. I simply create object of this class and then call defined method:
new SourceResolver().getSource();
I don't want to minify this file (or even directory). I want to leave this file like it is in production build (as separte file). Leter if someone would like to change this method it will be possible even in production build.
How can achieve this? I tried to exlude this file/directory in webpack file, but with no success. Is it even possible?
Here is my webpack file https://pastebin.com/xS6QkKzb
Here is my changed webpack file, I tried to add exclude everyhere because I don't know why it doesn't work. https://pastebin.com/6brcNRq3

Failed to get mock metadata (babel 7 upgrade)

I am mocking out a git submodule (which is essentially just a javascript library we use at work), and all my mocks started returning:
Failed to get mock metadata
This happened after we upgraded the git submodule project to babel 7 (from 6). If I go into the submodule and revert the changes back to babel 6 the tests return to passing.
I mock out the module like so:
// path is an alias defined in webpack config
import { MyModule } from 'my-module';
jest.mock('my-module');
MyModule.someAttr.mockResolvedValue({data});
Any ideas why upgrading to babel 7 introduced these errors, everything else (including the submodule) works fine, just the tests on our UI which utilizes the submodule don't pass
The error is related to the way Jest resolves manual mocks. It may be that the import statement is resolved by Webpack with the alias resolver, but the jest.mock() function would not have that alias resolver into consideration. Manual mocks are resolved in the __mocks__/ folders relative to the
Because your mock module is part of a git submodule. You could either set up your submodule directory to be placed in the root of your project inside a __mocks__/ folder or either have the exported module of your submodule dependency inside a __mocks__/ folder.
.
├── config
├── __mocks__
│ └── mock-module.js
├── submodule
│ ├── __mocks__
│ │ └── mock-submodule.js
│ └── index.js
├── node_modules
└── src
Removing jest.mock('my-module'); from my code worked. I've faced this error for vue3-lottie package.

Compile files to original location with Babel

I have a folder structure like the following
src
├── a
│   └── test-a.js
└── b
└── test-b.js
I want to use babel to compile the files from ./src. So If I run the following command, I can get it done.
./node_modules/.bin/babel ./src/ -d ./dist/
This will create compiled files (preserving the tree) into ./dist directory. However, I need to compile files and keep in the same directory.
For example, The tree then should look like this
src
├── a
│   └── test-a.js
│   └── test-a.dist.js
└── b
└── test-b.js
└── test-b.dist.js
Is there a way to do that?
I don't know if there's a built-in way to do it with the CLI (I kind of doubt it), but you can script it with the API. Do something like glob in src/**/*.js, loop over the pathnames calling require("babel-core").transform() on each, then do a replace on the pathname like replace(/\.js$/, ".dist.js") and write to the new pathname. There's probably also a way to shell script it to transform to dist/ with the CLI like you're doing now then rename & move those files into src/.

Categories