I am trying to tackle that I am currently have with my JavaScript project structure. I am writing ES6 syntax with webpack. My current directory structure looks like this
project-dir
|_ packages.json
|_ webpack.config.js
|_ html
| |_ ***
| |_ ***
|_ js
|_ app.js
|_ routes
|_ abc-component
| |_ components
| | |_ abc1.js
| | |_ abc2.js
| |_ index.js
|
|_ xyz-component
| |_ components
| | |_ xyz1.js
| | |_ xyz2.js
| |_ index.js
|_ reducers.js
This is simpler structure. But the problem is that the "reducers.js" and the "abc1.js/xyz1.js" need access to the same functionality. So, if that functionality lives in "reducers.js" then the "abc1.js/xyz1.js" would have to import it as "../../reducers.js". If that functionality lives split up in "abc1.js" and "xyz1.js" etc, then "reducers.js" will have to import each one of them as ".\abc-component\components\abc1.js" and ".\xyz-component\component\xyz1.js". The first way, it feels like i am reaching way up and the second way, it feels like i am reaching way up. The code is also constrained to the structure of the file layout. This directory structure could go deeper down and we will end up with very ugly imports.
So, it makes sense for me to pull out this functionality that is shared by reducers.js and "abc1/js/xyz1.js" into a different module. In doing that i thought about putting that in a another git repo, but this functionality is very project specific and didn't want the hassle of putting it in another repo.
I tried creating a "lib" folder under the "js" folder and put the common functionality it in that folder along with a packages.json file. That way, i thought i would just add it as a "dependencies" in the project's package.json file with the "file:\lib\common" value. This way, it will reference that module locally. But this landed me in a load of trouble. The problems i am encountering are 1) I couldn't write ES6 in this common module since webpack is not processing it 2) Everytime I change something in the common.js, i have to run "npm install" 3) npm seems to be caching an old version of the common module ever after i change it and run "npm install". Not sure where it comes from, i did clean the npm cache by running "npm cache clean" 4) Even when i just write old javascript in this common module and not ES6, webpack watch does not pick up on changes to this file and re-render the app.
I was wondering if i could get some thoughts on how best to go about solving this problem. Also, thoughts about how i tried and exceptionally failed in solving the problem with a local module would be very helpful.
One way to deal with this is to put your common code into a node module and just import it like you would any other library.
This would work similarly to the "lib" idea you suggested but without needing relative imports.
Related
When creating javascript-based React projects, installing a Service Worker is just a matter of changing serviceWorker.unregister() to serviceWorker.register() on index.jsx.
With such project structure:
- src
|- index.jsx
|- serviceWorker.js
When this code gets built, the /build folder will look as followed:
- build
|- static
| |- css
| |- js
| |- media
|
|- index.html
|- service-worker.js
This will work fine and will result in the Service Worker being registered properly.
On the other hand, when setting up a project in Typescript, given the same project structure (where the js / jsx files are ts / tsx instead), the /build will look something similar:
- build
|- static
| |- js
| |- bundled-js.chunk.js
| |- bundled-js2.chunk.js
| |- ..
|
|- index.html
So it seems Typescript builds the serviceWorker bundling it with all the other js files.
This will then result in the Service Worker not being registered, with the following error in console:
Error during service worker registration: DOMException: Failed to register a ServiceWorker for scope ('https://example.com/') with script ('https://example.com/service-worker.js'): The script has an unsupported MIME type ('text/html').
The live site with the error can be seen here; The open source code can be found here.
Any idea of what I may be doing wrong?
Any tip is greatly appreciated, thank you in advance!
Ok, looking at your code your using Create React App,. This uses webpack, and it's that what is bundling, it's nothing to do with Typescript, the same would happen for Javascript when it gets bundled.
The problem is overriding the webpack config's is not easy, there is CRACO -> https://github.com/gsoft-inc/craco/blob/master/packages/craco/README.md#webpack-api
But even then it's a little tricky!! You need to tell webpack not to bundle service-worker.js, Also I would take the register bit out of the server-worker, as that can be bundled and wants to be included in your bundle.
The simplest option might be just to compile the webworker separately with Typescript directly. And again, keep the register bit separate inside your normal build.
I really like the folder structure as can be seen here when dealing with a React frontend and a some backend with express:
root
├── backend
| ├── node_modules
| ├── public
| ├── src
│ │ └── Server.ts
| ├── package.json
| └── tsconfig.json
├── frontend (created using create-react-app)
| ├── node_modules
| ├── public
| ├── src
│ │ └── Index.js
| ├── package.json
| └── tsconfig.json
I think that having separate packages with individual node_modules is reasonable since the frontend and backend are basically completely different things, e. g. they need different node modules. Also, this modular approach is visually appealing to me and the repository looks tidy.
However, I encounter a problem with this structure when I need to share content between the frontend and the backend. I added a shared folder under the root-of-project which contains its own project with its own tsconfig.json, package.json and so on. This approach is a mix of the approaches here and here. For the backend, this works totally fine: having set up the tsconfig.json appropriately (using TypeScript Project References and aliased imports), I can reference the file root/shared/src/myFile.ts like this:
import { myFunction } from #shared/myFile;
I created the React frontend using create-react-app. It's ok for me that alias imports don't work, so I would have to use (inside the src folder in frontend):
import { myFunction } from '../../shared/src/myFile';
Sadly, these imports from outside the src directory are not supported by create-react-app and I don't want to use eject since I have no experience with webpack and don't want to maintain all the configuration files on my own (that's why I used create-react-app in the first place).
I know I can move the shared content to the frontend's src directory. But this would mean, I had to add the tags needed for using Project References in TypeScript, e. g. setting composite to true, in the frontend's tsconfig.json which seems odd to me and feels more like a hack. I'd like to have a separate npm project with my shared content.
Since create-react-app does not inherently support imports from outside the src directory, I thought that maybe I'm getting the big picture wrong. Isn't the folder structure I use right now a valid way of how to setup a React project with a backend? What mechanism does create-react-app provide to link files between the frontend and the backend? I could also think of having a root project with a src folder and inside of that the two folders backend and frontend. But this means, that we'd have one shared node_modules folder in root.
It's my first project with React and I'd love to get to know some best practicese for this kind of architectural problem. Some links to trustful resources where project structures for full-stack React development are explained would be really helpful. Thank you 😊
It's perfectly reasonable to want to share code between your front and back end. It's one of the reasons to code in javascript instead of Ruby or PHP.
You can accomplish exactly what you want by using yarn instead of npm and yarn workspaces: https://yarnpkg.com/lang/en/docs/workspaces/. At the top level you set up three modules/packages in your package.json (make sure you name the workspaces correctly in their respective package.json files):
"workspaces": {
"packages": [
"backend",
"frontend",
"shared"
]
},
Once you do, you can import shared code in your CRA app or your back end simply like this:
import { myFunction } from 'shared/src/myFile';
The drawback is that you can't import react components from the shared directory into frontend as long as you are using CRA. This won't affect you now since you only have one react app. Should you need to share react components among multiple projects, look into some on the suggestions above like bit.dev.
ADDENDUM!!! It's now possible to use CRA and yarn workspaces to share React code, if you replace CRA with CRACO. All you do is create another workspace with the shared react code. Then create a symbolic link in each module where you want to access it:
root
├── fontend-one
| ├── symbolic link to frontend-shared
├── frontend-two
| ├── symbolic link to frontend-shared
├── frontend-shared
Each of the CRA front end modules modules also requires a craco.config.js file where you tell web-pack not to follow symbolic links:
module.exports = {
// ...
webpack: {
configure: {
resolve: {
symlinks: false
}
}
}
};
You import from shared-frontend normally:
import { myFunction } from 'shared-frontend/src/myFile';
It's a pretty lo-fi solution but has proven robust for the year we've been using it.
Architecture is a tricky one, everyone has a different opinion and every option has pro and cons.
Personally I believe its best to separate the backend and frontend into sperate projects and keep them that way. Now as JavaScript/React/Node encourage component-based approaches a really nice way of sharing code between them is Bit.dev.
https://bit.dev
I am currently using it to share components and functions between three web apps and a few Node microservices.
A good structure for React app can be found here, this one works well and scales nicely:
https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-4dab943092af
As for Express, there are so many ways to structure the project but personally recommend a folder for your Routes, a folder for your Controllers, this is where the logic for Routes live. Then go from there. Check this link out:
https://www.freecodecamp.org/news/how-to-write-a-production-ready-node-and-express-app-f214f0b17d8c/
Depending on what your building you may not even need a full backend you can check out the JAMStack here for more info:
https://jamstack.org
I would consider separating them though as the project scales it makes it much easier to manage. You can release your front end on something like Netlify and then use something like AWS or Azure to host your Node/Express server.
Having separate sub-projects for frontend and backend makes perfect sense due to vastly different dependencies. Mixing it up increases disk space consumption in production deployments and goes against security guidelines. Your folder structure is reasonable (except for public/ subfolders I'm unsure about, maybe I'm missing something).
The approach import { myFunction } from #shared/myFile; is fine. Just don't use CRA.
For a project with a single frontend and a single backend there is no need for a shared\ top-level folder because the frontend is the only source of 'UI truth' (e.g. the source of types and components related to UI) that is consumed by the frontend and the backend is the only source of 'API truth' consumed by both frontend and backend. With this arrangement only #backend/api_shared_stuff needs to be shared.
Some links to trustful resources where project structures for full-stack React development are explained would be really helpful. On the one hand, usually project authors have to explain plenty of other things and on the other hand keep the documentation (typically a README) reasonably concise. You may find that providing a justification/explanation why the subdirectory structure is this and not that was not the top priority.
I am working on an Angular 2 application in ASP.NET Core and I am trying to import oidc-client (https://github.com/IdentityModel/oidc-client-js) into one of my Typescript files. The oidc-client files were installed via bower which placed them in the expected bower_components folder. The oidc-client.js file is copied to the lib folder under wwwroot via a gulp task on build.
My directory structure looks like this (only relevent portions represented):
wwwroot
|
--app
| |
| --shared
| |
| --auth
| |
| --token.service.ts
--lib
| |
| --oidc-client
| |
| --oidc-client.js
|
--bower_components
| |
| --oidc-client
| |
| --dist
| | |
| | --oidc-client.js
| --oidc-client.d.ts
|
--typings
|
--index.d.ts
I need to use the oidc-client library in my token.service.ts file so I am doing this:
import { Oidc } from './lib/oidc-client/oidc-client';
But this results in an error 'Cannot find module './lib/oidc-client/oidc-client'.
I've tried different variations on the path, relative and non-relative, and I can't seem to figure out how to import the oid-client library.
Maybe its the typings not working? Do I need to do anything special with the d.ts file to make it work?
Thanks for any guidance.
As mentioned in a comment made above, using the NPM distribution of oidc-client would be the preferred option.
However, the typings that are included in the oidc-client are a little strange. Usually, the typings .d.ts file sits side-by-side with the .js file. With oidc-client, they are in different directories and that's what's causing problems with your import statement.
If you install the NPM module and add node_modules/oidc-client/oidc-client.d.ts to the files configuration of your tsconfig.json:
{
"files": [
"index.ts",
"node_modules/oidc-client/oidc-client.d.ts"
]
}
You should be able to create an index.ts file that imports oidc-client:
import { OidcClient } from "oidc-client";
If the .d.ts file and the .js file had been side-by-side, your import would have worked, but they are not, so it doesn't.
If you do not want to use the NPM distribution, it is likely that the separation of the .d.ts and .js files is what's causing the problem with the Bower distribution, too.
I've started to play around with using NPM Scripts and BabelJS to transpile my ES2015 AngularJS project. The problem is that the concatenated order is not correct and causes nomod errors.
Given this directory structure:
MyApp
+- src
| +- dashboard
| | +- search-bar
| | | +- search-bar.directive.js
| | | +- search-bar.service.js
| | | +- search-bar.spec.js
| | +- dashboard.module.js
+- dist
| +- js
| | +- dashboard.js
Ideally, dashboard.module.js should be the first file because that is where the actual dashboard module is created, followed by everything in /search-bar which actually doesn't require any order at all. The search bar is a feature/component, not a new module and is all part of dashboard.
The problem seems to be that simply running BabelJS will just concatenate all the files together using the same structure as the filesystem does.
babel ./src/dashboard/**/*.js -o ./dist/js/dashboard.js
There are also weird things when messing with the wildcards to try make sure all files are captured.
How can I process dashboard.js first before any of the other components?
It turns out you can specify many globs as input files.
Try using this command:
babel ./src/dashboard/*.js ./src/dashboard/**/ -o ./dist/js/dashboard.js
This will first process any .js immediately inside of /dashboard followed by the rest of the directory.
Gotcha: This will only really work as long as you only have the one file inside of the /dashboard root. If you were to add dashboard.config.js or any other file, then this would process those files in that filesystem order. Unfortunately, dashboard.config.js comes before dashboard.js so your problem will continue. :(
Also, if you have other files in /dashboard root or you have other kinds of .js that you don't want to be included, you'll need to ignore them.
Try:
babel ./src/dashboard/**/*.js ./src/dashboard/**/ --ignore *.spec.js --ignore *.conf.js -o ./dist/js/dashboard.js
This will put things in the right order and also skip any karma.config.js or search-bar.service.spec.js files you may put in there. I assume that you don't want those actually being used in production.
We have a large scale javascript application that we are trying to concatenate and minify using grunt-contrib-requirejs. We use the Aura framework. We use bower to pull in dependencies from other repositories into our main application.
Here is our app structure
- app/
|
|_ main.js
|_ index.html
|_ css
|_ bower_components/
|_ core/
|_ widget1/
|_ main.js
|_ views/
|_ models/
|_ widget2/
|_ extension1/
|_ extension2/
|_ other third party libraries, etc
- Gruntfile.js
- bower.json
main.js:
requirejs.config({
shim: {
...
},
paths: {
'core': 'bower_components/core/app/main',
'aura': 'bower_components/aura/dist/',
'jquery': 'bower_components/jquery/jquery',
'backbone': ...,
... lots of other paths
}
});
define(['core', 'jquery', 'jqueryui'], function(core, $) {
// Here we start Aura, which finds our extensions and widgets in
// bower_components
});
Our current requirejs task config:
requirejs: {
compile: {
options: {
mainConfigFile: 'app/main.js',
name: 'main',
include: ['bower_components/widget1/main', 'bower_components/widget2/main',
'bower_components/extension1/main', 'bower_components/extension2/main'],
exclude: ['jquery'],
insertRequire: ['main'],
out: 'dist/app.js'
}
}
}
This concats our app/main and its dependencies, but when we try to run it, we get errors like:
GET http://www.my-machine:8080/bower_components/underscore/underscore.js 404 (Not Found)
even though underscore is a dependency of many of the widgets we include.
We have extensively tried different options in the r.js examples, and read through many stackover flow issues trying to find an answer.
We want advice on how to build this into one minified file with this structure:
UPDATE #2: Correct file structure
- dist/
|_ index.html // copied using grunt copy
|_ css // copied using grunt copy
|_ app.js // Built with grunt requirejs
UPDATE
We have included underscore in our shim which fixed the above error, but we're still getting another error:
Failed to load resource: the server responded with a status of 404 (Not Found)
http://my-machine.com:8080/bower_components/core/app/main.js
This is included in the minimized file so I don't understand why it can't find that file:
define("bower_components/core/app/main.js", ["aura/aura", "bootstrap"], function(){
...
});
UPDATE #3
The define above came from the file generated by the optimization tool! The original define for that module, core/app/main.js, looks like:
define(['aura', 'bootstrap'], function(Aura) {
...
});
You mention having this in your optimized file:
define("bower_components/core/app/main.js", ["aura/aura", "bootstrap"], function(){
...
});
But this does not make sense. If I reproduce a structure similar to what you show in your question and run an optimization on it then the file located at bower_components/core/app/main.js is optimized as:
define("core", ...
because it is known as core to the rest of your application due to the paths setting 'core': 'bower_components/core/app/main'.
I can reproduce the incorrect module name if I add 'bower_components/core/app/main.js' to the include setting in the build configuration. When I add this name to the include list, I also get the loading error you mentioned. Make sure that your include list does not contain this name, and make sure that there is nothing anywhere which lists 'bower_components/core/app/main.js' as a dependency. This name should not appear in any define or require call.
A couple of considerations first:
First of all, you should avoid defining module with a name. The name is to be generated by the optimization tool normally. Please read here: http://requirejs.org/docs/api.html#modulename
Second, you should understand what the baseUrl is and when it's used to located resources. The baseUrl seems to be "app/" in your case although you have not defined it explicitly. You should read this also:
http://requirejs.org/docs/api.html#config-baseUrl
So if you use the ".js" extension, then the baseUrl won't be taken into account.
Now what you should try:
To start with, remove the first argument "bower_components/core/app/main.js" from define function. Then you should refer to this module by auto-generated module id that will be a path relative to the baseUrl without ".js" extension.