How to create cyclic dependencies between two NodeJS projects - javascript

I have a 2 projects which requires each others.
The root-app is run by node. It imports child-app/index.js.
The child-app has a dependency on root-app as well. It needs to import Component.js from it. child-app is never run independently of root-app, it is always imported by the root-app.
root-app:
/Component.js
/index.js
/package.json
child-app:
/index.js
/package.json
So basically the root-app requires the child-app and the child-app requires a component from the root-app.
root-app/index.js:
import ChildApp from 'child-app';
child-app/index.js
import Component from 'root-app/Component';
At the moment, my package.json files look like this:
root-app/package.json
{
"name": "root-app",
"dependencies": {
"child-app": "file:../child-app"
}
}
child-app/package.json
{
"name": "child-app",
"peerDependencies": {
"root-app": "file:../root-app"
}
}
This of course doesn't work and I get an error:
Unable to resolve module 'root-app' from
'root-app/node_modules/child-app/index.js'
So my question, is there any way to have this kind of dependencies working without the need of creating a third project that will work as a "bridge" between those two projects?
Thanks!

You have "name":"root-app" in child-app/package.json. But still, it's better to avoid circular dependencies between packages. You might take a look at the dependency inversion principle to get an idea how to do so.

Related

NextJS module aliases double declaration needed for import of index

I am setting up my module aliases in a NextJS project and everything runs fine so far.
The issue I have comes when I have a folder utils that includes both, an index.ts but also another someModule.ts.
My imports looked like this before:
import { someHelper } from '../utils'
import { anotherHelper } from '../utils/someModule'
Assuming I am setting the following module alias "#/utils/*": ["utils/*"], I would need to amend the first of the two imports like this:
import { someHelper } from '#/utils/index' // <-- see how I need to add index
unless I am extending the tsconfig with a second module alias like this: "#/utils": ["utils"].
This feels a bit like duplication to me, so I am wondering whether this is needed or do you have another option how to solve this?

Ways to deal with es6 module paths and dependencies

What are some ways to deal with es6 modules and dependencies?
In particular I have 1 library i'd like to split into 3 modules. Let's call the library "salad" and uses "utensils" and "ingredients". Right now all 3 parts are part of the same library but I'd like to separate them. "utensils" is useful on its own. "ingredients" should be separate if only because it only includes a few common ingredients. It's perfectly valid to use the "salad" library with your own ingredients.
So, in actually trying to do it I run into issues. Utensils is easy, it has no dependencies
// utensils.js
export { fork, knive, spoon, }
salad is also easy, it has no dependences
// salad.js
export { chop, toss, dice }
but ingredients is where the issues show up
// ingredients.js
import { knive } from './utensils.js'; // what path here?
import { chop, dice } from './salad.js'; // what path here?
export { choppedLettuce, dicedTomates } // they use 'chop' and 'dice'
If everything is local and in my own project this is not so difficult but I want all 3 projects to be publically available and easily usable.
Use cases include
using with rollup/webpack
using live in the browser no build
using via npm with rollup/webpack
using in node no build
If I use npm based paths (assuming the packages are named 'utentils' and 'salad'
import { knive } from 'utensils';
import { chop, dice } from 'salad';
Then ingredients.js is no longer usable without npm and pacakge.json. No more live import in a browser
If I use local paths
import { knive } from './utensils.js';
import { chop, dice } from './salad.js';
It only works if I put all the files in the same folder which kind of defeats the purpose of separating them.
In other languages I'd probably end up adding include paths or some other way for ingredients.js be able to reference paths that are resolved later. I think webpack has some #symbol solution but that's webpack specific.
One other solution which seemed less than desirable was to have a setup function in ingredients.js to pass in the dependencies
// ingredients.js
export { setup, choppedLettuce, dicedTomates }
which would then be used like
import { knive } from './utensils.js';
import { chop, dice } from './salad.js';
import { setup, choppedLettuce, dicedTomates } from 'ingredients.js';
// pass in the deps so no paths needs to be ingredients.js
setup({knive, chop, dice});
That seems like a non-scalable solution.
It could also be that every function in ingredients takes needs to be passed salad and utensils. That seems yuck.
Is there a solution? Do I just need to have multiple versions of the libraries, some for npm based inclusion, others for live inclusion? Am I overlooking some obvious solution?

Shared utils functions for testing with Jest

I have some utils functions that I'm using among various Jest tests, for example a function like this, for mocking a fetch response:
export const mockFetchJsonResponse = (data) => {
ok: () => true,
json: () => data
};
I would like to share those functions in a way that I can import them and reuse among my tests. For example:
// Some .spec.jsx file
// ...
import {mockFetchJsonResponse} from 'some/path/to/shared/tests/utils.jsx'
// Then I can use mockFetchJsonResponse inside this test
// ...
Where should I place such common utils functions?
My project folder looks like this:
components/
CompOne/
__tests__
index.jsx
CompTwo/
__tests__
...
utils/
__tests__
http.js
user.js
...
Should I place them inside the utils folder together with other utils functions that I use for my project? Then should I write unit tests also for these functions?
There is an ability to expose helpers as global functions without any need to import modules explicitly.
Jest allows to configure some files will be run before every test file executed through setupFiles configuration option
Also Jest provides global object that you can modify and everything you put there will be available in your tests.
Example
package.json:
"jest": {
"setupFiles": ["helpers.js"]
}
helpers.js:
global.mockFetchJsonResponse = (data) => {
ok: () => true,
json: () => data
};
somecomponent.test.js:
mockFetchJsonResponse(); // look mom, I can call this like say expect()!
With TypeScript
TypeScript will complain with cannot find name 'mockFetchJsonResponse'. You can fix that by adding a declaration file:
helpers.d.ts:
declare function mockFetchJsonResponse(data: any): any;
Create a new tsconfig.test.json file and add that file to the files section and extend your main tsconfig:
{
"extends": "./tsconfig.json",
"files": ["./.jest/helpers.d.ts"]
}
In your jest.config.js file, add a new global setup for ts-jest to have jest use your new tsconfig file:
// ...
globals: {
"ts-jest": {
tsconfig: "tsconfig.test.json"
}
}
// ...
Sure it does not answer you direct question "where to put the files" but it's anyway up to you. You just need specify those files in setupFiles section. Since there is no import needed in tests it does not really matter.
As for testing test helpers I'm not sure. See it's part of testing infrastructure like spec file itself. And we don't write tests for tests or it would never stop. Sure, it's up to you - say if logic behind is really-really complex and hard to follow. But if helper provides too complex/complicated logic it would lead to tests themselves be impossible to understand, do you agree?
kudos to that article on testing compoentns with intl. Have never dealt with globals in jest before.
TL;DR; create a /__utils__/ and update testPathIgnorePatterns
Full answer:
Here's just a suggestion:
testPathIgnorePatterns: ['/__fixtures__/', '/__utils__/'],
I use /__tests__/ for the tests and within it sometimes I need to add a folder with data that will be used by those tests, so I use /__fixtures__/ folder.
Likewise, when I have a shared logic across tests, I place them at /__utils__/ folder (also within /__tests__/)
For more details, please read more about testPathIgnorePatterns
Another approach is by having a test directory and moving helpers on it.
src/
components/
utils/
...
test/
testHelpers.js
Then on the test:
// src/components/MyComponent.spec.js
import { helperFn } from '../../test/testHelpers';
Benefits:
Be explicit of where the function is coming from
Separate helpers that need to be tested from those that do not ¹
Drawbacks:
The test directory might look silly by containing just a helper file
AFAIK this approach is no where specified on official documentation
Looks like GitLab is implementing this approach on their RoR project.
¹ no matter which approach you take, please don't test the test helpers. If the helper fails then your test must fail too. Otherwise your helper is not helping at all.

How to Embed StencilJS components inside Storybook React App?

I'm trying to integrate Stencil and Storybook inside the same project. I've been following this set up guide and this one however one of the steps is to publish the library of components to NPM and that's not what I want.
I have this repo which I've configured with components library (src folder) and with the reviewer of those components with Storybook, which resides in the storybook folder.
The problem is that when I compile the components using Stencil and copy the dist folder inside the Storybook app and import the component nothing renders. Tweaking the configuration using custom head tags I was able to import it correctly however no styles where applied.
When I open the network panel there is some error when importing the component:
And thus the component is present in the DOM but with visibility set to hidden, which I think it does when there is an error.
This is the component au-button:
import { Component } from '#stencil/core';
#Component({
tag: 'au-button',
styleUrl: 'button.css',
shadow: true
})
export class Button {
render() {
return (
<button class="test">Hello</button>
);
}
}
Here is the story my component:
import React from 'react';
import { storiesOf } from '#storybook/react';
import '../components/components.js'
storiesOf('Button', module)
.add('with text', () => <au-button></au-button>)
These are the scripts inside the Storybook app:
"scripts": {
"storybook": "start-storybook -p 9009",
"build-storybook": "build-storybook",
"copy": "cp -R ./../dist/* components"
},
And the workflow is as follows:
Launch storybook
Make changes in the component
Execute build command
Execute copy command
Also, I would like to automate the developer experience, but after I solve this problem first.
Any ideas of what I could be doing wrong?
Sample for this could be found in the repo
https://github.com/shanmugapriyaEK/stencil-storybook. It autogenerates stories with knobs and notes. Also it has custom theme in it. Hope it helps.
I'm using #storybook/polymer and it's working for me really well.
following your example:
import { Component } from '#stencil/core';
#Component({
tag: 'au-button',
styleUrl: 'button.css',
shadow: true
})
export class Button {
render() {
return (
<button class="test">Hello</button>
);
}
}
the story would be:
import { storiesOf } from '#storybook/polymer';
storiesOf('Button', module)
.add('with text', () => <au-button></au-button>)
the scripts in the package.json:
"scripts": {
"storybook": "start-storybook -p 9001 -c .storybook -s www"
},
the storybook config file:
import { configure, addDecorator } from '#storybook/polymer';
const req = require.context('../src', true, /\.stories\.js$/);
function loadStories() {
req.keys().forEach((filename) => req(filename))
}
configure(loadStories, module);
and storybook preview-head.html you have to add to the body the following:
<body>
<div id="root"></div>
<div id="error-message"></div>
<div id="error-stack"></div>
</body>
I've been following this set up guide and this one however one of the steps is to publish the library of components to NPM and that's not what I want.
My reading of those guides is that they're stating “publish to NPM” as a way to have your files at a known URL, that will work most easily for deployment.
Without doing that, you'll need to figure out a different deployment strategy. How will you get the build products – the dist directory and static files – published so that your HTML will be able to reference it at a known URL? By choosing to diverge from the guidelines, that's the problem you have to address manually instead.
Not an insurmountable problem, but there is no general solution for all. You've chosen (for your own reasons) to reject the solution offered by the how-to guides, which means you accept the mantle of “I know what I want” instead :-)

use Chart.js in Aurelia

I'd like to use chart.js in an aurelia project, but I'm getting errors. How do I add 3rd party node packages to an aurelia app?
I'm using aurelia-cli, BTW
Here's what I've done
npm install --save chart.js
In aurelia.json I added the following
"dependencies": [
...,
{
"name": "chart.js",
"path": "../node_modules/chart.js/dist",
"main": "Chart.min.js"
}
]
In app.html I then add the line
<require from="chart.js"></require>
But, I get the error:
vendor-bundle.js:1399 Unhandled rejection Error: Load timeout for modules: template-registry-entry!chart.html,text!chart.html
I've tried various things like injecting the Chart into the app.html
// DIDN'T WORK :-(
// app.js
import {inject} from 'aurelia-framework';
import {Chart} from 'chart.js';
export class App {
static inject() { return [Chart]};
constructor() {
this.message = 'Hello World!';
}
}
And, then, in app.html, I added the following require statement
<require from="Chart"></require>
HERE'S THE SOLUTION
You can checkout a working example here. Initially, I thought you had to use the aurelia-chart module, however, it's very difficult to use, and so, I'd recommend you just use Chart.JS package instead. Here's how to incorporate the chart.js module into your Aurelia app:
npm install --save chart.js
In aurelia.json add the following line to the prepend section
"prepend": [
...,
"node_modules/chart.js/dist/Chart.min.js"
],
In the app.js file (or any other model-view file), add the line
import {Chart} from 'node_modules/chart.js/dist/Chart.js';
For, example, if you wanted to display a chart on the home page:
// app.js
import {Chart} from 'node_modules/chart.js/dist/Chart.js';
export class App {
...
}
And that's it!
1. Problem with require
First of all, don't use <require from="Chart"></require> in your app.html project. That is the source of your error message, since it's trying to load an Aurelia module and chart.js is not an Aurelia module (view/viewmodel) in your source code.
2. Alternate import syntax
Skip the inject lines in app.js, but try one of the following (try them one at a time) in either app.js or in each module you'll be using Chart. One of these imports is likely to work.
import { Chart } from 'chart.js';
import * from 'chart.js';
import 'chart.js';
3. Legacy prepend
If none of the above works, import it as a legacy repo using the prepend section of aurelia.json (before the dependencies section) like this:
"prepend": [
// probably a couple other things already listed here...
"node_modules/chart.js/dist/Chart.min.js"
],
Update for Aurelia-Chart: (added for any later viewers)
Since you ended up going with aurelia-chart (by grofit), here's the dependency code for aurelia.json:
"dependencies": [
...,
{
"name": "chart.js",
"path": "../node_modules/chart.js/dist",
"main": "Chart.min.js"
},
{
"name": "aurelia-chart",
"path": "../node_modules/aurelia-chart/dist/amd",
"main": "index",
"deps": ["chart.js"]
}
]
I just got this working with an aurelia cli project and it required some extra modifications.
I used au install chart.js but there is an open issue that states it is not intelligent enough yet to add references to package dependencies.
To make things work I added the following to my aurelia.json dependencies:
"moment",
"chartjs-color",
"chartjs-color-string",
{
"name": "chart.js",
"main": "src/chart.js",
"path": "../node_modules/chart.js",
"deps": ["chartjs-color", "moment"]
},
{
"name": "color-convert",
"path": "../node_modules/color-convert",
"main": "index"
},
{
"name": "color-name",
"path": "../node_modules/color-name",
"main": "index"
}
I was then able to import { Chart } from 'chart.js'; in my view model and run the chart.js quick start example from the attached viewmodel lifecycle method.
In the chart.js docs they mention including the minified version can cause issues if your project already depends on the moment library.
The bundled version includes Moment.js built into the same file. This version should be used if you wish to use time axes and want a single file to include. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues.
This solution may help if you are in that position.

Categories